From 72c02729350943ea76929964a0cb7a323d09ca08 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Wed, 10 May 2023 16:16:54 +0500 Subject: [PATCH 001/179] WIP --- src/common/app.tsx | 2 + src/common/components/chat-box/index.scss | 354 +++++++++++++++ src/common/components/chat-box/index.tsx | 438 +++++++++++++++++++ src/common/components/search-user/index.scss | 13 + src/common/components/search-user/index.tsx | 51 +++ src/common/i18n/locales/en-US.json | 11 + src/common/img/svg.tsx | 88 ++++ 7 files changed, 957 insertions(+) create mode 100644 src/common/components/chat-box/index.scss create mode 100644 src/common/components/chat-box/index.tsx create mode 100644 src/common/components/search-user/index.scss create mode 100644 src/common/components/search-user/index.tsx diff --git a/src/common/app.tsx b/src/common/app.tsx index 59ac9581bab..367d9a17914 100644 --- a/src/common/app.tsx +++ b/src/common/app.tsx @@ -24,6 +24,7 @@ import { connect } from "react-redux"; import loadable from "@loadable/component"; import Announcement from "./components/announcement"; import FloatingFAQ from "./components/floating-faq"; +import ChatBox from "./components/chat-box"; import { useMappedStore } from "./store/use-mapped-store"; // Define lazy pages @@ -157,6 +158,7 @@ const App = (props: any) => { + ); }; diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss new file mode 100644 index 00000000000..fb6b1cd90f7 --- /dev/null +++ b/src/common/components/chat-box/index.scss @@ -0,0 +1,354 @@ +@import "src/style/colors"; +@import "src/style/variables"; +@import "src/style/bootstrap_vars"; +@import "src/style/mixins"; + +.chatbox-container { + position: fixed; + z-index: 99; + right: 15rem; + bottom: 0rem; + height: 53px; + width: 400px; + + border-top-right-radius: 16px; + border-top-left-radius: 16px; + + transition: height 0.5s ease; + + @include themify(day) { + box-shadow: rgb(101 119 134 / 20%) 0px 0px 15px, rgb(101 119 134 / 15%) 0px 0px 3px 1px; + background: $white; + } + + @include themify(night) { + background: $dark; + box-shadow: rgb(255 255 255 / 20%) 0px 0px 15px, rgb(255 255 255 / 15%) 0px 0px 3px 1px; + } + + &.expanded { + height: 530px; + } + + .chat-header { + border-bottom: 1px solid $white-four; + padding-left: 16px; + padding-right: 16px; + cursor: pointer; + height: 53px; + display: flex; + justify-content: space-between; + @include themify(day) { + border-bottom: 1px solid $white-four; + } + + @include themify(night) { + border-bottom: 1px solid $dark-indigo; + } + + .back-arrow-image { + margin: 1.1rem 1rem 0 0; + .back-arrow-svg { + border-radius: 50%; + padding: 8px 6px 10px 6px; + } + } + + .back-arrow-svg:hover { + background: rgba(29, 155, 240, 0.1); + } + .message-title { + width: 288px; + } + + .message-content { + margin-top: 1rem; + font-weight: 700; + line-height: 24px; + font-size: 20px; + font-family: $font-family-sans-serif; + + @include themify(day) { + color: $charcoal-grey; + } + + @include themify(night) { + color: $white-two; + } + } + + .actionable-imgs { + display: flex; + align-items: center; + justify-content: center; + margin-top: 13px; + .message-image { + margin: 0.4rem 0.3rem 0 0; + + .message-svg { + width: 40px; + height: 40px; + justify-content: center; + text-align: center; + border-radius: 50%; + padding-top: 13px; + display: flex; + + svg { + height: 20px; + width: 20px; + } + } + .message-svg:hover { + background: rgba(29, 155, 240, 0.1); + } + } + .arrow-image { + margin: 0.4rem 0.3rem 0 0; + .arrow-svg { + display: flex; + justify-content: center; + text-align: center; + padding-top: 7px; + width: 40px; + height: 40px; + border-radius: 50%; + } + + .arrow-svg:hover { + background: rgba(29, 155, 240, 0.1); + } + } + } + } + + .chat-body { + position: relative; + height: 470px; + overflow-y: auto; + + &.current-user { + height: 412px; + margin-bottom: 8px; + @include themify(day) { + border-bottom: 1px solid $white-three; + } + @include themify(night) { + border-bottom: 1px solid #172b44; + } + } + + .chat-content { + display: flex; + + .user-img { + padding: 18px 16px; + } + .user-title { + padding-top: 18px; + width: -webkit-fill-available; + cursor: pointer; + .username { + @include themify(day) { + color: $charcoal-grey; + } + + @include themify(night) { + color: $white-two; + } + + font-size: 16px; + + font-weight: 700; + margin-bottom: 0.2rem !important; + } + .last-message { + font-size: 15px; + margin-bottom: 0 !important; + } + } + } + + .chat-content:hover { + @include themify(day) { + background: $light-powder-blue; + } + @include themify(night) { + background: $gunmetal; + } + } + .user-profile { + border-bottom-right-radius: 20px; + border-bottom-left-radius: 20px; + @include themify(day) { + background: $white-three; + } + @include themify(night) { + background: #172b44; + } + + padding: 0 16px 16px 16px; + margin: 0 16px 16px 16px; + + .user-logo { + display: flex; + justify-content: center; + align-items: center; + padding-top: 16px; + } + .user-name { + font-size: 20px; + font-weight: 700; + @include themify(day) { + color: $charcoal-grey; + } + @include themify(night) { + color: $white; + } + } + .about { + text-align: center; + padding: 4px 0; + @include themify(day) { + color: $charcoal-grey; + } + @include themify(night) { + color: $white; + } + } + .joining-info { + justify-content: space-evenly; + } + .created-date { + padding-top: 8px; + font-size: 14px; + @include themify(day) { + color: $charcoal-grey; + } + @include themify(night) { + color: $white; + } + } + .followers { + font-size: 14px; + padding-top: 4px !important; + font-family: New Century Schoolbook; + } + } + + .user-profile:hover { + @include themify(day) { + background: $white-four; + } + @include themify(night) { + background: #0f223a; + } + } + + .scroller { + position: sticky; + margin-left: 83%; + width: 33px; + height: 33px; + box-shadow: $box-shadow; + background: $dark-sky-blue; + color: $white; + border-radius: 50%; + z-index: 10; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + svg { + width: 20px; + height: 20px; + } + } + + .chats { + .date-time-detail { + .date-time { + margin: 1rem 0; + text-align: center; + font-size: 13px; + } + } + .message { + display: flex; + .user-img { + padding: 18px 16px; + } + .user-info { + .receiver-message-content { + @include themify(day) { + background: #e4e6eb; + } + @include themify(night) { + background: #cee2ff; + + } + color: #050505; + max-width: 280px; + margin-top: 17px; + padding: 8px 12px 8px 12px; + border-radius: 18px 18px 18px 18px; + } + } + } + .sender { + display: flex; + margin-bottom: 0.17rem; + .sender-message { + border-radius: 18px 18px 18px 18px; + max-width: 280px; + margin-left: auto; + background-color: rgb(0, 132, 255); + display: flex; + justify-content: flex-end; + margin-right: 15px; + .sender-message-time { + color: $white; + margin-right: 30px; + font-size: 12px; + margin-top: 3px; + } + .sender-message-content { + margin-bottom: 0; + color: $white; + font-size: 16px; + font-weight: 400; + padding: 8px 12px 8px 12px; + } + } + } + } + } + .chat { + margin: 4px 4px; + padding: 4px; + + .chat-input-group { + width: 112%; + .chat-input { + height: 45px; + border-radius: 12px !important; + } + .msg-svg { + padding: 6px 8px 4px 5px; + position: inherit; + right: 41px; + border-radius: 50%; + margin: 4px 0; + margin-right: 11px; + z-index: 10; + &.active { + cursor: pointer; + } + &.active:hover { + background: rgba(29, 155, 240, 0.1); + border-radius: 50%; + } + } + } + } +} diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx new file mode 100644 index 00000000000..e083e80bad3 --- /dev/null +++ b/src/common/components/chat-box/index.tsx @@ -0,0 +1,438 @@ +import React, { useEffect, useState } from "react"; +import { Button, Form, FormControl, InputGroup } from "react-bootstrap"; +import { Link } from "react-router-dom"; + +import { Account } from "../../store/accounts/types"; +import { ActiveUser } from "../../store/active-user/types"; + +import Tooltip from "../tooltip"; +import UserAvatar from "../user-avatar"; +import SeachUser from "../search-user"; + +import { + addMessageSVG, + expandArrow, + collapseArrow, + arrowBackSvg, + messageSendSvg, + chevronUpSvg, + chevronDownSvgForSlider +} from "../../img/svg"; +import { dateToFormatted } from "../../helper/parse-date"; +import { _t } from "../../i18n"; +import { getAccountFull } from "../../api/hive"; + +import "./index.scss"; + +export interface profileData { + joiningData: string; + about: string | undefined; + followers: number | undefined; +} + +interface Props { + activeUser: ActiveUser | null; +} + +export default function ChatBox({ activeUser }: Props) { + const chatBodyDivRef = React.createRef(); + const [expanded, setExpanded] = useState(false); + const [currentUser, setCurrentUser] = useState(""); + const [isCurrentUser, setIsCurrentUser] = useState(false); + const [message, setMessage] = useState(""); + const [isMessageText, setIsMessageText] = useState(false); + const [currentUserAccount, setCurrentUserAccount] = useState(); + const [profileData, setProfileData] = useState(); + const [isScrollToTop, setIsScrollToTop] = useState(false); + const [isScrollToBottom, setIsScrollToBottom] = useState(false); + const [showSearchUser, setShowSearchUser] = useState(false); + + useEffect(() => { + if (currentUser) { + fetchAccountData(); + } + }, [currentUser]); + + const contentList = [ + { + username: "good-karma", + lastMessage: "Hy Hope so you are doing well" + }, + { + username: "ecency", + lastMessage: "Hy" + }, + { + username: "demo.com", + lastMessage: "Whats Up" + }, + { + username: "hive-189310", + lastMessage: "Hello" + }, + { + username: "hispapro", + lastMessage: "Hy Hope so you are doing well" + }, + { + username: "galenkp", + lastMessage: "How are you?" + }, + { + username: "deanliu", + lastMessage: "Bro" + }, + { + username: "demo123", + lastMessage: "What are you doing" + }, + { + username: "fastchrisuk", + lastMessage: "Hy Hope so you are doing well" + }, + { + username: "incublus", + lastMessage: "Hy Hope so you are doing well" + }, + { + username: "ipexito", + lastMessage: "Hello" + }, + { + username: "belemo", + lastMessage: "Hy Hope so you are doing well" + }, + { + username: "foodchunk", + lastMessage: "How are you?" + }, + { + username: "macro1997", + lastMessage: "Bro" + }, + { + username: "gelenkp", + lastMessage: "What are you doing" + }, + { + username: "der-prophet", + lastMessage: "Hy Hope so you are doing well" + } + ]; + + const messageList = [ + { + username: "demo.com", + time: "4.27 pm", + message: "Hy How are you.", + date: "4/9/2022" + }, + { + username: "mtsaeed", + time: "4.44 pm", + message: "I am fine", + date: "6/9/2022" + }, + { + username: "mtsaeed", + time: "4.44 pm", + message: "What's about you", + date: "6/9/2022" + }, + { + username: "demo.com", + time: "4.45 pm", + message: "Looks good", + date: "12/9/2022" + }, + { + username: "mtsaeed", + time: "4.46 pm", + message: "Thanks for asking", + date: "12/9/2022" + }, + { + username: "demo.com", + time: "4.48 pm", + message: "What are you doing Nowadays", + date: "14/9/2022" + }, + { + username: "mtsaeed", + time: "4.49 pm", + message: + "He was educated at the Aitchison College and Cathedral School in Lahore, and then the Royal Grammar School Worcester in England, where he excelled at cricket. In 1972, he enrolled in Keble College, Oxford where he studied Philosophy, Politics and Economics, graduating in 1975.", + date: "24/9/2022" + }, + { + username: "demo.com", + time: "4.50 pm", + message: "Seems good", + date: "4/10/2022" + }, + { + username: "mtsaeed", + time: "4.50 pm", + message: "Excellent", + date: "4/10/2022" + }, + { + username: "mtsaeed", + time: "4.52 pm", + message: "Thanks", + date: "4/11/2022" + } + ]; + + const fetchAccountData = async () => { + const response = await getAccountFull(currentUser); + setCurrentUserAccount(response); + setProfileData({ + joiningData: response.created, + about: response.profile?.about, + followers: response.follow_stats?.follower_count + }); + console.log(response); + }; + + const userClicked = (username: string) => { + setIsCurrentUser(true); + setCurrentUser(username); + }; + + const handleMessage = (e: React.ChangeEvent) => { + setMessage(e.target.value); + setIsMessageText(e.target.value.length !== 0); + }; + + const sendMessage = () => { + if (message.length !== 0) { + setMessage(""); + setIsMessageText(false); + } + }; + + useEffect(() => { + chatBodyDivRef?.current?.scrollTo(0, isCurrentUser ? chatBodyDivRef.current.scrollHeight : 0); + }, [isCurrentUser]); + + const formatFollowers = (count: number | undefined) => { + if (count) { + return count >= 1e6 + ? (count / 1e6).toLocaleString() + "M" + : count >= 1e3 + ? (count / 1e3).toLocaleString() + "K" + : count.toLocaleString(); + } + return count; + }; + + const handleScroll = (event: React.UIEvent) => { + var element = event.currentTarget; + let srollHeight: number = (element.scrollHeight / 100) * 25; + if (!isCurrentUser) { + if (element.scrollTop >= srollHeight) { + setIsScrollToTop(true); + return; + } + setIsScrollToTop(false); + } else { + if (element.scrollTop <= (element.scrollHeight / 100) * 40 && element.scrollHeight > 700) { + setIsScrollToBottom(true); + return; + } + setIsScrollToBottom(false); + } + }; + + const ScrollerClicked = () => { + chatBodyDivRef?.current?.scrollTo({ + top: isCurrentUser ? chatBodyDivRef?.current?.scrollHeight : 0, + behavior: "smooth" + }); + }; + + const handleMessageSvgClick = () => { + setShowSearchUser(true); + console.log("i am clicked"); + }; + + const setSearchUser = (d: boolean) => { + setShowSearchUser(d); + }; + + return ( + <> +
+
+ {currentUser && expanded && ( +
+ { + setCurrentUser(""); + setIsCurrentUser(false); + }} + > + {" "} + {arrowBackSvg} + +
+ )} +
setExpanded(!expanded)}> +

{currentUser ? currentUser : _t("chat.messages")}

+
+
+ {!currentUser && ( +
+ +

{addMessageSVG}

+
+
+ )} +
+ +

setExpanded(!expanded)}> + {expanded ? expandArrow : collapseArrow} +

+
+
+
+
+ +
+ {currentUser.length !== 0 ? ( + <> + + {profileData?.joiningData && ( +
+ + + +

{currentUser}

+ {profileData.about &&

{profileData.about}

} + +
+

+ {" "} + {_t("chat.joined")} {dateToFormatted(profileData!.joiningData, "LL")} +

+

+ {" "} + {formatFollowers(profileData!.followers)} {_t("chat.followers")} +

+
+
+ )} + +
+ {messageList.map((msg) => { + if (msg.username !== activeUser?.username) { + return ( + <> +
+

+ {msg.date}, {msg.time} +

+
+
+
+ + + +
+
+

{msg.message}

+
+
+ + ); + } else { + return ( +
+
+ {/* {msg.time} */} +

{msg.message}

+
+
+ ); + } + })} +
+ + ) : ( + <> + {contentList.map((user) => { + return ( +
+ +
+ + + +
+ + +
userClicked(user.username)}> +

{user.username}

+

{user.lastMessage}

+
+
+ ); + })} + + )} + + {((isScrollToTop && !isCurrentUser) || (isCurrentUser && isScrollToBottom)) && ( + +
+ {isCurrentUser ? chevronDownSvgForSlider : chevronUpSvg} +
+
+ )} +
+ + {currentUser && ( +
+
{ + e.preventDefault(); + e.stopPropagation(); + sendMessage(); + }} + > + + + + {messageSendSvg} + + +
+
+ )} +
+ {showSearchUser && } + + ); +} diff --git a/src/common/components/search-user/index.scss b/src/common/components/search-user/index.scss new file mode 100644 index 00000000000..0c5396a34f5 --- /dev/null +++ b/src/common/components/search-user/index.scss @@ -0,0 +1,13 @@ +.search-user-dialog { + .search-header { + padding-left: 5px; + } + .modal-content { + padding-left: 1.5rem; + padding-right: 1.5rem; + } + + .modal-body { + padding: 0 0 10px 0; + } +} diff --git a/src/common/components/search-user/index.tsx b/src/common/components/search-user/index.tsx new file mode 100644 index 00000000000..aac0764a9ec --- /dev/null +++ b/src/common/components/search-user/index.tsx @@ -0,0 +1,51 @@ +import React, { useEffect, useState } from "react"; +import { Modal, Form, FormControl } from "react-bootstrap"; + +import { _t } from "../../i18n"; +import "./index.scss"; + +interface Props { + setSearchUser: (d: boolean) => void; +} + +export default function SeachUser(props: Props) { + const [searchtext, setSearchText] = useState(""); + + const setStep = () => { + const { setSearchUser } = props; + setSearchUser(false); + }; + return ( + + + New message + + + { + setSearchText(e.target.value); + }} + /> + + + {/* */} + + + ); +} diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 1114e28031f..14b2868ddac 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1612,6 +1612,17 @@ "update-successful": "Update successful", "update-success-message": "Your Recovery email has been updated successfully" }, + "chat": { + "messages": "Messages", + "new-message": "New Message", + "collapse": "Collapse", + "expand": "Expand", + "scroll-to-bottom": "scroll to Bottom", + "joined": "Joined", + "followers": "Followers", + "start-chat-placeholder": "Start a new message", + "search": "Search people" + }, "add-image": { "title": "Add Image", "text-label": "Image Text", diff --git a/src/common/img/svg.tsx b/src/common/img/svg.tsx index 98a0114b70b..0f9c502ed05 100644 --- a/src/common/img/svg.tsx +++ b/src/common/img/svg.tsx @@ -1983,3 +1983,91 @@ export const dragSvg = ( /> ); + +export const addMessageSVG = ( + + + + +); + +export const expandArrow = ( + + + +); + +export const collapseArrow = ( + + + +); + +export const arrowBackSvg = ( + + + +); + +export const messageSendSvg = ( + + + + + +); From b95a6505cd88f9f7b36e3e5fff6873ca97f7dd95 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Fri, 12 May 2023 10:48:04 +0500 Subject: [PATCH 002/179] Design User search Modal --- src/common/components/chat-box/index.tsx | 15 ++++- src/common/components/search-user/index.scss | 29 ++++++++++ src/common/components/search-user/index.tsx | 61 +++++++++++++++++--- 3 files changed, 93 insertions(+), 12 deletions(-) diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index e083e80bad3..0e7b7c2b0d7 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -192,7 +192,6 @@ export default function ChatBox({ activeUser }: Props) { about: response.profile?.about, followers: response.follow_stats?.follower_count }); - console.log(response); }; const userClicked = (username: string) => { @@ -200,6 +199,12 @@ export default function ChatBox({ activeUser }: Props) { setCurrentUser(username); }; + const setCurrentUserFromSearch = (username: string) => { + setCurrentUser(username); + setExpanded(true); + setIsCurrentUser(true); + }; + const handleMessage = (e: React.ChangeEvent) => { setMessage(e.target.value); setIsMessageText(e.target.value.length !== 0); @@ -254,7 +259,6 @@ export default function ChatBox({ activeUser }: Props) { const handleMessageSvgClick = () => { setShowSearchUser(true); - console.log("i am clicked"); }; const setSearchUser = (d: boolean) => { @@ -432,7 +436,12 @@ export default function ChatBox({ activeUser }: Props) { )} - {showSearchUser && } + {showSearchUser && ( + + )} ); } diff --git a/src/common/components/search-user/index.scss b/src/common/components/search-user/index.scss index 0c5396a34f5..b840aa65d54 100644 --- a/src/common/components/search-user/index.scss +++ b/src/common/components/search-user/index.scss @@ -10,4 +10,33 @@ .modal-body { padding: 0 0 10px 0; } + + .search-content { + padding: 0.7rem; + border-radius: 10px; + cursor: pointer; + display: flex; + margin-bottom: 1rem; + + .search-user-img { + justify-content: center; + align-items: center; + display: flex; + margin-left: 10px; + } + + .search-user-title { + margin-left: 1rem; + .search-username { + margin-top: 7px; + margin-bottom: 0; + font-size: 20px; + font-weight: 700; + } + } + } + + .search-content:hover { + background: #e7e7e7; + } } diff --git a/src/common/components/search-user/index.tsx b/src/common/components/search-user/index.tsx index aac0764a9ec..1b3cf3dede3 100644 --- a/src/common/components/search-user/index.tsx +++ b/src/common/components/search-user/index.tsx @@ -1,26 +1,59 @@ import React, { useEffect, useState } from "react"; import { Modal, Form, FormControl } from "react-bootstrap"; +import useDebounce from "react-use/lib/useDebounce"; +import { lookupAccounts } from "../../api/hive"; +import UserAvatar from "../user-avatar"; import { _t } from "../../i18n"; import "./index.scss"; interface Props { setSearchUser: (d: boolean) => void; + setCurrentUserFromSearch: (username: string) => void; } export default function SeachUser(props: Props) { const [searchtext, setSearchText] = useState(""); + const [userList, setUserList] = useState([]); + const [showModal, setShowModal] = useState(true); - const setStep = () => { + // const setStep = () => { + // const { setSearchUser } = props; + // setSearchUser(false); + // }; + + useDebounce( + async () => { + const resp = await lookupAccounts(searchtext, 7); + setUserList(resp); + }, + 500, + [searchtext] + ); + + useEffect(() => { + setUserList(["good-karma", "good-akai", "good-ali", "good-angle", "good-bad"]); + }, []); + + const searchUserClicked = (username: string) => { + const { setCurrentUserFromSearch, setSearchUser } = props; + setCurrentUserFromSearch(username); + setSearchUser(false); + }; + + const handleCloseModal = () => { + setShowModal(false); + console.log("Close run"); const { setSearchUser } = props; setSearchUser(false); }; + return ( - {/* */} + {userList.map((user) => { + return ( +
searchUserClicked(user)}> +
+ + + +
+ +
+

{user}

+ {/*

{user}

*/} +
+
+ ); + })}
); From 4803cb721eabf2d7e0a2f7e1b23314c5cdf3f9f5 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Mon, 15 May 2023 17:44:23 +0500 Subject: [PATCH 003/179] Add join Chat button --- src/common/components/chat-box/index.scss | 58 +++-- src/common/components/chat-box/index.tsx | 243 ++++++++++++------- src/common/components/search-user/index.scss | 17 +- src/common/components/search-user/index.tsx | 41 ++-- src/common/i18n/locales/en-US.json | 3 +- 5 files changed, 230 insertions(+), 132 deletions(-) diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss index fb6b1cd90f7..3161780b957 100644 --- a/src/common/components/chat-box/index.scss +++ b/src/common/components/chat-box/index.scss @@ -47,10 +47,13 @@ } .back-arrow-image { - margin: 1.1rem 1rem 0 0; + justify-content: center; + display: flex; + align-items: center; .back-arrow-svg { border-radius: 50%; - padding: 8px 6px 10px 6px; + padding: 6px; + // padding: 8px 6px 10px 6px; } } @@ -58,11 +61,16 @@ background: rgba(29, 155, 240, 0.1); } .message-title { + display: flex; width: 288px; + margin-left: 0.8rem; + .user-icon { + margin: 14px 11px 0 0; + } } .message-content { - margin-top: 1rem; + margin-top: 0.8rem; font-weight: 700; line-height: 24px; font-size: 20px; @@ -81,9 +89,9 @@ display: flex; align-items: center; justify-content: center; - margin-top: 13px; + margin-top: 1.1rem; .message-image { - margin: 0.4rem 0.3rem 0 0; + margin: 0.2rem 0.3rem 0 0; .message-svg { width: 40px; @@ -91,7 +99,7 @@ justify-content: center; text-align: center; border-radius: 50%; - padding-top: 13px; + padding-top: 11px; display: flex; svg { @@ -104,12 +112,12 @@ } } .arrow-image { - margin: 0.4rem 0.3rem 0 0; + margin: 1.5 0.3rem 0 0; .arrow-svg { display: flex; justify-content: center; text-align: center; - padding-top: 7px; + padding-top: 6px; width: 40px; height: 40px; border-radius: 50%; @@ -138,6 +146,12 @@ } } + &.join-chat { + display: flex; + justify-content: center; + align-items: center; + } + .chat-content { display: flex; @@ -195,6 +209,7 @@ justify-content: center; align-items: center; padding-top: 16px; + cursor: pointer; } .user-name { font-size: 20px; @@ -247,19 +262,31 @@ .scroller { position: sticky; - margin-left: 83%; + margin-left: 89%; width: 33px; height: 33px; - box-shadow: $box-shadow; - background: $dark-sky-blue; - color: $white; border-radius: 50%; z-index: 10; display: flex; align-items: center; justify-content: center; cursor: pointer; + @include themify(day) { + background: #e4e6eb; + border: 1px solid $white-five; + } + @include themify(night) { + background: #0f223a; + } + svg { + @include themify(day) { + color: $dark-sky-blue; + } + @include themify(night) { + color: $white; + } + width: 20px; height: 20px; } @@ -276,7 +303,7 @@ .message { display: flex; .user-img { - padding: 18px 16px; + padding: 18px 8px 18px 16px; } .user-info { .receiver-message-content { @@ -285,11 +312,10 @@ } @include themify(night) { background: #cee2ff; - } color: #050505; max-width: 280px; - margin-top: 17px; + margin-top: 19px; padding: 8px 12px 8px 12px; border-radius: 18px 18px 18px 18px; } @@ -336,7 +362,7 @@ .msg-svg { padding: 6px 8px 4px 5px; position: inherit; - right: 41px; + right: 44px; border-radius: 50%; margin: 4px 0; margin-right: 11px; diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 0e7b7c2b0d7..476e02f0d8c 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { Button, Form, FormControl, InputGroup } from "react-bootstrap"; +import { Button, Form, FormControl, InputGroup, Spinner } from "react-bootstrap"; import { Link } from "react-router-dom"; import { Account } from "../../store/accounts/types"; @@ -23,6 +23,7 @@ import { _t } from "../../i18n"; import { getAccountFull } from "../../api/hive"; import "./index.scss"; +import { updateProfile } from "../../api/operations"; export interface profileData { joiningData: string; @@ -41,15 +42,27 @@ export default function ChatBox({ activeUser }: Props) { const [isCurrentUser, setIsCurrentUser] = useState(false); const [message, setMessage] = useState(""); const [isMessageText, setIsMessageText] = useState(false); - const [currentUserAccount, setCurrentUserAccount] = useState(); + const [accountData, setAccountData] = useState(); const [profileData, setProfileData] = useState(); const [isScrollToTop, setIsScrollToTop] = useState(false); const [isScrollToBottom, setIsScrollToBottom] = useState(false); const [showSearchUser, setShowSearchUser] = useState(false); + const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); + const [inProgress, setInProgress] = useState(false); + + useEffect(() => { + fetchProfileData(); + }, []); + + useEffect(() => { + fetchProfileData(); + setCurrentUser(""); + setIsCurrentUser(false); + }, [activeUser]); useEffect(() => { if (currentUser) { - fetchAccountData(); + fetchCurrentUserData(); } }, [currentUser]); @@ -179,14 +192,19 @@ export default function ChatBox({ activeUser }: Props) { { username: "mtsaeed", time: "4.52 pm", - message: "Thanks", + message: "Thanks. I am very grateful to you.", date: "4/11/2022" + }, + { + username: "mtsaeed", + time: "4.54 pm", + message: "Bundle of thanks.", + date: "5/11/2022" } ]; - const fetchAccountData = async () => { + const fetchCurrentUserData = async () => { const response = await getAccountFull(currentUser); - setCurrentUserAccount(response); setProfileData({ joiningData: response.created, about: response.profile?.about, @@ -194,6 +212,16 @@ export default function ChatBox({ activeUser }: Props) { }); }; + const fetchProfileData = async () => { + const response = await getAccountFull(activeUser?.username!); + setAccountData(response); + const { posting_json_metadata } = response; + const profile = JSON.parse(posting_json_metadata!).profile; + + const hasNoStrKey = !!profile.noStrKey && profile.noStrKey.trim() !== ""; + setHasUserJoinedChat(hasNoStrKey); + }; + const userClicked = (username: string) => { setIsCurrentUser(true); setCurrentUser(username); @@ -235,19 +263,14 @@ export default function ChatBox({ activeUser }: Props) { const handleScroll = (event: React.UIEvent) => { var element = event.currentTarget; let srollHeight: number = (element.scrollHeight / 100) * 25; - if (!isCurrentUser) { - if (element.scrollTop >= srollHeight) { - setIsScrollToTop(true); - return; - } - setIsScrollToTop(false); - } else { - if (element.scrollTop <= (element.scrollHeight / 100) * 40 && element.scrollHeight > 700) { - setIsScrollToBottom(true); - return; - } - setIsScrollToBottom(false); - } + const isScrollToTop = !isCurrentUser && element.scrollTop >= srollHeight; + const isScrollToBottom = + isCurrentUser && + element.scrollTop <= (element.scrollHeight / 100) * 50 && + element.scrollHeight > 700; + + setIsScrollToTop(isScrollToTop); + setIsScrollToBottom(isScrollToBottom); }; const ScrollerClicked = () => { @@ -265,6 +288,27 @@ export default function ChatBox({ activeUser }: Props) { setShowSearchUser(d); }; + const handleJoinChat = () => { + setInProgress(true); + setTimeout(() => { + setInProgress(false); + setHasUserJoinedChat(true); + }, 4000); + + // const { profile } = response; + + // const newProfile = { + // noStrKey: "nsec1mefplh7mwup68r84x6gxkqn4w0ymj5q8cr0frj0lgflx8vrwvw7sdxh3ew" + // }; + + // const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); + // console.log(updatedProfile); + }; + + const chatButtonSpinner = ( + + ); + return ( <>
@@ -284,10 +328,16 @@ export default function ChatBox({ activeUser }: Props) {
)}
setExpanded(!expanded)}> + {currentUser && ( +

+ +

+ )} +

{currentUser ? currentUser : _t("chat.messages")}

- {!currentUser && ( + {!currentUser && hasUserJoinedChat && (

{addMessageSVG}

@@ -305,90 +355,105 @@ export default function ChatBox({ activeUser }: Props) {
- {currentUser.length !== 0 ? ( + {hasUserJoinedChat ? ( <> - - {profileData?.joiningData && ( -
- - - -

{currentUser}

- {profileData.about &&

{profileData.about}

} - -
-

- {" "} - {_t("chat.joined")} {dateToFormatted(profileData!.joiningData, "LL")} -

-

- {" "} - {formatFollowers(profileData!.followers)} {_t("chat.followers")} -

-
-
- )} - -
- {messageList.map((msg) => { - if (msg.username !== activeUser?.username) { - return ( - <> -
-

- {msg.date}, {msg.time} + {currentUser.length !== 0 ? ( + <> + + {profileData?.joiningData && ( +

+ + + +

{currentUser}

+ {profileData.about && ( +

{profileData.about}

+ )} + +
+

+ {" "} + {_t("chat.joined")} {dateToFormatted(profileData!.joiningData, "LL")} +

+

+ {" "} + {formatFollowers(profileData!.followers)} {_t("chat.followers")}

-
+
+ )} + +
+ {messageList.map((msg) => { + if (msg.username !== activeUser?.username) { + return ( + <> +
+

+ {msg.date}, {msg.time} +

+
+
+
+ + + + + +
+
+

{msg.message}

+
+
+ + ); + } else { + return ( +
+
+ {/* {msg.time} */} +

{msg.message}

+
+
+ ); + } + })} +
+ + ) : ( + <> + {contentList.map((user) => { + return ( +
+
- +
-
-

{msg.message}

-
-
- - ); - } else { - return ( -
-
- {/* {msg.time} */} -

{msg.message}

+ + +
userClicked(user.username)}> +

{user.username}

+

{user.lastMessage}

); - } - })} -
+ })} + + )} ) : ( - <> - {contentList.map((user) => { - return ( -
- -
- - - -
- - -
userClicked(user.username)}> -

{user.username}

-

{user.lastMessage}

-
-
- ); - })} - + )} {((isScrollToTop && !isCurrentUser) || (isCurrentUser && isScrollToBottom)) && ( diff --git a/src/common/components/search-user/index.scss b/src/common/components/search-user/index.scss index b840aa65d54..a107eccbf8f 100644 --- a/src/common/components/search-user/index.scss +++ b/src/common/components/search-user/index.scss @@ -1,3 +1,8 @@ +@import "src/style/colors"; +@import "src/style/variables"; +@import "src/style/bootstrap_vars"; +@import "src/style/mixins"; + .search-user-dialog { .search-header { padding-left: 5px; @@ -34,9 +39,13 @@ font-weight: 700; } } - } - - .search-content:hover { - background: #e7e7e7; + &:hover { + @include themify(day) { + background: $white-four; + } + @include themify(night) { + background: $charcoal-grey; + } + } } } diff --git a/src/common/components/search-user/index.tsx b/src/common/components/search-user/index.tsx index 1b3cf3dede3..7e830be65a8 100644 --- a/src/common/components/search-user/index.tsx +++ b/src/common/components/search-user/index.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { Modal, Form, FormControl } from "react-bootstrap"; +import { Modal, Form } from "react-bootstrap"; import useDebounce from "react-use/lib/useDebounce"; import { lookupAccounts } from "../../api/hive"; @@ -17,11 +17,6 @@ export default function SeachUser(props: Props) { const [userList, setUserList] = useState([]); const [showModal, setShowModal] = useState(true); - // const setStep = () => { - // const { setSearchUser } = props; - // setSearchUser(false); - // }; - useDebounce( async () => { const resp = await lookupAccounts(searchtext, 7); @@ -43,7 +38,6 @@ export default function SeachUser(props: Props) { const handleCloseModal = () => { setShowModal(false); - console.log("Close run"); const { setSearchUser } = props; setSearchUser(false); }; @@ -59,35 +53,38 @@ export default function SeachUser(props: Props) { size="lg" > - New message + {_t("chat.new-message")} { setSearchText(e.target.value); }} /> - {userList.map((user) => { - return ( -
searchUserClicked(user)}> -
- - - -
+
+ {userList.map((user, index) => { + return ( +
searchUserClicked(user)}> +
+ + + +
-
-

{user}

- {/*

{user}

*/} +
+

{user}

+ {/*

{user}

*/} +
-
- ); - })} + ); + })} +
); diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 14b2868ddac..c7504507a75 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1621,7 +1621,8 @@ "joined": "Joined", "followers": "Followers", "start-chat-placeholder": "Start a new message", - "search": "Search people" + "search": "Search people", + "join-chat": "Join chat" }, "add-image": { "title": "Add Image", From e6a709e9bfccb3ded5b6af701e41628dae633603 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Wed, 17 May 2023 14:29:35 +0500 Subject: [PATCH 004/179] Show chat-box just for active user --- src/common/components/chat-box/index.tsx | 347 ++++++++++++----------- 1 file changed, 177 insertions(+), 170 deletions(-) diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 476e02f0d8c..a3366cb061c 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -49,15 +49,19 @@ export default function ChatBox({ activeUser }: Props) { const [showSearchUser, setShowSearchUser] = useState(false); const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); const [inProgress, setInProgress] = useState(false); + const [show, setShow] = useState(false); useEffect(() => { fetchProfileData(); + setShow(!!activeUser?.username); }, []); useEffect(() => { fetchProfileData(); setCurrentUser(""); setIsCurrentUser(false); + setShow(!!activeUser?.username); + setExpanded(false); }, [activeUser]); useEffect(() => { @@ -311,196 +315,199 @@ export default function ChatBox({ activeUser }: Props) { return ( <> -
-
- {currentUser && expanded && ( -
- { - setCurrentUser(""); - setIsCurrentUser(false); - }} - > - {" "} - {arrowBackSvg} - -
- )} -
setExpanded(!expanded)}> - {currentUser && ( -

- -

+ {show && ( +
+
+ {currentUser && expanded && ( +
+ { + setCurrentUser(""); + setIsCurrentUser(false); + }} + > + {" "} + {arrowBackSvg} + +
)} +
setExpanded(!expanded)}> + {currentUser && ( +

+ +

+ )} -

{currentUser ? currentUser : _t("chat.messages")}

-
-
- {!currentUser && hasUserJoinedChat && ( -
- -

{addMessageSVG}

+

{currentUser ? currentUser : _t("chat.messages")}

+
+
+ {!currentUser && hasUserJoinedChat && ( +
+ +

{addMessageSVG}

+
+
+ )} +
+ +

setExpanded(!expanded)}> + {expanded ? expandArrow : collapseArrow} +

- )} -
- -

setExpanded(!expanded)}> - {expanded ? expandArrow : collapseArrow} -

-
-
-
- {hasUserJoinedChat ? ( - <> - {currentUser.length !== 0 ? ( - <> - - {profileData?.joiningData && ( -
- - - -

{currentUser}

- {profileData.about && ( -

{profileData.about}

- )} - -
-

- {" "} - {_t("chat.joined")} {dateToFormatted(profileData!.joiningData, "LL")} -

-

- {" "} - {formatFollowers(profileData!.followers)} {_t("chat.followers")} -

+
+ {hasUserJoinedChat ? ( + <> + {currentUser.length !== 0 ? ( + <> + + {profileData?.joiningData && ( +
+ + + +

{currentUser}

+ {profileData.about && ( +

{profileData.about}

+ )} + +
+

+ {" "} + {_t("chat.joined")} {dateToFormatted(profileData!.joiningData, "LL")} +

+

+ {" "} + {formatFollowers(profileData!.followers)} {_t("chat.followers")} +

+
-
- )} - -
- {messageList.map((msg) => { - if (msg.username !== activeUser?.username) { - return ( - <> -
-

- {msg.date}, {msg.time} -

-
-
-
- - - - - + )} + +
+ {messageList.map((msg) => { + if (msg.username !== activeUser?.username) { + return ( + <> +
+

+ {msg.date}, {msg.time} +

+
+
+
+ + + + + +
+
+

{msg.message}

+
-
-

{msg.message}

+ + ); + } else { + return ( +
+
+ {/* {msg.time} */} +

{msg.message}

- - ); - } else { - return ( -
-
- {/* {msg.time} */} -

{msg.message}

+ ); + } + })} +
+ + ) : ( + <> + {contentList.map((user) => { + return ( +
+ +
+ + +
+ + +
userClicked(user.username)}> +

{user.username}

+

{user.lastMessage}

- ); - } +
+ ); })} -
- - ) : ( - <> - {contentList.map((user) => { - return ( -
- -
- - - -
- + + )} + + ) : ( + + )} -
userClicked(user.username)}> -

{user.username}

-

{user.lastMessage}

-
-
- ); - })} - - )} - - ) : ( - - )} + {((isScrollToTop && !isCurrentUser) || (isCurrentUser && isScrollToBottom)) && ( + +
+ {isCurrentUser ? chevronDownSvgForSlider : chevronUpSvg} +
+
+ )} +
- {((isScrollToTop && !isCurrentUser) || (isCurrentUser && isScrollToBottom)) && ( - -
+
{ + e.preventDefault(); + e.stopPropagation(); + sendMessage(); + }} > - {isCurrentUser ? chevronDownSvgForSlider : chevronUpSvg} -
-
+ + + + {messageSendSvg} + + + +
)}
+ )} - {currentUser && ( -
-
{ - e.preventDefault(); - e.stopPropagation(); - sendMessage(); - }} - > - - - - {messageSendSvg} - - -
-
- )} -
{showSearchUser && ( Date: Thu, 18 May 2023 16:48:58 +0500 Subject: [PATCH 005/179] Make chat-utils --- src/common/helper/chat-utils.ts | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 src/common/helper/chat-utils.ts diff --git a/src/common/helper/chat-utils.ts b/src/common/helper/chat-utils.ts new file mode 100644 index 00000000000..4cfd38e3771 --- /dev/null +++ b/src/common/helper/chat-utils.ts @@ -0,0 +1,31 @@ +import { generatePrivateKey, getPublicKey } from "nostr-tools"; +import { getAccountFull } from "../api/hive"; +import { updateProfile } from "../api/operations"; +import { ActiveUser } from "../store/active-user/types"; + +export const getProfileMetaData = async (activeUser: ActiveUser | null) => { + const response = await getAccountFull(activeUser?.username!); + const { posting_json_metadata } = response; + const profile = JSON.parse(posting_json_metadata!).profile; + + return profile; +}; + +export const setProfileMetaData = async (activeUser: ActiveUser | null, key: string) => { + const response = await getAccountFull(activeUser?.username!); + + const { posting_json_metadata } = response; + const profile = JSON.parse(posting_json_metadata!).profile; + const newProfile = { + noStrKey: key + }; + + const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); + return updatedProfile; +}; + +export const createNoStrAccount = () => { + const privKey = generatePrivateKey(); + const pubKey = getPublicKey(privKey); + return { pubKey, privKey }; +}; From 5bd88206a9c8b7d3f1435dcf5a1694f482d1a2e5 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Fri, 19 May 2023 14:14:28 +0500 Subject: [PATCH 006/179] Add noStr --- package.json | 1 + yarn.lock | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/package.json b/package.json index a0635653191..55c5e20fd30 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "moment": "^2.29.4", "node-cache": "^5.1.0", "node-html-parser": "^5.3.3", + "nostr-tools": "^1.11.1", "numeral": "^2.0.6", "path-to-regexp": "^6.1.0", "qrcode": "^1.5.1", diff --git a/yarn.lock b/yarn.lock index b52c005c9d0..924dbe5cca8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1717,6 +1717,18 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@noble/curves@1.0.0", "@noble/curves@~1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.0.0.tgz#e40be8c7daf088aaf291887cbc73f43464a92932" + integrity sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw== + dependencies: + "@noble/hashes" "1.3.0" + +"@noble/hashes@1.3.0", "@noble/hashes@~1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" + integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1785,6 +1797,28 @@ dependencies: dequal "^2.0.2" +"@scure/base@1.1.1", "@scure/base@~1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" + integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== + +"@scure/bip32@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.0.tgz#6c8d980ef3f290987736acd0ee2e0f0d50068d87" + integrity sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q== + dependencies: + "@noble/curves" "~1.0.0" + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.0.tgz#a207e2ef96de354de7d0002292ba1503538fc77b" + integrity sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg== + dependencies: + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -8380,6 +8414,17 @@ normalize-url@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== +nostr-tools@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.11.1.tgz#7f479f86dc25ef4625747a33f8430f464aa225e5" + integrity sha512-b8BpCiD3wxjBZwrn0wc+CkVj6/7s4sQxp+Az7UkCG80mJu7xTspZsOoUP/geBNwZVYETzEwj+CPBvW8WIP8mBQ== + dependencies: + "@noble/curves" "1.0.0" + "@noble/hashes" "1.3.0" + "@scure/base" "1.1.1" + "@scure/bip32" "1.3.0" + "@scure/bip39" "1.2.0" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" From 4b802e4f43e03f727c3f679a92559a872d667dde Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Fri, 26 May 2023 11:13:34 +0500 Subject: [PATCH 007/179] Error in build --- package.json | 6 +- razzle.config.js | 24 + src/common/pages/sign-up.tsx | 3 + yarn.lock | 994 +++++++++++++++++++++++++++++++++-- 4 files changed, 996 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index bea263593aa..6d3044d1a49 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,9 @@ "start:prod": "NODE_ENV=production node build/server.js" }, "dependencies": { + "@babel/core": "^7.21.8", + "@babel/preset-env": "^7.21.5", + "@babel/preset-modules": "^0.1.5", "@ecency/render-helper": "^2.2.24", "@ecency/render-helper-amp": "^1.1.0", "@firebase/analytics": "^0.8.0", @@ -24,6 +27,7 @@ "@loadable/server": "^5.15.2", "@webscopeio/react-textarea-autocomplete": "^4.8.1", "axios": "^0.21.2", + "babel-loader": "8.2.5", "bs58": "^4.0.1", "connected-react-router": "^6.8.0", "cookie-parser": "^1.4.5", @@ -45,7 +49,7 @@ "moment": "^2.29.4", "node-cache": "^5.1.0", "node-html-parser": "^5.3.3", - "nostr-tools": "^1.11.1", + "nostr-tools": "1.10.1", "numeral": "^2.0.6", "path-to-regexp": "^6.1.0", "qrcode": "^1.5.1", diff --git a/razzle.config.js b/razzle.config.js index fcc4629bbc4..cced0f2490b 100644 --- a/razzle.config.js +++ b/razzle.config.js @@ -8,6 +8,7 @@ const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPl module.exports = { plugins: ["typescript", "scss"], resolve: { + extensions: [".js", ".jsx", ".json", ".ts", ".tsx", ".mjs"], alias: { styles: path.join(__dirname, "src/style/") } @@ -41,6 +42,29 @@ module.exports = { writeToDisk: { filename } }) ); + + webpackConfig.module.rules.push({ + test: /\.mjs$/, + exclude: /node_modules\/(?!(nostr-tools)\/).*/, + // test: /\.mjs$/, + // include: /node_modules\/nostr-tools\//, + use: { + loader: "babel-loader", + options: { + presets: [ + [ + "@babel/preset-env", + { + targets: { + node: "current" + } + } + ] + ] + } + } + }); + console.log(webpackConfig.module.rules); } // Enable SSR lazy-loading diff --git a/src/common/pages/sign-up.tsx b/src/common/pages/sign-up.tsx index af3e34cc53c..8fa1a1cd4ff 100644 --- a/src/common/pages/sign-up.tsx +++ b/src/common/pages/sign-up.tsx @@ -22,6 +22,7 @@ import { Tsx } from "../i18n/helper"; import { handleInvalid, handleOnInput } from "../util/input-util"; import { getAccount } from "../api/hive"; import "./sign-up.scss"; +import { createNoStrAccount } from "../helper/chat-utils"; type FormChangeEvent = React.ChangeEvent; @@ -120,6 +121,8 @@ export const SignUp = (props: PageProps) => { }, [username, usernameTouched]); const regularRegister = async () => { + const key = createNoStrAccount(); + console.log(key); setInProgress(true); try { const response = await signUp(username, email, referral); diff --git a/yarn.lock b/yarn.lock index 2a41b17bb1c..eb7b9538a4f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,6 +10,14 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" +"@ampproject/remapping@^2.2.0": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" + integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + "@ampproject/toolbox-core@2.8.0", "@ampproject/toolbox-core@^2.8.0": version "2.8.0" resolved "https://registry.yarnpkg.com/@ampproject/toolbox-core/-/toolbox-core-2.8.0.tgz#4c291c470fa30c0c2f3c7952dbcd6df2a4ec0df9" @@ -88,6 +96,13 @@ dependencies: "@babel/highlight" "^7.16.7" +"@babel/code-frame@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== + dependencies: + "@babel/highlight" "^7.18.6" + "@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.0", "@babel/compat-data@^7.17.10": version "7.17.10" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.10.tgz#711dc726a492dfc8be8220028b1b92482362baab" @@ -98,6 +113,11 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.7.tgz#7b047d7a3a89a67d2258dc61f604f098f1bc7e08" integrity sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw== +"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.5": + version "7.21.9" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.9.tgz#10a2e7fda4e51742c907938ac3b7229426515514" + integrity sha512-FUGed8kfhyWvbYug/Un/VPJD41rDIgoVVcR+FuzhzOYyRz5uED+Gd3SLZml0Uw2l2aHFb7ZgdW5mGA3G2cCCnQ== + "@babel/core@^7.1.0", "@babel/core@^7.7.5": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.8.tgz#20cdf7c84b5d86d83fac8710a8bc605a7ba3f010" @@ -140,6 +160,27 @@ json5 "^2.2.1" semver "^6.3.0" +"@babel/core@^7.21.8": + version "7.21.8" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.8.tgz#2a8c7f0f53d60100ba4c32470ba0281c92aa9aa4" + integrity sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.5" + "@babel/helper-compilation-targets" "^7.21.5" + "@babel/helper-module-transforms" "^7.21.5" + "@babel/helpers" "^7.21.5" + "@babel/parser" "^7.21.8" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.5" + "@babel/types" "^7.21.5" + convert-source-map "^1.7.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.2" + semver "^6.3.0" + "@babel/generator@^7.14.8": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.8.tgz#bf86fd6af96cf3b74395a8ca409515f89423e070" @@ -158,6 +199,16 @@ "@jridgewell/gen-mapping" "^0.1.0" jsesc "^2.5.1" +"@babel/generator@^7.21.5": + version "7.21.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.9.tgz#3a1b706e07d836e204aee0650e8ee878d3aaa241" + integrity sha512-F3fZga2uv09wFdEjEQIJxXALXfz0+JaOb7SabvVMmjHxeVTuGW8wgE8Vp1Hd7O+zMTYtcfEISGRzPkeiaPPsvg== + dependencies: + "@babel/types" "^7.21.5" + "@jridgewell/gen-mapping" "^0.3.2" + "@jridgewell/trace-mapping" "^0.3.17" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" @@ -165,6 +216,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-annotate-as-pure@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" + integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" @@ -173,6 +231,13 @@ "@babel/helper-explode-assignable-expression" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.21.5.tgz#817f73b6c59726ab39f6ba18c234268a519e5abb" + integrity sha512-uNrjKztPLkUk7bpCNC0jEKDJzzkvel/W+HguzbN8krA+LPfC1CEobJEvAvGka2A/M+ViOqXdcRL0GqPUJSjx9g== + dependencies: + "@babel/types" "^7.21.5" + "@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.10": version "7.17.10" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz#09c63106d47af93cf31803db6bc49fef354e2ebe" @@ -193,6 +258,17 @@ browserslist "^4.16.6" semver "^6.3.0" +"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz#631e6cc784c7b660417421349aac304c94115366" + integrity sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w== + dependencies: + "@babel/compat-data" "^7.21.5" + "@babel/helper-validator-option" "^7.21.0" + browserslist "^4.21.3" + lru-cache "^5.1.1" + semver "^6.3.0" + "@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.16.7", "@babel/helper-create-class-features-plugin@^7.17.6": version "7.17.9" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.9.tgz#71835d7fb9f38bd9f1378e40a4c0902fdc2ea49d" @@ -206,6 +282,21 @@ "@babel/helper-replace-supers" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": + version "7.21.8" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.8.tgz#205b26330258625ef8869672ebca1e0dee5a0f02" + integrity sha512-+THiN8MqiH2AczyuZrnrKL6cAxFRRQDKW9h1YkBvbgKmAm6mwiacig1qT73DHIWMGo40GRnsEfN3LA+E6NtmSw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.21.5" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-member-expression-to-functions" "^7.21.5" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-replace-supers" "^7.21.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/helper-split-export-declaration" "^7.18.6" + semver "^6.3.0" + "@babel/helper-create-regexp-features-plugin@^7.16.7", "@babel/helper-create-regexp-features-plugin@^7.17.0": version "7.17.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz#1dcc7d40ba0c6b6b25618997c5dbfd310f186fe1" @@ -214,6 +305,15 @@ "@babel/helper-annotate-as-pure" "^7.16.7" regexpu-core "^5.0.1" +"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": + version "7.21.8" + resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.8.tgz#a7886f61c2e29e21fd4aaeaf1e473deba6b571dc" + integrity sha512-zGuSdedkFtsFHGbexAvNuipg1hbtitDLo2XE8/uf6Y9sOQV1xsYX/2pNbtedp/X0eU1pIt+kGvaqHCowkRbS5g== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + regexpu-core "^5.3.1" + semver "^6.3.0" + "@babel/helper-define-polyfill-provider@^0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" @@ -228,6 +328,18 @@ resolve "^1.14.2" semver "^6.1.2" +"@babel/helper-define-polyfill-provider@^0.3.3": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" + integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== + dependencies: + "@babel/helper-compilation-targets" "^7.17.7" + "@babel/helper-plugin-utils" "^7.16.7" + debug "^4.1.1" + lodash.debounce "^4.0.8" + resolve "^1.14.2" + semver "^6.1.2" + "@babel/helper-environment-visitor@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" @@ -235,6 +347,11 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz#c769afefd41d171836f7cb63e295bedf689d48ba" + integrity sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ== + "@babel/helper-explode-assignable-expression@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" @@ -259,6 +376,14 @@ "@babel/template" "^7.16.7" "@babel/types" "^7.17.0" +"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" + integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== + dependencies: + "@babel/template" "^7.20.7" + "@babel/types" "^7.21.0" + "@babel/helper-get-function-arity@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815" @@ -280,6 +405,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-hoist-variables@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" + integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-member-expression-to-functions@^7.14.5": version "7.14.7" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz#97e56244beb94211fe277bd818e3a329c66f7970" @@ -294,6 +426,13 @@ dependencies: "@babel/types" "^7.17.0" +"@babel/helper-member-expression-to-functions@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.5.tgz#3b1a009af932e586af77c1030fba9ee0bde396c0" + integrity sha512-nIcGfgwpH2u4n9GG1HpStW5Ogx7x7ekiFHbjjFRKXbn5zUvqO9ZgotCO4x1aNbKn/x/xOUaXEhyNHCwtFCpxWg== + dependencies: + "@babel/types" "^7.21.5" + "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" @@ -308,6 +447,13 @@ dependencies: "@babel/types" "^7.14.5" +"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" + integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== + dependencies: + "@babel/types" "^7.21.4" + "@babel/helper-module-transforms@^7.14.5", "@babel/helper-module-transforms@^7.14.8": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.8.tgz#d4279f7e3fd5f4d5d342d833af36d4dd87d7dc49" @@ -336,6 +482,20 @@ "@babel/traverse" "^7.17.3" "@babel/types" "^7.17.0" +"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz#d937c82e9af68d31ab49039136a222b17ac0b420" + integrity sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw== + dependencies: + "@babel/helper-environment-visitor" "^7.21.5" + "@babel/helper-module-imports" "^7.21.4" + "@babel/helper-simple-access" "^7.21.5" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.5" + "@babel/types" "^7.21.5" + "@babel/helper-optimise-call-expression@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c" @@ -350,6 +510,13 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-optimise-call-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" + integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== + dependencies: + "@babel/types" "^7.18.6" + "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" @@ -360,6 +527,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== +"@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz#345f2377d05a720a4e5ecfa39cbf4474a4daed56" + integrity sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg== + "@babel/helper-remap-async-to-generator@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3" @@ -369,6 +541,16 @@ "@babel/helper-wrap-function" "^7.16.8" "@babel/types" "^7.16.8" +"@babel/helper-remap-async-to-generator@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" + integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-wrap-function" "^7.18.9" + "@babel/types" "^7.18.9" + "@babel/helper-replace-supers@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz#0ecc0b03c41cd567b4024ea016134c28414abb94" @@ -390,6 +572,18 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7", "@babel/helper-replace-supers@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.21.5.tgz#a6ad005ba1c7d9bc2973dfde05a1bba7065dde3c" + integrity sha512-/y7vBgsr9Idu4M6MprbOVUfH3vs7tsIfnVWv/Ml2xgwvyH6LTngdfbf5AdsKwkJy4zgy1X/kuNrEKvhhK28Yrg== + dependencies: + "@babel/helper-environment-visitor" "^7.21.5" + "@babel/helper-member-expression-to-functions" "^7.21.5" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.5" + "@babel/types" "^7.21.5" + "@babel/helper-simple-access@^7.14.5", "@babel/helper-simple-access@^7.14.8": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz#82e1fec0644a7e775c74d305f212c39f8fe73924" @@ -404,6 +598,13 @@ dependencies: "@babel/types" "^7.17.0" +"@babel/helper-simple-access@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz#d697a7971a5c39eac32c7e63c0921c06c8a249ee" + integrity sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg== + dependencies: + "@babel/types" "^7.21.5" + "@babel/helper-skip-transparent-expression-wrappers@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" @@ -411,6 +612,13 @@ dependencies: "@babel/types" "^7.16.0" +"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" + integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== + dependencies: + "@babel/types" "^7.20.0" + "@babel/helper-split-export-declaration@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a" @@ -425,6 +633,18 @@ dependencies: "@babel/types" "^7.16.7" +"@babel/helper-split-export-declaration@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" + integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== + dependencies: + "@babel/types" "^7.18.6" + +"@babel/helper-string-parser@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" + integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== + "@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.8": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz#32be33a756f29e278a0d644fa08a2c9e0f88a34c" @@ -435,6 +655,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== +"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + "@babel/helper-validator-option@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" @@ -445,6 +670,11 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== +"@babel/helper-validator-option@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" + integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== + "@babel/helper-wrap-function@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200" @@ -455,6 +685,16 @@ "@babel/traverse" "^7.16.8" "@babel/types" "^7.16.8" +"@babel/helper-wrap-function@^7.18.9": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" + integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== + dependencies: + "@babel/helper-function-name" "^7.19.0" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.20.5" + "@babel/types" "^7.20.5" + "@babel/helpers@^7.14.8": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.8.tgz#839f88f463025886cff7f85a35297007e2da1b77" @@ -473,6 +713,15 @@ "@babel/traverse" "^7.17.9" "@babel/types" "^7.17.0" +"@babel/helpers@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.5.tgz#5bac66e084d7a4d2d9696bdf0175a93f7fb63c08" + integrity sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA== + dependencies: + "@babel/template" "^7.20.7" + "@babel/traverse" "^7.21.5" + "@babel/types" "^7.21.5" + "@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5", "@babel/highlight@^7.8.3": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" @@ -491,6 +740,15 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@babel/parser@^7.1.0", "@babel/parser@^7.14.5", "@babel/parser@^7.14.8": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.8.tgz#66fd41666b2d7b840bd5ace7f7416d5ac60208d4" @@ -501,6 +759,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.10.tgz#873b16db82a8909e0fbd7f115772f4b739f6ce78" integrity sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ== +"@babel/parser@^7.21.5", "@babel/parser@^7.21.8", "@babel/parser@^7.21.9": + version "7.21.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.9.tgz#ab18ea3b85b4bc33ba98a8d4c2032c557d23cf14" + integrity sha512-q5PNg/Bi1OpGgx5jYlvWZwAorZepEudDMCLtj967aeS7WMont7dUZI46M2XwcIQqvUlMxWfdLFu4S/qSxeUu5g== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz#4eda6d6c2a0aa79c70fa7b6da67763dfe2141050" @@ -508,6 +771,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" + integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz#cc001234dfc139ac45f6bcf801866198c8c72ff9" @@ -517,6 +787,15 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-proposal-optional-chaining" "^7.16.7" +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1" + integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-proposal-optional-chaining" "^7.20.7" + "@babel/plugin-proposal-async-generator-functions@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz#3bdd1ebbe620804ea9416706cd67d60787504bc8" @@ -526,6 +805,16 @@ "@babel/helper-remap-async-to-generator" "^7.16.8" "@babel/plugin-syntax-async-generators" "^7.8.4" +"@babel/plugin-proposal-async-generator-functions@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" + integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== + dependencies: + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-proposal-class-properties@^7.16.7", "@babel/plugin-proposal-class-properties@^7.8.3": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" @@ -534,6 +823,14 @@ "@babel/helper-create-class-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-proposal-class-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" + integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-proposal-class-static-block@^7.17.6": version "7.17.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz#164e8fd25f0d80fa48c5a4d1438a6629325ad83c" @@ -543,6 +840,15 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-class-static-block" "^7.14.5" +"@babel/plugin-proposal-class-static-block@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz#77bdd66fb7b605f3a61302d224bdfacf5547977d" + integrity sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-proposal-dynamic-import@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2" @@ -551,6 +857,14 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-dynamic-import" "^7.8.3" +"@babel/plugin-proposal-dynamic-import@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" + integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-proposal-export-namespace-from@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz#09de09df18445a5786a305681423ae63507a6163" @@ -559,6 +873,14 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" +"@babel/plugin-proposal-export-namespace-from@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" + integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-proposal-json-strings@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz#9732cb1d17d9a2626a08c5be25186c195b6fa6e8" @@ -567,6 +889,14 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-json-strings" "^7.8.3" +"@babel/plugin-proposal-json-strings@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" + integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-proposal-logical-assignment-operators@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz#be23c0ba74deec1922e639832904be0bea73cdea" @@ -575,6 +905,14 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" +"@babel/plugin-proposal-logical-assignment-operators@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83" + integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4", "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" @@ -583,6 +921,14 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" +"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" + integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-proposal-numeric-separator@^7.10.4", "@babel/plugin-proposal-numeric-separator@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9" @@ -591,6 +937,14 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-numeric-separator" "^7.10.4" +"@babel/plugin-proposal-numeric-separator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" + integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-proposal-object-rest-spread@^7.17.3", "@babel/plugin-proposal-object-rest-spread@^7.9.5": version "7.17.3" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz#d9eb649a54628a51701aef7e0ea3d17e2b9dd390" @@ -602,6 +956,17 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-transform-parameters" "^7.16.7" +"@babel/plugin-proposal-object-rest-spread@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" + integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== + dependencies: + "@babel/compat-data" "^7.20.5" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-transform-parameters" "^7.20.7" + "@babel/plugin-proposal-optional-catch-binding@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" @@ -610,6 +975,14 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" +"@babel/plugin-proposal-optional-catch-binding@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" + integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-proposal-optional-chaining@^7.11.0", "@babel/plugin-proposal-optional-chaining@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" @@ -619,6 +992,15 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" +"@babel/plugin-proposal-optional-chaining@^7.20.7", "@babel/plugin-proposal-optional-chaining@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" + integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-proposal-private-methods@^7.16.11": version "7.16.11" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz#e8df108288555ff259f4527dbe84813aac3a1c50" @@ -627,6 +1009,14 @@ "@babel/helper-create-class-features-plugin" "^7.16.10" "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-proposal-private-methods@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" + integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== + dependencies: + "@babel/helper-create-class-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz#b0b8cef543c2c3d57e59e2c611994861d46a3fce" @@ -637,6 +1027,16 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" +"@babel/plugin-proposal-private-property-in-object@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz#19496bd9883dd83c23c7d7fc45dcd9ad02dfa1dc" + integrity sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.21.0" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-proposal-unicode-property-regex@^7.16.7", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz#635d18eb10c6214210ffc5ff4932552de08188a2" @@ -645,6 +1045,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-proposal-unicode-property-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" + integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -687,7 +1095,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-import-meta@^7.8.3": +"@babel/plugin-syntax-import-assertions@^7.20.0": + version "7.20.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" + integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== + dependencies: + "@babel/helper-plugin-utils" "^7.19.0" + +"@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== @@ -778,6 +1193,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-arrow-functions@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz#9bb42a53de447936a57ba256fbf537fc312b6929" + integrity sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-transform-async-to-generator@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz#b83dff4b970cf41f1b819f8b49cc0cfbaa53a808" @@ -787,6 +1209,15 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-remap-async-to-generator" "^7.16.8" +"@babel/plugin-transform-async-to-generator@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" + integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== + dependencies: + "@babel/helper-module-imports" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-remap-async-to-generator" "^7.18.9" + "@babel/plugin-transform-block-scoped-functions@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" @@ -794,6 +1225,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-block-scoped-functions@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" + integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-block-scoping@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" @@ -801,6 +1239,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-block-scoping@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" + integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-transform-classes@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" @@ -815,6 +1260,21 @@ "@babel/helper-split-export-declaration" "^7.16.7" globals "^11.1.0" +"@babel/plugin-transform-classes@^7.21.0": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" + integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== + dependencies: + "@babel/helper-annotate-as-pure" "^7.18.6" + "@babel/helper-compilation-targets" "^7.20.7" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-optimise-call-expression" "^7.18.6" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-replace-supers" "^7.20.7" + "@babel/helper-split-export-declaration" "^7.18.6" + globals "^11.1.0" + "@babel/plugin-transform-computed-properties@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" @@ -822,6 +1282,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-computed-properties@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz#3a2d8bb771cd2ef1cd736435f6552fe502e11b44" + integrity sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/template" "^7.20.7" + "@babel/plugin-transform-destructuring@^7.17.7": version "7.17.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz#49dc2675a7afa9a5e4c6bdee636061136c3408d1" @@ -829,6 +1297,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-destructuring@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" + integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241" @@ -837,6 +1312,14 @@ "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-dotall-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" + integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-duplicate-keys@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz#2207e9ca8f82a0d36a5a67b6536e7ef8b08823c9" @@ -844,6 +1327,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-duplicate-keys@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" + integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-transform-exponentiation-operator@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" @@ -852,6 +1342,14 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-exponentiation-operator@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" + integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-for-of@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" @@ -859,6 +1357,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-for-of@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz#e890032b535f5a2e237a18535f56a9fdaa7b83fc" + integrity sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-transform-function-name@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" @@ -868,6 +1373,15 @@ "@babel/helper-function-name" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-function-name@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" + integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== + dependencies: + "@babel/helper-compilation-targets" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-transform-literals@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" @@ -875,6 +1389,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" + integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-transform-member-expression-literals@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" @@ -882,6 +1403,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-member-expression-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" + integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-modules-amd@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz#b28d323016a7daaae8609781d1f8c9da42b13186" @@ -891,6 +1419,14 @@ "@babel/helper-plugin-utils" "^7.16.7" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-amd@^7.20.11": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" + integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== + dependencies: + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-transform-modules-commonjs@^7.10.4", "@babel/plugin-transform-modules-commonjs@^7.17.9": version "7.17.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.9.tgz#274be1a2087beec0254d4abd4d86e52442e1e5b6" @@ -911,6 +1447,15 @@ "@babel/helper-simple-access" "^7.14.5" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-commonjs@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz#d69fb947eed51af91de82e4708f676864e5e47bc" + integrity sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ== + dependencies: + "@babel/helper-module-transforms" "^7.21.5" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-simple-access" "^7.21.5" + "@babel/plugin-transform-modules-systemjs@^7.17.8": version "7.17.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz#81fd834024fae14ea78fbe34168b042f38703859" @@ -922,6 +1467,16 @@ "@babel/helper-validator-identifier" "^7.16.7" babel-plugin-dynamic-import-node "^2.3.3" +"@babel/plugin-transform-modules-systemjs@^7.20.11": + version "7.20.11" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e" + integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== + dependencies: + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-module-transforms" "^7.20.11" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-validator-identifier" "^7.19.1" + "@babel/plugin-transform-modules-umd@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz#23dad479fa585283dbd22215bff12719171e7618" @@ -930,6 +1485,14 @@ "@babel/helper-module-transforms" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-modules-umd@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" + integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== + dependencies: + "@babel/helper-module-transforms" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex@^7.17.10": version "7.17.10" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.17.10.tgz#715dbcfafdb54ce8bccd3d12e8917296a4ba66a4" @@ -937,6 +1500,14 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.17.0" +"@babel/plugin-transform-named-capturing-groups-regex@^7.20.5": + version "7.20.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" + integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.20.5" + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-transform-new-target@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz#9967d89a5c243818e0800fdad89db22c5f514244" @@ -944,6 +1515,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-new-target@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" + integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-object-super@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" @@ -952,6 +1530,14 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-replace-supers" "^7.16.7" +"@babel/plugin-transform-object-super@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" + integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-replace-supers" "^7.18.6" + "@babel/plugin-transform-parameters@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" @@ -959,6 +1545,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.21.3": + version "7.21.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db" + integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/plugin-transform-property-literals@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" @@ -966,6 +1559,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-property-literals@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" + integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-react-display-name@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" @@ -1006,6 +1606,14 @@ dependencies: regenerator-transform "^0.15.0" +"@babel/plugin-transform-regenerator@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz#576c62f9923f94bcb1c855adc53561fd7913724e" + integrity sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + regenerator-transform "^0.15.1" + "@babel/plugin-transform-reserved-words@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz#1d798e078f7c5958eec952059c460b220a63f586" @@ -1013,6 +1621,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-reserved-words@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" + integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-runtime@^7.9.0": version "7.17.10" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.10.tgz#b89d821c55d61b5e3d3c3d1d636d8d5a81040ae1" @@ -1032,6 +1647,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-shorthand-properties@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" + integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-spread@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" @@ -1040,6 +1662,14 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" +"@babel/plugin-transform-spread@^7.20.7": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" + integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== + dependencies: + "@babel/helper-plugin-utils" "^7.20.2" + "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" + "@babel/plugin-transform-sticky-regex@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" @@ -1047,6 +1677,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-sticky-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" + integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== + dependencies: + "@babel/helper-plugin-utils" "^7.18.6" + "@babel/plugin-transform-template-literals@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" @@ -1054,6 +1691,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-template-literals@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" + integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-transform-typeof-symbol@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz#9cdbe622582c21368bd482b660ba87d5545d4f7e" @@ -1061,6 +1705,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-typeof-symbol@^7.18.9": + version "7.18.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" + integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== + dependencies: + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/plugin-transform-typescript@^7.16.7": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" @@ -1077,6 +1728,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-unicode-escapes@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz#1e55ed6195259b0e9061d81f5ef45a9b009fb7f2" + integrity sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg== + dependencies: + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/plugin-transform-unicode-regex@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" @@ -1085,6 +1743,96 @@ "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" +"@babel/plugin-transform-unicode-regex@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" + integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== + dependencies: + "@babel/helper-create-regexp-features-plugin" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.6" + +"@babel/preset-env@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.21.5.tgz#db2089d99efd2297716f018aeead815ac3decffb" + integrity sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg== + dependencies: + "@babel/compat-data" "^7.21.5" + "@babel/helper-compilation-targets" "^7.21.5" + "@babel/helper-plugin-utils" "^7.21.5" + "@babel/helper-validator-option" "^7.21.0" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.20.7" + "@babel/plugin-proposal-async-generator-functions" "^7.20.7" + "@babel/plugin-proposal-class-properties" "^7.18.6" + "@babel/plugin-proposal-class-static-block" "^7.21.0" + "@babel/plugin-proposal-dynamic-import" "^7.18.6" + "@babel/plugin-proposal-export-namespace-from" "^7.18.9" + "@babel/plugin-proposal-json-strings" "^7.18.6" + "@babel/plugin-proposal-logical-assignment-operators" "^7.20.7" + "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" + "@babel/plugin-proposal-numeric-separator" "^7.18.6" + "@babel/plugin-proposal-object-rest-spread" "^7.20.7" + "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" + "@babel/plugin-proposal-optional-chaining" "^7.21.0" + "@babel/plugin-proposal-private-methods" "^7.18.6" + "@babel/plugin-proposal-private-property-in-object" "^7.21.0" + "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" + "@babel/plugin-syntax-async-generators" "^7.8.4" + "@babel/plugin-syntax-class-properties" "^7.12.13" + "@babel/plugin-syntax-class-static-block" "^7.14.5" + "@babel/plugin-syntax-dynamic-import" "^7.8.3" + "@babel/plugin-syntax-export-namespace-from" "^7.8.3" + "@babel/plugin-syntax-import-assertions" "^7.20.0" + "@babel/plugin-syntax-import-meta" "^7.10.4" + "@babel/plugin-syntax-json-strings" "^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" + "@babel/plugin-syntax-numeric-separator" "^7.10.4" + "@babel/plugin-syntax-object-rest-spread" "^7.8.3" + "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" + "@babel/plugin-syntax-optional-chaining" "^7.8.3" + "@babel/plugin-syntax-private-property-in-object" "^7.14.5" + "@babel/plugin-syntax-top-level-await" "^7.14.5" + "@babel/plugin-transform-arrow-functions" "^7.21.5" + "@babel/plugin-transform-async-to-generator" "^7.20.7" + "@babel/plugin-transform-block-scoped-functions" "^7.18.6" + "@babel/plugin-transform-block-scoping" "^7.21.0" + "@babel/plugin-transform-classes" "^7.21.0" + "@babel/plugin-transform-computed-properties" "^7.21.5" + "@babel/plugin-transform-destructuring" "^7.21.3" + "@babel/plugin-transform-dotall-regex" "^7.18.6" + "@babel/plugin-transform-duplicate-keys" "^7.18.9" + "@babel/plugin-transform-exponentiation-operator" "^7.18.6" + "@babel/plugin-transform-for-of" "^7.21.5" + "@babel/plugin-transform-function-name" "^7.18.9" + "@babel/plugin-transform-literals" "^7.18.9" + "@babel/plugin-transform-member-expression-literals" "^7.18.6" + "@babel/plugin-transform-modules-amd" "^7.20.11" + "@babel/plugin-transform-modules-commonjs" "^7.21.5" + "@babel/plugin-transform-modules-systemjs" "^7.20.11" + "@babel/plugin-transform-modules-umd" "^7.18.6" + "@babel/plugin-transform-named-capturing-groups-regex" "^7.20.5" + "@babel/plugin-transform-new-target" "^7.18.6" + "@babel/plugin-transform-object-super" "^7.18.6" + "@babel/plugin-transform-parameters" "^7.21.3" + "@babel/plugin-transform-property-literals" "^7.18.6" + "@babel/plugin-transform-regenerator" "^7.21.5" + "@babel/plugin-transform-reserved-words" "^7.18.6" + "@babel/plugin-transform-shorthand-properties" "^7.18.6" + "@babel/plugin-transform-spread" "^7.20.7" + "@babel/plugin-transform-sticky-regex" "^7.18.6" + "@babel/plugin-transform-template-literals" "^7.18.9" + "@babel/plugin-transform-typeof-symbol" "^7.18.9" + "@babel/plugin-transform-unicode-escapes" "^7.21.5" + "@babel/plugin-transform-unicode-regex" "^7.18.6" + "@babel/preset-modules" "^0.1.5" + "@babel/types" "^7.21.5" + babel-plugin-polyfill-corejs2 "^0.3.3" + babel-plugin-polyfill-corejs3 "^0.6.0" + babel-plugin-polyfill-regenerator "^0.4.1" + core-js-compat "^3.25.1" + semver "^6.3.0" + "@babel/preset-env@^7.9.5": version "7.17.10" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.17.10.tgz#a81b093669e3eb6541bb81a23173c5963c5de69c" @@ -1197,6 +1945,11 @@ "@babel/helper-validator-option" "^7.16.7" "@babel/plugin-transform-typescript" "^7.16.7" +"@babel/regjsgen@^0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" + integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== + "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.13.8", "@babel/runtime@^7.13.9", "@babel/runtime@^7.14.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446" @@ -1243,6 +1996,15 @@ "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" +"@babel/template@^7.18.10", "@babel/template@^7.20.7": + version "7.21.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.21.9.tgz#bf8dad2859130ae46088a99c1f265394877446fb" + integrity sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/parser" "^7.21.9" + "@babel/types" "^7.21.5" + "@babel/traverse@^7.1.0", "@babel/traverse@^7.14.5", "@babel/traverse@^7.14.8": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.8.tgz#c0253f02677c5de1a8ff9df6b0aacbec7da1a8ce" @@ -1274,6 +2036,22 @@ debug "^4.1.0" globals "^11.1.0" +"@babel/traverse@^7.20.5", "@babel/traverse@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.5.tgz#ad22361d352a5154b498299d523cf72998a4b133" + integrity sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw== + dependencies: + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.5" + "@babel/helper-environment-visitor" "^7.21.5" + "@babel/helper-function-name" "^7.21.0" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.21.5" + "@babel/types" "^7.21.5" + debug "^4.1.0" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.14.5", "@babel/types@^7.14.8", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.8.tgz#38109de8fcadc06415fbd9b74df0065d4d41c728" @@ -1290,6 +2068,15 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" +"@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.5", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5": + version "7.21.5" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.5.tgz#18dfbd47c39d3904d5db3d3dc2cc80bedb60e5b6" + integrity sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q== + dependencies: + "@babel/helper-string-parser" "^7.21.5" + "@babel/helper-validator-identifier" "^7.19.1" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1670,6 +2457,20 @@ "@jridgewell/set-array" "^1.0.0" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + "@jridgewell/resolve-uri@^3.0.3": version "3.0.7" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" @@ -1680,11 +2481,29 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea" integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ== +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/sourcemap-codec@1.4.14": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.13" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== +"@jridgewell/trace-mapping@^0.3.17": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + "@jridgewell/trace-mapping@^0.3.9": version "0.3.13" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" @@ -1724,17 +2543,15 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@noble/curves@1.0.0", "@noble/curves@~1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.0.0.tgz#e40be8c7daf088aaf291887cbc73f43464a92932" - integrity sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw== - dependencies: - "@noble/hashes" "1.3.0" +"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" + integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== -"@noble/hashes@1.3.0", "@noble/hashes@~1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" - integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== +"@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" + integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1809,21 +2626,21 @@ resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== -"@scure/bip32@1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.0.tgz#6c8d980ef3f290987736acd0ee2e0f0d50068d87" - integrity sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q== +"@scure/bip32@1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.4.tgz#2c91a7be0156b15f26dd0c843a06a1917f129efd" + integrity sha512-m925ACYK0wPELsF7Z/VdLGmKj1StIeHraPMYB9xiAFiq/PnvqWd/99I0TQ2OZhjjlMDsDJeZlyXMWi0beaA7NA== dependencies: - "@noble/curves" "~1.0.0" - "@noble/hashes" "~1.3.0" + "@noble/hashes" "~1.2.0" + "@noble/secp256k1" "~1.7.0" "@scure/base" "~1.1.0" -"@scure/bip39@1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.0.tgz#a207e2ef96de354de7d0002292ba1503538fc77b" - integrity sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg== +"@scure/bip39@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" + integrity sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg== dependencies: - "@noble/hashes" "~1.3.0" + "@noble/hashes" "~1.2.0" "@scure/base" "~1.1.0" "@sinonjs/commons@^1.7.0": @@ -2932,6 +3749,16 @@ babel-jest@^26.3.0, babel-jest@^26.6.3: graceful-fs "^4.2.4" slash "^3.0.0" +babel-loader@8.2.5: + version "8.2.5" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.5.tgz#d45f585e654d5a5d90f5350a779d7647c5ed512e" + integrity sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ== + dependencies: + find-cache-dir "^3.3.1" + loader-utils "^2.0.0" + make-dir "^3.1.0" + schema-utils "^2.6.5" + babel-loader@^8.0.6: version "8.2.2" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.2.tgz#9363ce84c10c9a40e6c753748e1441b60c8a0b81" @@ -2979,6 +3806,15 @@ babel-plugin-polyfill-corejs2@^0.3.0: "@babel/helper-define-polyfill-provider" "^0.3.1" semver "^6.1.1" +babel-plugin-polyfill-corejs2@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" + integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== + dependencies: + "@babel/compat-data" "^7.17.7" + "@babel/helper-define-polyfill-provider" "^0.3.3" + semver "^6.1.1" + babel-plugin-polyfill-corejs3@^0.5.0: version "0.5.2" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" @@ -2987,6 +3823,14 @@ babel-plugin-polyfill-corejs3@^0.5.0: "@babel/helper-define-polyfill-provider" "^0.3.1" core-js-compat "^3.21.0" +babel-plugin-polyfill-corejs3@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" + integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.3" + core-js-compat "^3.25.1" + babel-plugin-polyfill-regenerator@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" @@ -2994,6 +3838,13 @@ babel-plugin-polyfill-regenerator@^0.3.0: dependencies: "@babel/helper-define-polyfill-provider" "^0.3.1" +babel-plugin-polyfill-regenerator@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" + integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== + dependencies: + "@babel/helper-define-polyfill-provider" "^0.3.3" + babel-plugin-syntax-jsx@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" @@ -3328,6 +4179,16 @@ browserslist@^4.20.2, browserslist@^4.20.3: node-releases "^2.0.3" picocolors "^1.0.0" +browserslist@^4.21.3, browserslist@^4.21.5: + version "4.21.5" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" + integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== + dependencies: + caniuse-lite "^1.0.30001449" + electron-to-chromium "^1.4.284" + node-releases "^2.0.8" + update-browserslist-db "^1.0.10" + bs-logger@0.x: version "0.2.6" resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" @@ -3566,6 +4427,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001023, caniuse-lite@^1.0.30001125, can resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz" integrity sha512-q7cpoPPvZYgtyC4VaBSN0Bt+PJ4c4EYRf0DrduInOz2SkFpHD5p3LnvEpqBp7UnJn+8x1Ogl1s38saUxe+ihQQ== +caniuse-lite@^1.0.30001449: + version "1.0.30001489" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz#ca82ee2d4e4dbf2bd2589c9360d3fcc2c7ba3bd8" + integrity sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ== + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -4128,6 +4994,13 @@ core-js-compat@^3.21.0, core-js-compat@^3.22.1: browserslist "^4.20.3" semver "7.0.0" +core-js-compat@^3.25.1: + version "3.30.2" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.2.tgz#83f136e375babdb8c80ad3c22d67c69098c1dd8b" + integrity sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA== + dependencies: + browserslist "^4.21.5" + core-js@^3.6.4: version "3.15.2" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.2.tgz#740660d2ff55ef34ce664d7e2455119c5bdd3d61" @@ -4991,6 +5864,11 @@ electron-to-chromium@^1.4.118: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz#186180a45617283f1c012284458510cd99d6787f" integrity sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA== +electron-to-chromium@^1.4.284: + version "1.4.407" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.407.tgz#087e2ab97b3eb092aa6217c05986086b7dd370cc" + integrity sha512-5smEvFSFYMv90tICOzRVP7Opp98DAC4KW7RRipg3BuNpGbbV3N+x24Zh3sbLb1T5haGtOSy/hrBfXsWnIM9aCg== + elliptic@^6.5.2, elliptic@^6.5.3: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" @@ -7484,6 +8362,11 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +json5@^2.2.2: + version "2.2.3" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" + integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== + jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" @@ -8374,6 +9257,11 @@ node-releases@^2.0.3: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.4.tgz#f38252370c43854dc48aa431c766c6c398f40476" integrity sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ== +node-releases@^2.0.8: + version "2.0.12" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.12.tgz#35627cc224a23bfb06fb3380f2b3afaaa7eb1039" + integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ== + normalize-html-whitespace@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/normalize-html-whitespace/-/normalize-html-whitespace-1.0.0.tgz#5e3c8e192f1b06c3b9eee4b7e7f28854c7601e34" @@ -8421,16 +9309,16 @@ normalize-url@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== -nostr-tools@^1.11.1: - version "1.11.1" - resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.11.1.tgz#7f479f86dc25ef4625747a33f8430f464aa225e5" - integrity sha512-b8BpCiD3wxjBZwrn0wc+CkVj6/7s4sQxp+Az7UkCG80mJu7xTspZsOoUP/geBNwZVYETzEwj+CPBvW8WIP8mBQ== +nostr-tools@1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.10.1.tgz#b52043b3031f4314478d0a3bfaa8ffb9cc4f98a0" + integrity sha512-zgTYJeuZQ3CDASsmBEcB5i6V6l0IaA6cjnll6OVik3FoZcvbCaL7yP8I40hYnOIi3KlJykV7jEF9fn8h1NzMnA== dependencies: - "@noble/curves" "1.0.0" - "@noble/hashes" "1.3.0" + "@noble/hashes" "1.2.0" + "@noble/secp256k1" "1.7.1" "@scure/base" "1.1.1" - "@scure/bip32" "1.3.0" - "@scure/bip39" "1.2.0" + "@scure/bip32" "1.1.4" + "@scure/bip39" "1.1.1" npm-run-path@^2.0.0: version "2.0.2" @@ -10332,6 +11220,13 @@ regenerate-unicode-properties@^10.0.1: dependencies: regenerate "^1.4.2" +regenerate-unicode-properties@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" + integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== + dependencies: + regenerate "^1.4.2" + regenerate@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" @@ -10354,6 +11249,13 @@ regenerator-transform@^0.15.0: dependencies: "@babel/runtime" "^7.8.4" +regenerator-transform@^0.15.1: + version "0.15.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" + integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== + dependencies: + "@babel/runtime" "^7.8.4" + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -10387,6 +11289,18 @@ regexpu-core@^5.0.1: unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.0.0" +regexpu-core@^5.3.1: + version "5.3.2" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" + integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== + dependencies: + "@babel/regjsgen" "^0.8.0" + regenerate "^1.4.2" + regenerate-unicode-properties "^10.1.0" + regjsparser "^0.9.1" + unicode-match-property-ecmascript "^2.0.0" + unicode-match-property-value-ecmascript "^2.1.0" + regjsgen@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" @@ -10399,6 +11313,13 @@ regjsparser@^0.8.2: dependencies: jsesc "~0.5.0" +regjsparser@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" + integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== + dependencies: + jsesc "~0.5.0" + relateurl@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" @@ -12157,6 +13078,11 @@ unicode-match-property-value-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== +unicode-match-property-value-ecmascript@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" + integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== + unicode-property-aliases-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" @@ -12229,6 +13155,14 @@ upath@^1.1.1: resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== +update-browserslist-db@^1.0.10: + version "1.0.11" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" + integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" From d14401cd632bf0149e65493a398a29e1505a3402 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Mon, 29 May 2023 12:07:57 +0500 Subject: [PATCH 008/179] WIP --- package.json | 4 +- razzle.config.js | 2 +- src/common/components/chat-box/index.tsx | 22 ++++--- src/common/pages/sign-up.tsx | 4 +- yarn.lock | 84 ++++++++++++++++++++---- 5 files changed, 87 insertions(+), 29 deletions(-) diff --git a/package.json b/package.json index 60459d8702c..7404970bdab 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@loadable/server": "^5.15.2", "@webscopeio/react-textarea-autocomplete": "^4.8.1", "axios": "^0.21.2", - "babel-loader": "8.2.5", + "babel-loader": "^9.1.2", "bs58": "^4.0.1", "connected-react-router": "^6.8.0", "cookie-parser": "^1.4.5", @@ -49,7 +49,7 @@ "moment": "^2.29.4", "node-cache": "^5.1.0", "node-html-parser": "^5.3.3", - "nostr-tools": "1.10.1", + "nostr-tools": "1.10.0", "numeral": "^2.0.6", "path-to-regexp": "^6.1.0", "qrcode": "^1.5.1", diff --git a/razzle.config.js b/razzle.config.js index cced0f2490b..fde7e864b1c 100644 --- a/razzle.config.js +++ b/razzle.config.js @@ -45,7 +45,7 @@ module.exports = { webpackConfig.module.rules.push({ test: /\.mjs$/, - exclude: /node_modules\/(?!(nostr-tools)\/).*/, + include: /node_modules\/nostr-tools\//, // test: /\.mjs$/, // include: /node_modules\/nostr-tools\//, use: { diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index a3366cb061c..601676da57c 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -52,12 +52,12 @@ export default function ChatBox({ activeUser }: Props) { const [show, setShow] = useState(false); useEffect(() => { - fetchProfileData(); + // fetchProfileData(); setShow(!!activeUser?.username); }, []); useEffect(() => { - fetchProfileData(); + // fetchProfileData(); setCurrentUser(""); setIsCurrentUser(false); setShow(!!activeUser?.username); @@ -208,6 +208,7 @@ export default function ChatBox({ activeUser }: Props) { ]; const fetchCurrentUserData = async () => { + console.log("Fetch Current data called"); const response = await getAccountFull(currentUser); setProfileData({ joiningData: response.created, @@ -216,15 +217,16 @@ export default function ChatBox({ activeUser }: Props) { }); }; - const fetchProfileData = async () => { - const response = await getAccountFull(activeUser?.username!); - setAccountData(response); - const { posting_json_metadata } = response; - const profile = JSON.parse(posting_json_metadata!).profile; + // const fetchProfileData = async () => { + // console.log("Fetch profile data called"); + // const response = await getAccountFull(activeUser?.username!); + // setAccountData(response); + // const { posting_json_metadata } = response; + // const profile = JSON.parse(posting_json_metadata!).profile; - const hasNoStrKey = !!profile.noStrKey && profile.noStrKey.trim() !== ""; - setHasUserJoinedChat(hasNoStrKey); - }; + // const hasNoStrKey = !!profile.noStrKey && profile.noStrKey.trim() !== ""; + // setHasUserJoinedChat(hasNoStrKey); + // }; const userClicked = (username: string) => { setIsCurrentUser(true); diff --git a/src/common/pages/sign-up.tsx b/src/common/pages/sign-up.tsx index 8fa1a1cd4ff..532ad15a83b 100644 --- a/src/common/pages/sign-up.tsx +++ b/src/common/pages/sign-up.tsx @@ -121,8 +121,8 @@ export const SignUp = (props: PageProps) => { }, [username, usernameTouched]); const regularRegister = async () => { - const key = createNoStrAccount(); - console.log(key); + // const key = createNoStrAccount(); + // console.log(key); setInProgress(true); try { const response = await signUp(username, email, referral); diff --git a/yarn.lock b/yarn.lock index ca5c2dde9d0..0f7ba36ffa9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2851,6 +2851,11 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818" integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg== +"@types/json-schema@^7.0.9": + version "7.0.12" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" + integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== + "@types/loadable__component@^5.13.4": version "5.13.4" resolved "https://registry.yarnpkg.com/@types/loadable__component/-/loadable__component-5.13.4.tgz#a4646b2406b1283efac1a9d9485824a905b33d4a" @@ -3443,11 +3448,25 @@ ajv-errors@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== +ajv-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -3458,6 +3477,16 @@ ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.0, ajv@^8.9.0: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -3749,16 +3778,6 @@ babel-jest@^26.3.0, babel-jest@^26.6.3: graceful-fs "^4.2.4" slash "^3.0.0" -babel-loader@8.2.5: - version "8.2.5" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.5.tgz#d45f585e654d5a5d90f5350a779d7647c5ed512e" - integrity sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ== - dependencies: - find-cache-dir "^3.3.1" - loader-utils "^2.0.0" - make-dir "^3.1.0" - schema-utils "^2.6.5" - babel-loader@^8.0.6: version "8.2.2" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.2.tgz#9363ce84c10c9a40e6c753748e1441b60c8a0b81" @@ -3769,6 +3788,14 @@ babel-loader@^8.0.6: make-dir "^3.1.0" schema-utils "^2.6.5" +babel-loader@^9.1.2: + version "9.1.2" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-9.1.2.tgz#a16a080de52d08854ee14570469905a5fc00d39c" + integrity sha512-mN14niXW43tddohGl8HPu5yfQq70iUThvFL/4QzESA7GcZoC0eVOhvWdQ8+3UlSjaDE9MVtsW9mxDY07W7VpVA== + dependencies: + find-cache-dir "^3.3.2" + schema-utils "^4.0.0" + babel-plugin-dynamic-import-node@^2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" @@ -6489,6 +6516,15 @@ find-cache-dir@^3.3.1: make-dir "^3.0.2" pkg-dir "^4.1.0" +find-cache-dir@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + find-up@4.1.0, find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -8345,6 +8381,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json3@^3.3.2: version "3.3.3" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" @@ -9309,10 +9350,10 @@ normalize-url@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== -nostr-tools@1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.10.1.tgz#b52043b3031f4314478d0a3bfaa8ffb9cc4f98a0" - integrity sha512-zgTYJeuZQ3CDASsmBEcB5i6V6l0IaA6cjnll6OVik3FoZcvbCaL7yP8I40hYnOIi3KlJykV7jEF9fn8h1NzMnA== +nostr-tools@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.10.0.tgz#58d4b713c3d499c177612e9ac66f73f36c9fb654" + integrity sha512-Bbkucv25M69TaV+slqz1Vfce8G5O3dqSQOqqOZd/LwhooBJp62qt5V3JvlCV55OUqxaucC2brggeIER5Wliy1w== dependencies: "@noble/hashes" "1.2.0" "@noble/secp256k1" "1.7.1" @@ -11379,6 +11420,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" @@ -11722,6 +11768,16 @@ schema-utils@^3.0.0: ajv "^6.12.5" ajv-keywords "^3.5.2" +schema-utils@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.1.tgz#eb2d042df8b01f4b5c276a2dfd41ba0faab72e8d" + integrity sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + screenfull@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba" From b13669b2a89bfc67d94a7c7122ed6d62dd310ffc Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Mon, 5 Jun 2023 12:07:44 +0500 Subject: [PATCH 009/179] Removing dependencies --- package.json | 5 - razzle.config.js | 24 -- yarn.lock | 1037 +--------------------------------------------- 3 files changed, 1 insertion(+), 1065 deletions(-) diff --git a/package.json b/package.json index 7404970bdab..942d5475b83 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,6 @@ "start:prod": "NODE_ENV=production node build/server.js" }, "dependencies": { - "@babel/core": "^7.21.8", - "@babel/preset-env": "^7.21.5", - "@babel/preset-modules": "^0.1.5", "@ecency/render-helper": "^2.2.24", "@ecency/render-helper-amp": "^1.1.0", "@firebase/analytics": "^0.8.0", @@ -27,7 +24,6 @@ "@loadable/server": "^5.15.2", "@webscopeio/react-textarea-autocomplete": "^4.8.1", "axios": "^0.21.2", - "babel-loader": "^9.1.2", "bs58": "^4.0.1", "connected-react-router": "^6.8.0", "cookie-parser": "^1.4.5", @@ -49,7 +45,6 @@ "moment": "^2.29.4", "node-cache": "^5.1.0", "node-html-parser": "^5.3.3", - "nostr-tools": "1.10.0", "numeral": "^2.0.6", "path-to-regexp": "^6.1.0", "qrcode": "^1.5.1", diff --git a/razzle.config.js b/razzle.config.js index fde7e864b1c..fcc4629bbc4 100644 --- a/razzle.config.js +++ b/razzle.config.js @@ -8,7 +8,6 @@ const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPl module.exports = { plugins: ["typescript", "scss"], resolve: { - extensions: [".js", ".jsx", ".json", ".ts", ".tsx", ".mjs"], alias: { styles: path.join(__dirname, "src/style/") } @@ -42,29 +41,6 @@ module.exports = { writeToDisk: { filename } }) ); - - webpackConfig.module.rules.push({ - test: /\.mjs$/, - include: /node_modules\/nostr-tools\//, - // test: /\.mjs$/, - // include: /node_modules\/nostr-tools\//, - use: { - loader: "babel-loader", - options: { - presets: [ - [ - "@babel/preset-env", - { - targets: { - node: "current" - } - } - ] - ] - } - } - }); - console.log(webpackConfig.module.rules); } // Enable SSR lazy-loading diff --git a/yarn.lock b/yarn.lock index 0f7ba36ffa9..b3c36272843 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,14 +10,6 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@ampproject/remapping@^2.2.0": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" - integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== - dependencies: - "@jridgewell/gen-mapping" "^0.3.0" - "@jridgewell/trace-mapping" "^0.3.9" - "@ampproject/toolbox-core@2.8.0", "@ampproject/toolbox-core@^2.8.0": version "2.8.0" resolved "https://registry.yarnpkg.com/@ampproject/toolbox-core/-/toolbox-core-2.8.0.tgz#4c291c470fa30c0c2f3c7952dbcd6df2a4ec0df9" @@ -96,13 +88,6 @@ dependencies: "@babel/highlight" "^7.16.7" -"@babel/code-frame@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" - integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== - dependencies: - "@babel/highlight" "^7.18.6" - "@babel/compat-data@^7.13.11", "@babel/compat-data@^7.17.0", "@babel/compat-data@^7.17.10": version "7.17.10" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.10.tgz#711dc726a492dfc8be8220028b1b92482362baab" @@ -113,11 +98,6 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.7.tgz#7b047d7a3a89a67d2258dc61f604f098f1bc7e08" integrity sha512-nS6dZaISCXJ3+518CWiBfEr//gHyMO02uDxBkXTKZDN5POruCnOZ1N4YBRZDCabwF8nZMWBpRxIicmXtBs+fvw== -"@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.5": - version "7.21.9" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.9.tgz#10a2e7fda4e51742c907938ac3b7229426515514" - integrity sha512-FUGed8kfhyWvbYug/Un/VPJD41rDIgoVVcR+FuzhzOYyRz5uED+Gd3SLZml0Uw2l2aHFb7ZgdW5mGA3G2cCCnQ== - "@babel/core@^7.1.0", "@babel/core@^7.7.5": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.8.tgz#20cdf7c84b5d86d83fac8710a8bc605a7ba3f010" @@ -160,27 +140,6 @@ json5 "^2.2.1" semver "^6.3.0" -"@babel/core@^7.21.8": - version "7.21.8" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.8.tgz#2a8c7f0f53d60100ba4c32470ba0281c92aa9aa4" - integrity sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.5" - "@babel/helper-compilation-targets" "^7.21.5" - "@babel/helper-module-transforms" "^7.21.5" - "@babel/helpers" "^7.21.5" - "@babel/parser" "^7.21.8" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.5" - "@babel/types" "^7.21.5" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.2" - semver "^6.3.0" - "@babel/generator@^7.14.8": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.14.8.tgz#bf86fd6af96cf3b74395a8ca409515f89423e070" @@ -199,16 +158,6 @@ "@jridgewell/gen-mapping" "^0.1.0" jsesc "^2.5.1" -"@babel/generator@^7.21.5": - version "7.21.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.9.tgz#3a1b706e07d836e204aee0650e8ee878d3aaa241" - integrity sha512-F3fZga2uv09wFdEjEQIJxXALXfz0+JaOb7SabvVMmjHxeVTuGW8wgE8Vp1Hd7O+zMTYtcfEISGRzPkeiaPPsvg== - dependencies: - "@babel/types" "^7.21.5" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - "@babel/helper-annotate-as-pure@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862" @@ -216,13 +165,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-annotate-as-pure@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" - integrity sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA== - dependencies: - "@babel/types" "^7.18.6" - "@babel/helper-builder-binary-assignment-operator-visitor@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.16.7.tgz#38d138561ea207f0f69eb1626a418e4f7e6a580b" @@ -231,13 +173,6 @@ "@babel/helper-explode-assignable-expression" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-builder-binary-assignment-operator-visitor@^7.18.6": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.21.5.tgz#817f73b6c59726ab39f6ba18c234268a519e5abb" - integrity sha512-uNrjKztPLkUk7bpCNC0jEKDJzzkvel/W+HguzbN8krA+LPfC1CEobJEvAvGka2A/M+ViOqXdcRL0GqPUJSjx9g== - dependencies: - "@babel/types" "^7.21.5" - "@babel/helper-compilation-targets@^7.13.0", "@babel/helper-compilation-targets@^7.16.7", "@babel/helper-compilation-targets@^7.17.10": version "7.17.10" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.17.10.tgz#09c63106d47af93cf31803db6bc49fef354e2ebe" @@ -258,17 +193,6 @@ browserslist "^4.16.6" semver "^6.3.0" -"@babel/helper-compilation-targets@^7.17.7", "@babel/helper-compilation-targets@^7.18.9", "@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz#631e6cc784c7b660417421349aac304c94115366" - integrity sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w== - dependencies: - "@babel/compat-data" "^7.21.5" - "@babel/helper-validator-option" "^7.21.0" - browserslist "^4.21.3" - lru-cache "^5.1.1" - semver "^6.3.0" - "@babel/helper-create-class-features-plugin@^7.16.10", "@babel/helper-create-class-features-plugin@^7.16.7", "@babel/helper-create-class-features-plugin@^7.17.6": version "7.17.9" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.9.tgz#71835d7fb9f38bd9f1378e40a4c0902fdc2ea49d" @@ -282,21 +206,6 @@ "@babel/helper-replace-supers" "^7.16.7" "@babel/helper-split-export-declaration" "^7.16.7" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0": - version "7.21.8" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.21.8.tgz#205b26330258625ef8869672ebca1e0dee5a0f02" - integrity sha512-+THiN8MqiH2AczyuZrnrKL6cAxFRRQDKW9h1YkBvbgKmAm6mwiacig1qT73DHIWMGo40GRnsEfN3LA+E6NtmSw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.21.5" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-member-expression-to-functions" "^7.21.5" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-replace-supers" "^7.21.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/helper-split-export-declaration" "^7.18.6" - semver "^6.3.0" - "@babel/helper-create-regexp-features-plugin@^7.16.7", "@babel/helper-create-regexp-features-plugin@^7.17.0": version "7.17.0" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.17.0.tgz#1dcc7d40ba0c6b6b25618997c5dbfd310f186fe1" @@ -305,15 +214,6 @@ "@babel/helper-annotate-as-pure" "^7.16.7" regexpu-core "^5.0.1" -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.20.5": - version "7.21.8" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.21.8.tgz#a7886f61c2e29e21fd4aaeaf1e473deba6b571dc" - integrity sha512-zGuSdedkFtsFHGbexAvNuipg1hbtitDLo2XE8/uf6Y9sOQV1xsYX/2pNbtedp/X0eU1pIt+kGvaqHCowkRbS5g== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - regexpu-core "^5.3.1" - semver "^6.3.0" - "@babel/helper-define-polyfill-provider@^0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.1.tgz#52411b445bdb2e676869e5a74960d2d3826d2665" @@ -328,18 +228,6 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-define-polyfill-provider@^0.3.3": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.3.tgz#8612e55be5d51f0cd1f36b4a5a83924e89884b7a" - integrity sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww== - dependencies: - "@babel/helper-compilation-targets" "^7.17.7" - "@babel/helper-plugin-utils" "^7.16.7" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - semver "^6.1.2" - "@babel/helper-environment-visitor@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.16.7.tgz#ff484094a839bde9d89cd63cba017d7aae80ecd7" @@ -347,11 +235,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-environment-visitor@^7.18.9", "@babel/helper-environment-visitor@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz#c769afefd41d171836f7cb63e295bedf689d48ba" - integrity sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ== - "@babel/helper-explode-assignable-expression@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.7.tgz#12a6d8522fdd834f194e868af6354e8650242b7a" @@ -376,14 +259,6 @@ "@babel/template" "^7.16.7" "@babel/types" "^7.17.0" -"@babel/helper-function-name@^7.18.9", "@babel/helper-function-name@^7.19.0", "@babel/helper-function-name@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz#d552829b10ea9f120969304023cd0645fa00b1b4" - integrity sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg== - dependencies: - "@babel/template" "^7.20.7" - "@babel/types" "^7.21.0" - "@babel/helper-get-function-arity@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.14.5.tgz#25fbfa579b0937eee1f3b805ece4ce398c431815" @@ -405,13 +280,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== - dependencies: - "@babel/types" "^7.18.6" - "@babel/helper-member-expression-to-functions@^7.14.5": version "7.14.7" resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz#97e56244beb94211fe277bd818e3a329c66f7970" @@ -426,13 +294,6 @@ dependencies: "@babel/types" "^7.17.0" -"@babel/helper-member-expression-to-functions@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.21.5.tgz#3b1a009af932e586af77c1030fba9ee0bde396c0" - integrity sha512-nIcGfgwpH2u4n9GG1HpStW5Ogx7x7ekiFHbjjFRKXbn5zUvqO9ZgotCO4x1aNbKn/x/xOUaXEhyNHCwtFCpxWg== - dependencies: - "@babel/types" "^7.21.5" - "@babel/helper-module-imports@^7.12.13", "@babel/helper-module-imports@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.16.7.tgz#25612a8091a999704461c8a222d0efec5d091437" @@ -447,13 +308,6 @@ dependencies: "@babel/types" "^7.14.5" -"@babel/helper-module-imports@^7.18.6", "@babel/helper-module-imports@^7.21.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" - integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== - dependencies: - "@babel/types" "^7.21.4" - "@babel/helper-module-transforms@^7.14.5", "@babel/helper-module-transforms@^7.14.8": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.14.8.tgz#d4279f7e3fd5f4d5d342d833af36d4dd87d7dc49" @@ -482,20 +336,6 @@ "@babel/traverse" "^7.17.3" "@babel/types" "^7.17.0" -"@babel/helper-module-transforms@^7.18.6", "@babel/helper-module-transforms@^7.20.11", "@babel/helper-module-transforms@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz#d937c82e9af68d31ab49039136a222b17ac0b420" - integrity sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw== - dependencies: - "@babel/helper-environment-visitor" "^7.21.5" - "@babel/helper-module-imports" "^7.21.4" - "@babel/helper-simple-access" "^7.21.5" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.5" - "@babel/types" "^7.21.5" - "@babel/helper-optimise-call-expression@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c" @@ -510,13 +350,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-optimise-call-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz#9369aa943ee7da47edab2cb4e838acf09d290ffe" - integrity sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA== - dependencies: - "@babel/types" "^7.18.6" - "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.8.0": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9" @@ -527,11 +360,6 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5" integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA== -"@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.21.5.tgz#345f2377d05a720a4e5ecfa39cbf4474a4daed56" - integrity sha512-0WDaIlXKOX/3KfBK/dwP1oQGiPh6rjMkT7HIRv7i5RR2VUMwrx5ZL0dwBkKx7+SW1zwNdgjHd34IMk5ZjTeHVg== - "@babel/helper-remap-async-to-generator@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3" @@ -541,16 +369,6 @@ "@babel/helper-wrap-function" "^7.16.8" "@babel/types" "^7.16.8" -"@babel/helper-remap-async-to-generator@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" - integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-wrap-function" "^7.18.9" - "@babel/types" "^7.18.9" - "@babel/helper-replace-supers@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.14.5.tgz#0ecc0b03c41cd567b4024ea016134c28414abb94" @@ -572,18 +390,6 @@ "@babel/traverse" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/helper-replace-supers@^7.18.6", "@babel/helper-replace-supers@^7.20.7", "@babel/helper-replace-supers@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.21.5.tgz#a6ad005ba1c7d9bc2973dfde05a1bba7065dde3c" - integrity sha512-/y7vBgsr9Idu4M6MprbOVUfH3vs7tsIfnVWv/Ml2xgwvyH6LTngdfbf5AdsKwkJy4zgy1X/kuNrEKvhhK28Yrg== - dependencies: - "@babel/helper-environment-visitor" "^7.21.5" - "@babel/helper-member-expression-to-functions" "^7.21.5" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.5" - "@babel/types" "^7.21.5" - "@babel/helper-simple-access@^7.14.5", "@babel/helper-simple-access@^7.14.8": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.14.8.tgz#82e1fec0644a7e775c74d305f212c39f8fe73924" @@ -598,13 +404,6 @@ dependencies: "@babel/types" "^7.17.0" -"@babel/helper-simple-access@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz#d697a7971a5c39eac32c7e63c0921c06c8a249ee" - integrity sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg== - dependencies: - "@babel/types" "^7.21.5" - "@babel/helper-skip-transparent-expression-wrappers@^7.16.0": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09" @@ -612,13 +411,6 @@ dependencies: "@babel/types" "^7.16.0" -"@babel/helper-skip-transparent-expression-wrappers@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.20.0.tgz#fbe4c52f60518cab8140d77101f0e63a8a230684" - integrity sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg== - dependencies: - "@babel/types" "^7.20.0" - "@babel/helper-split-export-declaration@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a" @@ -633,18 +425,6 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-string-parser@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz#2b3eea65443c6bdc31c22d037c65f6d323b6b2bd" - integrity sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w== - "@babel/helper-validator-identifier@^7.14.5", "@babel/helper-validator-identifier@^7.14.8": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz#32be33a756f29e278a0d644fa08a2c9e0f88a34c" @@ -655,11 +435,6 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== -"@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.19.1": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== - "@babel/helper-validator-option@^7.14.5": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3" @@ -670,11 +445,6 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23" integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ== -"@babel/helper-validator-option@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" - integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== - "@babel/helper-wrap-function@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200" @@ -685,16 +455,6 @@ "@babel/traverse" "^7.16.8" "@babel/types" "^7.16.8" -"@babel/helper-wrap-function@^7.18.9": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.20.5.tgz#75e2d84d499a0ab3b31c33bcfe59d6b8a45f62e3" - integrity sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q== - dependencies: - "@babel/helper-function-name" "^7.19.0" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.20.5" - "@babel/types" "^7.20.5" - "@babel/helpers@^7.14.8": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.8.tgz#839f88f463025886cff7f85a35297007e2da1b77" @@ -713,15 +473,6 @@ "@babel/traverse" "^7.17.9" "@babel/types" "^7.17.0" -"@babel/helpers@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.21.5.tgz#5bac66e084d7a4d2d9696bdf0175a93f7fb63c08" - integrity sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA== - dependencies: - "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.5" - "@babel/types" "^7.21.5" - "@babel/highlight@^7.10.4", "@babel/highlight@^7.14.5", "@babel/highlight@^7.8.3": version "7.14.5" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.5.tgz#6861a52f03966405001f6aa534a01a24d99e8cd9" @@ -740,15 +491,6 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" - js-tokens "^4.0.0" - "@babel/parser@^7.1.0", "@babel/parser@^7.14.5", "@babel/parser@^7.14.8": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.8.tgz#66fd41666b2d7b840bd5ace7f7416d5ac60208d4" @@ -759,11 +501,6 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.10.tgz#873b16db82a8909e0fbd7f115772f4b739f6ce78" integrity sha512-n2Q6i+fnJqzOaq2VkdXxy2TCPCWQZHiCo0XqmrCvDWcZQKRyZzYi4Z0yxlBuN0w+r2ZHmre+Q087DSrw3pbJDQ== -"@babel/parser@^7.21.5", "@babel/parser@^7.21.8", "@babel/parser@^7.21.9": - version "7.21.9" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.9.tgz#ab18ea3b85b4bc33ba98a8d4c2032c557d23cf14" - integrity sha512-q5PNg/Bi1OpGgx5jYlvWZwAorZepEudDMCLtj967aeS7WMont7dUZI46M2XwcIQqvUlMxWfdLFu4S/qSxeUu5g== - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz#4eda6d6c2a0aa79c70fa7b6da67763dfe2141050" @@ -771,13 +508,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" - integrity sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.16.7.tgz#cc001234dfc139ac45f6bcf801866198c8c72ff9" @@ -787,15 +517,6 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-proposal-optional-chaining" "^7.16.7" -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.20.7.tgz#d9c85589258539a22a901033853101a6198d4ef1" - integrity sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-proposal-optional-chaining" "^7.20.7" - "@babel/plugin-proposal-async-generator-functions@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz#3bdd1ebbe620804ea9416706cd67d60787504bc8" @@ -805,16 +526,6 @@ "@babel/helper-remap-async-to-generator" "^7.16.8" "@babel/plugin-syntax-async-generators" "^7.8.4" -"@babel/plugin-proposal-async-generator-functions@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.20.7.tgz#bfb7276d2d573cb67ba379984a2334e262ba5326" - integrity sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-proposal-class-properties@^7.16.7", "@babel/plugin-proposal-class-properties@^7.8.3": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.16.7.tgz#925cad7b3b1a2fcea7e59ecc8eb5954f961f91b0" @@ -823,14 +534,6 @@ "@babel/helper-create-class-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-proposal-class-properties@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" - integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-proposal-class-static-block@^7.17.6": version "7.17.6" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz#164e8fd25f0d80fa48c5a4d1438a6629325ad83c" @@ -840,15 +543,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-class-static-block" "^7.14.5" -"@babel/plugin-proposal-class-static-block@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.21.0.tgz#77bdd66fb7b605f3a61302d224bdfacf5547977d" - integrity sha512-XP5G9MWNUskFuP30IfFSEFB0Z6HzLIUcjYM4bYOPHXl7eiJ9HFv8tWj6TXTN5QODiEhDZAeI4hLok2iHFFV4hw== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.21.0" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-proposal-dynamic-import@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.16.7.tgz#c19c897eaa46b27634a00fee9fb7d829158704b2" @@ -857,14 +551,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-dynamic-import" "^7.8.3" -"@babel/plugin-proposal-dynamic-import@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz#72bcf8d408799f547d759298c3c27c7e7faa4d94" - integrity sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-proposal-export-namespace-from@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.16.7.tgz#09de09df18445a5786a305681423ae63507a6163" @@ -873,14 +559,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-export-namespace-from" "^7.8.3" -"@babel/plugin-proposal-export-namespace-from@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz#5f7313ab348cdb19d590145f9247540e94761203" - integrity sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-proposal-json-strings@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.16.7.tgz#9732cb1d17d9a2626a08c5be25186c195b6fa6e8" @@ -889,14 +567,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-json-strings" "^7.8.3" -"@babel/plugin-proposal-json-strings@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz#7e8788c1811c393aff762817e7dbf1ebd0c05f0b" - integrity sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-proposal-logical-assignment-operators@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.16.7.tgz#be23c0ba74deec1922e639832904be0bea73cdea" @@ -905,14 +575,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" -"@babel/plugin-proposal-logical-assignment-operators@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.20.7.tgz#dfbcaa8f7b4d37b51e8bfb46d94a5aea2bb89d83" - integrity sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-proposal-nullish-coalescing-operator@^7.10.4", "@babel/plugin-proposal-nullish-coalescing-operator@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.16.7.tgz#141fc20b6857e59459d430c850a0011e36561d99" @@ -921,14 +583,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" -"@babel/plugin-proposal-nullish-coalescing-operator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz#fdd940a99a740e577d6c753ab6fbb43fdb9467e1" - integrity sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-proposal-numeric-separator@^7.10.4", "@babel/plugin-proposal-numeric-separator@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.16.7.tgz#d6b69f4af63fb38b6ca2558442a7fb191236eba9" @@ -937,14 +591,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-numeric-separator" "^7.10.4" -"@babel/plugin-proposal-numeric-separator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz#899b14fbafe87f053d2c5ff05b36029c62e13c75" - integrity sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-proposal-object-rest-spread@^7.17.3", "@babel/plugin-proposal-object-rest-spread@^7.9.5": version "7.17.3" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.17.3.tgz#d9eb649a54628a51701aef7e0ea3d17e2b9dd390" @@ -956,17 +602,6 @@ "@babel/plugin-syntax-object-rest-spread" "^7.8.3" "@babel/plugin-transform-parameters" "^7.16.7" -"@babel/plugin-proposal-object-rest-spread@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz#aa662940ef425779c75534a5c41e9d936edc390a" - integrity sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg== - dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.20.7" - "@babel/plugin-proposal-optional-catch-binding@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.16.7.tgz#c623a430674ffc4ab732fd0a0ae7722b67cb74cf" @@ -975,14 +610,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-proposal-optional-catch-binding@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz#f9400d0e6a3ea93ba9ef70b09e72dd6da638a2cb" - integrity sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-proposal-optional-chaining@^7.11.0", "@babel/plugin-proposal-optional-chaining@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.16.7.tgz#7cd629564724816c0e8a969535551f943c64c39a" @@ -992,15 +619,6 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" "@babel/plugin-syntax-optional-chaining" "^7.8.3" -"@babel/plugin-proposal-optional-chaining@^7.20.7", "@babel/plugin-proposal-optional-chaining@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.21.0.tgz#886f5c8978deb7d30f678b2e24346b287234d3ea" - integrity sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-proposal-private-methods@^7.16.11": version "7.16.11" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.16.11.tgz#e8df108288555ff259f4527dbe84813aac3a1c50" @@ -1009,14 +627,6 @@ "@babel/helper-create-class-features-plugin" "^7.16.10" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-proposal-private-methods@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz#5209de7d213457548a98436fa2882f52f4be6bea" - integrity sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-proposal-private-property-in-object@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.16.7.tgz#b0b8cef543c2c3d57e59e2c611994861d46a3fce" @@ -1027,16 +637,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-proposal-private-property-in-object@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0.tgz#19496bd9883dd83c23c7d7fc45dcd9ad02dfa1dc" - integrity sha512-ha4zfehbJjc5MmXBlHec1igel5TJXXLDDRbuJ4+XT2TJcyD9/V1919BA8gMvsdHcNMBy4WBUBiRb3nw/EQUtBw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-create-class-features-plugin" "^7.21.0" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-proposal-unicode-property-regex@^7.16.7", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.16.7.tgz#635d18eb10c6214210ffc5ff4932552de08188a2" @@ -1045,14 +645,6 @@ "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-proposal-unicode-property-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" - integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -1095,14 +687,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-import-assertions@^7.20.0": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.20.0.tgz#bb50e0d4bea0957235390641209394e87bdb9cc4" - integrity sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ== - dependencies: - "@babel/helper-plugin-utils" "^7.19.0" - -"@babel/plugin-syntax-import-meta@^7.10.4", "@babel/plugin-syntax-import-meta@^7.8.3": +"@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== @@ -1193,13 +778,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-arrow-functions@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.21.5.tgz#9bb42a53de447936a57ba256fbf537fc312b6929" - integrity sha512-wb1mhwGOCaXHDTcsRYMKF9e5bbMgqwxtqa2Y1ifH96dXJPwbuLX9qHy3clhrxVqgMz7nyNXs8VkxdH8UBcjKqA== - dependencies: - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/plugin-transform-async-to-generator@^7.16.8": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz#b83dff4b970cf41f1b819f8b49cc0cfbaa53a808" @@ -1209,15 +787,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-remap-async-to-generator" "^7.16.8" -"@babel/plugin-transform-async-to-generator@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.20.7.tgz#dfee18623c8cb31deb796aa3ca84dda9cea94354" - integrity sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q== - dependencies: - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-remap-async-to-generator" "^7.18.9" - "@babel/plugin-transform-block-scoped-functions@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.16.7.tgz#4d0d57d9632ef6062cdf354bb717102ee042a620" @@ -1225,13 +794,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-block-scoped-functions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz#9187bf4ba302635b9d70d986ad70f038726216a8" - integrity sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-transform-block-scoping@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.16.7.tgz#f50664ab99ddeaee5bc681b8f3a6ea9d72ab4f87" @@ -1239,13 +801,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-block-scoping@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.21.0.tgz#e737b91037e5186ee16b76e7ae093358a5634f02" - integrity sha512-Mdrbunoh9SxwFZapeHVrwFmri16+oYotcZysSzhNIVDwIAb1UV+kvnxULSYq9J3/q5MDG+4X6w8QVgD1zhBXNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-transform-classes@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.16.7.tgz#8f4b9562850cd973de3b498f1218796eb181ce00" @@ -1260,21 +815,6 @@ "@babel/helper-split-export-declaration" "^7.16.7" globals "^11.1.0" -"@babel/plugin-transform-classes@^7.21.0": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.21.0.tgz#f469d0b07a4c5a7dbb21afad9e27e57b47031665" - integrity sha512-RZhbYTCEUAe6ntPehC4hlslPWosNHDox+vAs4On/mCLRLfoDVHf6hVEd7kuxr1RnHwJmxFfUM3cZiZRmPxJPXQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.18.6" - "@babel/helper-compilation-targets" "^7.20.7" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-optimise-call-expression" "^7.18.6" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-replace-supers" "^7.20.7" - "@babel/helper-split-export-declaration" "^7.18.6" - globals "^11.1.0" - "@babel/plugin-transform-computed-properties@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.16.7.tgz#66dee12e46f61d2aae7a73710f591eb3df616470" @@ -1282,14 +822,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-computed-properties@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.21.5.tgz#3a2d8bb771cd2ef1cd736435f6552fe502e11b44" - integrity sha512-TR653Ki3pAwxBxUe8srfF3e4Pe3FTA46uaNHYyQwIoM4oWKSoOZiDNyHJ0oIoDIUPSRQbQG7jzgVBX3FPVne1Q== - dependencies: - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/template" "^7.20.7" - "@babel/plugin-transform-destructuring@^7.17.7": version "7.17.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.7.tgz#49dc2675a7afa9a5e4c6bdee636061136c3408d1" @@ -1297,13 +829,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-destructuring@^7.21.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.21.3.tgz#73b46d0fd11cd6ef57dea8a381b1215f4959d401" - integrity sha512-bp6hwMFzuiE4HqYEyoGJ/V2LeIWn+hLVKc4pnj++E5XQptwhtcGmSayM029d/j2X1bPKGTlsyPwAubuU22KhMA== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-transform-dotall-regex@^7.16.7", "@babel/plugin-transform-dotall-regex@^7.4.4": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.16.7.tgz#6b2d67686fab15fb6a7fd4bd895d5982cfc81241" @@ -1312,14 +837,6 @@ "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-dotall-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz#b286b3e7aae6c7b861e45bed0a2fafd6b1a4fef8" - integrity sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-transform-duplicate-keys@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.16.7.tgz#2207e9ca8f82a0d36a5a67b6536e7ef8b08823c9" @@ -1327,13 +844,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-duplicate-keys@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz#687f15ee3cdad6d85191eb2a372c4528eaa0ae0e" - integrity sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-transform-exponentiation-operator@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.16.7.tgz#efa9862ef97e9e9e5f653f6ddc7b665e8536fe9b" @@ -1342,14 +852,6 @@ "@babel/helper-builder-binary-assignment-operator-visitor" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-exponentiation-operator@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz#421c705f4521888c65e91fdd1af951bfefd4dacd" - integrity sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-transform-for-of@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.16.7.tgz#649d639d4617dff502a9a158c479b3b556728d8c" @@ -1357,13 +859,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-for-of@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.21.5.tgz#e890032b535f5a2e237a18535f56a9fdaa7b83fc" - integrity sha512-nYWpjKW/7j/I/mZkGVgHJXh4bA1sfdFnJoOXwJuj4m3Q2EraO/8ZyrkCau9P5tbHQk01RMSt6KYLCsW7730SXQ== - dependencies: - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/plugin-transform-function-name@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.16.7.tgz#5ab34375c64d61d083d7d2f05c38d90b97ec65cf" @@ -1373,15 +868,6 @@ "@babel/helper-function-name" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-function-name@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz#cc354f8234e62968946c61a46d6365440fc764e0" - integrity sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ== - dependencies: - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-transform-literals@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.16.7.tgz#254c9618c5ff749e87cb0c0cef1a0a050c0bdab1" @@ -1389,13 +875,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-literals@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz#72796fdbef80e56fba3c6a699d54f0de557444bc" - integrity sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-transform-member-expression-literals@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.16.7.tgz#6e5dcf906ef8a098e630149d14c867dd28f92384" @@ -1403,13 +882,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-member-expression-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz#ac9fdc1a118620ac49b7e7a5d2dc177a1bfee88e" - integrity sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-transform-modules-amd@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.16.7.tgz#b28d323016a7daaae8609781d1f8c9da42b13186" @@ -1419,14 +891,6 @@ "@babel/helper-plugin-utils" "^7.16.7" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-amd@^7.20.11": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.20.11.tgz#3daccca8e4cc309f03c3a0c4b41dc4b26f55214a" - integrity sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g== - dependencies: - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-transform-modules-commonjs@^7.10.4", "@babel/plugin-transform-modules-commonjs@^7.17.9": version "7.17.9" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.17.9.tgz#274be1a2087beec0254d4abd4d86e52442e1e5b6" @@ -1447,15 +911,6 @@ "@babel/helper-simple-access" "^7.14.5" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-commonjs@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.21.5.tgz#d69fb947eed51af91de82e4708f676864e5e47bc" - integrity sha512-OVryBEgKUbtqMoB7eG2rs6UFexJi6Zj6FDXx+esBLPTCxCNxAY9o+8Di7IsUGJ+AVhp5ncK0fxWUBd0/1gPhrQ== - dependencies: - "@babel/helper-module-transforms" "^7.21.5" - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/helper-simple-access" "^7.21.5" - "@babel/plugin-transform-modules-systemjs@^7.17.8": version "7.17.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.17.8.tgz#81fd834024fae14ea78fbe34168b042f38703859" @@ -1467,16 +922,6 @@ "@babel/helper-validator-identifier" "^7.16.7" babel-plugin-dynamic-import-node "^2.3.3" -"@babel/plugin-transform-modules-systemjs@^7.20.11": - version "7.20.11" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.20.11.tgz#467ec6bba6b6a50634eea61c9c232654d8a4696e" - integrity sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw== - dependencies: - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-module-transforms" "^7.20.11" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-validator-identifier" "^7.19.1" - "@babel/plugin-transform-modules-umd@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.16.7.tgz#23dad479fa585283dbd22215bff12719171e7618" @@ -1485,14 +930,6 @@ "@babel/helper-module-transforms" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-modules-umd@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz#81d3832d6034b75b54e62821ba58f28ed0aab4b9" - integrity sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ== - dependencies: - "@babel/helper-module-transforms" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-transform-named-capturing-groups-regex@^7.17.10": version "7.17.10" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.17.10.tgz#715dbcfafdb54ce8bccd3d12e8917296a4ba66a4" @@ -1500,14 +937,6 @@ dependencies: "@babel/helper-create-regexp-features-plugin" "^7.17.0" -"@babel/plugin-transform-named-capturing-groups-regex@^7.20.5": - version "7.20.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.20.5.tgz#626298dd62ea51d452c3be58b285d23195ba69a8" - integrity sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.20.5" - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-transform-new-target@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.16.7.tgz#9967d89a5c243818e0800fdad89db22c5f514244" @@ -1515,13 +944,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-new-target@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz#d128f376ae200477f37c4ddfcc722a8a1b3246a8" - integrity sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-transform-object-super@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.16.7.tgz#ac359cf8d32cf4354d27a46867999490b6c32a94" @@ -1530,14 +952,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-replace-supers" "^7.16.7" -"@babel/plugin-transform-object-super@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz#fb3c6ccdd15939b6ff7939944b51971ddc35912c" - integrity sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-replace-supers" "^7.18.6" - "@babel/plugin-transform-parameters@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.16.7.tgz#a1721f55b99b736511cb7e0152f61f17688f331f" @@ -1545,13 +959,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-parameters@^7.20.7", "@babel/plugin-transform-parameters@^7.21.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.21.3.tgz#18fc4e797cf6d6d972cb8c411dbe8a809fa157db" - integrity sha512-Wxc+TvppQG9xWFYatvCGPvZ6+SIUxQ2ZdiBP+PHYMIjnPXD+uThCshaz4NZOnODAtBjjcVQQ/3OKs9LW28purQ== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/plugin-transform-property-literals@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.16.7.tgz#2dadac85155436f22c696c4827730e0fe1057a55" @@ -1559,13 +966,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-property-literals@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz#e22498903a483448e94e032e9bbb9c5ccbfc93a3" - integrity sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-transform-react-display-name@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.16.7.tgz#7b6d40d232f4c0f550ea348593db3b21e2404340" @@ -1606,14 +1006,6 @@ dependencies: regenerator-transform "^0.15.0" -"@babel/plugin-transform-regenerator@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.21.5.tgz#576c62f9923f94bcb1c855adc53561fd7913724e" - integrity sha512-ZoYBKDb6LyMi5yCsByQ5jmXsHAQDDYeexT1Szvlmui+lADvfSecr5Dxd/PkrTC3pAD182Fcju1VQkB4oCp9M+w== - dependencies: - "@babel/helper-plugin-utils" "^7.21.5" - regenerator-transform "^0.15.1" - "@babel/plugin-transform-reserved-words@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.16.7.tgz#1d798e078f7c5958eec952059c460b220a63f586" @@ -1621,13 +1013,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-reserved-words@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz#b1abd8ebf8edaa5f7fe6bbb8d2133d23b6a6f76a" - integrity sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-transform-runtime@^7.9.0": version "7.17.10" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.17.10.tgz#b89d821c55d61b5e3d3c3d1d636d8d5a81040ae1" @@ -1647,13 +1032,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-shorthand-properties@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz#6d6df7983d67b195289be24909e3f12a8f664dc9" - integrity sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-transform-spread@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.16.7.tgz#a303e2122f9f12e0105daeedd0f30fb197d8ff44" @@ -1662,14 +1040,6 @@ "@babel/helper-plugin-utils" "^7.16.7" "@babel/helper-skip-transparent-expression-wrappers" "^7.16.0" -"@babel/plugin-transform-spread@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.20.7.tgz#c2d83e0b99d3bf83e07b11995ee24bf7ca09401e" - integrity sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw== - dependencies: - "@babel/helper-plugin-utils" "^7.20.2" - "@babel/helper-skip-transparent-expression-wrappers" "^7.20.0" - "@babel/plugin-transform-sticky-regex@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.16.7.tgz#c84741d4f4a38072b9a1e2e3fd56d359552e8660" @@ -1677,13 +1047,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-sticky-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz#c6706eb2b1524028e317720339583ad0f444adcc" - integrity sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-transform-template-literals@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.16.7.tgz#f3d1c45d28967c8e80f53666fc9c3e50618217ab" @@ -1691,13 +1054,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-template-literals@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz#04ec6f10acdaa81846689d63fae117dd9c243a5e" - integrity sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-transform-typeof-symbol@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.16.7.tgz#9cdbe622582c21368bd482b660ba87d5545d4f7e" @@ -1705,13 +1061,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-typeof-symbol@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz#c8cea68263e45addcd6afc9091429f80925762c0" - integrity sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw== - dependencies: - "@babel/helper-plugin-utils" "^7.18.9" - "@babel/plugin-transform-typescript@^7.16.7": version "7.16.8" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.16.8.tgz#591ce9b6b83504903fa9dd3652c357c2ba7a1ee0" @@ -1728,13 +1077,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-unicode-escapes@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.21.5.tgz#1e55ed6195259b0e9061d81f5ef45a9b009fb7f2" - integrity sha512-LYm/gTOwZqsYohlvFUe/8Tujz75LqqVC2w+2qPHLR+WyWHGCZPN1KBpJCJn+4Bk4gOkQy/IXKIge6az5MqwlOg== - dependencies: - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/plugin-transform-unicode-regex@^7.16.7": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.16.7.tgz#0f7aa4a501198976e25e82702574c34cfebe9ef2" @@ -1743,96 +1085,6 @@ "@babel/helper-create-regexp-features-plugin" "^7.16.7" "@babel/helper-plugin-utils" "^7.16.7" -"@babel/plugin-transform-unicode-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz#194317225d8c201bbae103364ffe9e2cea36cdca" - integrity sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/preset-env@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.21.5.tgz#db2089d99efd2297716f018aeead815ac3decffb" - integrity sha512-wH00QnTTldTbf/IefEVyChtRdw5RJvODT/Vb4Vcxq1AZvtXj6T0YeX0cAcXhI6/BdGuiP3GcNIL4OQbI2DVNxg== - dependencies: - "@babel/compat-data" "^7.21.5" - "@babel/helper-compilation-targets" "^7.21.5" - "@babel/helper-plugin-utils" "^7.21.5" - "@babel/helper-validator-option" "^7.21.0" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.20.7" - "@babel/plugin-proposal-async-generator-functions" "^7.20.7" - "@babel/plugin-proposal-class-properties" "^7.18.6" - "@babel/plugin-proposal-class-static-block" "^7.21.0" - "@babel/plugin-proposal-dynamic-import" "^7.18.6" - "@babel/plugin-proposal-export-namespace-from" "^7.18.9" - "@babel/plugin-proposal-json-strings" "^7.18.6" - "@babel/plugin-proposal-logical-assignment-operators" "^7.20.7" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.18.6" - "@babel/plugin-proposal-numeric-separator" "^7.18.6" - "@babel/plugin-proposal-object-rest-spread" "^7.20.7" - "@babel/plugin-proposal-optional-catch-binding" "^7.18.6" - "@babel/plugin-proposal-optional-chaining" "^7.21.0" - "@babel/plugin-proposal-private-methods" "^7.18.6" - "@babel/plugin-proposal-private-property-in-object" "^7.21.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.18.6" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.20.0" - "@babel/plugin-syntax-import-meta" "^7.10.4" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-transform-arrow-functions" "^7.21.5" - "@babel/plugin-transform-async-to-generator" "^7.20.7" - "@babel/plugin-transform-block-scoped-functions" "^7.18.6" - "@babel/plugin-transform-block-scoping" "^7.21.0" - "@babel/plugin-transform-classes" "^7.21.0" - "@babel/plugin-transform-computed-properties" "^7.21.5" - "@babel/plugin-transform-destructuring" "^7.21.3" - "@babel/plugin-transform-dotall-regex" "^7.18.6" - "@babel/plugin-transform-duplicate-keys" "^7.18.9" - "@babel/plugin-transform-exponentiation-operator" "^7.18.6" - "@babel/plugin-transform-for-of" "^7.21.5" - "@babel/plugin-transform-function-name" "^7.18.9" - "@babel/plugin-transform-literals" "^7.18.9" - "@babel/plugin-transform-member-expression-literals" "^7.18.6" - "@babel/plugin-transform-modules-amd" "^7.20.11" - "@babel/plugin-transform-modules-commonjs" "^7.21.5" - "@babel/plugin-transform-modules-systemjs" "^7.20.11" - "@babel/plugin-transform-modules-umd" "^7.18.6" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.20.5" - "@babel/plugin-transform-new-target" "^7.18.6" - "@babel/plugin-transform-object-super" "^7.18.6" - "@babel/plugin-transform-parameters" "^7.21.3" - "@babel/plugin-transform-property-literals" "^7.18.6" - "@babel/plugin-transform-regenerator" "^7.21.5" - "@babel/plugin-transform-reserved-words" "^7.18.6" - "@babel/plugin-transform-shorthand-properties" "^7.18.6" - "@babel/plugin-transform-spread" "^7.20.7" - "@babel/plugin-transform-sticky-regex" "^7.18.6" - "@babel/plugin-transform-template-literals" "^7.18.9" - "@babel/plugin-transform-typeof-symbol" "^7.18.9" - "@babel/plugin-transform-unicode-escapes" "^7.21.5" - "@babel/plugin-transform-unicode-regex" "^7.18.6" - "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.21.5" - babel-plugin-polyfill-corejs2 "^0.3.3" - babel-plugin-polyfill-corejs3 "^0.6.0" - babel-plugin-polyfill-regenerator "^0.4.1" - core-js-compat "^3.25.1" - semver "^6.3.0" - "@babel/preset-env@^7.9.5": version "7.17.10" resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.17.10.tgz#a81b093669e3eb6541bb81a23173c5963c5de69c" @@ -1945,11 +1197,6 @@ "@babel/helper-validator-option" "^7.16.7" "@babel/plugin-transform-typescript" "^7.16.7" -"@babel/regjsgen@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" - integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== - "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.13.8", "@babel/runtime@^7.13.9", "@babel/runtime@^7.14.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.8.tgz#7119a56f421018852694290b9f9148097391b446" @@ -1996,15 +1243,6 @@ "@babel/parser" "^7.16.7" "@babel/types" "^7.16.7" -"@babel/template@^7.18.10", "@babel/template@^7.20.7": - version "7.21.9" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.21.9.tgz#bf8dad2859130ae46088a99c1f265394877446fb" - integrity sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ== - dependencies: - "@babel/code-frame" "^7.21.4" - "@babel/parser" "^7.21.9" - "@babel/types" "^7.21.5" - "@babel/traverse@^7.1.0", "@babel/traverse@^7.14.5", "@babel/traverse@^7.14.8": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.14.8.tgz#c0253f02677c5de1a8ff9df6b0aacbec7da1a8ce" @@ -2036,22 +1274,6 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.20.5", "@babel/traverse@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.5.tgz#ad22361d352a5154b498299d523cf72998a4b133" - integrity sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw== - dependencies: - "@babel/code-frame" "^7.21.4" - "@babel/generator" "^7.21.5" - "@babel/helper-environment-visitor" "^7.21.5" - "@babel/helper-function-name" "^7.21.0" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.5" - "@babel/types" "^7.21.5" - debug "^4.1.0" - globals "^11.1.0" - "@babel/types@^7.0.0", "@babel/types@^7.14.5", "@babel/types@^7.14.8", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.14.8" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.8.tgz#38109de8fcadc06415fbd9b74df0065d4d41c728" @@ -2068,15 +1290,6 @@ "@babel/helper-validator-identifier" "^7.16.7" to-fast-properties "^2.0.0" -"@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.20.0", "@babel/types@^7.20.5", "@babel/types@^7.21.0", "@babel/types@^7.21.4", "@babel/types@^7.21.5": - version "7.21.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.5.tgz#18dfbd47c39d3904d5db3d3dc2cc80bedb60e5b6" - integrity sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q== - dependencies: - "@babel/helper-string-parser" "^7.21.5" - "@babel/helper-validator-identifier" "^7.19.1" - to-fast-properties "^2.0.0" - "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -2457,20 +1670,6 @@ "@jridgewell/set-array" "^1.0.0" "@jridgewell/sourcemap-codec" "^1.4.10" -"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" - integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== - dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/resolve-uri@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== - "@jridgewell/resolve-uri@^3.0.3": version "3.0.7" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" @@ -2481,29 +1680,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea" integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ== -"@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== - -"@jridgewell/sourcemap-codec@1.4.14": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.13" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== -"@jridgewell/trace-mapping@^0.3.17": - version "0.3.18" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" - integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== - dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" - "@jridgewell/trace-mapping@^0.3.9": version "0.3.13" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" @@ -2543,16 +1724,6 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" - integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== - -"@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" - integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== - "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -2621,28 +1792,6 @@ dependencies: dequal "^2.0.2" -"@scure/base@1.1.1", "@scure/base@~1.1.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" - integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== - -"@scure/bip32@1.1.4": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.4.tgz#2c91a7be0156b15f26dd0c843a06a1917f129efd" - integrity sha512-m925ACYK0wPELsF7Z/VdLGmKj1StIeHraPMYB9xiAFiq/PnvqWd/99I0TQ2OZhjjlMDsDJeZlyXMWi0beaA7NA== - dependencies: - "@noble/hashes" "~1.2.0" - "@noble/secp256k1" "~1.7.0" - "@scure/base" "~1.1.0" - -"@scure/bip39@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" - integrity sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg== - dependencies: - "@noble/hashes" "~1.2.0" - "@scure/base" "~1.1.0" - "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -2851,11 +2000,6 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.8.tgz#edf1bf1dbf4e04413ca8e5b17b3b7d7d54b59818" integrity sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg== -"@types/json-schema@^7.0.9": - version "7.0.12" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" - integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== - "@types/loadable__component@^5.13.4": version "5.13.4" resolved "https://registry.yarnpkg.com/@types/loadable__component/-/loadable__component-5.13.4.tgz#a4646b2406b1283efac1a9d9485824a905b33d4a" @@ -3448,25 +2592,11 @@ ajv-errors@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== -ajv-formats@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" - integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== - dependencies: - ajv "^8.0.0" - ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv-keywords@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" - integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== - dependencies: - fast-deep-equal "^3.1.3" - ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -3477,16 +2607,6 @@ ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.9.0: - version "8.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" - integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -3788,14 +2908,6 @@ babel-loader@^8.0.6: make-dir "^3.1.0" schema-utils "^2.6.5" -babel-loader@^9.1.2: - version "9.1.2" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-9.1.2.tgz#a16a080de52d08854ee14570469905a5fc00d39c" - integrity sha512-mN14niXW43tddohGl8HPu5yfQq70iUThvFL/4QzESA7GcZoC0eVOhvWdQ8+3UlSjaDE9MVtsW9mxDY07W7VpVA== - dependencies: - find-cache-dir "^3.3.2" - schema-utils "^4.0.0" - babel-plugin-dynamic-import-node@^2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" @@ -3833,15 +2945,6 @@ babel-plugin-polyfill-corejs2@^0.3.0: "@babel/helper-define-polyfill-provider" "^0.3.1" semver "^6.1.1" -babel-plugin-polyfill-corejs2@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.3.tgz#5d1bd3836d0a19e1b84bbf2d9640ccb6f951c122" - integrity sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q== - dependencies: - "@babel/compat-data" "^7.17.7" - "@babel/helper-define-polyfill-provider" "^0.3.3" - semver "^6.1.1" - babel-plugin-polyfill-corejs3@^0.5.0: version "0.5.2" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.2.tgz#aabe4b2fa04a6e038b688c5e55d44e78cd3a5f72" @@ -3850,14 +2953,6 @@ babel-plugin-polyfill-corejs3@^0.5.0: "@babel/helper-define-polyfill-provider" "^0.3.1" core-js-compat "^3.21.0" -babel-plugin-polyfill-corejs3@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.6.0.tgz#56ad88237137eade485a71b52f72dbed57c6230a" - integrity sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.3" - core-js-compat "^3.25.1" - babel-plugin-polyfill-regenerator@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" @@ -3865,13 +2960,6 @@ babel-plugin-polyfill-regenerator@^0.3.0: dependencies: "@babel/helper-define-polyfill-provider" "^0.3.1" -babel-plugin-polyfill-regenerator@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.1.tgz#390f91c38d90473592ed43351e801a9d3e0fd747" - integrity sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.3" - babel-plugin-syntax-jsx@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" @@ -4206,16 +3294,6 @@ browserslist@^4.20.2, browserslist@^4.20.3: node-releases "^2.0.3" picocolors "^1.0.0" -browserslist@^4.21.3, browserslist@^4.21.5: - version "4.21.5" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.5.tgz#75c5dae60063ee641f977e00edd3cfb2fb7af6a7" - integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== - dependencies: - caniuse-lite "^1.0.30001449" - electron-to-chromium "^1.4.284" - node-releases "^2.0.8" - update-browserslist-db "^1.0.10" - bs-logger@0.x: version "0.2.6" resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" @@ -4454,11 +3532,6 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001023, caniuse-lite@^1.0.30001125, can resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001480.tgz" integrity sha512-q7cpoPPvZYgtyC4VaBSN0Bt+PJ4c4EYRf0DrduInOz2SkFpHD5p3LnvEpqBp7UnJn+8x1Ogl1s38saUxe+ihQQ== -caniuse-lite@^1.0.30001449: - version "1.0.30001489" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz#ca82ee2d4e4dbf2bd2589c9360d3fcc2c7ba3bd8" - integrity sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ== - capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -5021,13 +4094,6 @@ core-js-compat@^3.21.0, core-js-compat@^3.22.1: browserslist "^4.20.3" semver "7.0.0" -core-js-compat@^3.25.1: - version "3.30.2" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.30.2.tgz#83f136e375babdb8c80ad3c22d67c69098c1dd8b" - integrity sha512-nriW1nuJjUgvkEjIot1Spwakz52V9YkYHZAQG6A1eCgC8AA1p0zngrQEP9R0+V6hji5XilWKG1Bd0YRppmGimA== - dependencies: - browserslist "^4.21.5" - core-js@^3.6.4: version "3.15.2" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.2.tgz#740660d2ff55ef34ce664d7e2455119c5bdd3d61" @@ -5891,11 +4957,6 @@ electron-to-chromium@^1.4.118: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.137.tgz#186180a45617283f1c012284458510cd99d6787f" integrity sha512-0Rcpald12O11BUogJagX3HsCN3FE83DSqWjgXoHo5a72KUKMSfI39XBgJpgNNxS9fuGzytaFjE06kZkiVFy2qA== -electron-to-chromium@^1.4.284: - version "1.4.407" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.407.tgz#087e2ab97b3eb092aa6217c05986086b7dd370cc" - integrity sha512-5smEvFSFYMv90tICOzRVP7Opp98DAC4KW7RRipg3BuNpGbbV3N+x24Zh3sbLb1T5haGtOSy/hrBfXsWnIM9aCg== - elliptic@^6.5.2, elliptic@^6.5.3: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" @@ -6516,15 +5577,6 @@ find-cache-dir@^3.3.1: make-dir "^3.0.2" pkg-dir "^4.1.0" -find-cache-dir@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" - integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== - dependencies: - commondir "^1.0.1" - make-dir "^3.0.2" - pkg-dir "^4.1.0" - find-up@4.1.0, find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -8381,11 +7433,6 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - json3@^3.3.2: version "3.3.3" resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" @@ -8403,11 +7450,6 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" -json5@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae" @@ -9298,11 +8340,6 @@ node-releases@^2.0.3: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.4.tgz#f38252370c43854dc48aa431c766c6c398f40476" integrity sha512-gbMzqQtTtDz/00jQzZ21PQzdI9PyLYqUSvD0p3naOhX4odFji0ZxYdnVwPTxmSwkmxhcFImpozceidSG+AgoPQ== -node-releases@^2.0.8: - version "2.0.12" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.12.tgz#35627cc224a23bfb06fb3380f2b3afaaa7eb1039" - integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ== - normalize-html-whitespace@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/normalize-html-whitespace/-/normalize-html-whitespace-1.0.0.tgz#5e3c8e192f1b06c3b9eee4b7e7f28854c7601e34" @@ -9350,17 +8387,6 @@ normalize-url@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== -nostr-tools@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.10.0.tgz#58d4b713c3d499c177612e9ac66f73f36c9fb654" - integrity sha512-Bbkucv25M69TaV+slqz1Vfce8G5O3dqSQOqqOZd/LwhooBJp62qt5V3JvlCV55OUqxaucC2brggeIER5Wliy1w== - dependencies: - "@noble/hashes" "1.2.0" - "@noble/secp256k1" "1.7.1" - "@scure/base" "1.1.1" - "@scure/bip32" "1.1.4" - "@scure/bip39" "1.1.1" - npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -11268,13 +10294,6 @@ regenerate-unicode-properties@^10.0.1: dependencies: regenerate "^1.4.2" -regenerate-unicode-properties@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" - integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== - dependencies: - regenerate "^1.4.2" - regenerate@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" @@ -11297,13 +10316,6 @@ regenerator-transform@^0.15.0: dependencies: "@babel/runtime" "^7.8.4" -regenerator-transform@^0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" - integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== - dependencies: - "@babel/runtime" "^7.8.4" - regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -11337,18 +10349,6 @@ regexpu-core@^5.0.1: unicode-match-property-ecmascript "^2.0.0" unicode-match-property-value-ecmascript "^2.0.0" -regexpu-core@^5.3.1: - version "5.3.2" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" - integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== - dependencies: - "@babel/regjsgen" "^0.8.0" - regenerate "^1.4.2" - regenerate-unicode-properties "^10.1.0" - regjsparser "^0.9.1" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.1.0" - regjsgen@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.6.0.tgz#83414c5354afd7d6627b16af5f10f41c4e71808d" @@ -11361,13 +10361,6 @@ regjsparser@^0.8.2: dependencies: jsesc "~0.5.0" -regjsparser@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" - integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== - dependencies: - jsesc "~0.5.0" - relateurl@^0.2.7: version "0.2.7" resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" @@ -11420,11 +10413,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - require-main-filename@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" @@ -11768,16 +10756,6 @@ schema-utils@^3.0.0: ajv "^6.12.5" ajv-keywords "^3.5.2" -schema-utils@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-4.0.1.tgz#eb2d042df8b01f4b5c276a2dfd41ba0faab72e8d" - integrity sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ== - dependencies: - "@types/json-schema" "^7.0.9" - ajv "^8.9.0" - ajv-formats "^2.1.1" - ajv-keywords "^5.1.0" - screenfull@^5.1.0: version "5.2.0" resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba" @@ -13141,11 +12119,6 @@ unicode-match-property-value-ecmascript@^2.0.0: resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.0.0.tgz#1a01aa57247c14c568b89775a54938788189a714" integrity sha512-7Yhkc0Ye+t4PNYzOGKedDhXbYIBe1XEQYQxOPyhcXNMJ0WCABqqj6ckydd6pWRZTHV4GuCPKdBAUiMc60tsKVw== -unicode-match-property-value-ecmascript@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" - integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== - unicode-property-aliases-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.0.0.tgz#0a36cb9a585c4f6abd51ad1deddb285c165297c8" @@ -13218,14 +12191,6 @@ upath@^1.1.1: resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== -update-browserslist-db@^1.0.10: - version "1.0.11" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" - integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" From 035a0d64fac19367764ef8a8ad60e830a2905b91 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Thu, 15 Jun 2023 16:54:50 +0500 Subject: [PATCH 010/179] WIP --- package.json | 2 + src/client/index.tsx | 5 + src/common/app.tsx | 4 +- src/common/components/chat-box/index.scss | 5 +- src/common/components/chat-box/index.tsx | 335 ++++----- src/common/helper/chat-utils.ts | 50 +- src/common/helper/message-event-emitter.ts | 112 +++ src/common/helper/message-global.ts | 16 + src/common/helper/message-helper.ts | 798 +++++++++++++++++++++ src/common/pages/sign-up.tsx | 2 - src/common/store/actions.ts | 5 +- src/common/store/chat/index.ts | 63 ++ src/common/store/chat/types.ts | 41 ++ src/common/store/index.ts | 4 +- src/common/store/initial-state.ts | 4 +- src/lib/nostr-tools/.eslintrc.json | 145 ++++ src/lib/nostr-tools/.gitignore | 7 + src/lib/nostr-tools/.prettierrc.yaml | 10 + src/lib/nostr-tools/README.md | 192 +++++ src/lib/nostr-tools/build.js | 41 ++ src/lib/nostr-tools/event.ts | 98 +++ src/lib/nostr-tools/filter.ts | 45 ++ src/lib/nostr-tools/index.ts | 21 + src/lib/nostr-tools/keys.ts | 9 + src/lib/nostr-tools/nip04.ts | 42 ++ src/lib/nostr-tools/nip05.ts | 48 ++ src/lib/nostr-tools/nip06.ts | 19 + src/lib/nostr-tools/nip19.ts | 127 ++++ src/lib/nostr-tools/nip26.ts | 71 ++ src/lib/nostr-tools/nip42.ts | 46 ++ src/lib/nostr-tools/package.json | 40 ++ src/lib/nostr-tools/path.ts | 223 ++++++ src/lib/nostr-tools/pool.ts | 206 ++++++ src/lib/nostr-tools/relay.ts | 584 +++++++++++++++ src/lib/nostr-tools/tsconfig.json | 15 + src/lib/nostr-tools/utils.ts | 2 + src/providers/message-provider-types.ts | 55 ++ src/providers/message-provider.tsx | 300 ++++++++ yarn.lock | 107 +++ 39 files changed, 3681 insertions(+), 218 deletions(-) create mode 100644 src/common/helper/message-event-emitter.ts create mode 100644 src/common/helper/message-global.ts create mode 100644 src/common/helper/message-helper.ts create mode 100644 src/common/store/chat/index.ts create mode 100644 src/common/store/chat/types.ts create mode 100644 src/lib/nostr-tools/.eslintrc.json create mode 100644 src/lib/nostr-tools/.gitignore create mode 100644 src/lib/nostr-tools/.prettierrc.yaml create mode 100644 src/lib/nostr-tools/README.md create mode 100755 src/lib/nostr-tools/build.js create mode 100644 src/lib/nostr-tools/event.ts create mode 100644 src/lib/nostr-tools/filter.ts create mode 100644 src/lib/nostr-tools/index.ts create mode 100644 src/lib/nostr-tools/keys.ts create mode 100644 src/lib/nostr-tools/nip04.ts create mode 100644 src/lib/nostr-tools/nip05.ts create mode 100644 src/lib/nostr-tools/nip06.ts create mode 100644 src/lib/nostr-tools/nip19.ts create mode 100644 src/lib/nostr-tools/nip26.ts create mode 100644 src/lib/nostr-tools/nip42.ts create mode 100644 src/lib/nostr-tools/package.json create mode 100644 src/lib/nostr-tools/path.ts create mode 100644 src/lib/nostr-tools/pool.ts create mode 100644 src/lib/nostr-tools/relay.ts create mode 100644 src/lib/nostr-tools/tsconfig.json create mode 100644 src/lib/nostr-tools/utils.ts create mode 100644 src/providers/message-provider-types.ts create mode 100644 src/providers/message-provider.tsx diff --git a/package.json b/package.json index 942d5475b83..74181fe6f40 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@hiveio/hivescript": "^1.2.7", "@loadable/component": "^5.15.2", "@loadable/server": "^5.15.2", + "@noble/secp256k1": "^1.7.1", "@webscopeio/react-textarea-autocomplete": "^4.8.1", "axios": "^0.21.2", "bs58": "^4.0.1", @@ -45,6 +46,7 @@ "moment": "^2.29.4", "node-cache": "^5.1.0", "node-html-parser": "^5.3.3", + "nostr-relaypool": "^0.6.28", "numeral": "^2.0.6", "path-to-regexp": "^6.1.0", "qrcode": "^1.5.1", diff --git a/src/client/index.tsx b/src/client/index.tsx index 9d6a1e938e9..0bb65188059 100644 --- a/src/client/index.tsx +++ b/src/client/index.tsx @@ -11,6 +11,7 @@ import { AppWindow } from "./window"; import "../style/style.scss"; import "./base-handlers"; import { loadableReady } from "@loadable/component"; +import MessageProvider from "../providers/message-provider"; declare var window: AppWindow; @@ -39,7 +40,9 @@ loadableReady().then(() => { hydrate( + {/* */} + {/* */} , document.getElementById("root") @@ -64,7 +67,9 @@ if (module.hot) { hydrate( + {/* */} + {/* */} , document.getElementById("root") diff --git a/src/common/app.tsx b/src/common/app.tsx index 367d9a17914..bac75ef9a54 100644 --- a/src/common/app.tsx +++ b/src/common/app.tsx @@ -4,6 +4,7 @@ import EntryIndexContainer from "./pages/index"; import EntryContainer from "./pages/entry"; import { SearchPageContainer, SearchMorePageContainer } from "./pages/search"; import { ProposalsIndexContainer, ProposalDetailContainer } from "./pages/proposals"; +import MessageProvider from "../providers/message-provider"; import NotFound from "./components/404"; import Tracker from "./tracker"; import { @@ -158,7 +159,8 @@ const App = (props: any) => { - + + ); }; diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss index 3161780b957..410f21e63f9 100644 --- a/src/common/components/chat-box/index.scss +++ b/src/common/components/chat-box/index.scss @@ -154,6 +154,7 @@ .chat-content { display: flex; + cursor: pointer; .user-img { padding: 18px 16px; @@ -161,7 +162,7 @@ .user-title { padding-top: 18px; width: -webkit-fill-available; - cursor: pointer; + .username { @include themify(day) { color: $charcoal-grey; @@ -339,6 +340,8 @@ margin-top: 3px; } .sender-message-content { + max-width: 280px; + word-wrap: break-word; margin-bottom: 0; color: $white; font-size: 16px; diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 601676da57c..2c4fbc9ff44 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -4,10 +4,13 @@ import { Link } from "react-router-dom"; import { Account } from "../../store/accounts/types"; import { ActiveUser } from "../../store/active-user/types"; +import { DirectContactsType } from "../../store/chat/types"; +import { DirectMessage, Keys } from "../../../providers/message-provider-types"; import Tooltip from "../tooltip"; import UserAvatar from "../user-avatar"; import SeachUser from "../search-user"; +import { setNostrkeys } from "../../../providers/message-provider"; import { addMessageSVG, @@ -19,11 +22,19 @@ import { chevronDownSvgForSlider } from "../../img/svg"; import { dateToFormatted } from "../../helper/parse-date"; +import { + createNoStrAccount, + getDirectMessages, + getProfileMetaData, + setProfileMetaData +} from "../../helper/chat-utils"; import { _t } from "../../i18n"; import { getAccountFull } from "../../api/hive"; import "./index.scss"; -import { updateProfile } from "../../api/operations"; +import { RavenEvents } from "../../helper/message-helper"; +import { Chat } from "../../store/chat/types"; +import { useMappedStore } from "../../store/use-mapped-store"; export interface profileData { joiningData: string; @@ -33,9 +44,10 @@ export interface profileData { interface Props { activeUser: ActiveUser | null; + chat: Chat; } -export default function ChatBox({ activeUser }: Props) { +export default function ChatBox(props: Props) { const chatBodyDivRef = React.createRef(); const [expanded, setExpanded] = useState(false); const [currentUser, setCurrentUser] = useState(""); @@ -50,183 +62,108 @@ export default function ChatBox({ activeUser }: Props) { const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); const [inProgress, setInProgress] = useState(false); const [show, setShow] = useState(false); + const [senderPubKey, setSenderPubKey] = useState(""); + const [receiverPubKey, setReceiverPubKey] = useState(""); + const [messagesList, setMessagesList] = useState([]); + + const { chat } = useMappedStore(); useEffect(() => { - // fetchProfileData(); - setShow(!!activeUser?.username); + fetchProfileData(); + setShow(!!props.activeUser?.username); }, []); useEffect(() => { - // fetchProfileData(); - setCurrentUser(""); - setIsCurrentUser(false); - setShow(!!activeUser?.username); - setExpanded(false); - }, [activeUser]); + const msgsList = getDirectMessages(chat.directMessages, receiverPubKey!); + setMessagesList(msgsList); + }, [chat.directMessages]); + + useEffect(() => { + chatBodyDivRef?.current?.scrollTo(0, isCurrentUser ? chatBodyDivRef.current.scrollHeight : 0); + console.log(chatBodyDivRef?.current?.scrollHeight, "ref"); + }, [isCurrentUser]); + + // useEffect(() => { + // fetchProfileData(); + // setCurrentUser(""); + // // setIsCurrentUser(false); + // setShow(!!props.activeUser?.username); + // // setExpanded(false); + // }, [props.activeUser]); + + // useEffect(() => { + // currentUser ? fetchCurrentUserData() : setMessage(""); + // }, [currentUser]); useEffect(() => { if (currentUser) { fetchCurrentUserData(); + const peer = chat.directContacts.find((x) => x.name === currentUser)?.creator ?? null; + setReceiverPubKey(peer!); + const msgsList = getDirectMessages(chat.directMessages, peer!); + setMessagesList(msgsList); + } else { + setMessage(""); } }, [currentUser]); - const contentList = [ - { - username: "good-karma", - lastMessage: "Hy Hope so you are doing well" - }, - { - username: "ecency", - lastMessage: "Hy" - }, - { - username: "demo.com", - lastMessage: "Whats Up" - }, - { - username: "hive-189310", - lastMessage: "Hello" - }, - { - username: "hispapro", - lastMessage: "Hy Hope so you are doing well" - }, - { - username: "galenkp", - lastMessage: "How are you?" - }, - { - username: "deanliu", - lastMessage: "Bro" - }, - { - username: "demo123", - lastMessage: "What are you doing" - }, - { - username: "fastchrisuk", - lastMessage: "Hy Hope so you are doing well" - }, - { - username: "incublus", - lastMessage: "Hy Hope so you are doing well" - }, - { - username: "ipexito", - lastMessage: "Hello" - }, - { - username: "belemo", - lastMessage: "Hy Hope so you are doing well" - }, - { - username: "foodchunk", - lastMessage: "How are you?" - }, - { - username: "macro1997", - lastMessage: "Bro" - }, - { - username: "gelenkp", - lastMessage: "What are you doing" - }, - { - username: "der-prophet", - lastMessage: "Hy Hope so you are doing well" - } - ]; - - const messageList = [ - { - username: "demo.com", - time: "4.27 pm", - message: "Hy How are you.", - date: "4/9/2022" - }, - { - username: "mtsaeed", - time: "4.44 pm", - message: "I am fine", - date: "6/9/2022" - }, - { - username: "mtsaeed", - time: "4.44 pm", - message: "What's about you", - date: "6/9/2022" - }, - { - username: "demo.com", - time: "4.45 pm", - message: "Looks good", - date: "12/9/2022" - }, - { - username: "mtsaeed", - time: "4.46 pm", - message: "Thanks for asking", - date: "12/9/2022" - }, - { - username: "demo.com", - time: "4.48 pm", - message: "What are you doing Nowadays", - date: "14/9/2022" - }, - { - username: "mtsaeed", - time: "4.49 pm", - message: - "He was educated at the Aitchison College and Cathedral School in Lahore, and then the Royal Grammar School Worcester in England, where he excelled at cricket. In 1972, he enrolled in Keble College, Oxford where he studied Philosophy, Politics and Economics, graduating in 1975.", - date: "24/9/2022" - }, - { - username: "demo.com", - time: "4.50 pm", - message: "Seems good", - date: "4/10/2022" - }, - { - username: "mtsaeed", - time: "4.50 pm", - message: "Excellent", - date: "4/10/2022" - }, - { - username: "mtsaeed", - time: "4.52 pm", - message: "Thanks. I am very grateful to you.", - date: "4/11/2022" - }, - { - username: "mtsaeed", - time: "4.54 pm", - message: "Bundle of thanks.", - date: "5/11/2022" + useEffect(() => { + chatBodyDivRef?.current?.scrollTo(0, isCurrentUser ? chatBodyDivRef.current.scrollHeight : 0); + }, [isCurrentUser]); + + const formatFollowers = (count: number | undefined) => { + if (count) { + return count >= 1e6 + ? (count / 1e6).toLocaleString() + "M" + : count >= 1e3 + ? (count / 1e3).toLocaleString() + "K" + : count.toLocaleString(); } - ]; + return count; + }; + + //Event listening + + // const handleDirectMessage = (data: DirectMessage[]) => { + // console.log("handleDirectMessage in chat compoenent", data); + // // const append = data.filter((x) => directMessages.find((y) => y.id === x.id) === undefined); + // // raven?.loadProfiles(append.map((x) => x.peer)); + // // setDirectMessages([...directMessages, ...append]); + // }; + + // useEffect(() => { + // if (window.raven) { + // window.raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); + // window.raven?.addListener(RavenEvents.DirectMessage, handleDirectMessage); + // } + + // return () => { + // if (window.raven) { + // window.raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); + // } + // }; + // }, []); const fetchCurrentUserData = async () => { - console.log("Fetch Current data called"); const response = await getAccountFull(currentUser); setProfileData({ joiningData: response.created, about: response.profile?.about, followers: response.follow_stats?.follower_count }); + const currentUserProfile = await getProfileMetaData(currentUser); + // console.log(currentUserProfile.noStrKey); + setReceiverPubKey(currentUserProfile.noStrKey.pub); }; - // const fetchProfileData = async () => { - // console.log("Fetch profile data called"); - // const response = await getAccountFull(activeUser?.username!); - // setAccountData(response); - // const { posting_json_metadata } = response; - // const profile = JSON.parse(posting_json_metadata!).profile; - - // const hasNoStrKey = !!profile.noStrKey && profile.noStrKey.trim() !== ""; - // setHasUserJoinedChat(hasNoStrKey); - // }; + const fetchProfileData = async () => { + const profileData = await getProfileMetaData(props.activeUser?.username!); + console.log(profileData, "keys"); + setSenderPubKey(profileData?.noStrKey.pub); + const hasNoStrKey = "noStrKey" in profileData; + setHasUserJoinedChat(hasNoStrKey); + setShow(!!props.activeUser?.username); + }; const userClicked = (username: string) => { setIsCurrentUser(true); @@ -248,24 +185,10 @@ export default function ChatBox({ activeUser }: Props) { if (message.length !== 0) { setMessage(""); setIsMessageText(false); + window.raven?.sendDirectMessage(receiverPubKey, message); } }; - useEffect(() => { - chatBodyDivRef?.current?.scrollTo(0, isCurrentUser ? chatBodyDivRef.current.scrollHeight : 0); - }, [isCurrentUser]); - - const formatFollowers = (count: number | undefined) => { - if (count) { - return count >= 1e6 - ? (count / 1e6).toLocaleString() + "M" - : count >= 1e3 - ? (count / 1e3).toLocaleString() + "K" - : count.toLocaleString(); - } - return count; - }; - const handleScroll = (event: React.UIEvent) => { var element = event.currentTarget; let srollHeight: number = (element.scrollHeight / 100) * 25; @@ -286,6 +209,22 @@ export default function ChatBox({ activeUser }: Props) { }); }; + // const scrollToBottomClicked = () => { + // console.log("Scroll to bottom clicked"); + // chatBodyDivRef?.current?.scrollTo({ + // top: chatBodyDivRef?.current?.scrollHeight+10, + // behavior: "smooth" + // }); + // }; + + // const scrollToTopClicked = () => { + // console.log("Scroll to top clicked"); + // chatBodyDivRef?.current?.scrollTo({ + // top: 0, + // behavior: "smooth" + // }); + // }; + const handleMessageSvgClick = () => { setShowSearchUser(true); }; @@ -296,19 +235,12 @@ export default function ChatBox({ activeUser }: Props) { const handleJoinChat = () => { setInProgress(true); - setTimeout(() => { - setInProgress(false); - setHasUserJoinedChat(true); - }, 4000); - - // const { profile } = response; - - // const newProfile = { - // noStrKey: "nsec1mefplh7mwup68r84x6gxkqn4w0ymj5q8cr0frj0lgflx8vrwvw7sdxh3ew" - // }; - - // const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); - // console.log(updatedProfile); + const keys = createNoStrAccount(); + setProfileMetaData(props.activeUser, keys); + setInProgress(false); + setHasUserJoinedChat(true); + setNostrkeys(keys); + // window.raven?.updateProfile({ name: activeUser?.username!, about: "", picture: "" }); }; const chatButtonSpinner = ( @@ -397,35 +329,35 @@ export default function ChatBox({ activeUser }: Props) { )}
- {messageList.map((msg) => { - if (msg.username !== activeUser?.username) { + {messagesList.map((msg) => { + if (msg.creator !== senderPubKey) { return ( <> -
+ {/*

{msg.date}, {msg.time}

-
-
+
*/} +
- + - +
-

{msg.message}

+

{msg.content}

); } else { return ( -
+
{/* {msg.time} */} -

{msg.message}

+

{msg.content}

); @@ -435,20 +367,20 @@ export default function ChatBox({ activeUser }: Props) { ) : ( <> - {contentList.map((user) => { + {chat.directContacts.map((user: DirectContactsType) => { return ( -
- +
+
- +
-
userClicked(user.username)}> -

{user.username}

-

{user.lastMessage}

+
userClicked(user.name)}> +

{user.name}

+ {/*

{user.lastMessage}

*/}
); @@ -489,6 +421,7 @@ export default function ChatBox({ activeUser }: Props) { > { - const response = await getAccountFull(activeUser?.username!); +export interface NostrKeys { + pub: string; + priv: string; +} + +export const getProfileMetaData = async (username: string) => { + const response = await getAccountFull(username); const { posting_json_metadata } = response; - const profile = JSON.parse(posting_json_metadata!).profile; + if (posting_json_metadata) { + const profile = JSON.parse(posting_json_metadata!).profile; - return profile; + return profile; + } }; -export const setProfileMetaData = async (activeUser: ActiveUser | null, key: string) => { +export const setProfileMetaData = async (activeUser: ActiveUser | null, keys: NostrKeys) => { const response = await getAccountFull(activeUser?.username!); const { posting_json_metadata } = response; const profile = JSON.parse(posting_json_metadata!).profile; const newProfile = { - noStrKey: key + noStrKey: keys }; const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); @@ -25,7 +34,28 @@ export const setProfileMetaData = async (activeUser: ActiveUser | null, key: str }; export const createNoStrAccount = () => { - const privKey = generatePrivateKey(); - const pubKey = getPublicKey(privKey); - return { pubKey, privKey }; + const priv = generatePrivateKey(); + const pub = getPublicKey(priv); + return { pub, priv }; +}; + +export function notEmpty(value: TValue | null | undefined): value is TValue { + return value !== null && value !== undefined; +} + +export const GLOBAL_CHAT: Channel = { + id: "f412192fdc846952c75058e911d37a7392aa7fd2e727330f4344badc92fb8a22", + name: "Global Chat", + about: "Whatever you want it to be, just be nice", + picture: "", + creator: "aea59833635dd0868bc7cf923926e51df936405d8e6a753b78038981c75c4a74", + created: 1678198928 +}; + +export const getDirectMessages = (messages: DirectMessage[], peer?: string) => { + const clean = messages.filter((x) => x.peer === peer).sort((a, b) => a.created - b.created); + + return clean + .map((c) => ({ ...c, children: clean.filter((x) => x.root === c.id) })) + .filter((x) => !x.root); }; diff --git a/src/common/helper/message-event-emitter.ts b/src/common/helper/message-event-emitter.ts new file mode 100644 index 00000000000..e4b97f13ea6 --- /dev/null +++ b/src/common/helper/message-event-emitter.ts @@ -0,0 +1,112 @@ +import { EventEmitter } from "events"; + +export enum EventEmitterEvents { + NewListener = "newListener", + RemoveListener = "removeListener", + Error = "error" +} + +type AnyListener = (...args: any) => any; +export type ListenerMap = { [eventName in E]: AnyListener }; +type EventEmitterEventListener = (eventName: string, listener: AnyListener) => void; +type EventEmitterErrorListener = (error: Error) => void; + +export type Listener< + E extends string, + A extends ListenerMap, + T extends E | EventEmitterEvents +> = T extends E + ? A[T] + : T extends EventEmitterEvents + ? EventEmitterErrorListener + : EventEmitterEventListener; + +/** + * Typed Event Emitter class which can act as a Base Model for all our model + * and communication events. + * This makes it much easier for us to distinguish between events, as we now need + * to properly type this, so that our events are not stringly-based and prone + * to silly typos. + */ +export class TypedEventEmitter< + Events extends string, + Arguments extends ListenerMap, + SuperclassArguments extends ListenerMap = Arguments +> extends EventEmitter { + public addListener( + event: T, + listener: Listener + ): this { + return super.addListener(event, listener); + } + + public emit(event: T, ...args: Parameters): boolean; + public emit(event: T, ...args: Parameters): boolean; + public emit(event: T, ...args: any[]): boolean { + return super.emit(event, ...args); + } + + public eventNames(): (Events | EventEmitterEvents)[] { + return super.eventNames() as Array; + } + + public listenerCount(event: Events | EventEmitterEvents): number { + return super.listenerCount(event); + } + + public listeners(event: Events | EventEmitterEvents): ReturnType { + return super.listeners(event); + } + + public off( + event: T, + listener: Listener + ): this { + return super.off(event, listener); + } + + public on( + event: T, + listener: Listener + ): this { + return super.on(event, listener); + } + + public once( + event: T, + listener: Listener + ): this { + return super.once(event, listener); + } + + public prependListener( + event: T, + listener: Listener + ): this { + return super.prependListener(event, listener); + } + + public prependOnceListener( + event: T, + listener: Listener + ): this { + return super.prependOnceListener(event, listener); + } + + public removeAllListeners(event?: Events | EventEmitterEvents): this { + return super.removeAllListeners(event); + } + + public removeListener( + event: T, + listener: Listener + ): this { + return super.removeListener(event, listener); + } + + public rawListeners( + event: Events | EventEmitterEvents + ): ReturnType { + return super.rawListeners(event); + } +} diff --git a/src/common/helper/message-global.ts b/src/common/helper/message-global.ts new file mode 100644 index 00000000000..c0981223bcb --- /dev/null +++ b/src/common/helper/message-global.ts @@ -0,0 +1,16 @@ +import Raven from "./message-helper"; +import { Event } from "../../lib/nostr-tools/event"; + +declare global { + interface Window { + raven?: Raven; + nostr?: { + getPublicKey: () => Promise; + signEvent: (event: Event) => Promise; + nip04: { + encrypt: (pubkey: string, content: string) => Promise; + decrypt: (pubkey: string, content: string) => Promise; + }; + }; + } +} diff --git a/src/common/helper/message-helper.ts b/src/common/helper/message-helper.ts new file mode 100644 index 00000000000..48ceb9362d6 --- /dev/null +++ b/src/common/helper/message-helper.ts @@ -0,0 +1,798 @@ +import { useMappedStore } from "./../store/use-mapped-store"; +// import { Event, Filter, getEventHash, Kind, nip04, signEvent, SimplePool, Sub } from "nostr-tools"; +// import { TypedEventEmitter } from "raven/helper/event-emitter"; +// import { +// Channel, +// ChannelMessageHide, +// ChannelUpdate, +// ChannelUserMute, +// DirectMessage, +// EventDeletion, +// Keys, +// Metadata, +// MuteList, +// Profile, +// PublicMessage +// } from "types"; +// import chunk from 'lodash.chunk'; +// import uniq from 'lodash.uniq'; +// import {getRelays} from 'helper'; +// import {GLOBAL_CHAT, MESSAGE_PER_PAGE} from 'const'; +// import {notEmpty} from 'util/misc'; + +import { Sub } from "../../lib/nostr-tools/relay"; +import { Kind } from "../../lib/nostr-tools/event"; +import { Filter } from "../../lib/nostr-tools/filter"; +import { TypedEventEmitter } from "./message-event-emitter"; +import uniq from "lodash"; +import chunk from "lodash"; +import { + Channel, + ChannelMessageHide, + ChannelUpdate, + ChannelUserMute, + DirectMessage, + EventDeletion, + Keys, + Metadata, + MuteList, + Profile, + PublicMessage +} from "../../providers/message-provider-types"; +import { encrypt, decrypt } from "../../lib/nostr-tools/nip04"; +import SimplePool from "../../lib/nostr-tools/pool"; +import { signEvent, getEventHash, Event } from "../../lib/nostr-tools/event"; +import { getDirectMessages, GLOBAL_CHAT, notEmpty } from "./chat-utils"; +import * as ls from "../util/local-storage"; +// import { addDirectContacts } from "../store/chat"; + +const relays = { + "wss://relay1.nostrchat.io": { read: true, write: true }, + "wss://relay2.nostrchat.io": { read: true, write: true }, + "wss://relay.damus.io": { read: true, write: true }, + "wss://relay.snort.social": { read: true, write: true }, + "wss://nos.lol": { read: true, write: true } +}; + +enum NewKinds { + MuteList = 10000 +} + +export enum RavenEvents { + Ready = "ready", + ProfileUpdate = "profile_update", + ChannelCreation = "channel_creation", + ChannelUpdate = "channel_update", + EventDeletion = "event_deletion", + PublicMessage = "public_message", + DirectMessage = "direct_message", + ChannelMessageHide = "channel_message_hide", + ChannelUserMute = "channel_user_mute", + MuteList = "mute_list" +} + +type EventHandlerMap = { + [RavenEvents.Ready]: () => void; + [RavenEvents.ProfileUpdate]: (data: Profile[]) => void; + [RavenEvents.ChannelCreation]: (data: Channel[]) => void; + [RavenEvents.ChannelUpdate]: (data: ChannelUpdate[]) => void; + [RavenEvents.EventDeletion]: (data: EventDeletion[]) => void; + [RavenEvents.PublicMessage]: (data: PublicMessage[]) => void; + [RavenEvents.DirectMessage]: (data: DirectMessage[]) => void; + [RavenEvents.ChannelMessageHide]: (data: ChannelMessageHide[]) => void; + [RavenEvents.ChannelUserMute]: (data: ChannelUserMute[]) => void; + [RavenEvents.MuteList]: (data: MuteList) => void; +}; + +class Raven extends TypedEventEmitter { + private pool: SimplePool; + private poolL: SimplePool; + + private readonly priv: string | "nip07"; + private readonly pub: string; + + private readonly readRelays = Object.keys(relays).filter((r) => relays[r].read); + private readonly writeRelays = Object.keys(relays).filter((r) => relays[r].write); + + private eventQueue: Event[] = []; + private eventQueueTimer: any; + private eventQueueFlag = true; + private eventQueueBuffer: Event[] = []; + + private nameCache: Record = {}; + + listenerSub: Sub | null = null; + messageListenerSub: Sub | null = null; + + constructor(priv: string, pub: string) { + super(); + + this.priv = priv; + this.pub = pub; + + this.pool = new SimplePool(); + this.poolL = new SimplePool({ eoseSubTimeout: 10000 }); + + this.init().then(); + } + + private async init() { + const filters: Filter[] = [ + { + kinds: [Kind.Metadata], + authors: [this.pub] + }, + // { + // kinds: [Kind.ChannelHideMessage, Kind.ChannelMuteUser], + // authors: [this.pub] + // }, + // { + // kinds: [NewKinds.MuteList], + // authors: [this.pub] + // }, + // { + // kinds: [Kind.ChannelMessage], + // authors: [this.pub] + // }, + { + kinds: [Kind.EncryptedDirectMessage], + authors: [this.pub] + }, + { + kinds: [Kind.EncryptedDirectMessage], + "#p": [this.pub] + } + ]; + this.fetchP(filters).then((resp) => { + console.log("Init called", resp); + // console.log("ddd", addDirectContacts); + // // const deletions = resp + // .filter((x) => x.kind === Kind.EventDeletion) + // .map((x) => Raven.findTagValue(x, "e")); + const events = resp.sort((a, b) => b.created_at - a.created_at); + const profile = events.find((x) => x.kind === Kind.Metadata); + console.log(profile, "profile in init"); + if (profile) this.pushToEventBuffer(profile, "init"); + // const muteList = events.find((x) => x.kind.toString() === NewKinds.MuteList.toString()); + // if (muteList) this.pushToEventBuffer(muteList, "init"); + // for (const e of events.filter((x) => + // [Kind.ChannelHideMessage, Kind.ChannelMuteUser].includes(x.kind) + // )) { + // this.pushToEventBuffer(e, "init"); + // } + // const channels = uniq( + // events + // .map((x) => { + // if (x.kind === Kind.ChannelCreation) { + // return x.id; + // } + // if (x.kind === Kind.ChannelMessage) { + // return Raven.findTagValue(x, "e"); + // } + // return undefined; + // }) + // .filter((x) => !deletions.includes(x)) + // .filter(notEmpty) + // ); + // if (!channels.includes(GLOBAL_CHAT.id)) { + // channels.push(GLOBAL_CHAT.id); + // } + // console.log(events, "Events"); + const directContacts = Array.from( + new Set( + events.map((x) => { + if (x.kind === Kind.EncryptedDirectMessage) { + const receiver = Raven.findTagValue(x, "p"); + if (!receiver) return undefined; + return receiver === this.pub ? x.pubkey : receiver; + } + return undefined; + }) + ) + ).filter(notEmpty); + + // directContacts.map((p)=> { + // console.log("p",p); + // const profile = + // }) + // directContacts.map(p => { + // console.log(p) + // // .filter((x) => x.kind === Kind.Metadata) + // // const profile = profiles.find(x => x.creator === p.pub); + // // const label = profile?.name || truncateMiddle(p.npub, 28, ':'); + // // const isSelected = p.pub === directMessage && location.pathname.startsWith('/dm/'); + // )} + + console.log("directContacts", directContacts); + // ls.set("direct_contacts", directContacts); + + // addDirectContacts([ + // "0be16d2d36570b96080c0f9c351feb6be515a182b8b75b90738ae0180c27c6d5", + // "1331c42d27e2a043ea5f478301a41ff4bab6c70bbb73f4edbf82a6756a7f51dd" + // ]); + + // this.fetch([ + // { + // kinds: [Kind.Metadata], + // authors: directContacts + // } + // ]); + const filters: Filter[] = [ + { + kinds: [Kind.Metadata], + authors: directContacts + }, + ...directContacts.map((x) => ({ + kinds: [Kind.EncryptedDirectMessage], + "#p": [this.pub], + authors: [x] + })), + ...directContacts.map((x) => ({ + kinds: [Kind.EncryptedDirectMessage], + "#p": [x], + authors: [this.pub] + })) + ]; + filters.forEach((c) => { + this.fetch([c]); + }); + this.emit(RavenEvents.Ready); + + // let directMessages = getDirectMessages(events as DirectMessage[],) + }); + } + + // public fetchPrevMessages(channel: string, until: number) { + // return this.fetchP( + // [ + // { + // kinds: [Kind.ChannelMessage], + // "#e": [channel], + // until, + // limit: MESSAGE_PER_PAGE + // } + // ], + // true + // ).then((events) => { + // events.forEach((ev) => { + // this.pushToEventBuffer(ev); + // }); + + // return events.length; + // }); + // } + + private fetch(filters: Filter[], unsub: boolean = true) { + console.log("Fetch Calledd", filters); + + const sub = this.pool.sub(this.readRelays, filters); + + sub.on("event", (event: Event) => { + this.pushToEventBuffer(event, "fetch"); + }); + + sub.on("eose", () => { + if (unsub) { + sub.unsub(); + } + }); + + return sub; + } + + private fetchP(filters: Filter[], lowLatencyPool: boolean = false): Promise { + return new Promise((resolve) => { + const sub = (lowLatencyPool ? this.poolL : this.pool).sub(this.readRelays, filters); + const events: Event[] = []; + + sub.on("event", (event: Event) => { + events.push(event); + }); + + sub.on("eose", () => { + sub.unsub(); + resolve(events); + }); + }); + } + + public loadChannel(id: string) { + // console.log("Load channel called"); + const filters: Filter[] = [ + { + kinds: [Kind.ChannelCreation], + ids: [id] + }, + { + kinds: [Kind.ChannelMetadata, Kind.EventDeletion], + "#e": [id] + }, + { + kinds: [Kind.ChannelMessage], + "#e": [id], + limit: 30 + } + ]; + + this.fetch(filters); + } + + // public loadProfiles(pubs: string[]) { + // console.log("pubs", pubs); + // const authors = Array.from(new Set(pubs)).filter((p) => this.nameCache[p] === undefined); + // console.log(authors, "authors"); + // if (authors.length === 0) { + // return; + // } + // authors.forEach((a) => (this.nameCache[a] = Date.now())); + + // const batchSize = 20; // Number of authors to process in each batch + + // for (let i = 0; i < authors.length; i += batchSize) { + // const batch = authors.slice(i, i + batchSize); + + // this.fetch([ + // { + // kinds: [Kind.Metadata], + // authors: batch + // } + // ]); + // } + + // } + + // public loadProfiles(pubs: string[]) { + // console.log(pubs, "pubs"); + // const authors = Array.from(new Set(pubs)).filter((p) => this.nameCache[p] === undefined); + // console.log(authors, "authors"); + // if (authors.length === 0) { + // return; + // } + // authors.forEach((a) => (this.nameCache[a] = Date.now())); + + // authors.forEach((a) => { + // this.fetch([ + // { + // kinds: [Kind.Metadata], + // authors: [a] + // } + // ]); + // }); + // } + + public listen(since: number) { + if (this.listenerSub) { + this.listenerSub.unsub(); + } + + this.listenerSub = this.fetch( + [ + { + authors: [this.pub], + since + }, + { + kinds: [Kind.EncryptedDirectMessage], + "#p": [this.pub], + since + } + ], + false + ); + } + + listenMessages = (ids: string[]) => { + // console.log("Listen messages called"); + if (this.messageListenerSub) { + this.messageListenerSub.unsub(); + } + + this.messageListenerSub = this.fetch( + [ + { + kinds: [Kind.EventDeletion, Kind.ChannelMessage, Kind.Reaction], + "#e": ids + } + ], + false + ); + }; + + private async findHealthyRelay(relays: string[]) { + for (const relay of relays) { + try { + await this.pool.ensureRelay(relay); + return relay; + } catch (e) {} + } + + throw new Error("Couldn't find a working relay"); + } + + public async updateProfile(profile: Metadata) { + // console.log("Update profile run", profile); + return this.publish(Kind.Metadata, [], JSON.stringify(profile)); + } + + // public async createChannel(meta: Metadata) { + // return this.publish(Kind.ChannelCreation, [], JSON.stringify(meta)); + // } + + // public async updateChannel(channel: Channel, meta: Metadata) { + // return this.findHealthyRelay(this.pool.seenOn(channel.id)).then((relay) => { + // return this.publish(Kind.ChannelMetadata, [["e", channel.id, relay]], JSON.stringify(meta)); + // }); + // } + + // public async deleteEvents(ids: string[], why: string = "") { + // return this.publish(Kind.EventDeletion, [...ids.map((id) => ["e", id])], why); + // } + + // public async sendPublicMessage( + // channel: Channel, + // message: string, + // mentions?: string[], + // parent?: string + // ) { + // const root = parent || channel.id; + // const relay = await this.findHealthyRelay(this.pool.seenOn(root)); + // const tags = [["e", root, relay, "root"]]; + // if (mentions) { + // mentions.forEach((m) => tags.push(["p", m])); + // } + // return this.publish(Kind.ChannelMessage, tags, message); + // } + + public async sendDirectMessage(toPubkey: string, message: string, parent?: string) { + const encrypted = await (this.priv === "nip07" + ? window.nostr!.nip04.encrypt(toPubkey, message) + : encrypt(this.priv, toPubkey, message)); + const tags = [["p", toPubkey]]; + if (parent) { + const relay = await this.findHealthyRelay(this.pool.seenOn(parent) as string[]); + tags.push(["e", parent, relay, "root"]); + } + // this.emit(RavenEvents.DirectMessage, [ + // { + // id: "7a6c0db898ee043045f21fa49c5fd4b0765b506d84cb8d71539ba4cb37292040", + // content: "HYYYYYyyyy", + // peer: "1331c42d27e2a043ea5f478301a41ff4bab6c70bbb73f4edbf82a6756a7f51dd", + // creator: "7379e436f673358cdbf4f0d52ef5240c82d802755295b53182b107f41e9f43e5", + // created: 1686567232, + // decrypted: true + // } + // ]); + // console.log("Event is emitted"); + return this.publish(Kind.EncryptedDirectMessage, tags, encrypted); + } + + // public async recommendRelay(relay: string) { + // return this.publish(Kind.RecommendRelay, [], relay); + // } + + // public async hideChannelMessage(id: string, reason: string) { + // return this.publish(Kind.ChannelHideMessage, [["e", id]], JSON.stringify({ reason })); + // } + + // public async muteChannelUser(pubkey: string, reason: string) { + // return this.publish(Kind.ChannelMuteUser, [["p", pubkey]], JSON.stringify({ reason })); + // } + + // public async updateMuteList(userIds: string[]) { + // const list = [...userIds.map((id) => ["p", id])]; + // const content = await (this.priv === "nip07" + // ? window.nostr!.nip04.encrypt(this.pub, JSON.stringify(list)) + // : nip04.encrypt(this.priv, this.pub, JSON.stringify(list))); + // return this.publish(NewKinds.MuteList, [], content); + // } + + private publish(kind: number, tags: Array[], content: string): Promise { + return new Promise((resolve, reject) => { + this.signEvent({ + kind, + tags, + pubkey: this.pub, + content, + created_at: Math.floor(Date.now() / 1000), + id: "", + sig: "" + }) + .then((event) => { + if (!event) { + reject("Couldn't sign event!"); + return; + } + const pub = this.pool.publish(this.writeRelays, event); + pub.on("ok", () => { + console.log("My event is okkkkkkkkkkkkkkkkkkkkkkk", event); + resolve(event); + }); + + pub.on("failed", () => { + reject("Couldn't sign event!"); + }); + }) + .catch(() => { + reject("Couldn't sign event!"); + }); + }); + } + + private async signEvent(event: Event): Promise { + if (this.priv === "nip07") { + return window.nostr?.signEvent(event); + } else { + return { + ...event, + id: getEventHash(event), + sig: await signEvent(event, this.priv) + }; + } + } + + pushToEventBuffer(event: Event, from: string) { + console.log("Push to event buffer called frommmmmmmmmm", from); + const cacheKey = `${event.id}_emitted`; + if (this.nameCache[cacheKey] === undefined) { + if (this.eventQueueFlag) { + if (this.eventQueueBuffer.length > 0) { + this.eventQueue.push(...this.eventQueueBuffer); + this.eventQueueBuffer = []; + } + clearTimeout(this.eventQueueTimer); + this.eventQueue.push(event); + this.eventQueueTimer = setTimeout(() => { + this.processEventQueue().then(); + }, 50); + } else { + this.eventQueueBuffer.push(event); + } + + this.nameCache[cacheKey] = 1; + } + } + + async processEventQueue() { + this.eventQueueFlag = false; + + const profileUpdates: Profile[] = this.eventQueue + .filter((x) => x.kind === Kind.Metadata) + .map((ev) => { + const content = Raven.parseJson(ev.content); + return content + ? { + id: ev.id, + creator: ev.pubkey, + created: ev.created_at, + ...Raven.normalizeMetadata(content) + } + : null; + }) + .filter(notEmpty); + if (profileUpdates.length > 0) { + this.emit(RavenEvents.ProfileUpdate, profileUpdates); + } + + // const channelCreations: Channel[] = this.eventQueue + // .filter((x) => x.kind === Kind.ChannelCreation) + // .map((ev) => { + // const content = Raven.parseJson(ev.content); + // return content + // ? { + // id: ev.id, + // creator: ev.pubkey, + // created: ev.created_at, + // ...Raven.normalizeMetadata(content) + // } + // : null; + // }) + // .filter(notEmpty); + // if (channelCreations.length > 0) { + // this.emit(RavenEvents.ChannelCreation, channelCreations); + // } + + // const channelUpdates: ChannelUpdate[] = this.eventQueue + // .filter((x) => x.kind === Kind.ChannelMetadata) + // .map((ev) => { + // const content = Raven.parseJson(ev.content); + // const channelId = Raven.findTagValue(ev, "e"); + // if (!channelId) return null; + // return content + // ? { + // id: ev.id, + // creator: ev.pubkey, + // created: ev.created_at, + // channelId, + // ...Raven.normalizeMetadata(content) + // } + // : null; + // }) + // .filter(notEmpty); + // if (channelUpdates.length > 0) { + // this.emit(RavenEvents.ChannelUpdate, channelUpdates); + // } + + // const deletions: EventDeletion[] = this.eventQueue + // .filter((x) => x.kind === Kind.EventDeletion) + // .map((ev) => { + // const eventId = Raven.findTagValue(ev, "e"); + // if (!eventId) return null; + // return { + // eventId, + // why: ev.content || "" + // }; + // }) + // .filter(notEmpty) + // .flat(); + // if (deletions.length > 0) { + // this.emit(RavenEvents.EventDeletion, deletions); + // } + + // const publicMessages: PublicMessage[] = this.eventQueue + // .filter((x) => x.kind === Kind.ChannelMessage) + // .map((ev) => { + // const eTags = Raven.filterTagValue(ev, "e"); + // const root = eTags.find((x) => x[3] === "root")?.[1]; + // const mentions = Raven.filterTagValue(ev, "p") + // .map((x) => x?.[1]) + // .filter(notEmpty); + // if (!root) return null; + // return ev.content + // ? { + // id: ev.id, + // root, + // content: ev.content, + // creator: ev.pubkey, + // mentions, + // created: ev.created_at + // } + // : null; + // }) + // .filter(notEmpty); + // if (publicMessages.length > 0) { + // this.emit(RavenEvents.PublicMessage, publicMessages); + // } + + Promise.all( + this.eventQueue + .filter((x) => x.kind === Kind.EncryptedDirectMessage) + .map((ev) => { + const receiver = Raven.findTagValue(ev, "p"); + if (!receiver) return null; + const eTags = Raven.filterTagValue(ev, "e"); + const root = eTags.find((x) => x[3] === "root")?.[1]; + + const peer = receiver === this.pub ? ev.pubkey : receiver; + const msg = { + id: ev.id, + root, + content: ev.content, + peer, + creator: ev.pubkey, + created: ev.created_at, + decrypted: false + }; + + if (this.priv === "nip07") { + return msg; + } + + return decrypt(this.priv, peer, ev.content).then((content) => { + return { + ...msg, + content, + decrypted: true + }; + }); + }) + .filter(notEmpty) + ).then((directMessages: DirectMessage[]) => { + this.emit(RavenEvents.DirectMessage, directMessages); + }); + + // const channelMessageHides: ChannelMessageHide[] = this.eventQueue + // .filter((x) => x.kind === Kind.ChannelHideMessage) + // .map((ev) => { + // const content = Raven.parseJson(ev.content); + // const id = Raven.findTagValue(ev, "e"); + // if (!id) return null; + // return { + // id, + // reason: content?.reason || "" + // }; + // }) + // .filter(notEmpty); + // if (channelMessageHides.length > 0) { + // this.emit(RavenEvents.ChannelMessageHide, channelMessageHides); + // } + + // const channelUserMutes: ChannelUserMute[] = this.eventQueue + // .filter((x) => x.kind === Kind.ChannelMuteUser) + // .map((ev) => { + // const content = Raven.parseJson(ev.content); + // const pubkey = Raven.findTagValue(ev, "p"); + // if (!pubkey) return null; + // return { + // pubkey, + // reason: content?.reason || "" + // }; + // }) + // .filter(notEmpty); + // if (channelUserMutes.length > 0) { + // this.emit(RavenEvents.ChannelUserMute, channelUserMutes); + // } + + // const muteListEv = this.eventQueue + // .filter((x) => x.kind.toString() === NewKinds.MuteList.toString()) + // .sort((a, b) => b.created_at - a.created_at)[0]; + + // if (muteListEv) { + // const visiblePubkeys = Raven.filterTagValue(muteListEv, "p").map((x) => x?.[1]); + + // if (muteListEv.content !== "" && this.priv !== "nip07") { + // decrypt(this.priv, this.pub, muteListEv.content) + // .then((e) => JSON.parse(e)) + // .then((resp) => { + // const allPubkeys = [...visiblePubkeys, ...resp.map((x: any) => x?.[1])]; + // this.emit(RavenEvents.MuteList, { + // pubkeys: uniq(allPubkeys), + // encrypted: "" + // }); + // }); + // } else { + // this.emit(RavenEvents.MuteList, { + // pubkeys: visiblePubkeys, + // encrypted: muteListEv.content.trim() + // }); + // } + // } + + this.eventQueue = []; + this.eventQueueFlag = true; + } + + close = () => { + this.pool.close(this.readRelays); + this.removeAllListeners(); + }; + + static normalizeMetadata(meta: Metadata) { + return { + name: meta.name || "", + about: meta.about || "", + picture: meta.picture || "" + }; + } + + static parseJson(d: string) { + try { + return JSON.parse(d); + } catch (e) { + return null; + } + } + + static findTagValue(ev: Event, tag: "e" | "p") { + return ev.tags.find(([t]) => t === tag)?.[1]; + } + + static filterTagValue(ev: Event, tag: "e" | "p") { + return ev.tags.filter(([t]) => t === tag); + } +} + +export default Raven; + +export const initRaven = (keys: Keys): Raven | undefined => { + console.log("Init raven run"); + if (window.raven) { + window.raven.close(); + window.raven = undefined; + } + + if (keys) { + window.raven = new Raven(keys.priv, keys.pub); + } + + return window.raven; +}; diff --git a/src/common/pages/sign-up.tsx b/src/common/pages/sign-up.tsx index 532ad15a83b..a9516486cbe 100644 --- a/src/common/pages/sign-up.tsx +++ b/src/common/pages/sign-up.tsx @@ -121,8 +121,6 @@ export const SignUp = (props: PageProps) => { }, [username, usernameTouched]); const regularRegister = async () => { - // const key = createNoStrAccount(); - // console.log(key); setInProgress(true); try { const response = await signUp(username, email, referral); diff --git a/src/common/store/actions.ts b/src/common/store/actions.ts index c6f7fd2f075..dd2d391c865 100644 --- a/src/common/store/actions.ts +++ b/src/common/store/actions.ts @@ -41,6 +41,7 @@ import { fetchPoints, resetPoints } from "./points"; import { setSigningKey } from "./signing-key"; import { setEntryPin, trackEntryPin } from "./entry-pin-tracker"; import { savePageScroll } from "./persistent-page-scroll"; +import { addDirectContacts, addDirectMessages } from "./chat"; // @note Do not use it directly export const ACTIONS = { @@ -89,7 +90,9 @@ export const ACTIONS = { setEntryPin, updateNotificationsSettings, fetchNotificationsSettings, - setNotificationsSettingsItem + setNotificationsSettingsItem, + addDirectContacts, + addDirectMessages }; export const getActions = () => ({ diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts new file mode 100644 index 00000000000..be2fa23e0f0 --- /dev/null +++ b/src/common/store/chat/index.ts @@ -0,0 +1,63 @@ +import { DirectMessage } from "./../../../providers/message-provider-types"; +import { Dispatch } from "redux"; + +import { + Chat, + Actions, + ActionTypes, + DirectContactsAction, + DirectContactsType, + DirectMessagesAction +} from "./types"; + +export const initialState: Chat = { + directContacts: [], + directMessages: [] +}; + +export default (state: Chat = initialState, action: Actions): Chat => { + switch (action.type) { + case ActionTypes.DIRECTCONTACTS: { + const { data } = action; + return { + ...state, + directContacts: [...state.directContacts, ...data] + }; + } + case ActionTypes.DIRECTMESSAGES: { + const { data } = action; + return { + ...state, + directMessages: [...state.directMessages, ...data] + }; + } + default: + return state; + } +}; + +/* Actions */ +export const addDirectContacts = (data: DirectContactsType[]) => (dispatch: Dispatch) => { + dispatch(addDirectContactsAct(data)); +}; + +export const addDirectMessages = (data: DirectMessage[]) => (dispatch: Dispatch) => { + console.log("data in store", data); + dispatch(addDirectMessagesAct(data)); +}; + +/* Action Creators */ + +export const addDirectContactsAct = (data: DirectContactsType[]): DirectContactsAction => { + return { + type: ActionTypes.DIRECTCONTACTS, + data + }; +}; + +export const addDirectMessagesAct = (data: DirectMessage[]): DirectMessagesAction => { + return { + type: ActionTypes.DIRECTMESSAGES, + data + }; +}; diff --git a/src/common/store/chat/types.ts b/src/common/store/chat/types.ts new file mode 100644 index 00000000000..ddfc443cf4a --- /dev/null +++ b/src/common/store/chat/types.ts @@ -0,0 +1,41 @@ +import { DirectMessage } from "./../../../providers/message-provider-types"; +import { Account } from "../accounts/types"; + +export interface DirectContactsType { + name: string; + creator: string; +} + +export interface Chat { + directContacts: DirectContactsType[]; + directMessages: DirectMessage[]; + // points: UserPoints; +} + +export enum ActionTypes { + DIRECTCONTACTS = "@chat/DIRECTCONTACTS", + DIRECTMESSAGES = "@chat/DIRECTMESSAGES" + // LOGOUT = "@active-user/LOGOUT", + // UPDATE = "@active-user/UPDATE" +} + +export interface DirectContactsAction { + type: ActionTypes.DIRECTCONTACTS; + data: DirectContactsType[]; +} + +export interface DirectMessagesAction { + type: ActionTypes.DIRECTMESSAGES; + data: DirectMessage[]; +} +// export interface LogoutAction { +// type: ActionTypes.LOGOUT; +// } + +// export interface UpdateAction { +// type: ActionTypes.UPDATE; +// data: Account; +// points: UserPoints; +// } + +export type Actions = DirectContactsAction | DirectMessagesAction; diff --git a/src/common/store/index.ts b/src/common/store/index.ts index a55ca8e763e..39e97e8472c 100644 --- a/src/common/store/index.ts +++ b/src/common/store/index.ts @@ -24,6 +24,7 @@ import entryPinTracker from "./entry-pin-tracker"; import persistentPageScroll from "./persistent-page-scroll"; import filterTagExtract from "../helper/filter-tag-extract"; +import chat from "./chat"; let reducers = { global, @@ -43,7 +44,8 @@ let reducers = { points, signingKey, entryPinTracker, - persistentPageScroll + persistentPageScroll, + chat }; export let history: History | undefined; diff --git a/src/common/store/initial-state.ts b/src/common/store/initial-state.ts index dc14065e2b7..a6152a49dde 100644 --- a/src/common/store/initial-state.ts +++ b/src/common/store/initial-state.ts @@ -18,6 +18,7 @@ import { initialState as pointsInitialState } from "./points"; import { initialState as signingKeyInitialState } from "./signing-key"; import { initialState as entryPinTrackerInitialState } from "./entry-pin-tracker"; import { initialState as persistentPageScrollInitialState } from "./persistent-page-scroll"; +import { initialState as chatInitialState } from "./chat"; const initialState: AppState = { global: globalInitialState, @@ -37,7 +38,8 @@ const initialState: AppState = { points: pointsInitialState, signingKey: signingKeyInitialState, entryPinTracker: entryPinTrackerInitialState, - persistentPageScroll: persistentPageScrollInitialState + persistentPageScroll: persistentPageScrollInitialState, + chat: chatInitialState }; export default initialState; diff --git a/src/lib/nostr-tools/.eslintrc.json b/src/lib/nostr-tools/.eslintrc.json new file mode 100644 index 00000000000..faca633c5e0 --- /dev/null +++ b/src/lib/nostr-tools/.eslintrc.json @@ -0,0 +1,145 @@ +{ + "root": true, + + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + + "parserOptions": { + "ecmaVersion": 9, + "ecmaFeatures": { + "jsx": true + }, + "sourceType": "module", + "allowImportExportEverywhere": false + }, + + "env": { + "es6": true, + "node": true + }, + + "plugins": ["babel"], + + "globals": { + "document": false, + "navigator": false, + "window": false, + "location": false, + "URL": false, + "URLSearchParams": false, + "fetch": false, + "EventSource": false, + "localStorage": false, + "sessionStorage": false + }, + + "rules": { + "accessor-pairs": 2, + "arrow-spacing": [2, { "before": true, "after": true }], + "block-spacing": [2, "always"], + "brace-style": [2, "1tbs", { "allowSingleLine": true }], + "comma-dangle": 0, + "comma-spacing": [2, { "before": false, "after": true }], + "comma-style": [2, "last"], + "constructor-super": 2, + "curly": [0, "multi-line"], + "dot-location": [2, "property"], + "eol-last": 2, + "eqeqeq": [2, "allow-null"], + "generator-star-spacing": [2, { "before": true, "after": true }], + "handle-callback-err": [2, "^(err|error)$"], + "indent": 0, + "jsx-quotes": [2, "prefer-double"], + "key-spacing": [2, { "beforeColon": false, "afterColon": true }], + "keyword-spacing": [2, { "before": true, "after": true }], + "new-cap": 0, + "new-parens": 0, + "no-array-constructor": 2, + "no-caller": 2, + "no-class-assign": 2, + "no-cond-assign": 2, + "no-const-assign": 2, + "no-control-regex": 0, + "no-debugger": 0, + "no-delete-var": 2, + "no-dupe-args": 2, + "no-dupe-class-members": 2, + "no-dupe-keys": 2, + "no-duplicate-case": 2, + "no-empty-character-class": 2, + "no-empty-pattern": 2, + "no-eval": 0, + "no-ex-assign": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-extra-boolean-cast": 2, + "no-extra-parens": [2, "functions"], + "no-fallthrough": 2, + "no-floating-decimal": 2, + "no-func-assign": 2, + "no-implied-eval": 2, + "no-inner-declarations": [0, "functions"], + "no-invalid-regexp": 2, + "no-irregular-whitespace": 2, + "no-iterator": 2, + "no-label-var": 2, + "no-labels": [2, { "allowLoop": false, "allowSwitch": false }], + "no-lone-blocks": 2, + "no-mixed-spaces-and-tabs": 2, + "no-multi-spaces": 2, + "no-multi-str": 2, + "no-multiple-empty-lines": [2, { "max": 2 }], + "no-native-reassign": 2, + "no-negated-in-lhs": 2, + "no-new": 0, + "no-new-func": 2, + "no-new-object": 2, + "no-new-require": 2, + "no-new-symbol": 2, + "no-new-wrappers": 2, + "no-obj-calls": 2, + "no-octal": 2, + "no-octal-escape": 2, + "no-path-concat": 0, + "no-proto": 2, + "no-redeclare": 2, + "no-regex-spaces": 2, + "no-return-assign": 0, + "no-self-assign": 2, + "no-self-compare": 2, + "no-sequences": 2, + "no-shadow-restricted-names": 2, + "no-spaced-func": 2, + "no-sparse-arrays": 2, + "no-this-before-super": 2, + "no-throw-literal": 2, + "no-trailing-spaces": 2, + "no-undef": 2, + "no-undef-init": 2, + "no-unexpected-multiline": 2, + "no-unneeded-ternary": [2, { "defaultAssignment": false }], + "no-unreachable": 2, + "no-unused-vars": [2, { "vars": "local", "args": "none", "varsIgnorePattern": "^_" }], + "no-useless-call": 2, + "no-useless-constructor": 2, + "no-with": 2, + "one-var": [0, { "initialized": "never" }], + "operator-linebreak": [2, "after", { "overrides": { "?": "before", ":": "before" } }], + "padded-blocks": [2, "never"], + "quotes": [2, "single", { "avoidEscape": true, "allowTemplateLiterals": true }], + "semi": [2, "never"], + "semi-spacing": [2, { "before": false, "after": true }], + "space-before-blocks": [2, "always"], + "space-before-function-paren": 0, + "space-in-parens": [2, "never"], + "space-infix-ops": 2, + "space-unary-ops": [2, { "words": true, "nonwords": false }], + "spaced-comment": 0, + "template-curly-spacing": [2, "never"], + "use-isnan": 2, + "valid-typeof": 2, + "wrap-iife": [2, "any"], + "yield-star-spacing": [2, "both"], + "yoda": [0] + } +} diff --git a/src/lib/nostr-tools/.gitignore b/src/lib/nostr-tools/.gitignore new file mode 100644 index 00000000000..f9940ed24f0 --- /dev/null +++ b/src/lib/nostr-tools/.gitignore @@ -0,0 +1,7 @@ +node_modules +dist +yarn.lock +package-lock.json +.envrc +lib +test.html diff --git a/src/lib/nostr-tools/.prettierrc.yaml b/src/lib/nostr-tools/.prettierrc.yaml new file mode 100644 index 00000000000..16c878ec925 --- /dev/null +++ b/src/lib/nostr-tools/.prettierrc.yaml @@ -0,0 +1,10 @@ +semi: false +arrowParens: avoid +insertPragma: false +printWidth: 80 +proseWrap: preserve +singleQuote: true +trailingComma: none +useTabs: false +jsxBracketSameLine: false +bracketSpacing: false diff --git a/src/lib/nostr-tools/README.md b/src/lib/nostr-tools/README.md new file mode 100644 index 00000000000..df60942d4d4 --- /dev/null +++ b/src/lib/nostr-tools/README.md @@ -0,0 +1,192 @@ +# nostr-tools + +Tools for developing [Nostr](https://github.com/fiatjaf/nostr) clients. + +## Usage + +### Generating a private key and a public key + +```js +import { generatePrivateKey, getPublicKey } from "nostr-tools"; + +let sk = generatePrivateKey(); // `sk` is a hex string +let pk = getPublicKey(sk); // `pk` is a hex string +``` + +### Creating, signing and verifying events + +```js +import { validateEvent, verifySignature, signEvent, getEventHash, getPublicKey } from "nostr-tools"; + +let event = { + kind: 1, + created_at: Math.floor(Date.now() / 1000), + tags: [], + content: "hello" +}; + +event.id = getEventHash(event.id); +event.pubkey = getPublicKey(privateKey); +event.sig = await signEvent(event, privateKey); + +let ok = validateEvent(event); +let veryOk = await verifySignature(event); +``` + +### Interacting with a relay + +```js +import { relayInit, generatePrivateKey, getPublicKey, getEventHash, signEvent } from "nostr-tools"; + +const relay = relayInit("wss://relay.example.com"); +relay.connect(); + +relay.on("connect", () => { + console.log(`connected to ${relay.url}`); +}); +relay.on("error", () => { + console.log(`failed to connect to ${relay.url}`); +}); + +// let's query for an event that exists +let sub = relay.sub([ + { + ids: ["d7dd5eb3ab747e16f8d0212d53032ea2a7cadef53837e5a6c66d42849fcb9027"] + } +]); +sub.on("event", (event) => { + console.log("we got the event we wanted:", event); +}); +sub.on("eose", () => { + sub.unsub(); +}); + +// let's publish a new event while simultaneously monitoring the relay for it +let sk = generatePrivateKey(); +let pk = getPublicKey(sk); + +let sub = relay.sub([ + { + kinds: [1], + authors: [pk] + } +]); + +sub.on("event", (event) => { + console.log("got event:", event); +}); + +let event = { + kind: 1, + pubkey: pk, + created_at: Math.floor(Date.now() / 1000), + tags: [], + content: "hello world" +}; +event.id = getEventHash(event); +event.sig = await signEvent(event, sk); + +let pub = relay.publish(event); +pub.on("ok", () => { + console.log(`{relay.url} has accepted our event`); +}); +pub.on("seen", () => { + console.log(`we saw the event on {relay.url}`); +}); +pub.on("failed", (reason) => { + console.log(`failed to publish to {relay.url}: ${reason}`); +}); + +await relay.close(); +``` + +### Querying profile data from a NIP-05 address + +```js +import { nip05 } from "nostr-tools"; + +let profile = await nip05.queryProfile("jb55.com"); +console.log(profile.pubkey); +// prints: 32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245 +console.log(profile.relays); +// prints: [wss://relay.damus.io] + +// on nodejs, install node-fetch@2 and call this first: +nip05.useFetchImplementation(require("node-fetch")); +``` + +### Encoding and decoding NIP-19 codes + +```js +import { nip19, generatePrivateKey, getPublicKey } from "nostr-tools"; + +let sk = generatePrivateKey(); +let nsec = nip19.nsecEncode(sk); +let { type, data } = nip19.decode(nsec); +assert(type === "nsec"); +assert(data === sk); + +let pk = getPublicKey(generatePrivateKey()); +let npub = nip19.npubEncode(pk); +let { type, data } = nip19.decode(npub); +assert(type === "npub"); +assert(data === pk); + +let pk = getPublicKey(generatePrivateKey()); +let relays = ["wss://relay.nostr.example.mydomain.example.com", "wss://nostr.banana.com"]; +let nprofile = nip19.nprofileEncode({ pubkey: pk, relays }); +let { type, data } = nip19.decode(nprofile); +assert(type === "nprofile"); +assert(data.pubkey === pk); +assert(data.relays.length === 2); +``` + +### Encrypting and decrypting direct messages + +```js +import {nip04, getPublicKey, generatePrivateKey} from 'nostr-tools' + +// sender +let sk1 = generatePrivateKey() +let pk1 = getPublicKey(sk1) + +// receiver +let sk2 = generatePrivateKey() +let pk2 = getPublicKey(sk2) + +// on the sender side +let message = 'hello' +let ciphertext = nip04.encrypt(sk1, pk2, 'hello') + +let event = { + kind: 4, + pubkey: pk1, + tags: [['p', pk2]], + content: ciphertext, + ...otherProperties +} + +sendEvent(event) + +// on the receiver side +sub.on('event', (event) => { + let sender = event.tags.find(([k, v]) => k === 'p' && && v && v !== '')[1] + pk1 === sender + let plaintext = nip04.decrypt(sk2, pk1, event.content) +}) +``` + +Please consult the tests or [the source code](https://github.com/fiatjaf/nostr-tools) for more information that isn't available here. + +### Using from the browser (if you don't want to use a bundler) + +```html + + +``` + +## License + +Public domain. diff --git a/src/lib/nostr-tools/build.js b/src/lib/nostr-tools/build.js new file mode 100755 index 00000000000..31d53470d82 --- /dev/null +++ b/src/lib/nostr-tools/build.js @@ -0,0 +1,41 @@ +#!/usr/bin/env node + +const esbuild = require("esbuild"); + +let common = { + entryPoints: ["index.ts"], + bundle: true, + sourcemap: "external" +}; + +esbuild + .build({ + ...common, + outfile: "lib/nostr.esm.js", + format: "esm", + packages: "external" + }) + .then(() => console.log("esm build success.")); + +esbuild + .build({ + ...common, + outfile: "lib/nostr.cjs.js", + format: "cjs", + packages: "external" + }) + .then(() => console.log("cjs build success.")); + +esbuild + .build({ + ...common, + outfile: "lib/nostr.bundle.js", + format: "iife", + globalName: "NostrTools", + define: { + window: "self", + global: "self", + process: '{"env": {}}' + } + }) + .then(() => console.log("standalone build success.")); diff --git a/src/lib/nostr-tools/event.ts b/src/lib/nostr-tools/event.ts new file mode 100644 index 00000000000..1945a4195f8 --- /dev/null +++ b/src/lib/nostr-tools/event.ts @@ -0,0 +1,98 @@ +import * as secp256k1 from "@noble/secp256k1"; +import { sha256 } from "@noble/hashes/sha256"; + +import { utf8Encoder } from "./utils"; + +/* eslint-disable no-unused-vars */ + +export enum Kind { + Metadata = 0, + Text = 1, + RecommendRelay = 2, + Contacts = 3, + EncryptedDirectMessage = 4, + EventDeletion = 5, + Repost = 6, + Reaction = 7, + BadgeAward = 8, + ChannelCreation = 40, + ChannelMetadata = 41, + ChannelMessage = 42, + ChannelHideMessage = 43, + ChannelMuteUser = 44, + Report = 1984, + ZapRequest = 9734, + Zap = 9735, + RelayList = 10002, + BlockList = 16462, + FlagList = 16463, + ClientAuth = 22242, + ReplaceableByTag = 30000, + BadgeDefinition = 30008, + ProfileBadge = 30009, + Article = 30023 +} + +export type EventTemplate = { + kind: Kind; + tags: string[][]; + content: string; + created_at: number; +}; + +export type UnsignedEvent = EventTemplate & { + pubkey: string; +}; + +export type Event = UnsignedEvent & { + id: string; + sig: string; +}; + +export function getBlankEvent(): EventTemplate { + return { + kind: 255, + content: "", + tags: [], + created_at: 0 + }; +} + +export function serializeEvent(evt: Event): string { + if (!validateEvent(evt)) + throw new Error("can't serialize event with wrong or missing properties"); + + return JSON.stringify([0, evt.pubkey, evt.created_at, evt.kind, evt.tags, evt.content]); +} + +export function getEventHash(event: Event): string { + let eventHash = sha256(utf8Encoder.encode(serializeEvent(event))); + return secp256k1.utils.bytesToHex(eventHash); +} + +export function validateEvent(event: Event): boolean { + if (typeof event.content !== "string") return false; + if (typeof event.created_at !== "number") return false; + if (typeof event.pubkey !== "string") return false; + if (!event.pubkey.match(/^[a-f0-9]{64}$/)) return false; + + if (!Array.isArray(event.tags)) return false; + for (let i = 0; i < event.tags.length; i++) { + let tag = event.tags[i]; + if (!Array.isArray(tag)) return false; + for (let j = 0; j < tag.length; j++) { + if (typeof tag[j] === "object") return false; + } + } + + return true; +} + +export async function verifySignature(event: Event & { sig: string }): Promise { + return await secp256k1.schnorr.verify(event.sig, getEventHash(event), event.pubkey); +} + +export async function signEvent(event: Event, key: string): Promise { + const encrypted = await secp256k1.schnorr.sign(getEventHash(event), key); + return secp256k1.utils.bytesToHex(encrypted); +} diff --git a/src/lib/nostr-tools/filter.ts b/src/lib/nostr-tools/filter.ts new file mode 100644 index 00000000000..b995c795391 --- /dev/null +++ b/src/lib/nostr-tools/filter.ts @@ -0,0 +1,45 @@ +import { Event } from "./event"; + +export type Filter = { + ids?: string[]; + kinds?: number[]; + authors?: string[]; + keywords?: string[]; + since?: number; + until?: number; + limit?: number; + [key: `#${string}`]: string[]; +}; + +export function matchFilter(filter: Filter, event: Event & { id: string }): boolean { + if (filter.ids && filter.ids.indexOf(event.id) === -1) return false; + if (filter.kinds && filter.kinds.indexOf(event.kind) === -1) return false; + if (filter.keywords) { + const lowercase = event.content?.toLowerCase(); + for (let i = 0; i < filter.keywords.length; i++) { + if (lowercase?.indexOf(filter.keywords[i].toLowerCase()) === -1) return false; + } + } + if (filter.authors && filter.authors.indexOf(event.pubkey) === -1) return false; + + for (let f in filter) { + if (f[0] === "#") { + let tagName = f.slice(1); + let values = filter[`#${tagName}`]; + if (values && !event.tags?.find(([t, v]) => t === f.slice(1) && values.indexOf(v) !== -1)) + return false; + } + } + + if (filter.since && event.created_at < filter.since) return false; + if (filter.until && event.created_at >= filter.until) return false; + + return true; +} + +export function matchFilters(filters: Filter[], event: Event & { id: string }): boolean { + for (let i = 0; i < filters.length; i++) { + if (matchFilter(filters[i], event)) return true; + } + return false; +} diff --git a/src/lib/nostr-tools/index.ts b/src/lib/nostr-tools/index.ts new file mode 100644 index 00000000000..62ed810120c --- /dev/null +++ b/src/lib/nostr-tools/index.ts @@ -0,0 +1,21 @@ +// import * as keys from './keys' +// import * as relay from './relay' +// import * as event from './event' +// import * as filter from './filter' +// import * as path from './path' + +// export {keys, relay, event, filter, path} + +// export * as nip04 from './nip04' +export * as nip05 from "./nip05"; +export * as nip06 from "./nip06"; +export * as nip19 from "./nip19"; +export * as nip26 from "./nip26"; + +// monkey patch secp256k1 +import * as secp256k1 from "@noble/secp256k1"; +import { hmac } from "@noble/hashes/hmac"; +import { sha256 } from "@noble/hashes/sha256"; +secp256k1.utils.hmacSha256Sync = (key, ...msgs) => + hmac(sha256, key, secp256k1.utils.concatBytes(...msgs)); +secp256k1.utils.sha256Sync = (...msgs) => sha256(secp256k1.utils.concatBytes(...msgs)); diff --git a/src/lib/nostr-tools/keys.ts b/src/lib/nostr-tools/keys.ts new file mode 100644 index 00000000000..f373133a7a4 --- /dev/null +++ b/src/lib/nostr-tools/keys.ts @@ -0,0 +1,9 @@ +import * as secp256k1 from "@noble/secp256k1"; + +export function generatePrivateKey(): string { + return secp256k1.utils.bytesToHex(secp256k1.utils.randomPrivateKey()); +} + +export function getPublicKey(privateKey: string): string { + return secp256k1.utils.bytesToHex(secp256k1.schnorr.getPublicKey(privateKey)); +} diff --git a/src/lib/nostr-tools/nip04.ts b/src/lib/nostr-tools/nip04.ts new file mode 100644 index 00000000000..6d8a088bdd1 --- /dev/null +++ b/src/lib/nostr-tools/nip04.ts @@ -0,0 +1,42 @@ +import { randomBytes } from "@noble/hashes/utils"; +import * as secp256k1 from "@noble/secp256k1"; +import { base64 } from "@scure/base"; + +import { utf8Decoder, utf8Encoder } from "./utils"; + +export async function encrypt(privkey: string, pubkey: string, text: string): Promise { + const key = secp256k1.getSharedSecret(privkey, "02" + pubkey); + const normalizedKey = getNormalizedX(key); + + let iv = Uint8Array.from(randomBytes(16)); + let plaintext = utf8Encoder.encode(text); + let cryptoKey = await crypto.subtle.importKey("raw", normalizedKey, { name: "AES-CBC" }, false, [ + "encrypt" + ]); + let ciphertext = await crypto.subtle.encrypt({ name: "AES-CBC", iv }, cryptoKey, plaintext); + let ctb64 = base64.encode(new Uint8Array(ciphertext)); + let ivb64 = base64.encode(new Uint8Array(iv.buffer)); + + return `${ctb64}?iv=${ivb64}`; +} + +export async function decrypt(privkey: string, pubkey: string, data: string): Promise { + let [ctb64, ivb64] = data.split("?iv="); + let key = secp256k1.getSharedSecret(privkey, "02" + pubkey); + let normalizedKey = getNormalizedX(key); + + let cryptoKey = await crypto.subtle.importKey("raw", normalizedKey, { name: "AES-CBC" }, false, [ + "decrypt" + ]); + let ciphertext = base64.decode(ctb64); + let iv = base64.decode(ivb64); + + let plaintext = await crypto.subtle.decrypt({ name: "AES-CBC", iv }, cryptoKey, ciphertext); + + let text = utf8Decoder.decode(plaintext); + return text; +} + +function getNormalizedX(key: Uint8Array): Uint8Array { + return key.slice(1, 33); +} diff --git a/src/lib/nostr-tools/nip05.ts b/src/lib/nostr-tools/nip05.ts new file mode 100644 index 00000000000..410fbb969d4 --- /dev/null +++ b/src/lib/nostr-tools/nip05.ts @@ -0,0 +1,48 @@ +import { ProfilePointer } from "./nip19"; + +var _fetch: any; + +try { + _fetch = fetch; +} catch {} + +export function useFetchImplementation(fetchImplementation: any) { + _fetch = fetchImplementation; +} + +export async function searchDomain( + domain: string, + query = "" +): Promise<{ [name: string]: string }> { + try { + let res = await (await _fetch(`https://${domain}/.well-known/nostr.json?name=${query}`)).json(); + + return res.names; + } catch (_) { + return {}; + } +} + +export async function queryProfile(fullname: string): Promise { + let [name, domain] = fullname.split("@"); + + if (!domain) { + // if there is no @, it is because it is just a domain, so assume the name is "_" + domain = name; + name = "_"; + } + + if (!name.match(/^[a-z0-9-_]+$/)) return null; + + let res = await (await _fetch(`https://${domain}/.well-known/nostr.json?name=${name}`)).json(); + + if (!res?.names?.[name]) return null; + + let pubkey = res.names[name] as string; + let relays = (res.relays?.[pubkey] || []) as string[]; // nip35 + + return { + pubkey, + relays + }; +} diff --git a/src/lib/nostr-tools/nip06.ts b/src/lib/nostr-tools/nip06.ts new file mode 100644 index 00000000000..5b28b939780 --- /dev/null +++ b/src/lib/nostr-tools/nip06.ts @@ -0,0 +1,19 @@ +import * as secp256k1 from "@noble/secp256k1"; +import { wordlist } from "@scure/bip39/wordlists/english.js"; +import { generateMnemonic, mnemonicToSeedSync, validateMnemonic } from "@scure/bip39"; +import { HDKey } from "@scure/bip32"; + +export function privateKeyFromSeedWords(mnemonic: string, passphrase?: string): string { + let root = HDKey.fromMasterSeed(mnemonicToSeedSync(mnemonic, passphrase)); + let privateKey = root.derive(`m/44'/1237'/0'/0/0`).privateKey; + if (!privateKey) throw new Error("could not derive private key"); + return secp256k1.utils.bytesToHex(privateKey); +} + +export function generateSeedWords(): string { + return generateMnemonic(wordlist); +} + +export function validateWords(words: string): boolean { + return validateMnemonic(words, wordlist); +} diff --git a/src/lib/nostr-tools/nip19.ts b/src/lib/nostr-tools/nip19.ts new file mode 100644 index 00000000000..0fb945a06e1 --- /dev/null +++ b/src/lib/nostr-tools/nip19.ts @@ -0,0 +1,127 @@ +import * as secp256k1 from "@noble/secp256k1"; +import { bech32 } from "@scure/base"; + +import { utf8Decoder, utf8Encoder } from "./utils"; + +const Bech32MaxSize = 5000; + +export type ProfilePointer = { + pubkey: string; // hex + relays?: string[]; +}; + +export type EventPointer = { + id: string; // hex + relays?: string[]; +}; + +export function decode(nip19: string): { + type: string; + data: ProfilePointer | EventPointer | string; +} { + let { prefix, words } = bech32.decode(nip19, Bech32MaxSize); + let data = new Uint8Array(bech32.fromWords(words)); + + if (prefix === "nprofile") { + let tlv = parseTLV(data); + if (!tlv[0]?.[0]) throw new Error("missing TLV 0 for nprofile"); + if (tlv[0][0].length !== 32) throw new Error("TLV 0 should be 32 bytes"); + + return { + type: "nprofile", + data: { + pubkey: secp256k1.utils.bytesToHex(tlv[0][0]), + relays: tlv[1].map((d) => utf8Decoder.decode(d)) + } + }; + } + + if (prefix === "nevent") { + let tlv = parseTLV(data); + if (!tlv[0]?.[0]) throw new Error("missing TLV 0 for nevent"); + if (tlv[0][0].length !== 32) throw new Error("TLV 0 should be 32 bytes"); + + return { + type: "nevent", + data: { + id: secp256k1.utils.bytesToHex(tlv[0][0]), + relays: tlv[1].map((d) => utf8Decoder.decode(d)) + } + }; + } + + if (prefix === "nsec" || prefix === "npub" || prefix === "note") { + return { type: prefix, data: secp256k1.utils.bytesToHex(data) }; + } + + throw new Error(`unknown prefix ${prefix}`); +} + +type TLV = { [t: number]: Uint8Array[] }; + +function parseTLV(data: Uint8Array): TLV { + let result: TLV = {}; + let rest = data; + while (rest.length > 0) { + let t = rest[0]; + let l = rest[1]; + let v = rest.slice(2, 2 + l); + rest = rest.slice(2 + l); + if (v.length < l) continue; + result[t] = result[t] || []; + result[t].push(v); + } + return result; +} + +export function nsecEncode(hex: string): string { + return encodeBytes("nsec", hex); +} + +export function npubEncode(hex: string): string { + return encodeBytes("npub", hex); +} + +export function noteEncode(hex: string): string { + return encodeBytes("note", hex); +} + +function encodeBytes(prefix: string, hex: string): string { + let data = secp256k1.utils.hexToBytes(hex); + let words = bech32.toWords(data); + return bech32.encode(prefix, words, Bech32MaxSize); +} + +export function nprofileEncode(profile: ProfilePointer): string { + let data = encodeTLV({ + 0: [secp256k1.utils.hexToBytes(profile.pubkey)], + 1: (profile.relays || []).map((url) => utf8Encoder.encode(url)) + }); + let words = bech32.toWords(data); + return bech32.encode("nprofile", words, Bech32MaxSize); +} + +export function neventEncode(event: EventPointer): string { + let data = encodeTLV({ + 0: [secp256k1.utils.hexToBytes(event.id)], + 1: (event.relays || []).map((url) => utf8Encoder.encode(url)) + }); + let words = bech32.toWords(data); + return bech32.encode("nevent", words, Bech32MaxSize); +} + +function encodeTLV(tlv: TLV): Uint8Array { + let entries: Uint8Array[] = []; + + Object.entries(tlv).forEach(([t, vs]) => { + vs.forEach((v) => { + let entry = new Uint8Array(v.length + 2); + entry.set([parseInt(t)], 0); + entry.set([v.length], 1); + entry.set(v, 2); + entries.push(entry); + }); + }); + + return secp256k1.utils.concatBytes(...entries); +} diff --git a/src/lib/nostr-tools/nip26.ts b/src/lib/nostr-tools/nip26.ts new file mode 100644 index 00000000000..bdaed49e65b --- /dev/null +++ b/src/lib/nostr-tools/nip26.ts @@ -0,0 +1,71 @@ +import * as secp256k1 from "@noble/secp256k1"; +import { sha256 } from "@noble/hashes/sha256"; + +import { Event } from "./event"; +import { utf8Encoder } from "./utils"; +import { getPublicKey } from "./keys"; + +export type Parameters = { + pubkey: string; // the key to whom the delegation will be given + kind: number | undefined; + until: number | undefined; // delegation will only be valid until this date + since: number | undefined; // delegation will be valid from this date on +}; + +export type Delegation = { + from: string; // the pubkey who signed the delegation + to: string; // the pubkey that is allowed to use the delegation + cond: string; // the string of conditions as they should be included in the event tag + sig: string; +}; + +export function createDelegation(privateKey: string, parameters: Parameters): Delegation { + let conditions = []; + if ((parameters.kind || -1) >= 0) conditions.push(`kind=${parameters.kind}`); + if (parameters.until) conditions.push(`created_at<${parameters.until}`); + if (parameters.since) conditions.push(`created_at>${parameters.since}`); + let cond = conditions.join("&"); + + if (cond === "") throw new Error("refusing to create a delegation without any conditions"); + + let sighash = sha256(utf8Encoder.encode(`nostr:delegation:${parameters.pubkey}:${cond}`)); + + let sig = secp256k1.utils.bytesToHex(secp256k1.schnorr.signSync(sighash, privateKey)); + + return { + from: getPublicKey(privateKey), + to: parameters.pubkey, + cond, + sig + }; +} + +export function getDelegator(event: Event): string | null { + // find delegation tag + let tag = event.tags.find((tag) => tag[0] === "delegation" && tag.length >= 4); + if (!tag) return null; + + let pubkey = tag[1]; + let cond = tag[2]; + let sig = tag[3]; + + // check conditions + let conditions = cond.split("&"); + for (let i = 0; i < conditions.length; i++) { + let [key, operator, value] = conditions[i].split(/\b/); + + // the supported conditions are just 'kind' and 'created_at' for now + if (key === "kind" && operator === "=" && event.kind === parseInt(value)) continue; + else if (key === "created_at" && operator === "<" && event.created_at < parseInt(value)) + continue; + else if (key === "created_at" && operator === ">" && event.created_at > parseInt(value)) + continue; + else return null; // invalid condition + } + + // check signature + let sighash = sha256(utf8Encoder.encode(`nostr:delegation:${event.pubkey}:${cond}`)); + if (!secp256k1.schnorr.verifySync(sig, sighash, pubkey)) return null; + + return pubkey; +} diff --git a/src/lib/nostr-tools/nip42.ts b/src/lib/nostr-tools/nip42.ts new file mode 100644 index 00000000000..07b25b47a39 --- /dev/null +++ b/src/lib/nostr-tools/nip42.ts @@ -0,0 +1,46 @@ +import { Event, Kind } from "./event"; +import { Relay } from "./relay"; + +/** + * Authenticate via NIP-42 flow. + * + * @example + * const sign = window.nostr.signEvent + * relay.on('auth', challenge => + * authenticate({ relay, sign, challenge }) + * ) + */ +export const authenticate = async ({ + challenge, + relay, + sign +}: { + challenge: string; + relay: Relay; + sign: (e: Partial) => Promise; +}): Promise => { + const e: Partial = { + kind: Kind.ClientAuth, + created_at: Math.floor(Date.now() / 1000), + tags: [ + ["relay", relay.url], + ["challenge", challenge] + ], + content: "" + }; + const signed = await sign(e); + if (!signed.sig) { + throw new Error("Canceled signing"); + } + const pub = relay.auth(signed); + return new Promise((resolve, reject) => { + pub.on("ok", function ok() { + pub.off("ok", ok); + resolve(); + }); + pub.on("failed", function fail(reason: string) { + pub.off("failed", fail); + reject(reason); + }); + }); +}; diff --git a/src/lib/nostr-tools/package.json b/src/lib/nostr-tools/package.json new file mode 100644 index 00000000000..6ca9bd9153e --- /dev/null +++ b/src/lib/nostr-tools/package.json @@ -0,0 +1,40 @@ +{ + "name": "nostr-tools", + "version": "1.1.2", + "description": "Tools for making a Nostr client.", + "repository": { + "type": "git", + "url": "https://github.com/fiatjaf/nostr-tools.git" + }, + "main": "lib/nostr.cjs.js", + "module": "lib/nostr.esm.js", + "dependencies": {}, + "keywords": [ + "decentralization", + "social", + "censorship-resistance", + "client", + "nostr" + ], + "devDependencies": { + "@types/node": "^18.0.3", + "@typescript-eslint/eslint-plugin": "^5.46.1", + "@typescript-eslint/parser": "^5.46.1", + "esbuild": "0.16.9", + "esbuild-plugin-alias": "^0.2.1", + "eslint": "^8.30.0", + "eslint-plugin-babel": "^5.3.1", + "esm-loader-typescript": "^1.0.1", + "events": "^3.3.0", + "jest": "^29.3.1", + "node-fetch": "2", + "ts-jest": "^29.0.3", + "tsd": "^0.22.0", + "typescript": "^4.9.4" + }, + "scripts": { + "build": "node build.js", + "pretest": "node build.js", + "test": "jest" + } +} diff --git a/src/lib/nostr-tools/path.ts b/src/lib/nostr-tools/path.ts new file mode 100644 index 00000000000..e6be6596d1f --- /dev/null +++ b/src/lib/nostr-tools/path.ts @@ -0,0 +1,223 @@ +/** +Path API for Nostr, built on NIP33 replaceable-by-tag events of kind 30000. + +``` +const path = new Path(myPublishFn, mySubscribeFn, myUnsubscribeFn, { authors: [myPubKey} }) +path.set('reactions/[noteID]', '😎') +path.get('reactions/[noteID]', (value, path, event) => console.log(event.pubkey, 'reacted with', value)) +``` + +TODO: +``` +path.list('reactions', (value, path, event) => { + console.log( + event.pubkey, 'reacted to', path.slice('/')[1], 'with', value + ) + }, { authors: myFollows } +) +``` + +In-memory caches the most recent event for the subscribed path, and only calls back with the most recent value. + +This API allows us to build all kinds of applications on top of Nostr (github replacement for example) without having to +specify new event kinds all the time and implement them in all applications and relays. + +NIP33: https://github.com/nostr-protocol/nips/blob/master/33.md + */ +import { Filter, matchFilter } from "./filter"; +import { Event } from "./event"; + +const EVENT_KIND = 30000; + +type CompleteEvent = Event & { id: string }; +type PathCallback = (value: any, path: string, event: Event) => void; +type Listener = { + filter: Filter; + callback: PathCallback; + subscription?: string; + off: () => void; +}; +type Publish = (event: Partial) => Promise; +type Subscribe = (filters: Filter[], callback: (event: Event) => void) => string; +type Unsubscribe = (id: string) => void; +type Encrypt = (content: string) => Promise; +type Decrypt = (content: string) => Promise; + +export function getEventPath(event: Event): string | undefined { + return event.tags?.find(([t]) => t === "d")?.[1]; +} + +export function getFilterPath(filter: Filter): string | undefined { + return filter["#d"]?.[0]; +} + +// We can later add other storages like IndexedDB or localStorage +class MemoryStorage { + eventsByPathAndAuthor = new Map>(); + + // returns a boolean indicating whether the event was added (newer than existing) + set(event: Event): boolean { + const path = getEventPath(event); + if (!path) { + //throw new Error(`event has no d tag: ${JSON.stringify(event)}`) + return false; + } + if (!this.eventsByPathAndAuthor.has(path)) { + this.eventsByPathAndAuthor.set(path, new Map()); + } + let valuesByAuthor = this.eventsByPathAndAuthor.get(path); + if (!valuesByAuthor) { + valuesByAuthor = new Map(); + this.eventsByPathAndAuthor.set(path, valuesByAuthor); + } + const existing = valuesByAuthor?.get(event.pubkey); + if (existing && existing.created_at > event.created_at) { + return false; + } + valuesByAuthor.set(event.pubkey, event); + return true; + } + + get(filter: Filter, callback: (event: Event) => void) { + const path = getFilterPath(filter); + if (!path) { + throw new Error(`filter has no #d tag: ${JSON.stringify(filter)}`); + } + const valuesByAuthor = this.eventsByPathAndAuthor.get(path); + if (!valuesByAuthor) { + return; + } + for (let [author, event] of valuesByAuthor) { + if (!filter.authors || filter.authors.indexOf(author) !== -1) { + callback(event); + } + } + } +} + +export class Path { + store: MemoryStorage; + listeners: Map; + publish: Publish; + subscribe: Subscribe; + unsubscribe: Unsubscribe; + filter: Filter; + encrypt?: Encrypt; + decrypt?: Decrypt; + + constructor( + publish: Publish, + subscribe: Subscribe, + unsubscribe: Unsubscribe, + filter: Filter, + encrypt?: Encrypt, + decrypt?: Decrypt + ) { + this.publish = publish; + this.subscribe = subscribe; + this.unsubscribe = unsubscribe; + this.filter = filter; + this.encrypt = encrypt; + this.decrypt = decrypt; + this.store = new MemoryStorage(); + this.listeners = new Map(); + } + + async publishSetEvent(path: string, value: any): Promise { + let content: string; + if (this.encrypt) { + // TODO: path should be deterministically encrypted hash(path + secret) but NIP07 provides no way for that + const contentStr = JSON.stringify(value); + content = await this.encrypt(contentStr); + if (contentStr === content) { + throw new Error(`Encryption failed: ${contentStr} === ${content}`); + } + } else { + content = JSON.stringify(value); + } + return this.publish({ + kind: EVENT_KIND, + tags: [["d", path]], + content, + created_at: Math.floor(Date.now() / 1000) + }); + } + + async set(path: string, value: any): Promise { + try { + const event = await this.publishSetEvent(path, value); + if (event) { + if (this.store.set(event)) { + this.notifyListeners(event as CompleteEvent); + } + return true; + } + } catch (e) { + console.error(e); + } + return false; + } + + async getEventValue(event: Event): Promise { + let value = this.decrypt ? await this.decrypt(event.content) : event.content; + try { + value = JSON.parse(value); + } catch (e) { + throw new Error(`Failed to parse event content: ${value}`); + } + return value; + } + + get(path: string, callback: PathCallback, filter = {}): Listener { + filter = Object.assign({}, filter, this.filter, { "#d": [path], kinds: [EVENT_KIND] }); + const listener = this.addListener(filter, callback); + this.store.get(filter, (event) => this.callbackFromEvent(event, callback)); + this.subscribe([filter], async (event) => { + if (this.store.set(event)) { + this.notifyListeners(event as CompleteEvent); + } + }); + return listener; + } + + addListener(filter: Filter, callback: PathCallback): Listener { + const id = Math.random().toString(36).substr(2, 9); + const listener: Listener = { + filter, + callback, + off: () => { + this.listeners.delete(id); + if (listener.subscription) { + this.unsubscribe(listener.subscription); + } + } + }; + this.listeners.set(id, listener); + return listener; + } + + removeListener(id: string) { + const listener = this.listeners.get(id); + if (listener) { + listener.off(); + } + } + + callbackFromEvent(event: Event, callback: PathCallback) { + const path = getEventPath(event); + if (!path) { + throw new Error(`event has no d tag: ${JSON.stringify(event)}`); + } + this.getEventValue(event).then((value) => { + callback(value, path, event); + }); + } + + async notifyListeners(event: CompleteEvent) { + for (let listener of this.listeners.values()) { + if (matchFilter(listener.filter, event)) { + this.callbackFromEvent(event, listener.callback); + } + } + } +} diff --git a/src/lib/nostr-tools/pool.ts b/src/lib/nostr-tools/pool.ts new file mode 100644 index 00000000000..72ac0882777 --- /dev/null +++ b/src/lib/nostr-tools/pool.ts @@ -0,0 +1,206 @@ +import { Relay, relayInit } from "./relay"; +import { Filter } from "./filter"; +import { Event } from "./event"; + +// interface SimplePoolTypes { +// private _conn +// private _seenOn +// private eoseSubTimeout +// private getTimeout +// constructor(options?: {eoseSubTimeout?: number; getTimeout?: number}) +// close(relays: string[]): void +// ensureRelay(url: string): Promise +// sub(relays: string[], filters: Filter[], opts?: SubscriptionOptions): Sub +// get( +// relays: string[], +// filter: Filter, +// opts?: SubscriptionOptions +// ): Promise +// list( +// relays: string[], +// filters: Filter[], +// opts?: SubscriptionOptions +// ): Promise +// publish(relays: string[], event: Event): Pub +// seenOn(id: string): string[] +// } + +export interface SimplePoolOptions { + eoseSubTimeout?: number; + getTimeout?: number; + listTimeout?: number; +} + +function normalizeURL(url) { + let p = new URL(url); + p.pathname = p.pathname.replace(/\/+/g, "/"); + if (p.pathname.endsWith("/")) p.pathname = p.pathname.slice(0, -1); + if ((p.port === "80" && p.protocol === "ws:") || (p.port === "443" && p.protocol === "wss:")) + p.port = ""; + p.searchParams.sort(); + p.hash = ""; + return p.toString(); +} + +export default class SimplePool { + _conn; + _seenOn = {}; + eoseSubTimeout; + getTimeout; + constructor(options = {}) { + this._conn = {}; + this.eoseSubTimeout = options.eoseSubTimeout || 3400; + this.getTimeout = options.getTimeout || 3400; + } + + close(relays) { + relays.forEach((url) => { + let relay = this._conn[normalizeURL(url)]; + if (relay) relay.close(); + }); + } + async ensureRelay(url) { + const nm = normalizeURL(url); + if (!this._conn[nm]) { + this._conn[nm] = relayInit(nm, { + getTimeout: this.getTimeout * 0.9, + listTimeout: this.getTimeout * 0.9 + }); + } + const relay = this._conn[nm]; + await relay.connect(); + return relay; + } + sub(relays, filters, opts?) { + let _knownIds = /* @__PURE__ */ new Set(); + let modifiedOpts = { ...(opts || {}) }; + modifiedOpts.alreadyHaveEvent = (id, url) => { + if (opts?.alreadyHaveEvent?.(id, url)) { + return true; + } + let set = this._seenOn[id] || /* @__PURE__ */ new Set(); + set.add(url); + this._seenOn[id] = set; + return _knownIds.has(id); + }; + let subs = []; + let eventListeners = /* @__PURE__ */ new Set(); + let eoseListeners = /* @__PURE__ */ new Set(); + let eosesMissing = relays.length; + let eoseSent = false; + let eoseTimeout = setTimeout(() => { + eoseSent = true; + for (let cb of eoseListeners.values()) cb(); + }, this.eoseSubTimeout); + relays.forEach(async (relay) => { + let r; + try { + r = await this.ensureRelay(relay); + } catch (err) { + handleEose(); + return; + } + if (!r) return; + let s = r.sub(filters, modifiedOpts); + s.on("event", (event) => { + _knownIds.add(event.id); + for (let cb of eventListeners.values()) cb(event); + }); + s.on("eose", () => { + if (eoseSent) return; + handleEose(); + }); + subs.push(s); + function handleEose() { + eosesMissing--; + if (eosesMissing === 0) { + clearTimeout(eoseTimeout); + for (let cb of eoseListeners.values()) cb(); + } + } + }); + let greaterSub = { + sub(filters2, opts2) { + subs.forEach((sub) => sub.sub(filters2, opts2)); + return greaterSub; + }, + unsub() { + subs.forEach((sub) => sub.unsub()); + }, + on(type, cb) { + if (type === "event") { + eventListeners.add(cb); + } else if (type === "eose") { + eoseListeners.add(cb); + } + }, + off(type, cb) { + if (type === "event") { + eventListeners.delete(cb); + } else if (type === "eose") eoseListeners.delete(cb); + } + }; + return greaterSub; + } + get(relays, filter, opts) { + return new Promise((resolve) => { + let sub = this.sub(relays, [filter], opts); + let timeout = setTimeout(() => { + sub.unsub(); + resolve(null); + }, this.getTimeout); + sub.on("event", (event) => { + resolve(event); + clearTimeout(timeout); + sub.unsub(); + }); + }); + } + list(relays, filters, opts) { + return new Promise((resolve) => { + let events = []; + let sub = this.sub(relays, filters, opts); + sub.on("event", (event) => { + events.push(event); + }); + sub.on("eose", () => { + sub.unsub(); + resolve(events); + }); + }); + } + publish(relays, event) { + const pubPromises = relays.map(async (relay) => { + let r; + try { + r = await this.ensureRelay(relay); + return r.publish(event); + } catch (_) { + return { on() {}, off() {} }; + } + }); + const callbackMap = /* @__PURE__ */ new Map(); + return { + on(type, cb) { + relays.forEach(async (relay, i) => { + let pub = await pubPromises[i]; + let callback = () => cb(relay); + callbackMap.set(cb, callback); + pub.on(type, callback); + }); + }, + off(type, cb) { + relays.forEach(async (_, i) => { + let callback = callbackMap.get(cb); + if (callback) { + let pub = await pubPromises[i]; + pub.off(type, callback); + } + }); + } + }; + } + seenOn(id) { + return Array.from(this._seenOn[id]?.values?.() || []); + } +} diff --git a/src/lib/nostr-tools/relay.ts b/src/lib/nostr-tools/relay.ts new file mode 100644 index 00000000000..af55a6aa166 --- /dev/null +++ b/src/lib/nostr-tools/relay.ts @@ -0,0 +1,584 @@ +/* global WebSocket */ + +import { Event, verifySignature, validateEvent } from "./event"; +import { Filter } from "./filter"; +import { SimplePoolOptions } from "./pool"; + +type RelayEvent = "connect" | "disconnect" | "error" | "notice" | "auth"; + +export type Relay = { + enabled?: any; + url: string; + status: number; + connect: () => Promise; + close: () => Promise; + sub: (filters: Filter[], opts?: SubscriptionOptions) => Sub; + publish: (event: Event) => Pub; + auth: (event: Event) => Pub; + on: (type: RelayEvent, cb: any) => void; + off: (type: RelayEvent, cb: any) => void; +}; +export type Pub = { + on: (type: "ok" | "seen" | "failed", cb: any) => void; + off: (type: "ok" | "seen" | "failed", cb: any) => void; +}; +export type Sub = { + sub: (filters: Filter[], opts: SubscriptionOptions) => Sub; + unsub: () => void; + on: (type: "event" | "eose", cb: any) => void; + off: (type: "event" | "eose", cb: any) => void; +}; + +type SubscriptionOptions = { + skipVerification?: boolean; + id?: string; + alreadyHaveEvent?: any; +}; + +// TODO allowAuthor fn so we can skip JSON.parse and sig verify if we won't allow the author anyway +// export function relayInit( +// url: string, +// alreadyHaveEvent?: (id: string) => boolean +// ): Relay { +// var ws: WebSocket +// var resolveClose: () => void +// var resolveOpen: (value: PromiseLike | void) => void +// var untilOpen = new Promise(resolve => { +// resolveOpen = resolve +// }) +// var openSubs: {[id: string]: {filters: Filter[]} & SubscriptionOptions} = {} +// var listeners: { +// connect: Array<() => void> +// disconnect: Array<() => void> +// error: Array<() => void> +// notice: Array<(msg: string) => void> +// auth: Array<(challenge: string) => void> +// } = { +// connect: [], +// disconnect: [], +// error: [], +// notice: [], +// auth: [] +// } +// var subListeners: { +// [subid: string]: { +// event: Array<(event: Event) => void> +// eose: Array<() => void> +// } +// } = {} +// var pubListeners: { +// [eventid: string]: { +// ok: Array<() => void> +// seen: Array<() => void> +// failed: Array<(reason: string) => void> +// } +// } = {} +// let idRegex = /"id":"([a-fA-F0-9]+)"/ + +// async function connectRelay(): Promise { +// return new Promise((resolve, reject) => { +// ws = new WebSocket(url) + +// ws.onopen = () => { +// listeners.connect.forEach(cb => cb()) +// resolveOpen() +// resolve() +// } +// ws.onerror = () => { +// listeners.error.forEach(cb => cb()) +// reject() +// } +// ws.onclose = async () => { +// listeners.disconnect.forEach(cb => cb()) +// resolveClose && resolveClose() +// } + +// let incomingMessageQueue: any[] = [] +// let handleNextInterval: any + +// const handleNext = () => { +// if (incomingMessageQueue.length === 0) { +// clearInterval(handleNextInterval) +// handleNextInterval = null +// return +// } + +// var data = incomingMessageQueue.shift() +// if (data && alreadyHaveEvent !== undefined) { +// const match = idRegex.exec(data) +// if (match) { +// const id = match[1] +// if (alreadyHaveEvent(id)) { +// //console.log(`already have`); +// return +// } +// } +// } +// try { +// data = JSON.parse(data) +// } catch (err) {} + +// if (data.length >= 1) { +// switch (data[0]) { +// case 'EVENT': +// if (data.length !== 3) return // ignore empty or malformed EVENT + +// let id = data[1] +// let event = data[2] +// if ( +// validateEvent(event) && +// openSubs[id] && +// (openSubs[id].skipVerification || verifySignature(event)) && +// matchFilters(openSubs[id].filters, event) +// ) { +// openSubs[id] +// ;(subListeners[id]?.event || []).forEach(cb => cb(event)) +// } +// return +// case 'EOSE': { +// if (data.length !== 2) return // ignore empty or malformed EOSE +// let id = data[1] +// ;(subListeners[id]?.eose || []).forEach(cb => cb()) +// return +// } +// case 'OK': { +// if (data.length < 3) return // ignore empty or malformed OK +// let id: string = data[1] +// let ok: boolean = data[2] +// let reason: string = data[3] || '' +// if (ok) pubListeners[id]?.ok.forEach(cb => cb()) +// else pubListeners[id]?.failed.forEach(cb => cb(reason)) +// return +// } +// case 'NOTICE': +// if (data.length !== 2) return // ignore empty or malformed NOTICE +// let notice = data[1] +// listeners.notice.forEach(cb => cb(notice)) +// return +// case 'AUTH': { +// let challenge = data[1] +// listeners.auth?.forEach(cb => cb(challenge)) +// return +// } +// } +// } +// } + +// ws.onmessage = e => { +// incomingMessageQueue.push(e.data) +// if (!handleNextInterval) { +// handleNextInterval = setInterval(handleNext, 0) +// } +// } +// }) +// } + +// async function connect(): Promise { +// if (ws?.readyState && ws.readyState === 1) return // ws already open +// await connectRelay() +// } + +// async function trySend(params: [string, ...any]) { +// let msg = JSON.stringify(params) + +// await untilOpen +// try { +// ws.send(msg) +// } catch (err) { +// console.log(err) +// } +// } + +// const sub = ( +// filters: Filter[], +// { +// skipVerification = false, +// id = Math.random().toString().slice(2) +// }: SubscriptionOptions = {} +// ): Sub => { +// let subid = id + +// openSubs[subid] = { +// id: subid, +// filters, +// skipVerification +// } +// trySend(['REQ', subid, ...filters]) + +// return { +// sub: (newFilters, newOpts = {}) => +// sub(newFilters || filters, { +// skipVerification: newOpts.skipVerification || skipVerification, +// id: subid +// }), +// unsub: () => { +// delete openSubs[subid] +// delete subListeners[subid] +// trySend(['CLOSE', subid]) +// }, +// on: (type: 'event' | 'eose', cb: any): void => { +// subListeners[subid] = subListeners[subid] || { +// event: [], +// eose: [] +// } +// subListeners[subid][type].push(cb) +// }, +// off: (type: 'event' | 'eose', cb: any): void => { +// let listeners = subListeners[subid] +// let idx = listeners[type].indexOf(cb) +// if (idx >= 0) listeners[type].splice(idx, 1) +// } +// } +// } + +// function _publishEvent(event: Event, type: string) { +// if (!event.id) throw new Error(`event ${event} has no id`) +// let id = event.id + +// var sent = false +// var mustMonitor = false + +// trySend([type, event]) +// .then(() => { +// sent = true +// if (mustMonitor) { +// startMonitoring() +// mustMonitor = false +// } +// }) +// .catch(() => {}) + +// const startMonitoring = () => { +// let monitor = sub([{ids: [id]}], { +// id: `monitor-${id.slice(0, 5)}` +// }) +// let willUnsub = setTimeout(() => { +// ;(pubListeners[id]?.failed || []).forEach(cb => +// cb('event not seen after 5 seconds') +// ) +// monitor.unsub() +// }, 5000) +// monitor.on('event', () => { +// clearTimeout(willUnsub) +// ;(pubListeners[id]?.seen || []).forEach(cb => cb()) +// }) +// } + +// return { +// on: (type: 'ok' | 'seen' | 'failed', cb: any) => { +// pubListeners[id] = pubListeners[id] || { +// ok: [], +// seen: [], +// failed: [] +// } +// pubListeners[id][type].push(cb) + +// if (type === 'seen') { +// if (sent) startMonitoring() +// else mustMonitor = true +// } +// }, +// off: (type: 'ok' | 'seen' | 'failed', cb: any) => { +// let listeners = pubListeners[id] +// if (!listeners) return +// let idx = listeners[type].indexOf(cb) +// if (idx >= 0) listeners[type].splice(idx, 1) +// } +// } +// } + +// return { +// url, +// sub, +// on: (type: RelayEvent, cb: any): void => { +// listeners[type].push(cb) +// if (type === 'connect' && ws?.readyState === 1) { +// cb() +// } +// }, +// off: (type: RelayEvent, cb: any): void => { +// let index = listeners[type].indexOf(cb) +// if (index !== -1) listeners[type].splice(index, 1) +// }, +// publish(event: Event): Pub { +// return _publishEvent(event, 'EVENT') +// }, +// auth(event: Event): Pub { +// return _publishEvent(event, 'AUTH') +// }, +// connect, +// close(): Promise { +// ws.close() +// return new Promise(resolve => { +// resolveClose = resolve +// }) +// }, +// get status() { +// return ws?.readyState ?? 3 +// } +// } +// } + +function getSubscriptionId(json) { + let idx = json.slice(0, 22).indexOf(`"EVENT"`); + if (idx === -1) return null; + let pstart = json.slice(idx + 7 + 1).indexOf(`"`); + if (pstart === -1) return null; + let start = idx + 7 + 1 + pstart; + let pend = json.slice(start + 1, 80).indexOf(`"`); + if (pend === -1) return null; + let end = start + 1 + pend; + return json.slice(start + 1, end); +} + +function getHex64(json, field) { + let len = field.length + 3; + let idx = json.indexOf(`"${field}":`) + len; + let s = json.slice(idx).indexOf(`"`) + idx + 1; + return json.slice(s, s + 64); +} + +export function relayInit(url: string, options: SimplePoolOptions = {}) { + let { listTimeout = 3e3, getTimeout = 3e3 } = options; + var ws; + var openSubs = {}; + var listeners = { + connect: [], + disconnect: [], + error: [], + notice: [] + }; + var subListeners = {}; + var pubListeners = {}; + var connectionPromise; + async function connectRelay() { + if (connectionPromise) return connectionPromise; + connectionPromise = new Promise((resolve, reject) => { + try { + ws = new WebSocket(url); + } catch (err) { + reject(err); + } + ws.onopen = () => { + listeners.connect.forEach((cb) => cb()); + resolve(); + }; + ws.onerror = () => { + connectionPromise = void 0; + listeners.error.forEach((cb) => cb()); + reject(); + }; + ws.onclose = async () => { + connectionPromise = void 0; + listeners.disconnect.forEach((cb) => cb()); + }; + let incomingMessageQueue = []; + let handleNextInterval; + ws.onmessage = (e) => { + incomingMessageQueue.push(e.data); + if (!handleNextInterval) { + handleNextInterval = setInterval(handleNext, 0); + } + }; + async function handleNext() { + if (incomingMessageQueue.length === 0) { + clearInterval(handleNextInterval); + handleNextInterval = null; + return; + } + var json = incomingMessageQueue.shift(); + if (!json) return; + let subid = getSubscriptionId(json); + if (subid) { + let so = openSubs[subid]; + if (so && so.alreadyHaveEvent && so.alreadyHaveEvent(getHex64(json, "id"), url)) { + return; + } + } + try { + let data = JSON.parse(json); + switch (data[0]) { + case "EVENT": + let id = data[1]; + let event = data[2]; + if ( + validateEvent(event) && + openSubs[id] && + (openSubs[id].skipVerification || (await verifySignature(event))) + ) { + openSubs[id]; + (subListeners[id]?.event || []).forEach((cb) => cb(event)); + } + return; + case "EOSE": { + let id2 = data[1]; + if (id2 in subListeners) { + subListeners[id2].eose.forEach((cb) => cb()); + subListeners[id2].eose = []; + } + return; + } + case "OK": { + let id2 = data[1]; + let ok = data[2]; + let reason = data[3] || ""; + if (id2 in pubListeners) { + if (ok) pubListeners[id2].ok.forEach((cb) => cb()); + else pubListeners[id2].failed.forEach((cb) => cb(reason)); + pubListeners[id2].ok = []; + pubListeners[id2].failed = []; + } + return; + } + case "NOTICE": + let notice = data[1]; + listeners.notice.forEach((cb) => cb(notice)); + return; + } + } catch (err) { + return; + } + } + }); + return connectionPromise; + } + function connected() { + return ws?.readyState === 1; + } + async function connect() { + if (connected()) return; + await connectRelay(); + } + async function trySend(params) { + let msg = JSON.stringify(params); + if (!connected()) { + await new Promise((resolve) => setTimeout(resolve, 1e3)); + if (!connected()) { + return; + } + } + try { + ws.send(msg); + } catch (err) { + console.log(err); + } + } + const sub = ( + filters, + { + skipVerification = false, + alreadyHaveEvent = null, + id = Math.random().toString().slice(2) + } = {} + ) => { + let subid = id; + openSubs[subid] = { + id: subid, + filters, + skipVerification, + alreadyHaveEvent + }; + trySend(["REQ", subid, ...filters]); + return { + sub: (newFilters, newOpts: SubscriptionOptions = {}) => + sub(newFilters || filters, { + skipVerification: newOpts.skipVerification || skipVerification, + alreadyHaveEvent: newOpts.alreadyHaveEvent || alreadyHaveEvent, + id: subid + }), + unsub: () => { + delete openSubs[subid]; + delete subListeners[subid]; + trySend(["CLOSE", subid]); + }, + on: (type, cb) => { + subListeners[subid] = subListeners[subid] || { + event: [], + eose: [] + }; + subListeners[subid][type].push(cb); + }, + off: (type, cb) => { + let listeners2 = subListeners[subid]; + let idx = listeners2[type].indexOf(cb); + if (idx >= 0) listeners2[type].splice(idx, 1); + } + }; + }; + return { + url, + sub, + on: (type, cb) => { + listeners[type].push(cb); + if (type === "connect" && ws?.readyState === 1) { + cb(); + } + }, + off: (type, cb) => { + let index = listeners[type].indexOf(cb); + if (index !== -1) listeners[type].splice(index, 1); + }, + list: (filters, opts) => + new Promise((resolve) => { + let s = sub(filters, opts); + let events = []; + let timeout = setTimeout(() => { + s.unsub(); + resolve(events); + }, listTimeout); + s.on("eose", () => { + s.unsub(); + clearTimeout(timeout); + resolve(events); + }); + s.on("event", (event) => { + events.push(event); + }); + }), + get: (filter, opts) => + new Promise((resolve) => { + let s = sub([filter], opts); + let timeout = setTimeout(() => { + s.unsub(); + resolve(null); + }, getTimeout); + s.on("event", (event) => { + s.unsub(); + clearTimeout(timeout); + resolve(event); + }); + }), + publish(event) { + if (!event.id) throw new Error(`event ${event} has no id`); + let id = event.id; + trySend(["EVENT", event]); + return { + on: (type, cb) => { + pubListeners[id] = pubListeners[id] || { + ok: [], + failed: [] + }; + pubListeners[id][type].push(cb); + }, + off: (type, cb) => { + let listeners2 = pubListeners[id]; + if (!listeners2) return; + let idx = listeners2[type].indexOf(cb); + if (idx >= 0) listeners2[type].splice(idx, 1); + } + }; + }, + connect, + close() { + listeners = { connect: [], disconnect: [], error: [], notice: [] }; + subListeners = {}; + pubListeners = {}; + if (ws.readyState === WebSocket.OPEN) { + ws?.close(); + } + }, + status() { + return ws?.readyState ?? 3; + } + }; +} diff --git a/src/lib/nostr-tools/tsconfig.json b/src/lib/nostr-tools/tsconfig.json new file mode 100644 index 00000000000..22bbdb23d4d --- /dev/null +++ b/src/lib/nostr-tools/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "module": "esnext", + "target": "esnext", + "lib": ["dom", "dom.iterable", "esnext"], + "declaration": true, + "strict": true, + "moduleResolution": "node", + "skipLibCheck": true, + "esModuleInterop": true, + "emitDeclarationOnly": true, + "outDir": "dist", + "rootDir": "." + } +} diff --git a/src/lib/nostr-tools/utils.ts b/src/lib/nostr-tools/utils.ts new file mode 100644 index 00000000000..e3a9712883e --- /dev/null +++ b/src/lib/nostr-tools/utils.ts @@ -0,0 +1,2 @@ +export const utf8Decoder = new TextDecoder("utf-8"); +export const utf8Encoder = new TextEncoder(); diff --git a/src/providers/message-provider-types.ts b/src/providers/message-provider-types.ts new file mode 100644 index 00000000000..a553c7d16c2 --- /dev/null +++ b/src/providers/message-provider-types.ts @@ -0,0 +1,55 @@ +export type Keys = + | { + pub: string; + priv: string; + } + | { + pub: string; + priv: "nip07"; + } + | null; + +export type Metadata = { + name: string; + about: string; + picture: string; +}; + +export type Profile = { id: string; creator: string; created: number } & Metadata; + +export type Channel = { id: string; creator: string; created: number } & Metadata; + +export type ChannelUpdate = { channelId: string } & Channel; + +export type EventDeletion = { eventId: string; why: string }; + +export type PublicMessage = { + id: string; + root: string; + content: string; + creator: string; + created: number; + children?: PublicMessage[]; + mentions: string[]; +}; + +export type DirectMessage = { + id: string; + root?: string; + content: string; + peer: string; + creator: string; + created: number; + children?: DirectMessage[]; + decrypted: boolean; +}; + +export type Message = PublicMessage | DirectMessage; + +export type ChannelMessageHide = { id: string; reason: string }; + +export type ChannelUserMute = { pubkey: string; reason: string }; + +export type RelayDict = Record; + +export type MuteList = { pubkeys: string[]; encrypted: string }; diff --git a/src/providers/message-provider.tsx b/src/providers/message-provider.tsx new file mode 100644 index 00000000000..e5b823a0ea5 --- /dev/null +++ b/src/providers/message-provider.tsx @@ -0,0 +1,300 @@ +import React, { useEffect, useMemo, useState } from "react"; +import { getProfileMetaData, GLOBAL_CHAT, NostrKeys } from "../common/helper/chat-utils"; +import { ActiveUser } from "../common/store/active-user/types"; +// import uniq from 'lodash.uniq'; +// import {nip19} from 'nostr-tools'; + +// import { +// channelsAtom, +// channelUpdatesAtom, +// directContactsAtom, +// directMessagesAtom, +// eventDeletionsAtom, +// keysAtom, +// profileAtom, +// profilesAtom, +// publicMessagesAtom, +// ravenAtom, +// ravenReadyAtom, +// channelMessageHidesAtom, +// channelUserMutesAtom, +// muteListAtom, directMessageAtom +// } from 'store'; +import { initRaven, RavenEvents } from "../common/helper/message-helper"; +import { + Channel, + ChannelUpdate, + DirectMessage, + EventDeletion, + Profile, + PublicMessage, + ChannelMessageHide, + ChannelUserMute, + MuteList, + Keys +} from "./message-provider-types"; +import { useMappedStore } from "../common/store/use-mapped-store"; +import { Account } from "../common/store/accounts/types"; +import { Chat, DirectContactsType } from "../common/store/chat/types"; + +// import {createLogger} from 'logger'; + +// const logger = createLogger('RavenProvider'); + +export const setNostrkeys = (keys: NostrKeys) => { + const detail: NostrKeys = { + pub: keys.pub, + priv: keys.priv + }; + const ev = new CustomEvent("keysCreated", { detail }); + window.dispatchEvent(ev); +}; + +interface Props { + activeUser: ActiveUser; + chat: Chat; + addDirectContacts: (data?: DirectContactsType[]) => void; + addDirectMessages: (data?: DirectMessage[]) => void; +} + +const MessageProvider = (props: Props) => { + // console.log(props.addDirectMessages, "MessageProvider") + const { activeUser } = useMappedStore(); + const [ravenReady, setRavenReady] = useState(false); + // const [profile, setProfile] = useAtom(profileAtom); + // const [profiles, setProfiles] = useAtom(profilesAtom); + // const [channels, setChannels] = useAtom(channelsAtom); + // const [channelUpdates, setChannelUpdates] = useAtom(channelUpdatesAtom); + // const [eventDeletions, setEventDeletions] = useAtom(eventDeletionsAtom); + // const [publicMessages, setPublicMessages] = useAtom(publicMessagesAtom); + // const [directMessages, setDirectMessages] = useAtom(directMessagesAtom); + // const [directMessage,] = useAtom(directMessageAtom); + // const [channelMessageHides, setChannelMessageHides] = useAtom(channelMessageHidesAtom); + // const [channelUserMutes, setChannelUserMutes] = useAtom(channelUserMutesAtom); + // const [muteList, setMuteList] = useAtom(muteListAtom); + // const [, setDirectContacts] = useAtom(directContactsAtom); + const [since, setSince] = useState(0); + const [keys, setKeys] = useState(); + const [raven, setRaven] = useState(); + + // const ravenInstance = useMemo(() => initRaven(keys!), [keys]); + // setRaven(ravenInstance); + + useEffect(() => { + window.addEventListener("keysCreated", createRavenInstance); + + return () => { + window.removeEventListener("keysCreated", createRavenInstance); + }; + }, []); + + useEffect(() => { + if (!window.raven && keys) { + const ravenInstance = initRaven(keys); + setRaven(ravenInstance); + } + }, [keys]); + + // useEffect(() => { + // console.log(raven); + // if(raven){ + // raven?.updateProfile({name:activeUser?.username, about: "", picture: ""}); + // } + // }, [raven]); + + useEffect(() => { + if (activeUser) { + getNostrKeys(activeUser); + } + }, [activeUser]); + + const createRavenInstance = (e: Event) => { + const detail = (e as CustomEvent).detail as NostrKeys; + const ravenInstance = initRaven(detail); + setRaven(ravenInstance); + }; + + const getNostrKeys = async (activeUser: ActiveUser) => { + const profile = await getProfileMetaData(activeUser.username); + setKeys(profile.noStrKey); + }; + + //Listen for events in an interval. + useEffect(() => { + if (!ravenReady) return; + + const timer = setTimeout( + () => { + raven?.listen(Math.floor((since || Date.now()) / 1000)); + setSince(Date.now()); + }, + since === 0 ? 500 : 10000 + ); + + return () => { + clearTimeout(timer); + }; + }, [since, ravenReady, raven]); + + // // Trigger listen once the window visibility changes. + // const visibilityChange = () => { + // if (document.visibilityState === "visible") { + // raven?.listen( + // channels.map((x) => x.id), + // Math.floor((since || Date.now()) / 1000) + // ); + // setSince(Date.now()); + // } + // }; + // useEffect(() => { + // document.addEventListener("visibilitychange", visibilityChange); + + // return () => { + // document.removeEventListener("visibilitychange", visibilityChange); + // }; + // }, [since, ravenReady, raven, channels]); + + // useEffect(() => { + // setDirectContacts( + // [...new Set(directMessages.map((x) => x.peer))].map((p) => ({ + // pub: p, + // npub: nip19.npubEncode(p) + // })) + // ); + // }, [directMessages]); + + // // Ready state handler + const handleReadyState = () => { + setRavenReady(true); + }; + + useEffect(() => { + raven?.removeListener(RavenEvents.Ready, handleReadyState); + raven?.addListener(RavenEvents.Ready, handleReadyState); + + return () => { + raven?.removeListener(RavenEvents.Ready, handleReadyState); + }; + }, [ravenReady, raven]); + + // // Event deletion handler + // const handleEventDeletion = (data: EventDeletion[]) => { + // logger.info("handleEventDeletion", data); + // const append = data.filter( + // (x) => eventDeletions.find((y) => y.eventId === x.eventId) === undefined + // ); + // setEventDeletions([...eventDeletions, ...append]); + // }; + + // useEffect(() => { + // raven?.removeListener(RavenEvents.EventDeletion, handleEventDeletion); + // raven?.addListener(RavenEvents.EventDeletion, handleEventDeletion); + + // return () => { + // raven?.removeListener(RavenEvents.EventDeletion, handleEventDeletion); + // }; + // }, [raven, eventDeletions]); + + // // Direct message handler + const handleDirectMessage = (data: DirectMessage[]) => { + console.log("handleDirectMessage", data); + console.log(props.chat.directMessages); + const append = data.filter( + (x) => props.chat.directMessages.find((y) => y.id === x.id) === undefined + ); + // console.log("append", [...props.chat.directMessages, ...append]); + + const result = [...props.chat.directMessages, ...append]; + // console.log(result, 'result') + // raven?.loadProfiles(data.map((x) => x.peer)); + props.addDirectMessages(result); + // const clean = data + // .filter((x) => x.peer === "ad2b87073a67f664621c92dbc925f3d1be3e4df64968fdeb4d15c6736ef42300") + // .sort((a, b) => a.created - b.created); + + // console.log(clean, "clean"); + // const finalData = clean + // .map((c) => ({ ...c, children: clean.filter((x) => x.root === c.id) })) + // .filter((x) => !x.root); + + // console.log(finalData, "finalData"); + }; + + useEffect(() => { + raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); + raven?.addListener(RavenEvents.DirectMessage, handleDirectMessage); + return () => { + raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); + }; + }, [raven]); + + const handleProfileUpdate = (data: Profile[]) => { + // console.log("handleProfileData", data); + // console.log("Store state", props.chat.directContacts); + + const result = [...props.chat.directContacts]; + const activeUserName = props.activeUser?.username; + + data.forEach(({ name, creator }) => { + const isPresent = props.chat.directContacts.some( + (obj) => obj.name === name && obj.creator === creator + ); + const isCurrentUser = name === activeUserName; + if (!isPresent && !isCurrentUser) { + // Add the object to the result array if it's not already present in store state. + result.push({ name, creator }); + } + }); + + // console.log("result", result); + props.addDirectContacts(result); + }; + + useEffect(() => { + raven?.removeListener(RavenEvents.ProfileUpdate, handleProfileUpdate); + raven?.addListener(RavenEvents.ProfileUpdate, handleProfileUpdate); + return () => { + raven?.removeListener(RavenEvents.ProfileUpdate, handleProfileUpdate); + }; + }, [raven]); + + // // decrypt direct messages one by one to avoid show nip7 wallet dialog many times. + // useEffect(() => { + // if (directMessage) { + // const decrypted = directMessages + // .filter((m) => m.peer === directMessage) + // .find((x) => !x.decrypted); + // if (decrypted) { + // window.nostr?.nip04.decrypt(decrypted.peer, decrypted.content).then((content) => { + // setDirectMessages( + // directMessages.map((m) => { + // if (m.id === decrypted.id) { + // return { + // ...m, + // content, + // decrypted: true + // }; + // } + // return m; + // }) + // ); + // }); + // } + // } + // }, [directMessages, directMessage]); + + // Init raven + // useEffect(() => { + // return () => { + // raven?.removeListener(RavenEvents.Ready, handleReadyState); + + // // raven?.removeListener(RavenEvents.EventDeletion, handleEventDeletion); + + // raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); + // }; + // }, [raven]); + + return <>{}; +}; + +export default MessageProvider; diff --git a/yarn.lock b/yarn.lock index b3c36272843..512618b73ae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1591,6 +1591,15 @@ graceful-fs "^4.2.4" source-map "^0.6.0" +"@jest/source-map@^29.4.3": + version "29.4.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.4.3.tgz#ff8d05cbfff875d4a791ab679b4333df47951d20" + integrity sha512-qyt/mb6rLyd9j1jUts4EQncvS6Yy3PM9HghnNv86QBlV+zdL2inCdK1tuVlL+J+lpiw2BI67qXOrX3UurBqQ1w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.15" + callsites "^3.0.0" + graceful-fs "^4.2.9" + "@jest/test-result@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" @@ -1670,6 +1679,11 @@ "@jridgewell/set-array" "^1.0.0" "@jridgewell/sourcemap-codec" "^1.4.10" +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + "@jridgewell/resolve-uri@^3.0.3": version "3.0.7" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" @@ -1680,11 +1694,24 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea" integrity sha512-Ct5MqZkLGEXTVmQYbGtx9SVqD2fqwvdubdps5D3djjAkgkKwT918VNOz65pEHFaYTeWcukmJmH5SwsA9Tn2ObQ== +"@jridgewell/sourcemap-codec@1.4.14": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + "@jridgewell/sourcemap-codec@^1.4.10": version "1.4.13" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== +"@jridgewell/trace-mapping@^0.3.15": + version "0.3.18" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" + integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + "@jridgewell/trace-mapping@^0.3.9": version "0.3.13" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" @@ -1724,6 +1751,28 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@noble/curves@1.0.0", "@noble/curves@~1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.0.0.tgz#e40be8c7daf088aaf291887cbc73f43464a92932" + integrity sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw== + dependencies: + "@noble/hashes" "1.3.0" + +"@noble/hashes@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.0.tgz#085fd70f6d7d9d109671090ccae1d3bec62554a1" + integrity sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg== + +"@noble/hashes@~1.3.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" + integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== + +"@noble/secp256k1@^1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" + integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1792,6 +1841,28 @@ dependencies: dequal "^2.0.2" +"@scure/base@1.1.1", "@scure/base@~1.1.0": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" + integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== + +"@scure/bip32@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.0.tgz#6c8d980ef3f290987736acd0ee2e0f0d50068d87" + integrity sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q== + dependencies: + "@noble/curves" "~1.0.0" + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.0.tgz#a207e2ef96de354de7d0002292ba1503538fc77b" + integrity sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg== + dependencies: + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -5890,6 +5961,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6 resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== +graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -6911,6 +6987,11 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isomorphic-ws@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" + integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== + istanbul-lib-coverage@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" @@ -8387,6 +8468,27 @@ normalize-url@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== +nostr-relaypool@^0.6.28: + version "0.6.28" + resolved "https://registry.yarnpkg.com/nostr-relaypool/-/nostr-relaypool-0.6.28.tgz#50f1dc21c11bda1fca2a6d785d78fd6f6ead9a57" + integrity sha512-kyLChBunf6IB2O3MSPHBe9iSokBGyqZHvAJXZ5+eh9C4znAu7iY8L/yh0V4Ta/P9TtzDna3QTEgFWVan4m0vBA== + dependencies: + "@jest/source-map" "^29.4.3" + isomorphic-ws "^5.0.0" + nostr-tools "^1.10.0" + safe-stable-stringify "^2.4.2" + +nostr-tools@^1.10.0: + version "1.12.0" + resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.12.0.tgz#ec3618fc2298e029941b7db3bbe95187777c488f" + integrity sha512-fsIXaNJPKaSrO9MxsCEWbhI4tG4pToQK4D4sgLRD0fRDfZ6ocCg8CLlh9lcNx0o8pVErCMLVASxbJ+w4WNK0MA== + dependencies: + "@noble/curves" "1.0.0" + "@noble/hashes" "1.3.0" + "@scure/base" "1.1.1" + "@scure/bip32" "1.3.0" + "@scure/bip39" "1.2.0" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -10662,6 +10764,11 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" +safe-stable-stringify@^2.4.2: + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" From fd6875cd9aba7ee304c1de372c1aac9c327868b4 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Tue, 20 Jun 2023 14:43:30 +0500 Subject: [PATCH 011/179] create new instance when active user changes --- src/common/components/chat-box/index.scss | 115 ++++-- src/common/components/chat-box/index.tsx | 325 ++++++++-------- src/common/components/login/index.tsx | 16 +- src/common/helper/chat-utils.ts | 17 +- src/common/helper/message-helper.ts | 438 ++-------------------- src/common/i18n/locales/en-US.json | 6 +- src/common/store/actions.ts | 5 +- src/common/store/chat/index.ts | 22 +- src/common/store/chat/types.ts | 22 +- src/providers/message-provider.tsx | 180 +-------- 10 files changed, 350 insertions(+), 796 deletions(-) diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss index 410f21e63f9..1d952569261 100644 --- a/src/common/components/chat-box/index.scss +++ b/src/common/components/chat-box/index.scss @@ -53,7 +53,6 @@ .back-arrow-svg { border-radius: 50%; padding: 6px; - // padding: 8px 6px 10px 6px; } } @@ -136,6 +135,7 @@ overflow-y: auto; &.current-user { + padding-bottom: 10px; height: 412px; margin-bottom: 8px; @include themify(day) { @@ -151,16 +151,28 @@ justify-content: center; align-items: center; } + .no-chat { + display: flex; + align-items: center; + justify-content: center; + margin-top: 20%; + } + .start-chat-btn { + display: flex; + align-items: center; + justify-content: center; + } .chat-content { display: flex; cursor: pointer; + border-bottom: 0.5px solid rgba(134, 150, 160, 0.15); .user-img { padding: 18px 16px; } .user-title { - padding-top: 18px; + padding-top: 26px; width: -webkit-fill-available; .username { @@ -186,7 +198,7 @@ .chat-content:hover { @include themify(day) { - background: $light-powder-blue; + background: #eeeeee; } @include themify(night) { background: $gunmetal; @@ -195,6 +207,8 @@ .user-profile { border-bottom-right-radius: 20px; border-bottom-left-radius: 20px; + min-height: 266px; + @include themify(day) { background: $white-three; } @@ -294,19 +308,43 @@ } .chats { - .date-time-detail { - .date-time { - margin: 1rem 0; - text-align: center; - font-size: 13px; + .not-joined { + display: flex; + align-items: center; + justify-content: center; + margin-top: 14%; + } + .custom-divider { + margin: 0 24px; + font-size: 0.7em; + color: $dark; + .line { + flex-grow: 1; + height: 1px; + background-color: black; + margin: 0 10px; + } + .custom-divider-text { + display: flex; + justify-content: center; + padding: 0 8px; } } .message { display: flex; .user-img { - padding: 18px 8px 18px 16px; + padding: 18px 8px 8px 16px; } .user-info { + padding-top: 8px; + .user-msg-time { + margin: 0; + color: rgb(138, 141, 145); + font-weight: 400; + font-size: 12px; + margin-left: 9px; + margin-bottom: 2px; + } .receiver-message-content { @include themify(day) { background: #e4e6eb; @@ -316,31 +354,36 @@ } color: #050505; max-width: 280px; - margin-top: 19px; - padding: 8px 12px 8px 12px; - border-radius: 18px 18px 18px 18px; + word-wrap: break-word; + padding: 10px; + border-radius: 0px 10px 10px; } } } .sender { - display: flex; margin-bottom: 0.17rem; + .sender-message-time { + color: rgb(138, 141, 145); + display: flex; + font-weight: 400; + justify-content: flex-end; + font-size: 12px; + margin: 0; + margin-right: 18px; + margin-bottom: 2px; + } .sender-message { - border-radius: 18px 18px 18px 18px; max-width: 280px; margin-left: auto; - background-color: rgb(0, 132, 255); + display: flex; justify-content: flex-end; margin-right: 15px; - .sender-message-time { - color: $white; - margin-right: 30px; - font-size: 12px; - margin-top: 3px; - } + .sender-message-content { + border-radius: 10px 10px 0px; max-width: 280px; + background-color: rgb(0, 132, 255); word-wrap: break-word; margin-bottom: 0; color: $white; @@ -353,23 +396,31 @@ } } .chat { - margin: 4px 4px; + margin: 4px 8px; padding: 4px; + min-height: 45px; + border-radius: 20px; + @include themify(day) { + background: #e4e6eb; + } + @include themify(night) { + background: #cee2ff; + } .chat-input-group { - width: 112%; .chat-input { - height: 45px; - border-radius: 12px !important; + padding-top: 13px; + border: none; + @include themify(day) { + background: #e4e6eb; + } + @include themify(night) { + background: #cee2ff; + } } .msg-svg { - padding: 6px 8px 4px 5px; - position: inherit; - right: 44px; - border-radius: 50%; - margin: 4px 0; - margin-right: 11px; - z-index: 10; + padding: 8px; + &.active { cursor: pointer; } diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 2c4fbc9ff44..5a338be1d45 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -2,14 +2,15 @@ import React, { useEffect, useState } from "react"; import { Button, Form, FormControl, InputGroup, Spinner } from "react-bootstrap"; import { Link } from "react-router-dom"; -import { Account } from "../../store/accounts/types"; import { ActiveUser } from "../../store/active-user/types"; import { DirectContactsType } from "../../store/chat/types"; -import { DirectMessage, Keys } from "../../../providers/message-provider-types"; +import { DirectMessage } from "../../../providers/message-provider-types"; +import { useMappedStore } from "../../store/use-mapped-store"; import Tooltip from "../tooltip"; import UserAvatar from "../user-avatar"; import SeachUser from "../search-user"; +import LinearProgress from "../linear-progress"; import { setNostrkeys } from "../../../providers/message-provider"; import { @@ -24,17 +25,17 @@ import { import { dateToFormatted } from "../../helper/parse-date"; import { createNoStrAccount, + formatMessageDate, + formatMessageTime, getDirectMessages, getProfileMetaData, + NostrKeys, setProfileMetaData } from "../../helper/chat-utils"; import { _t } from "../../i18n"; import { getAccountFull } from "../../api/hive"; import "./index.scss"; -import { RavenEvents } from "../../helper/message-helper"; -import { Chat } from "../../store/chat/types"; -import { useMappedStore } from "../../store/use-mapped-store"; export interface profileData { joiningData: string; @@ -44,7 +45,6 @@ export interface profileData { interface Props { activeUser: ActiveUser | null; - chat: Chat; } export default function ChatBox(props: Props) { @@ -54,7 +54,6 @@ export default function ChatBox(props: Props) { const [isCurrentUser, setIsCurrentUser] = useState(false); const [message, setMessage] = useState(""); const [isMessageText, setIsMessageText] = useState(false); - const [accountData, setAccountData] = useState(); const [profileData, setProfileData] = useState(); const [isScrollToTop, setIsScrollToTop] = useState(false); const [isScrollToBottom, setIsScrollToBottom] = useState(false); @@ -62,9 +61,11 @@ export default function ChatBox(props: Props) { const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); const [inProgress, setInProgress] = useState(false); const [show, setShow] = useState(false); - const [senderPubKey, setSenderPubKey] = useState(""); + const [activeUserKeys, setActiveUserKeys] = useState(); const [receiverPubKey, setReceiverPubKey] = useState(""); + const [showSpinner, setShowSpinner] = useState(false); const [messagesList, setMessagesList] = useState([]); + const [isUserFromSearch, setIsUserFromSearch] = useState(false); const { chat } = useMappedStore(); @@ -79,21 +80,21 @@ export default function ChatBox(props: Props) { }, [chat.directMessages]); useEffect(() => { - chatBodyDivRef?.current?.scrollTo(0, isCurrentUser ? chatBodyDivRef.current.scrollHeight : 0); - console.log(chatBodyDivRef?.current?.scrollHeight, "ref"); - }, [isCurrentUser]); + scrollToBottom(); + }, [messagesList]); - // useEffect(() => { - // fetchProfileData(); - // setCurrentUser(""); - // // setIsCurrentUser(false); - // setShow(!!props.activeUser?.username); - // // setExpanded(false); - // }, [props.activeUser]); + useEffect(() => { + if (isCurrentUser) { + scrollToBottom(); + } + }, [isCurrentUser]); - // useEffect(() => { - // currentUser ? fetchCurrentUserData() : setMessage(""); - // }, [currentUser]); + useEffect(() => { + fetchProfileData(); + setShow(!!props.activeUser?.username); + const msgsList = getDirectMessages(chat.directMessages, receiverPubKey!); + setMessagesList(msgsList); + }, [props.activeUser]); useEffect(() => { if (currentUser) { @@ -102,15 +103,15 @@ export default function ChatBox(props: Props) { setReceiverPubKey(peer!); const msgsList = getDirectMessages(chat.directMessages, peer!); setMessagesList(msgsList); + if (!window.raven) { + setNostrkeys(activeUserKeys!); + } } else { setMessage(""); + setShowSpinner(false); } }, [currentUser]); - useEffect(() => { - chatBodyDivRef?.current?.scrollTo(0, isCurrentUser ? chatBodyDivRef.current.scrollHeight : 0); - }, [isCurrentUser]); - const formatFollowers = (count: number | undefined) => { if (count) { return count >= 1e6 @@ -122,29 +123,8 @@ export default function ChatBox(props: Props) { return count; }; - //Event listening - - // const handleDirectMessage = (data: DirectMessage[]) => { - // console.log("handleDirectMessage in chat compoenent", data); - // // const append = data.filter((x) => directMessages.find((y) => y.id === x.id) === undefined); - // // raven?.loadProfiles(append.map((x) => x.peer)); - // // setDirectMessages([...directMessages, ...append]); - // }; - - // useEffect(() => { - // if (window.raven) { - // window.raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); - // window.raven?.addListener(RavenEvents.DirectMessage, handleDirectMessage); - // } - - // return () => { - // if (window.raven) { - // window.raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); - // } - // }; - // }, []); - const fetchCurrentUserData = async () => { + setShowSpinner(true); const response = await getAccountFull(currentUser); setProfileData({ joiningData: response.created, @@ -152,14 +132,13 @@ export default function ChatBox(props: Props) { followers: response.follow_stats?.follower_count }); const currentUserProfile = await getProfileMetaData(currentUser); - // console.log(currentUserProfile.noStrKey); - setReceiverPubKey(currentUserProfile.noStrKey.pub); + setReceiverPubKey(currentUserProfile.noStrKey?.pub); + setShowSpinner(false); }; const fetchProfileData = async () => { const profileData = await getProfileMetaData(props.activeUser?.username!); - console.log(profileData, "keys"); - setSenderPubKey(profileData?.noStrKey.pub); + setActiveUserKeys(profileData?.noStrKey); const hasNoStrKey = "noStrKey" in profileData; setHasUserJoinedChat(hasNoStrKey); setShow(!!props.activeUser?.username); @@ -171,9 +150,12 @@ export default function ChatBox(props: Props) { }; const setCurrentUserFromSearch = (username: string) => { + setShowSpinner(true); setCurrentUser(username); setExpanded(true); setIsCurrentUser(true); + fetchCurrentUserData(); + setIsUserFromSearch(true); }; const handleMessage = (e: React.ChangeEvent) => { @@ -187,6 +169,10 @@ export default function ChatBox(props: Props) { setIsMessageText(false); window.raven?.sendDirectMessage(receiverPubKey, message); } + if (isUserFromSearch && receiverPubKey) { + window.raven?.addDirectContact(receiverPubKey); + setIsUserFromSearch(false); + } }; const handleScroll = (event: React.UIEvent) => { @@ -202,28 +188,18 @@ export default function ChatBox(props: Props) { setIsScrollToBottom(isScrollToBottom); }; - const ScrollerClicked = () => { + const scrollerClicked = () => { chatBodyDivRef?.current?.scrollTo({ top: isCurrentUser ? chatBodyDivRef?.current?.scrollHeight : 0, behavior: "smooth" }); }; - // const scrollToBottomClicked = () => { - // console.log("Scroll to bottom clicked"); - // chatBodyDivRef?.current?.scrollTo({ - // top: chatBodyDivRef?.current?.scrollHeight+10, - // behavior: "smooth" - // }); - // }; - - // const scrollToTopClicked = () => { - // console.log("Scroll to top clicked"); - // chatBodyDivRef?.current?.scrollTo({ - // top: 0, - // behavior: "smooth" - // }); - // }; + const scrollToBottom = () => { + chatBodyDivRef?.current?.scrollTo({ + top: chatBodyDivRef?.current?.scrollHeight + }); + }; const handleMessageSvgClick = () => { setShowSearchUser(true); @@ -240,31 +216,49 @@ export default function ChatBox(props: Props) { setInProgress(false); setHasUserJoinedChat(true); setNostrkeys(keys); - // window.raven?.updateProfile({ name: activeUser?.username!, about: "", picture: "" }); + window.raven?.updateProfile({ name: props.activeUser?.username!, about: "", picture: "" }); }; const chatButtonSpinner = ( ); + const getFormattedDateAndDay = (msg: DirectMessage, i: number) => { + const prevMsg = messagesList[i - 1]; + const msgDate = formatMessageDate(msg.created); + const prevMsgDate = prevMsg ? formatMessageDate(prevMsg.created) : null; + if (msgDate !== prevMsgDate) { + return ( +
+ + {msgDate} + +
+ ); + } + return <>; + }; + return ( <> {show && (
{currentUser && expanded && ( -
- { - setCurrentUser(""); - setIsCurrentUser(false); - }} - > - {" "} - {arrowBackSvg} - -
+ +
+ { + setCurrentUser(""); + setIsCurrentUser(false); + }} + > + {" "} + {arrowBackSvg} + +
+
)}
setExpanded(!expanded)}> {currentUser && ( @@ -305,86 +299,108 @@ export default function ChatBox(props: Props) { {currentUser.length !== 0 ? ( <> - {profileData?.joiningData && ( -
- - - -

{currentUser}

- {profileData.about && ( -

{profileData.about}

- )} - -
-

- {" "} - {_t("chat.joined")} {dateToFormatted(profileData!.joiningData, "LL")} -

-

- {" "} - {formatFollowers(profileData!.followers)} {_t("chat.followers")} -

+
+ {profileData?.joiningData && ( +
+ + + +

{currentUser}

+ {profileData.about && ( +

{profileData.about}

+ )} + +
+

+ {" "} + {_t("chat.joined")}{" "} + {dateToFormatted(profileData!.joiningData, "LL")} +

+

+ {" "} + {formatFollowers(profileData!.followers)} {_t("chat.followers")} +

+
-
- )} + )} +
- {messagesList.map((msg) => { - if (msg.creator !== senderPubKey) { + {receiverPubKey === null || receiverPubKey === undefined ? ( +

{_t("chat.not-joined")}

+ ) : ( + <> + {messagesList.map((msg, i) => { + const dayAndMonth = getFormattedDateAndDay(msg, i); + + return ( + + {dayAndMonth} + {msg.creator !== activeUserKeys?.pub ? ( +
+
+ + + + + +
+
+

+ {formatMessageTime(msg.created)} +

+

{msg.content}

+
+
+ ) : ( +
+

+ {formatMessageTime(msg.created)} +

+
+

{msg.content}

+
+
+ )} +
+ ); + })} + + )} +
+ + ) : ( + <> + {chat.directContacts.length !== 0 ? ( + + {chat.directContacts.map((user: DirectContactsType) => { return ( - <> - {/*
-

- {msg.date}, {msg.time} -

-
*/} -
+
+
- - - - - + + +
-
-

{msg.content}

-
-
- - ); - } else { - return ( -
-
- {/* {msg.time} */} -

{msg.content}

+ + +
userClicked(user.name)}> +

{user.name}

); - } - })} -
- - ) : ( - <> - {chat.directContacts.map((user: DirectContactsType) => { - return ( -
- -
- - - -
- - -
userClicked(user.name)}> -

{user.name}

- {/*

{user.lastMessage}

*/} -
+ })} + + ) : ( + +

{_t("chat.no-chat")}

+
+
- ); - })} +
+ )} )} @@ -402,14 +418,14 @@ export default function ChatBox(props: Props) {
{isCurrentUser ? chevronDownSvgForSlider : chevronUpSvg}
)}
- + {showSpinner && } {currentUser && (
void; deleteUser: (username: string) => void; toggleUIProp: (what: ToggleType) => void; + resetChat: () => void; } class LoginDialog extends Component { @@ -749,7 +752,9 @@ class LoginDialog extends Component { } doLogin = async (hsCode: string, postingKey: null | undefined | string, account: Account) => { - const { global, setActiveUser, updateActiveUser, addUser } = this.props; + const profile = await getProfileMetaData(account.name); + + const { global, setActiveUser, updateActiveUser, addUser, resetChat } = this.props; // get access token from code return hsTokenRenew(hsCode).then((x) => { @@ -764,6 +769,11 @@ class LoginDialog extends Component { // add / update user data addUser(user); + //reset the already stored chat + resetChat(); + + //create new raven instance for newly logged in user + setNostrkeys(profile.noStrKey); // activate user setActiveUser(user.username); @@ -817,7 +827,8 @@ export default ({ history }: Pick) => { setActiveUser, updateActiveUser, deleteUser, - toggleUIProp + toggleUIProp, + resetChat } = useMappedStore(); const location = useLocation(); @@ -829,6 +840,7 @@ export default ({ history }: Pick) => { ui={ui} users={users} activeUser={activeUser} + resetChat={resetChat} addUser={addUser} setActiveUser={setActiveUser} updateActiveUser={updateActiveUser} diff --git a/src/common/helper/chat-utils.ts b/src/common/helper/chat-utils.ts index 591115b9e03..9019059a61a 100644 --- a/src/common/helper/chat-utils.ts +++ b/src/common/helper/chat-utils.ts @@ -1,6 +1,6 @@ -import { useMemo } from "react"; +import moment from "moment"; import { generatePrivateKey, getPublicKey } from "../../lib/nostr-tools/keys"; -import { Channel, DirectMessage } from "../../providers/message-provider-types"; +import { DirectMessage } from "../../providers/message-provider-types"; import { getAccountFull } from "../api/hive"; import { updateProfile } from "../api/operations"; import { ActiveUser } from "../store/active-user/types"; @@ -43,15 +43,6 @@ export function notEmpty(value: TValue | null | undefined): value is TVa return value !== null && value !== undefined; } -export const GLOBAL_CHAT: Channel = { - id: "f412192fdc846952c75058e911d37a7392aa7fd2e727330f4344badc92fb8a22", - name: "Global Chat", - about: "Whatever you want it to be, just be nice", - picture: "", - creator: "aea59833635dd0868bc7cf923926e51df936405d8e6a753b78038981c75c4a74", - created: 1678198928 -}; - export const getDirectMessages = (messages: DirectMessage[], peer?: string) => { const clean = messages.filter((x) => x.peer === peer).sort((a, b) => a.created - b.created); @@ -59,3 +50,7 @@ export const getDirectMessages = (messages: DirectMessage[], peer?: string) => { .map((c) => ({ ...c, children: clean.filter((x) => x.root === c.id) })) .filter((x) => !x.root); }; + +export const formatMessageTime = (unixTs: number) => moment.unix(unixTs).format("h:mm a"); + +export const formatMessageDate = (unixTs: number) => moment.unix(unixTs).format("dddd, MMMM Do"); diff --git a/src/common/helper/message-helper.ts b/src/common/helper/message-helper.ts index 48ceb9362d6..f3cea0cff6f 100644 --- a/src/common/helper/message-helper.ts +++ b/src/common/helper/message-helper.ts @@ -1,31 +1,7 @@ -import { useMappedStore } from "./../store/use-mapped-store"; -// import { Event, Filter, getEventHash, Kind, nip04, signEvent, SimplePool, Sub } from "nostr-tools"; -// import { TypedEventEmitter } from "raven/helper/event-emitter"; -// import { -// Channel, -// ChannelMessageHide, -// ChannelUpdate, -// ChannelUserMute, -// DirectMessage, -// EventDeletion, -// Keys, -// Metadata, -// MuteList, -// Profile, -// PublicMessage -// } from "types"; -// import chunk from 'lodash.chunk'; -// import uniq from 'lodash.uniq'; -// import {getRelays} from 'helper'; -// import {GLOBAL_CHAT, MESSAGE_PER_PAGE} from 'const'; -// import {notEmpty} from 'util/misc'; - import { Sub } from "../../lib/nostr-tools/relay"; import { Kind } from "../../lib/nostr-tools/event"; import { Filter } from "../../lib/nostr-tools/filter"; import { TypedEventEmitter } from "./message-event-emitter"; -import uniq from "lodash"; -import chunk from "lodash"; import { Channel, ChannelMessageHide, @@ -42,9 +18,7 @@ import { import { encrypt, decrypt } from "../../lib/nostr-tools/nip04"; import SimplePool from "../../lib/nostr-tools/pool"; import { signEvent, getEventHash, Event } from "../../lib/nostr-tools/event"; -import { getDirectMessages, GLOBAL_CHAT, notEmpty } from "./chat-utils"; -import * as ls from "../util/local-storage"; -// import { addDirectContacts } from "../store/chat"; +import { notEmpty } from "./chat-utils"; const relays = { "wss://relay1.nostrchat.io": { read: true, write: true }, @@ -54,10 +28,6 @@ const relays = { "wss://nos.lol": { read: true, write: true } }; -enum NewKinds { - MuteList = 10000 -} - export enum RavenEvents { Ready = "ready", ProfileUpdate = "profile_update", @@ -98,6 +68,7 @@ class Raven extends TypedEventEmitter { private eventQueueTimer: any; private eventQueueFlag = true; private eventQueueBuffer: Event[] = []; + public directContacts: string[] = []; private nameCache: Record = {}; @@ -117,23 +88,15 @@ class Raven extends TypedEventEmitter { } private async init() { + this.eventQueue = []; + this.eventQueueFlag = true; + this.eventQueueBuffer = []; + const filters: Filter[] = [ { kinds: [Kind.Metadata], authors: [this.pub] }, - // { - // kinds: [Kind.ChannelHideMessage, Kind.ChannelMuteUser], - // authors: [this.pub] - // }, - // { - // kinds: [NewKinds.MuteList], - // authors: [this.pub] - // }, - // { - // kinds: [Kind.ChannelMessage], - // authors: [this.pub] - // }, { kinds: [Kind.EncryptedDirectMessage], authors: [this.pub] @@ -144,40 +107,9 @@ class Raven extends TypedEventEmitter { } ]; this.fetchP(filters).then((resp) => { - console.log("Init called", resp); - // console.log("ddd", addDirectContacts); - // // const deletions = resp - // .filter((x) => x.kind === Kind.EventDeletion) - // .map((x) => Raven.findTagValue(x, "e")); const events = resp.sort((a, b) => b.created_at - a.created_at); const profile = events.find((x) => x.kind === Kind.Metadata); - console.log(profile, "profile in init"); - if (profile) this.pushToEventBuffer(profile, "init"); - // const muteList = events.find((x) => x.kind.toString() === NewKinds.MuteList.toString()); - // if (muteList) this.pushToEventBuffer(muteList, "init"); - // for (const e of events.filter((x) => - // [Kind.ChannelHideMessage, Kind.ChannelMuteUser].includes(x.kind) - // )) { - // this.pushToEventBuffer(e, "init"); - // } - // const channels = uniq( - // events - // .map((x) => { - // if (x.kind === Kind.ChannelCreation) { - // return x.id; - // } - // if (x.kind === Kind.ChannelMessage) { - // return Raven.findTagValue(x, "e"); - // } - // return undefined; - // }) - // .filter((x) => !deletions.includes(x)) - // .filter(notEmpty) - // ); - // if (!channels.includes(GLOBAL_CHAT.id)) { - // channels.push(GLOBAL_CHAT.id); - // } - // console.log(events, "Events"); + if (profile) this.pushToEventBuffer(profile); const directContacts = Array.from( new Set( events.map((x) => { @@ -191,32 +123,8 @@ class Raven extends TypedEventEmitter { ) ).filter(notEmpty); - // directContacts.map((p)=> { - // console.log("p",p); - // const profile = - // }) - // directContacts.map(p => { - // console.log(p) - // // .filter((x) => x.kind === Kind.Metadata) - // // const profile = profiles.find(x => x.creator === p.pub); - // // const label = profile?.name || truncateMiddle(p.npub, 28, ':'); - // // const isSelected = p.pub === directMessage && location.pathname.startsWith('/dm/'); - // )} - - console.log("directContacts", directContacts); - // ls.set("direct_contacts", directContacts); - - // addDirectContacts([ - // "0be16d2d36570b96080c0f9c351feb6be515a182b8b75b90738ae0180c27c6d5", - // "1331c42d27e2a043ea5f478301a41ff4bab6c70bbb73f4edbf82a6756a7f51dd" - // ]); - - // this.fetch([ - // { - // kinds: [Kind.Metadata], - // authors: directContacts - // } - // ]); + this.directContacts.push(...directContacts); + const filters: Filter[] = [ { kinds: [Kind.Metadata], @@ -237,38 +145,27 @@ class Raven extends TypedEventEmitter { this.fetch([c]); }); this.emit(RavenEvents.Ready); - - // let directMessages = getDirectMessages(events as DirectMessage[],) }); } - // public fetchPrevMessages(channel: string, until: number) { - // return this.fetchP( - // [ - // { - // kinds: [Kind.ChannelMessage], - // "#e": [channel], - // until, - // limit: MESSAGE_PER_PAGE - // } - // ], - // true - // ).then((events) => { - // events.forEach((ev) => { - // this.pushToEventBuffer(ev); - // }); - - // return events.length; - // }); - // } + public addDirectContact(directContact: string) { + const filters: Filter[] = [ + { + kinds: [Kind.Metadata], + authors: [directContact] + } + ]; + this.directContacts.push(directContact); + filters.forEach((c) => { + this.fetch([c]); + }); + } private fetch(filters: Filter[], unsub: boolean = true) { - console.log("Fetch Calledd", filters); - const sub = this.pool.sub(this.readRelays, filters); sub.on("event", (event: Event) => { - this.pushToEventBuffer(event, "fetch"); + this.pushToEventBuffer(event); }); sub.on("eose", () => { @@ -296,70 +193,6 @@ class Raven extends TypedEventEmitter { }); } - public loadChannel(id: string) { - // console.log("Load channel called"); - const filters: Filter[] = [ - { - kinds: [Kind.ChannelCreation], - ids: [id] - }, - { - kinds: [Kind.ChannelMetadata, Kind.EventDeletion], - "#e": [id] - }, - { - kinds: [Kind.ChannelMessage], - "#e": [id], - limit: 30 - } - ]; - - this.fetch(filters); - } - - // public loadProfiles(pubs: string[]) { - // console.log("pubs", pubs); - // const authors = Array.from(new Set(pubs)).filter((p) => this.nameCache[p] === undefined); - // console.log(authors, "authors"); - // if (authors.length === 0) { - // return; - // } - // authors.forEach((a) => (this.nameCache[a] = Date.now())); - - // const batchSize = 20; // Number of authors to process in each batch - - // for (let i = 0; i < authors.length; i += batchSize) { - // const batch = authors.slice(i, i + batchSize); - - // this.fetch([ - // { - // kinds: [Kind.Metadata], - // authors: batch - // } - // ]); - // } - - // } - - // public loadProfiles(pubs: string[]) { - // console.log(pubs, "pubs"); - // const authors = Array.from(new Set(pubs)).filter((p) => this.nameCache[p] === undefined); - // console.log(authors, "authors"); - // if (authors.length === 0) { - // return; - // } - // authors.forEach((a) => (this.nameCache[a] = Date.now())); - - // authors.forEach((a) => { - // this.fetch([ - // { - // kinds: [Kind.Metadata], - // authors: [a] - // } - // ]); - // }); - // } - public listen(since: number) { if (this.listenerSub) { this.listenerSub.unsub(); @@ -381,23 +214,6 @@ class Raven extends TypedEventEmitter { ); } - listenMessages = (ids: string[]) => { - // console.log("Listen messages called"); - if (this.messageListenerSub) { - this.messageListenerSub.unsub(); - } - - this.messageListenerSub = this.fetch( - [ - { - kinds: [Kind.EventDeletion, Kind.ChannelMessage, Kind.Reaction], - "#e": ids - } - ], - false - ); - }; - private async findHealthyRelay(relays: string[]) { for (const relay of relays) { try { @@ -410,39 +226,9 @@ class Raven extends TypedEventEmitter { } public async updateProfile(profile: Metadata) { - // console.log("Update profile run", profile); return this.publish(Kind.Metadata, [], JSON.stringify(profile)); } - // public async createChannel(meta: Metadata) { - // return this.publish(Kind.ChannelCreation, [], JSON.stringify(meta)); - // } - - // public async updateChannel(channel: Channel, meta: Metadata) { - // return this.findHealthyRelay(this.pool.seenOn(channel.id)).then((relay) => { - // return this.publish(Kind.ChannelMetadata, [["e", channel.id, relay]], JSON.stringify(meta)); - // }); - // } - - // public async deleteEvents(ids: string[], why: string = "") { - // return this.publish(Kind.EventDeletion, [...ids.map((id) => ["e", id])], why); - // } - - // public async sendPublicMessage( - // channel: Channel, - // message: string, - // mentions?: string[], - // parent?: string - // ) { - // const root = parent || channel.id; - // const relay = await this.findHealthyRelay(this.pool.seenOn(root)); - // const tags = [["e", root, relay, "root"]]; - // if (mentions) { - // mentions.forEach((m) => tags.push(["p", m])); - // } - // return this.publish(Kind.ChannelMessage, tags, message); - // } - public async sendDirectMessage(toPubkey: string, message: string, parent?: string) { const encrypted = await (this.priv === "nip07" ? window.nostr!.nip04.encrypt(toPubkey, message) @@ -452,40 +238,9 @@ class Raven extends TypedEventEmitter { const relay = await this.findHealthyRelay(this.pool.seenOn(parent) as string[]); tags.push(["e", parent, relay, "root"]); } - // this.emit(RavenEvents.DirectMessage, [ - // { - // id: "7a6c0db898ee043045f21fa49c5fd4b0765b506d84cb8d71539ba4cb37292040", - // content: "HYYYYYyyyy", - // peer: "1331c42d27e2a043ea5f478301a41ff4bab6c70bbb73f4edbf82a6756a7f51dd", - // creator: "7379e436f673358cdbf4f0d52ef5240c82d802755295b53182b107f41e9f43e5", - // created: 1686567232, - // decrypted: true - // } - // ]); - // console.log("Event is emitted"); return this.publish(Kind.EncryptedDirectMessage, tags, encrypted); } - // public async recommendRelay(relay: string) { - // return this.publish(Kind.RecommendRelay, [], relay); - // } - - // public async hideChannelMessage(id: string, reason: string) { - // return this.publish(Kind.ChannelHideMessage, [["e", id]], JSON.stringify({ reason })); - // } - - // public async muteChannelUser(pubkey: string, reason: string) { - // return this.publish(Kind.ChannelMuteUser, [["p", pubkey]], JSON.stringify({ reason })); - // } - - // public async updateMuteList(userIds: string[]) { - // const list = [...userIds.map((id) => ["p", id])]; - // const content = await (this.priv === "nip07" - // ? window.nostr!.nip04.encrypt(this.pub, JSON.stringify(list)) - // : nip04.encrypt(this.priv, this.pub, JSON.stringify(list))); - // return this.publish(NewKinds.MuteList, [], content); - // } - private publish(kind: number, tags: Array[], content: string): Promise { return new Promise((resolve, reject) => { this.signEvent({ @@ -504,7 +259,6 @@ class Raven extends TypedEventEmitter { } const pub = this.pool.publish(this.writeRelays, event); pub.on("ok", () => { - console.log("My event is okkkkkkkkkkkkkkkkkkkkkkk", event); resolve(event); }); @@ -530,8 +284,7 @@ class Raven extends TypedEventEmitter { } } - pushToEventBuffer(event: Event, from: string) { - console.log("Push to event buffer called frommmmmmmmmm", from); + pushToEventBuffer(event: Event) { const cacheKey = `${event.id}_emitted`; if (this.nameCache[cacheKey] === undefined) { if (this.eventQueueFlag) { @@ -569,90 +322,15 @@ class Raven extends TypedEventEmitter { : null; }) .filter(notEmpty); + + const directContacts = profileUpdates.filter((obj) => + this.directContacts.includes(obj.creator) + ); + if (profileUpdates.length > 0) { - this.emit(RavenEvents.ProfileUpdate, profileUpdates); + this.emit(RavenEvents.ProfileUpdate, directContacts); } - // const channelCreations: Channel[] = this.eventQueue - // .filter((x) => x.kind === Kind.ChannelCreation) - // .map((ev) => { - // const content = Raven.parseJson(ev.content); - // return content - // ? { - // id: ev.id, - // creator: ev.pubkey, - // created: ev.created_at, - // ...Raven.normalizeMetadata(content) - // } - // : null; - // }) - // .filter(notEmpty); - // if (channelCreations.length > 0) { - // this.emit(RavenEvents.ChannelCreation, channelCreations); - // } - - // const channelUpdates: ChannelUpdate[] = this.eventQueue - // .filter((x) => x.kind === Kind.ChannelMetadata) - // .map((ev) => { - // const content = Raven.parseJson(ev.content); - // const channelId = Raven.findTagValue(ev, "e"); - // if (!channelId) return null; - // return content - // ? { - // id: ev.id, - // creator: ev.pubkey, - // created: ev.created_at, - // channelId, - // ...Raven.normalizeMetadata(content) - // } - // : null; - // }) - // .filter(notEmpty); - // if (channelUpdates.length > 0) { - // this.emit(RavenEvents.ChannelUpdate, channelUpdates); - // } - - // const deletions: EventDeletion[] = this.eventQueue - // .filter((x) => x.kind === Kind.EventDeletion) - // .map((ev) => { - // const eventId = Raven.findTagValue(ev, "e"); - // if (!eventId) return null; - // return { - // eventId, - // why: ev.content || "" - // }; - // }) - // .filter(notEmpty) - // .flat(); - // if (deletions.length > 0) { - // this.emit(RavenEvents.EventDeletion, deletions); - // } - - // const publicMessages: PublicMessage[] = this.eventQueue - // .filter((x) => x.kind === Kind.ChannelMessage) - // .map((ev) => { - // const eTags = Raven.filterTagValue(ev, "e"); - // const root = eTags.find((x) => x[3] === "root")?.[1]; - // const mentions = Raven.filterTagValue(ev, "p") - // .map((x) => x?.[1]) - // .filter(notEmpty); - // if (!root) return null; - // return ev.content - // ? { - // id: ev.id, - // root, - // content: ev.content, - // creator: ev.pubkey, - // mentions, - // created: ev.created_at - // } - // : null; - // }) - // .filter(notEmpty); - // if (publicMessages.length > 0) { - // this.emit(RavenEvents.PublicMessage, publicMessages); - // } - Promise.all( this.eventQueue .filter((x) => x.kind === Kind.EncryptedDirectMessage) @@ -690,63 +368,6 @@ class Raven extends TypedEventEmitter { this.emit(RavenEvents.DirectMessage, directMessages); }); - // const channelMessageHides: ChannelMessageHide[] = this.eventQueue - // .filter((x) => x.kind === Kind.ChannelHideMessage) - // .map((ev) => { - // const content = Raven.parseJson(ev.content); - // const id = Raven.findTagValue(ev, "e"); - // if (!id) return null; - // return { - // id, - // reason: content?.reason || "" - // }; - // }) - // .filter(notEmpty); - // if (channelMessageHides.length > 0) { - // this.emit(RavenEvents.ChannelMessageHide, channelMessageHides); - // } - - // const channelUserMutes: ChannelUserMute[] = this.eventQueue - // .filter((x) => x.kind === Kind.ChannelMuteUser) - // .map((ev) => { - // const content = Raven.parseJson(ev.content); - // const pubkey = Raven.findTagValue(ev, "p"); - // if (!pubkey) return null; - // return { - // pubkey, - // reason: content?.reason || "" - // }; - // }) - // .filter(notEmpty); - // if (channelUserMutes.length > 0) { - // this.emit(RavenEvents.ChannelUserMute, channelUserMutes); - // } - - // const muteListEv = this.eventQueue - // .filter((x) => x.kind.toString() === NewKinds.MuteList.toString()) - // .sort((a, b) => b.created_at - a.created_at)[0]; - - // if (muteListEv) { - // const visiblePubkeys = Raven.filterTagValue(muteListEv, "p").map((x) => x?.[1]); - - // if (muteListEv.content !== "" && this.priv !== "nip07") { - // decrypt(this.priv, this.pub, muteListEv.content) - // .then((e) => JSON.parse(e)) - // .then((resp) => { - // const allPubkeys = [...visiblePubkeys, ...resp.map((x: any) => x?.[1])]; - // this.emit(RavenEvents.MuteList, { - // pubkeys: uniq(allPubkeys), - // encrypted: "" - // }); - // }); - // } else { - // this.emit(RavenEvents.MuteList, { - // pubkeys: visiblePubkeys, - // encrypted: muteListEv.content.trim() - // }); - // } - // } - this.eventQueue = []; this.eventQueueFlag = true; } @@ -784,7 +405,6 @@ class Raven extends TypedEventEmitter { export default Raven; export const initRaven = (keys: Keys): Raven | undefined => { - console.log("Init raven run"); if (window.raven) { window.raven.close(); window.raven = undefined; diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index e64d8460c5a..d6f5d52715a 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1657,7 +1657,11 @@ "followers": "Followers", "start-chat-placeholder": "Start a new message", "search": "Search people", - "join-chat": "Join chat" + "join-chat": "Join chat", + "back": "Back", + "no-chat": "No conversation found", + "start-chat": "Start chat", + "not-joined": "This user hasn't joined the chat yet" }, "add-image": { "title": "Add Image", diff --git a/src/common/store/actions.ts b/src/common/store/actions.ts index dd2d391c865..27d8a7a87f2 100644 --- a/src/common/store/actions.ts +++ b/src/common/store/actions.ts @@ -41,7 +41,7 @@ import { fetchPoints, resetPoints } from "./points"; import { setSigningKey } from "./signing-key"; import { setEntryPin, trackEntryPin } from "./entry-pin-tracker"; import { savePageScroll } from "./persistent-page-scroll"; -import { addDirectContacts, addDirectMessages } from "./chat"; +import { addDirectContacts, addDirectMessages, resetChat } from "./chat"; // @note Do not use it directly export const ACTIONS = { @@ -92,7 +92,8 @@ export const ACTIONS = { fetchNotificationsSettings, setNotificationsSettingsItem, addDirectContacts, - addDirectMessages + addDirectMessages, + resetChat }; export const getActions = () => ({ diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts index be2fa23e0f0..55af8c96eb6 100644 --- a/src/common/store/chat/index.ts +++ b/src/common/store/chat/index.ts @@ -7,7 +7,8 @@ import { ActionTypes, DirectContactsAction, DirectContactsType, - DirectMessagesAction + DirectMessagesAction, + ResetChatAction } from "./types"; export const initialState: Chat = { @@ -19,9 +20,12 @@ export default (state: Chat = initialState, action: Actions): Chat => { switch (action.type) { case ActionTypes.DIRECTCONTACTS: { const { data } = action; + const uniqueDirectContacts = data.filter( + (contact) => !state.directContacts.includes(contact) + ); return { ...state, - directContacts: [...state.directContacts, ...data] + directContacts: [...state.directContacts, ...uniqueDirectContacts] }; } case ActionTypes.DIRECTMESSAGES: { @@ -31,6 +35,10 @@ export default (state: Chat = initialState, action: Actions): Chat => { directMessages: [...state.directMessages, ...data] }; } + + case ActionTypes.RESET: + return initialState; + default: return state; } @@ -42,10 +50,12 @@ export const addDirectContacts = (data: DirectContactsType[]) => (dispatch: Disp }; export const addDirectMessages = (data: DirectMessage[]) => (dispatch: Dispatch) => { - console.log("data in store", data); dispatch(addDirectMessagesAct(data)); }; +export const resetChat = () => (dispatch: Dispatch) => { + dispatch(resetChatAct()); +}; /* Action Creators */ export const addDirectContactsAct = (data: DirectContactsType[]): DirectContactsAction => { @@ -61,3 +71,9 @@ export const addDirectMessagesAct = (data: DirectMessage[]): DirectMessagesActio data }; }; + +export const resetChatAct = (): ResetChatAction => { + return { + type: ActionTypes.RESET + }; +}; diff --git a/src/common/store/chat/types.ts b/src/common/store/chat/types.ts index ddfc443cf4a..57e6c3ddded 100644 --- a/src/common/store/chat/types.ts +++ b/src/common/store/chat/types.ts @@ -1,5 +1,4 @@ import { DirectMessage } from "./../../../providers/message-provider-types"; -import { Account } from "../accounts/types"; export interface DirectContactsType { name: string; @@ -9,14 +8,12 @@ export interface DirectContactsType { export interface Chat { directContacts: DirectContactsType[]; directMessages: DirectMessage[]; - // points: UserPoints; } export enum ActionTypes { DIRECTCONTACTS = "@chat/DIRECTCONTACTS", - DIRECTMESSAGES = "@chat/DIRECTMESSAGES" - // LOGOUT = "@active-user/LOGOUT", - // UPDATE = "@active-user/UPDATE" + DIRECTMESSAGES = "@chat/DIRECTMESSAGES", + RESET = "@chat/RESET" } export interface DirectContactsAction { @@ -24,18 +21,13 @@ export interface DirectContactsAction { data: DirectContactsType[]; } +export interface ResetChatAction { + type: ActionTypes.RESET; +} + export interface DirectMessagesAction { type: ActionTypes.DIRECTMESSAGES; data: DirectMessage[]; } -// export interface LogoutAction { -// type: ActionTypes.LOGOUT; -// } - -// export interface UpdateAction { -// type: ActionTypes.UPDATE; -// data: Account; -// points: UserPoints; -// } -export type Actions = DirectContactsAction | DirectMessagesAction; +export type Actions = DirectContactsAction | DirectMessagesAction | ResetChatAction; diff --git a/src/providers/message-provider.tsx b/src/providers/message-provider.tsx index e5b823a0ea5..58daae77edf 100644 --- a/src/providers/message-provider.tsx +++ b/src/providers/message-provider.tsx @@ -1,52 +1,20 @@ -import React, { useEffect, useMemo, useState } from "react"; -import { getProfileMetaData, GLOBAL_CHAT, NostrKeys } from "../common/helper/chat-utils"; -import { ActiveUser } from "../common/store/active-user/types"; -// import uniq from 'lodash.uniq'; -// import {nip19} from 'nostr-tools'; +import React, { useEffect, useState } from "react"; -// import { -// channelsAtom, -// channelUpdatesAtom, -// directContactsAtom, -// directMessagesAtom, -// eventDeletionsAtom, -// keysAtom, -// profileAtom, -// profilesAtom, -// publicMessagesAtom, -// ravenAtom, -// ravenReadyAtom, -// channelMessageHidesAtom, -// channelUserMutesAtom, -// muteListAtom, directMessageAtom -// } from 'store'; -import { initRaven, RavenEvents } from "../common/helper/message-helper"; -import { - Channel, - ChannelUpdate, - DirectMessage, - EventDeletion, - Profile, - PublicMessage, - ChannelMessageHide, - ChannelUserMute, - MuteList, - Keys -} from "./message-provider-types"; -import { useMappedStore } from "../common/store/use-mapped-store"; -import { Account } from "../common/store/accounts/types"; +import { ActiveUser } from "../common/store/active-user/types"; import { Chat, DirectContactsType } from "../common/store/chat/types"; +import { DirectMessage, Profile, Keys } from "./message-provider-types"; -// import {createLogger} from 'logger'; +import { initRaven, RavenEvents } from "../common/helper/message-helper"; +import { getProfileMetaData, NostrKeys } from "../common/helper/chat-utils"; -// const logger = createLogger('RavenProvider'); +import { useMappedStore } from "../common/store/use-mapped-store"; export const setNostrkeys = (keys: NostrKeys) => { const detail: NostrKeys = { pub: keys.pub, priv: keys.priv }; - const ev = new CustomEvent("keysCreated", { detail }); + const ev = new CustomEvent("createRavenInstance", { detail }); window.dispatchEvent(ev); }; @@ -58,33 +26,17 @@ interface Props { } const MessageProvider = (props: Props) => { - // console.log(props.addDirectMessages, "MessageProvider") const { activeUser } = useMappedStore(); const [ravenReady, setRavenReady] = useState(false); - // const [profile, setProfile] = useAtom(profileAtom); - // const [profiles, setProfiles] = useAtom(profilesAtom); - // const [channels, setChannels] = useAtom(channelsAtom); - // const [channelUpdates, setChannelUpdates] = useAtom(channelUpdatesAtom); - // const [eventDeletions, setEventDeletions] = useAtom(eventDeletionsAtom); - // const [publicMessages, setPublicMessages] = useAtom(publicMessagesAtom); - // const [directMessages, setDirectMessages] = useAtom(directMessagesAtom); - // const [directMessage,] = useAtom(directMessageAtom); - // const [channelMessageHides, setChannelMessageHides] = useAtom(channelMessageHidesAtom); - // const [channelUserMutes, setChannelUserMutes] = useAtom(channelUserMutesAtom); - // const [muteList, setMuteList] = useAtom(muteListAtom); - // const [, setDirectContacts] = useAtom(directContactsAtom); const [since, setSince] = useState(0); const [keys, setKeys] = useState(); const [raven, setRaven] = useState(); - // const ravenInstance = useMemo(() => initRaven(keys!), [keys]); - // setRaven(ravenInstance); - useEffect(() => { - window.addEventListener("keysCreated", createRavenInstance); + window.addEventListener("createRavenInstance", createRavenInstance); return () => { - window.removeEventListener("keysCreated", createRavenInstance); + window.removeEventListener("createRavenInstance", createRavenInstance); }; }, []); @@ -95,13 +47,6 @@ const MessageProvider = (props: Props) => { } }, [keys]); - // useEffect(() => { - // console.log(raven); - // if(raven){ - // raven?.updateProfile({name:activeUser?.username, about: "", picture: ""}); - // } - // }, [raven]); - useEffect(() => { if (activeUser) { getNostrKeys(activeUser); @@ -136,33 +81,6 @@ const MessageProvider = (props: Props) => { }; }, [since, ravenReady, raven]); - // // Trigger listen once the window visibility changes. - // const visibilityChange = () => { - // if (document.visibilityState === "visible") { - // raven?.listen( - // channels.map((x) => x.id), - // Math.floor((since || Date.now()) / 1000) - // ); - // setSince(Date.now()); - // } - // }; - // useEffect(() => { - // document.addEventListener("visibilitychange", visibilityChange); - - // return () => { - // document.removeEventListener("visibilitychange", visibilityChange); - // }; - // }, [since, ravenReady, raven, channels]); - - // useEffect(() => { - // setDirectContacts( - // [...new Set(directMessages.map((x) => x.peer))].map((p) => ({ - // pub: p, - // npub: nip19.npubEncode(p) - // })) - // ); - // }, [directMessages]); - // // Ready state handler const handleReadyState = () => { setRavenReady(true); @@ -177,47 +95,15 @@ const MessageProvider = (props: Props) => { }; }, [ravenReady, raven]); - // // Event deletion handler - // const handleEventDeletion = (data: EventDeletion[]) => { - // logger.info("handleEventDeletion", data); - // const append = data.filter( - // (x) => eventDeletions.find((y) => y.eventId === x.eventId) === undefined - // ); - // setEventDeletions([...eventDeletions, ...append]); - // }; - - // useEffect(() => { - // raven?.removeListener(RavenEvents.EventDeletion, handleEventDeletion); - // raven?.addListener(RavenEvents.EventDeletion, handleEventDeletion); - - // return () => { - // raven?.removeListener(RavenEvents.EventDeletion, handleEventDeletion); - // }; - // }, [raven, eventDeletions]); - // // Direct message handler const handleDirectMessage = (data: DirectMessage[]) => { - console.log("handleDirectMessage", data); - console.log(props.chat.directMessages); const append = data.filter( (x) => props.chat.directMessages.find((y) => y.id === x.id) === undefined ); - // console.log("append", [...props.chat.directMessages, ...append]); const result = [...props.chat.directMessages, ...append]; - // console.log(result, 'result') - // raven?.loadProfiles(data.map((x) => x.peer)); - props.addDirectMessages(result); - // const clean = data - // .filter((x) => x.peer === "ad2b87073a67f664621c92dbc925f3d1be3e4df64968fdeb4d15c6736ef42300") - // .sort((a, b) => a.created - b.created); - - // console.log(clean, "clean"); - // const finalData = clean - // .map((c) => ({ ...c, children: clean.filter((x) => x.root === c.id) })) - // .filter((x) => !x.root); - // console.log(finalData, "finalData"); + props.addDirectMessages(result); }; useEffect(() => { @@ -229,24 +115,18 @@ const MessageProvider = (props: Props) => { }, [raven]); const handleProfileUpdate = (data: Profile[]) => { - // console.log("handleProfileData", data); - // console.log("Store state", props.chat.directContacts); - const result = [...props.chat.directContacts]; - const activeUserName = props.activeUser?.username; data.forEach(({ name, creator }) => { const isPresent = props.chat.directContacts.some( (obj) => obj.name === name && obj.creator === creator ); - const isCurrentUser = name === activeUserName; - if (!isPresent && !isCurrentUser) { - // Add the object to the result array if it's not already present in store state. + if (!isPresent) { + //Add the object to the result array if it's not already present in store state. result.push({ name, creator }); } }); - // console.log("result", result); props.addDirectContacts(result); }; @@ -258,42 +138,6 @@ const MessageProvider = (props: Props) => { }; }, [raven]); - // // decrypt direct messages one by one to avoid show nip7 wallet dialog many times. - // useEffect(() => { - // if (directMessage) { - // const decrypted = directMessages - // .filter((m) => m.peer === directMessage) - // .find((x) => !x.decrypted); - // if (decrypted) { - // window.nostr?.nip04.decrypt(decrypted.peer, decrypted.content).then((content) => { - // setDirectMessages( - // directMessages.map((m) => { - // if (m.id === decrypted.id) { - // return { - // ...m, - // content, - // decrypted: true - // }; - // } - // return m; - // }) - // ); - // }); - // } - // } - // }, [directMessages, directMessage]); - - // Init raven - // useEffect(() => { - // return () => { - // raven?.removeListener(RavenEvents.Ready, handleReadyState); - - // // raven?.removeListener(RavenEvents.EventDeletion, handleEventDeletion); - - // raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); - // }; - // }, [raven]); - return <>{}; }; From b4dc181769c3c920ab38724c34ed0d865a4c19f0 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Tue, 20 Jun 2023 14:48:32 +0500 Subject: [PATCH 012/179] Remove commented code --- src/client/index.tsx | 5 ----- src/common/components/search-user/index.tsx | 5 ----- 2 files changed, 10 deletions(-) diff --git a/src/client/index.tsx b/src/client/index.tsx index 0bb65188059..9d6a1e938e9 100644 --- a/src/client/index.tsx +++ b/src/client/index.tsx @@ -11,7 +11,6 @@ import { AppWindow } from "./window"; import "../style/style.scss"; import "./base-handlers"; import { loadableReady } from "@loadable/component"; -import MessageProvider from "../providers/message-provider"; declare var window: AppWindow; @@ -40,9 +39,7 @@ loadableReady().then(() => { hydrate( - {/* */} - {/* */} , document.getElementById("root") @@ -67,9 +64,7 @@ if (module.hot) { hydrate( - {/* */} - {/* */} , document.getElementById("root") diff --git a/src/common/components/search-user/index.tsx b/src/common/components/search-user/index.tsx index 7e830be65a8..36ef8450c44 100644 --- a/src/common/components/search-user/index.tsx +++ b/src/common/components/search-user/index.tsx @@ -26,10 +26,6 @@ export default function SeachUser(props: Props) { [searchtext] ); - useEffect(() => { - setUserList(["good-karma", "good-akai", "good-ali", "good-angle", "good-bad"]); - }, []); - const searchUserClicked = (username: string) => { const { setCurrentUserFromSearch, setSearchUser } = props; setCurrentUserFromSearch(username); @@ -79,7 +75,6 @@ export default function SeachUser(props: Props) {

{user}

- {/*

{user}

*/}
); From 0f89cfb40d78a447092d435bac90b36aaa86b81d Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Wed, 21 Jun 2023 18:47:49 +0500 Subject: [PATCH 013/179] Active user can send message to himself --- src/common/components/chat-box/index.scss | 6 - src/common/components/chat-box/index.tsx | 43 +-- src/common/helper/chat-utils.ts | 20 ++ src/common/helper/message-helper.ts | 46 ++- src/lib/nostr-tools/.eslintrc.json | 145 --------- src/lib/nostr-tools/.gitignore | 7 - src/lib/nostr-tools/.prettierrc.yaml | 10 - src/lib/nostr-tools/README.md | 192 ------------ src/lib/nostr-tools/build.js | 41 --- src/lib/nostr-tools/index.ts | 14 - src/lib/nostr-tools/package.json | 40 --- src/lib/nostr-tools/pool.ts | 91 ++---- src/lib/nostr-tools/relay.ts | 364 +++------------------- src/lib/nostr-tools/tsconfig.json | 15 - src/providers/message-provider.tsx | 11 +- 15 files changed, 177 insertions(+), 868 deletions(-) delete mode 100644 src/lib/nostr-tools/.eslintrc.json delete mode 100644 src/lib/nostr-tools/.gitignore delete mode 100644 src/lib/nostr-tools/.prettierrc.yaml delete mode 100644 src/lib/nostr-tools/README.md delete mode 100755 src/lib/nostr-tools/build.js delete mode 100644 src/lib/nostr-tools/package.json delete mode 100644 src/lib/nostr-tools/tsconfig.json diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss index 1d952569261..fd4c41bc385 100644 --- a/src/common/components/chat-box/index.scss +++ b/src/common/components/chat-box/index.scss @@ -318,12 +318,6 @@ margin: 0 24px; font-size: 0.7em; color: $dark; - .line { - flex-grow: 1; - height: 1px; - background-color: black; - margin: 0 10px; - } .custom-divider-text { display: flex; justify-content: center; diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 5a338be1d45..ba3fb063500 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -3,9 +3,9 @@ import { Button, Form, FormControl, InputGroup, Spinner } from "react-bootstrap" import { Link } from "react-router-dom"; import { ActiveUser } from "../../store/active-user/types"; -import { DirectContactsType } from "../../store/chat/types"; +import { Chat, DirectContactsType } from "../../store/chat/types"; import { DirectMessage } from "../../../providers/message-provider-types"; -import { useMappedStore } from "../../store/use-mapped-store"; +// import { useMappedStore } from "../../store/use-mapped-store"; import Tooltip from "../tooltip"; import UserAvatar from "../user-avatar"; @@ -30,7 +30,8 @@ import { getDirectMessages, getProfileMetaData, NostrKeys, - setProfileMetaData + setProfileMetaData, + correctProfile } from "../../helper/chat-utils"; import { _t } from "../../i18n"; import { getAccountFull } from "../../api/hive"; @@ -45,6 +46,8 @@ export interface profileData { interface Props { activeUser: ActiveUser | null; + chat: Chat; + resetChat: () => void; } export default function ChatBox(props: Props) { @@ -67,17 +70,16 @@ export default function ChatBox(props: Props) { const [messagesList, setMessagesList] = useState([]); const [isUserFromSearch, setIsUserFromSearch] = useState(false); - const { chat } = useMappedStore(); - useEffect(() => { + // correctProfile(props.activeUser); fetchProfileData(); setShow(!!props.activeUser?.username); }, []); useEffect(() => { - const msgsList = getDirectMessages(chat.directMessages, receiverPubKey!); + const msgsList = getDirectMessages(props.chat.directMessages, receiverPubKey!); setMessagesList(msgsList); - }, [chat.directMessages]); + }, [props.chat.directMessages]); useEffect(() => { scrollToBottom(); @@ -92,16 +94,16 @@ export default function ChatBox(props: Props) { useEffect(() => { fetchProfileData(); setShow(!!props.activeUser?.username); - const msgsList = getDirectMessages(chat.directMessages, receiverPubKey!); + const msgsList = getDirectMessages(props.chat.directMessages, receiverPubKey!); setMessagesList(msgsList); }, [props.activeUser]); useEffect(() => { if (currentUser) { fetchCurrentUserData(); - const peer = chat.directContacts.find((x) => x.name === currentUser)?.creator ?? null; + const peer = props.chat.directContacts.find((x) => x.name === currentUser)?.creator ?? null; setReceiverPubKey(peer!); - const msgsList = getDirectMessages(chat.directMessages, peer!); + const msgsList = getDirectMessages(props.chat.directMessages, peer!); setMessagesList(msgsList); if (!window.raven) { setNostrkeys(activeUserKeys!); @@ -132,7 +134,7 @@ export default function ChatBox(props: Props) { followers: response.follow_stats?.follower_count }); const currentUserProfile = await getProfileMetaData(currentUser); - setReceiverPubKey(currentUserProfile.noStrKey?.pub); + setReceiverPubKey(currentUserProfile?.noStrKey?.pub); setShowSpinner(false); }; @@ -154,7 +156,7 @@ export default function ChatBox(props: Props) { setCurrentUser(username); setExpanded(true); setIsCurrentUser(true); - fetchCurrentUserData(); + // fetchCurrentUserData(); setIsUserFromSearch(true); }; @@ -183,7 +185,6 @@ export default function ChatBox(props: Props) { isCurrentUser && element.scrollTop <= (element.scrollHeight / 100) * 50 && element.scrollHeight > 700; - setIsScrollToTop(isScrollToTop); setIsScrollToBottom(isScrollToBottom); }; @@ -209,14 +210,18 @@ export default function ChatBox(props: Props) { setShowSearchUser(d); }; - const handleJoinChat = () => { + const handleJoinChat = async () => { + const { resetChat } = props; setInProgress(true); + resetChat(); const keys = createNoStrAccount(); - setProfileMetaData(props.activeUser, keys); - setInProgress(false); + await setProfileMetaData(props.activeUser, keys); setHasUserJoinedChat(true); setNostrkeys(keys); window.raven?.updateProfile({ name: props.activeUser?.username!, about: "", picture: "" }); + // fetchProfileData(); + setActiveUserKeys(keys); + setInProgress(false); }; const chatButtonSpinner = ( @@ -230,9 +235,7 @@ export default function ChatBox(props: Props) { if (msgDate !== prevMsgDate) { return (
- {msgDate} -
); } @@ -371,9 +374,9 @@ export default function ChatBox(props: Props) { ) : ( <> - {chat.directContacts.length !== 0 ? ( + {props.chat.directContacts.length !== 0 ? ( - {chat.directContacts.map((user: DirectContactsType) => { + {props.chat.directContacts.map((user: DirectContactsType) => { return (
diff --git a/src/common/helper/chat-utils.ts b/src/common/helper/chat-utils.ts index 9019059a61a..6ac87fdd8a1 100644 --- a/src/common/helper/chat-utils.ts +++ b/src/common/helper/chat-utils.ts @@ -20,6 +20,26 @@ export const getProfileMetaData = async (username: string) => { } }; +export const correctProfile = async (activeUser: ActiveUser | null) => { + const profile = await getProfileMetaData(activeUser?.username!); + delete profile.noStrKey; + delete profile.profile; + delete profile.version; + const response = await getAccountFull(activeUser?.username!); + const profileC = { + name: activeUser?.username, + about: "", + cover_image: "", + profile_image: "", + website: "", + location: "", + pinned: "", + version: 2 + }; + const updatedProfile = await updateProfile(response, { ...profileC }); + console.log(updatedProfile); +}; + export const setProfileMetaData = async (activeUser: ActiveUser | null, keys: NostrKeys) => { const response = await getAccountFull(activeUser?.username!); diff --git a/src/common/helper/message-helper.ts b/src/common/helper/message-helper.ts index f3cea0cff6f..0c6449a9012 100644 --- a/src/common/helper/message-helper.ts +++ b/src/common/helper/message-helper.ts @@ -69,6 +69,7 @@ class Raven extends TypedEventEmitter { private eventQueueFlag = true; private eventQueueBuffer: Event[] = []; public directContacts: string[] = []; + private isActiveUserPushed: boolean = false; private nameCache: Record = {}; @@ -148,6 +149,39 @@ class Raven extends TypedEventEmitter { }); } + public loadProfiles(pubs: string[]) { + pubs.forEach((p) => { + if (!this.directContacts.includes(p)) { + if (p !== this.pub) { + this.directContacts.push(p); + } + this.fetch([ + { + kinds: [Kind.Metadata], + authors: [p] + } + ]); + } + }); + } + + public directContactForActiveUser(event: Event) { + if (!this.isActiveUserPushed && !this.directContacts.includes(event.pubkey)) { + const activeUserProfile = { + id: event.id, + creator: event.pubkey, + created: event.created_at, + name: JSON.parse(event.content).name, + about: JSON.parse(event.content).about || "", + picture: JSON.parse(event.content).picture || "" + }; + + this.emit(RavenEvents.ProfileUpdate, [activeUserProfile]); + this.isActiveUserPushed = true; + this.directContacts.push(event.pubkey); + } + } + public addDirectContact(directContact: string) { const filters: Filter[] = [ { @@ -155,10 +189,11 @@ class Raven extends TypedEventEmitter { authors: [directContact] } ]; - this.directContacts.push(directContact); - filters.forEach((c) => { - this.fetch([c]); - }); + + if (!this.directContacts.includes(directContact) && this.pub !== directContact) { + this.directContacts.push(directContact); + } + this.fetch(filters); } private fetch(filters: Filter[], unsub: boolean = true) { @@ -166,6 +201,9 @@ class Raven extends TypedEventEmitter { sub.on("event", (event: Event) => { this.pushToEventBuffer(event); + if (event.kind === Kind.Metadata && event.pubkey === this.pub) { + this.directContactForActiveUser(event); + } }); sub.on("eose", () => { diff --git a/src/lib/nostr-tools/.eslintrc.json b/src/lib/nostr-tools/.eslintrc.json deleted file mode 100644 index faca633c5e0..00000000000 --- a/src/lib/nostr-tools/.eslintrc.json +++ /dev/null @@ -1,145 +0,0 @@ -{ - "root": true, - - "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint"], - - "parserOptions": { - "ecmaVersion": 9, - "ecmaFeatures": { - "jsx": true - }, - "sourceType": "module", - "allowImportExportEverywhere": false - }, - - "env": { - "es6": true, - "node": true - }, - - "plugins": ["babel"], - - "globals": { - "document": false, - "navigator": false, - "window": false, - "location": false, - "URL": false, - "URLSearchParams": false, - "fetch": false, - "EventSource": false, - "localStorage": false, - "sessionStorage": false - }, - - "rules": { - "accessor-pairs": 2, - "arrow-spacing": [2, { "before": true, "after": true }], - "block-spacing": [2, "always"], - "brace-style": [2, "1tbs", { "allowSingleLine": true }], - "comma-dangle": 0, - "comma-spacing": [2, { "before": false, "after": true }], - "comma-style": [2, "last"], - "constructor-super": 2, - "curly": [0, "multi-line"], - "dot-location": [2, "property"], - "eol-last": 2, - "eqeqeq": [2, "allow-null"], - "generator-star-spacing": [2, { "before": true, "after": true }], - "handle-callback-err": [2, "^(err|error)$"], - "indent": 0, - "jsx-quotes": [2, "prefer-double"], - "key-spacing": [2, { "beforeColon": false, "afterColon": true }], - "keyword-spacing": [2, { "before": true, "after": true }], - "new-cap": 0, - "new-parens": 0, - "no-array-constructor": 2, - "no-caller": 2, - "no-class-assign": 2, - "no-cond-assign": 2, - "no-const-assign": 2, - "no-control-regex": 0, - "no-debugger": 0, - "no-delete-var": 2, - "no-dupe-args": 2, - "no-dupe-class-members": 2, - "no-dupe-keys": 2, - "no-duplicate-case": 2, - "no-empty-character-class": 2, - "no-empty-pattern": 2, - "no-eval": 0, - "no-ex-assign": 2, - "no-extend-native": 2, - "no-extra-bind": 2, - "no-extra-boolean-cast": 2, - "no-extra-parens": [2, "functions"], - "no-fallthrough": 2, - "no-floating-decimal": 2, - "no-func-assign": 2, - "no-implied-eval": 2, - "no-inner-declarations": [0, "functions"], - "no-invalid-regexp": 2, - "no-irregular-whitespace": 2, - "no-iterator": 2, - "no-label-var": 2, - "no-labels": [2, { "allowLoop": false, "allowSwitch": false }], - "no-lone-blocks": 2, - "no-mixed-spaces-and-tabs": 2, - "no-multi-spaces": 2, - "no-multi-str": 2, - "no-multiple-empty-lines": [2, { "max": 2 }], - "no-native-reassign": 2, - "no-negated-in-lhs": 2, - "no-new": 0, - "no-new-func": 2, - "no-new-object": 2, - "no-new-require": 2, - "no-new-symbol": 2, - "no-new-wrappers": 2, - "no-obj-calls": 2, - "no-octal": 2, - "no-octal-escape": 2, - "no-path-concat": 0, - "no-proto": 2, - "no-redeclare": 2, - "no-regex-spaces": 2, - "no-return-assign": 0, - "no-self-assign": 2, - "no-self-compare": 2, - "no-sequences": 2, - "no-shadow-restricted-names": 2, - "no-spaced-func": 2, - "no-sparse-arrays": 2, - "no-this-before-super": 2, - "no-throw-literal": 2, - "no-trailing-spaces": 2, - "no-undef": 2, - "no-undef-init": 2, - "no-unexpected-multiline": 2, - "no-unneeded-ternary": [2, { "defaultAssignment": false }], - "no-unreachable": 2, - "no-unused-vars": [2, { "vars": "local", "args": "none", "varsIgnorePattern": "^_" }], - "no-useless-call": 2, - "no-useless-constructor": 2, - "no-with": 2, - "one-var": [0, { "initialized": "never" }], - "operator-linebreak": [2, "after", { "overrides": { "?": "before", ":": "before" } }], - "padded-blocks": [2, "never"], - "quotes": [2, "single", { "avoidEscape": true, "allowTemplateLiterals": true }], - "semi": [2, "never"], - "semi-spacing": [2, { "before": false, "after": true }], - "space-before-blocks": [2, "always"], - "space-before-function-paren": 0, - "space-in-parens": [2, "never"], - "space-infix-ops": 2, - "space-unary-ops": [2, { "words": true, "nonwords": false }], - "spaced-comment": 0, - "template-curly-spacing": [2, "never"], - "use-isnan": 2, - "valid-typeof": 2, - "wrap-iife": [2, "any"], - "yield-star-spacing": [2, "both"], - "yoda": [0] - } -} diff --git a/src/lib/nostr-tools/.gitignore b/src/lib/nostr-tools/.gitignore deleted file mode 100644 index f9940ed24f0..00000000000 --- a/src/lib/nostr-tools/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -node_modules -dist -yarn.lock -package-lock.json -.envrc -lib -test.html diff --git a/src/lib/nostr-tools/.prettierrc.yaml b/src/lib/nostr-tools/.prettierrc.yaml deleted file mode 100644 index 16c878ec925..00000000000 --- a/src/lib/nostr-tools/.prettierrc.yaml +++ /dev/null @@ -1,10 +0,0 @@ -semi: false -arrowParens: avoid -insertPragma: false -printWidth: 80 -proseWrap: preserve -singleQuote: true -trailingComma: none -useTabs: false -jsxBracketSameLine: false -bracketSpacing: false diff --git a/src/lib/nostr-tools/README.md b/src/lib/nostr-tools/README.md deleted file mode 100644 index df60942d4d4..00000000000 --- a/src/lib/nostr-tools/README.md +++ /dev/null @@ -1,192 +0,0 @@ -# nostr-tools - -Tools for developing [Nostr](https://github.com/fiatjaf/nostr) clients. - -## Usage - -### Generating a private key and a public key - -```js -import { generatePrivateKey, getPublicKey } from "nostr-tools"; - -let sk = generatePrivateKey(); // `sk` is a hex string -let pk = getPublicKey(sk); // `pk` is a hex string -``` - -### Creating, signing and verifying events - -```js -import { validateEvent, verifySignature, signEvent, getEventHash, getPublicKey } from "nostr-tools"; - -let event = { - kind: 1, - created_at: Math.floor(Date.now() / 1000), - tags: [], - content: "hello" -}; - -event.id = getEventHash(event.id); -event.pubkey = getPublicKey(privateKey); -event.sig = await signEvent(event, privateKey); - -let ok = validateEvent(event); -let veryOk = await verifySignature(event); -``` - -### Interacting with a relay - -```js -import { relayInit, generatePrivateKey, getPublicKey, getEventHash, signEvent } from "nostr-tools"; - -const relay = relayInit("wss://relay.example.com"); -relay.connect(); - -relay.on("connect", () => { - console.log(`connected to ${relay.url}`); -}); -relay.on("error", () => { - console.log(`failed to connect to ${relay.url}`); -}); - -// let's query for an event that exists -let sub = relay.sub([ - { - ids: ["d7dd5eb3ab747e16f8d0212d53032ea2a7cadef53837e5a6c66d42849fcb9027"] - } -]); -sub.on("event", (event) => { - console.log("we got the event we wanted:", event); -}); -sub.on("eose", () => { - sub.unsub(); -}); - -// let's publish a new event while simultaneously monitoring the relay for it -let sk = generatePrivateKey(); -let pk = getPublicKey(sk); - -let sub = relay.sub([ - { - kinds: [1], - authors: [pk] - } -]); - -sub.on("event", (event) => { - console.log("got event:", event); -}); - -let event = { - kind: 1, - pubkey: pk, - created_at: Math.floor(Date.now() / 1000), - tags: [], - content: "hello world" -}; -event.id = getEventHash(event); -event.sig = await signEvent(event, sk); - -let pub = relay.publish(event); -pub.on("ok", () => { - console.log(`{relay.url} has accepted our event`); -}); -pub.on("seen", () => { - console.log(`we saw the event on {relay.url}`); -}); -pub.on("failed", (reason) => { - console.log(`failed to publish to {relay.url}: ${reason}`); -}); - -await relay.close(); -``` - -### Querying profile data from a NIP-05 address - -```js -import { nip05 } from "nostr-tools"; - -let profile = await nip05.queryProfile("jb55.com"); -console.log(profile.pubkey); -// prints: 32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245 -console.log(profile.relays); -// prints: [wss://relay.damus.io] - -// on nodejs, install node-fetch@2 and call this first: -nip05.useFetchImplementation(require("node-fetch")); -``` - -### Encoding and decoding NIP-19 codes - -```js -import { nip19, generatePrivateKey, getPublicKey } from "nostr-tools"; - -let sk = generatePrivateKey(); -let nsec = nip19.nsecEncode(sk); -let { type, data } = nip19.decode(nsec); -assert(type === "nsec"); -assert(data === sk); - -let pk = getPublicKey(generatePrivateKey()); -let npub = nip19.npubEncode(pk); -let { type, data } = nip19.decode(npub); -assert(type === "npub"); -assert(data === pk); - -let pk = getPublicKey(generatePrivateKey()); -let relays = ["wss://relay.nostr.example.mydomain.example.com", "wss://nostr.banana.com"]; -let nprofile = nip19.nprofileEncode({ pubkey: pk, relays }); -let { type, data } = nip19.decode(nprofile); -assert(type === "nprofile"); -assert(data.pubkey === pk); -assert(data.relays.length === 2); -``` - -### Encrypting and decrypting direct messages - -```js -import {nip04, getPublicKey, generatePrivateKey} from 'nostr-tools' - -// sender -let sk1 = generatePrivateKey() -let pk1 = getPublicKey(sk1) - -// receiver -let sk2 = generatePrivateKey() -let pk2 = getPublicKey(sk2) - -// on the sender side -let message = 'hello' -let ciphertext = nip04.encrypt(sk1, pk2, 'hello') - -let event = { - kind: 4, - pubkey: pk1, - tags: [['p', pk2]], - content: ciphertext, - ...otherProperties -} - -sendEvent(event) - -// on the receiver side -sub.on('event', (event) => { - let sender = event.tags.find(([k, v]) => k === 'p' && && v && v !== '')[1] - pk1 === sender - let plaintext = nip04.decrypt(sk2, pk1, event.content) -}) -``` - -Please consult the tests or [the source code](https://github.com/fiatjaf/nostr-tools) for more information that isn't available here. - -### Using from the browser (if you don't want to use a bundler) - -```html - - -``` - -## License - -Public domain. diff --git a/src/lib/nostr-tools/build.js b/src/lib/nostr-tools/build.js deleted file mode 100755 index 31d53470d82..00000000000 --- a/src/lib/nostr-tools/build.js +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env node - -const esbuild = require("esbuild"); - -let common = { - entryPoints: ["index.ts"], - bundle: true, - sourcemap: "external" -}; - -esbuild - .build({ - ...common, - outfile: "lib/nostr.esm.js", - format: "esm", - packages: "external" - }) - .then(() => console.log("esm build success.")); - -esbuild - .build({ - ...common, - outfile: "lib/nostr.cjs.js", - format: "cjs", - packages: "external" - }) - .then(() => console.log("cjs build success.")); - -esbuild - .build({ - ...common, - outfile: "lib/nostr.bundle.js", - format: "iife", - globalName: "NostrTools", - define: { - window: "self", - global: "self", - process: '{"env": {}}' - } - }) - .then(() => console.log("standalone build success.")); diff --git a/src/lib/nostr-tools/index.ts b/src/lib/nostr-tools/index.ts index 62ed810120c..e1fd087ef71 100644 --- a/src/lib/nostr-tools/index.ts +++ b/src/lib/nostr-tools/index.ts @@ -1,17 +1,3 @@ -// import * as keys from './keys' -// import * as relay from './relay' -// import * as event from './event' -// import * as filter from './filter' -// import * as path from './path' - -// export {keys, relay, event, filter, path} - -// export * as nip04 from './nip04' -export * as nip05 from "./nip05"; -export * as nip06 from "./nip06"; -export * as nip19 from "./nip19"; -export * as nip26 from "./nip26"; - // monkey patch secp256k1 import * as secp256k1 from "@noble/secp256k1"; import { hmac } from "@noble/hashes/hmac"; diff --git a/src/lib/nostr-tools/package.json b/src/lib/nostr-tools/package.json deleted file mode 100644 index 6ca9bd9153e..00000000000 --- a/src/lib/nostr-tools/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "nostr-tools", - "version": "1.1.2", - "description": "Tools for making a Nostr client.", - "repository": { - "type": "git", - "url": "https://github.com/fiatjaf/nostr-tools.git" - }, - "main": "lib/nostr.cjs.js", - "module": "lib/nostr.esm.js", - "dependencies": {}, - "keywords": [ - "decentralization", - "social", - "censorship-resistance", - "client", - "nostr" - ], - "devDependencies": { - "@types/node": "^18.0.3", - "@typescript-eslint/eslint-plugin": "^5.46.1", - "@typescript-eslint/parser": "^5.46.1", - "esbuild": "0.16.9", - "esbuild-plugin-alias": "^0.2.1", - "eslint": "^8.30.0", - "eslint-plugin-babel": "^5.3.1", - "esm-loader-typescript": "^1.0.1", - "events": "^3.3.0", - "jest": "^29.3.1", - "node-fetch": "2", - "ts-jest": "^29.0.3", - "tsd": "^0.22.0", - "typescript": "^4.9.4" - }, - "scripts": { - "build": "node build.js", - "pretest": "node build.js", - "test": "jest" - } -} diff --git a/src/lib/nostr-tools/pool.ts b/src/lib/nostr-tools/pool.ts index 72ac0882777..c95853db201 100644 --- a/src/lib/nostr-tools/pool.ts +++ b/src/lib/nostr-tools/pool.ts @@ -1,37 +1,14 @@ -import { Relay, relayInit } from "./relay"; +import { relayInit, SubscriptionOptions } from "./relay"; import { Filter } from "./filter"; import { Event } from "./event"; -// interface SimplePoolTypes { -// private _conn -// private _seenOn -// private eoseSubTimeout -// private getTimeout -// constructor(options?: {eoseSubTimeout?: number; getTimeout?: number}) -// close(relays: string[]): void -// ensureRelay(url: string): Promise -// sub(relays: string[], filters: Filter[], opts?: SubscriptionOptions): Sub -// get( -// relays: string[], -// filter: Filter, -// opts?: SubscriptionOptions -// ): Promise -// list( -// relays: string[], -// filters: Filter[], -// opts?: SubscriptionOptions -// ): Promise -// publish(relays: string[], event: Event): Pub -// seenOn(id: string): string[] -// } - export interface SimplePoolOptions { eoseSubTimeout?: number; getTimeout?: number; listTimeout?: number; } -function normalizeURL(url) { +function normalizeURL(url: string) { let p = new URL(url); p.pathname = p.pathname.replace(/\/+/g, "/"); if (p.pathname.endsWith("/")) p.pathname = p.pathname.slice(0, -1); @@ -43,23 +20,23 @@ function normalizeURL(url) { } export default class SimplePool { - _conn; + _conn: any; _seenOn = {}; - eoseSubTimeout; - getTimeout; - constructor(options = {}) { + eoseSubTimeout: number; + getTimeout: number; + constructor(options: SimplePoolOptions = {}) { this._conn = {}; this.eoseSubTimeout = options.eoseSubTimeout || 3400; this.getTimeout = options.getTimeout || 3400; } - close(relays) { - relays.forEach((url) => { + close(relays: string[]) { + relays.forEach((url: string) => { let relay = this._conn[normalizeURL(url)]; if (relay) relay.close(); }); } - async ensureRelay(url) { + async ensureRelay(url: string) { const nm = normalizeURL(url); if (!this._conn[nm]) { this._conn[nm] = relayInit(nm, { @@ -71,10 +48,10 @@ export default class SimplePool { await relay.connect(); return relay; } - sub(relays, filters, opts?) { + sub(relays: string[], filters: Filter[], opts?: SubscriptionOptions) { let _knownIds = /* @__PURE__ */ new Set(); let modifiedOpts = { ...(opts || {}) }; - modifiedOpts.alreadyHaveEvent = (id, url) => { + modifiedOpts.alreadyHaveEvent = (id: number, url: string) => { if (opts?.alreadyHaveEvent?.(id, url)) { return true; } @@ -83,16 +60,16 @@ export default class SimplePool { this._seenOn[id] = set; return _knownIds.has(id); }; - let subs = []; + let subs: any = []; let eventListeners = /* @__PURE__ */ new Set(); let eoseListeners = /* @__PURE__ */ new Set(); let eosesMissing = relays.length; let eoseSent = false; let eoseTimeout = setTimeout(() => { eoseSent = true; - for (let cb of eoseListeners.values()) cb(); + for (let cb of eoseListeners.values()) (cb as any)(); }, this.eoseSubTimeout); - relays.forEach(async (relay) => { + relays.forEach(async (relay: any) => { let r; try { r = await this.ensureRelay(relay); @@ -102,9 +79,9 @@ export default class SimplePool { } if (!r) return; let s = r.sub(filters, modifiedOpts); - s.on("event", (event) => { + s.on("event", (event: Event) => { _knownIds.add(event.id); - for (let cb of eventListeners.values()) cb(event); + for (let cb of eventListeners.values()) (cb as any)(event); }); s.on("eose", () => { if (eoseSent) return; @@ -115,26 +92,26 @@ export default class SimplePool { eosesMissing--; if (eosesMissing === 0) { clearTimeout(eoseTimeout); - for (let cb of eoseListeners.values()) cb(); + for (let cb of eoseListeners.values()) (cb as any)(); } } }); let greaterSub = { - sub(filters2, opts2) { - subs.forEach((sub) => sub.sub(filters2, opts2)); + sub(filters2: any, opts2: any) { + subs.forEach((sub: any) => sub.sub(filters2, opts2)); return greaterSub; }, unsub() { - subs.forEach((sub) => sub.unsub()); + subs.forEach((sub: any) => sub.unsub()); }, - on(type, cb) { + on(type: any, cb: any) { if (type === "event") { eventListeners.add(cb); } else if (type === "eose") { eoseListeners.add(cb); } }, - off(type, cb) { + off(type: any, cb: any) { if (type === "event") { eventListeners.delete(cb); } else if (type === "eose") eoseListeners.delete(cb); @@ -142,25 +119,25 @@ export default class SimplePool { }; return greaterSub; } - get(relays, filter, opts) { + get(relays: string[], filter: Filter, opts: SubscriptionOptions) { return new Promise((resolve) => { let sub = this.sub(relays, [filter], opts); let timeout = setTimeout(() => { sub.unsub(); resolve(null); }, this.getTimeout); - sub.on("event", (event) => { + sub.on("event", (event: Event) => { resolve(event); clearTimeout(timeout); sub.unsub(); }); }); } - list(relays, filters, opts) { + list(relays: string[], filters: Filter[], opts: SubscriptionOptions) { return new Promise((resolve) => { - let events = []; + let events: Event[] = []; let sub = this.sub(relays, filters, opts); - sub.on("event", (event) => { + sub.on("event", (event: Event) => { events.push(event); }); sub.on("eose", () => { @@ -169,8 +146,8 @@ export default class SimplePool { }); }); } - publish(relays, event) { - const pubPromises = relays.map(async (relay) => { + publish(relays: string[], event: Event) { + const pubPromises = relays.map(async (relay: string) => { let r; try { r = await this.ensureRelay(relay); @@ -181,16 +158,16 @@ export default class SimplePool { }); const callbackMap = /* @__PURE__ */ new Map(); return { - on(type, cb) { - relays.forEach(async (relay, i) => { + on(type: string, cb: { (): void; (): void; (arg0: any): any }) { + relays.forEach(async (relay: any, i: string | number) => { let pub = await pubPromises[i]; let callback = () => cb(relay); callbackMap.set(cb, callback); pub.on(type, callback); }); }, - off(type, cb) { - relays.forEach(async (_, i) => { + off(type: any, cb: any) { + relays.forEach(async (_: any, i: string | number) => { let callback = callbackMap.get(cb); if (callback) { let pub = await pubPromises[i]; @@ -200,7 +177,7 @@ export default class SimplePool { } }; } - seenOn(id) { + seenOn(id: string) { return Array.from(this._seenOn[id]?.values?.() || []); } } diff --git a/src/lib/nostr-tools/relay.ts b/src/lib/nostr-tools/relay.ts index af55a6aa166..69f65598069 100644 --- a/src/lib/nostr-tools/relay.ts +++ b/src/lib/nostr-tools/relay.ts @@ -29,297 +29,13 @@ export type Sub = { off: (type: "event" | "eose", cb: any) => void; }; -type SubscriptionOptions = { +export type SubscriptionOptions = { skipVerification?: boolean; id?: string; alreadyHaveEvent?: any; }; -// TODO allowAuthor fn so we can skip JSON.parse and sig verify if we won't allow the author anyway -// export function relayInit( -// url: string, -// alreadyHaveEvent?: (id: string) => boolean -// ): Relay { -// var ws: WebSocket -// var resolveClose: () => void -// var resolveOpen: (value: PromiseLike | void) => void -// var untilOpen = new Promise(resolve => { -// resolveOpen = resolve -// }) -// var openSubs: {[id: string]: {filters: Filter[]} & SubscriptionOptions} = {} -// var listeners: { -// connect: Array<() => void> -// disconnect: Array<() => void> -// error: Array<() => void> -// notice: Array<(msg: string) => void> -// auth: Array<(challenge: string) => void> -// } = { -// connect: [], -// disconnect: [], -// error: [], -// notice: [], -// auth: [] -// } -// var subListeners: { -// [subid: string]: { -// event: Array<(event: Event) => void> -// eose: Array<() => void> -// } -// } = {} -// var pubListeners: { -// [eventid: string]: { -// ok: Array<() => void> -// seen: Array<() => void> -// failed: Array<(reason: string) => void> -// } -// } = {} -// let idRegex = /"id":"([a-fA-F0-9]+)"/ - -// async function connectRelay(): Promise { -// return new Promise((resolve, reject) => { -// ws = new WebSocket(url) - -// ws.onopen = () => { -// listeners.connect.forEach(cb => cb()) -// resolveOpen() -// resolve() -// } -// ws.onerror = () => { -// listeners.error.forEach(cb => cb()) -// reject() -// } -// ws.onclose = async () => { -// listeners.disconnect.forEach(cb => cb()) -// resolveClose && resolveClose() -// } - -// let incomingMessageQueue: any[] = [] -// let handleNextInterval: any - -// const handleNext = () => { -// if (incomingMessageQueue.length === 0) { -// clearInterval(handleNextInterval) -// handleNextInterval = null -// return -// } - -// var data = incomingMessageQueue.shift() -// if (data && alreadyHaveEvent !== undefined) { -// const match = idRegex.exec(data) -// if (match) { -// const id = match[1] -// if (alreadyHaveEvent(id)) { -// //console.log(`already have`); -// return -// } -// } -// } -// try { -// data = JSON.parse(data) -// } catch (err) {} - -// if (data.length >= 1) { -// switch (data[0]) { -// case 'EVENT': -// if (data.length !== 3) return // ignore empty or malformed EVENT - -// let id = data[1] -// let event = data[2] -// if ( -// validateEvent(event) && -// openSubs[id] && -// (openSubs[id].skipVerification || verifySignature(event)) && -// matchFilters(openSubs[id].filters, event) -// ) { -// openSubs[id] -// ;(subListeners[id]?.event || []).forEach(cb => cb(event)) -// } -// return -// case 'EOSE': { -// if (data.length !== 2) return // ignore empty or malformed EOSE -// let id = data[1] -// ;(subListeners[id]?.eose || []).forEach(cb => cb()) -// return -// } -// case 'OK': { -// if (data.length < 3) return // ignore empty or malformed OK -// let id: string = data[1] -// let ok: boolean = data[2] -// let reason: string = data[3] || '' -// if (ok) pubListeners[id]?.ok.forEach(cb => cb()) -// else pubListeners[id]?.failed.forEach(cb => cb(reason)) -// return -// } -// case 'NOTICE': -// if (data.length !== 2) return // ignore empty or malformed NOTICE -// let notice = data[1] -// listeners.notice.forEach(cb => cb(notice)) -// return -// case 'AUTH': { -// let challenge = data[1] -// listeners.auth?.forEach(cb => cb(challenge)) -// return -// } -// } -// } -// } - -// ws.onmessage = e => { -// incomingMessageQueue.push(e.data) -// if (!handleNextInterval) { -// handleNextInterval = setInterval(handleNext, 0) -// } -// } -// }) -// } - -// async function connect(): Promise { -// if (ws?.readyState && ws.readyState === 1) return // ws already open -// await connectRelay() -// } - -// async function trySend(params: [string, ...any]) { -// let msg = JSON.stringify(params) - -// await untilOpen -// try { -// ws.send(msg) -// } catch (err) { -// console.log(err) -// } -// } - -// const sub = ( -// filters: Filter[], -// { -// skipVerification = false, -// id = Math.random().toString().slice(2) -// }: SubscriptionOptions = {} -// ): Sub => { -// let subid = id - -// openSubs[subid] = { -// id: subid, -// filters, -// skipVerification -// } -// trySend(['REQ', subid, ...filters]) - -// return { -// sub: (newFilters, newOpts = {}) => -// sub(newFilters || filters, { -// skipVerification: newOpts.skipVerification || skipVerification, -// id: subid -// }), -// unsub: () => { -// delete openSubs[subid] -// delete subListeners[subid] -// trySend(['CLOSE', subid]) -// }, -// on: (type: 'event' | 'eose', cb: any): void => { -// subListeners[subid] = subListeners[subid] || { -// event: [], -// eose: [] -// } -// subListeners[subid][type].push(cb) -// }, -// off: (type: 'event' | 'eose', cb: any): void => { -// let listeners = subListeners[subid] -// let idx = listeners[type].indexOf(cb) -// if (idx >= 0) listeners[type].splice(idx, 1) -// } -// } -// } - -// function _publishEvent(event: Event, type: string) { -// if (!event.id) throw new Error(`event ${event} has no id`) -// let id = event.id - -// var sent = false -// var mustMonitor = false - -// trySend([type, event]) -// .then(() => { -// sent = true -// if (mustMonitor) { -// startMonitoring() -// mustMonitor = false -// } -// }) -// .catch(() => {}) - -// const startMonitoring = () => { -// let monitor = sub([{ids: [id]}], { -// id: `monitor-${id.slice(0, 5)}` -// }) -// let willUnsub = setTimeout(() => { -// ;(pubListeners[id]?.failed || []).forEach(cb => -// cb('event not seen after 5 seconds') -// ) -// monitor.unsub() -// }, 5000) -// monitor.on('event', () => { -// clearTimeout(willUnsub) -// ;(pubListeners[id]?.seen || []).forEach(cb => cb()) -// }) -// } - -// return { -// on: (type: 'ok' | 'seen' | 'failed', cb: any) => { -// pubListeners[id] = pubListeners[id] || { -// ok: [], -// seen: [], -// failed: [] -// } -// pubListeners[id][type].push(cb) - -// if (type === 'seen') { -// if (sent) startMonitoring() -// else mustMonitor = true -// } -// }, -// off: (type: 'ok' | 'seen' | 'failed', cb: any) => { -// let listeners = pubListeners[id] -// if (!listeners) return -// let idx = listeners[type].indexOf(cb) -// if (idx >= 0) listeners[type].splice(idx, 1) -// } -// } -// } - -// return { -// url, -// sub, -// on: (type: RelayEvent, cb: any): void => { -// listeners[type].push(cb) -// if (type === 'connect' && ws?.readyState === 1) { -// cb() -// } -// }, -// off: (type: RelayEvent, cb: any): void => { -// let index = listeners[type].indexOf(cb) -// if (index !== -1) listeners[type].splice(index, 1) -// }, -// publish(event: Event): Pub { -// return _publishEvent(event, 'EVENT') -// }, -// auth(event: Event): Pub { -// return _publishEvent(event, 'AUTH') -// }, -// connect, -// close(): Promise { -// ws.close() -// return new Promise(resolve => { -// resolveClose = resolve -// }) -// }, -// get status() { -// return ws?.readyState ?? 3 -// } -// } -// } - -function getSubscriptionId(json) { +function getSubscriptionId(json: string | any[]) { let idx = json.slice(0, 22).indexOf(`"EVENT"`); if (idx === -1) return null; let pstart = json.slice(idx + 7 + 1).indexOf(`"`); @@ -331,7 +47,7 @@ function getSubscriptionId(json) { return json.slice(start + 1, end); } -function getHex64(json, field) { +function getHex64(json: string | string[], field: string | any[]) { let len = field.length + 3; let idx = json.indexOf(`"${field}":`) + len; let s = json.slice(idx).indexOf(`"`) + idx + 1; @@ -340,7 +56,7 @@ function getHex64(json, field) { export function relayInit(url: string, options: SimplePoolOptions = {}) { let { listTimeout = 3e3, getTimeout = 3e3 } = options; - var ws; + var ws: WebSocket; var openSubs = {}; var listeners = { connect: [], @@ -350,7 +66,7 @@ export function relayInit(url: string, options: SimplePoolOptions = {}) { }; var subListeners = {}; var pubListeners = {}; - var connectionPromise; + var connectionPromise: Promise | undefined; async function connectRelay() { if (connectionPromise) return connectionPromise; connectionPromise = new Promise((resolve, reject) => { @@ -360,20 +76,20 @@ export function relayInit(url: string, options: SimplePoolOptions = {}) { reject(err); } ws.onopen = () => { - listeners.connect.forEach((cb) => cb()); + listeners.connect.forEach((cb: any) => cb()); resolve(); }; ws.onerror = () => { connectionPromise = void 0; - listeners.error.forEach((cb) => cb()); + listeners.error.forEach((cb: any) => cb()); reject(); }; ws.onclose = async () => { connectionPromise = void 0; - listeners.disconnect.forEach((cb) => cb()); + listeners.disconnect.forEach((cb: any) => cb()); }; - let incomingMessageQueue = []; - let handleNextInterval; + let incomingMessageQueue: any[] = []; + let handleNextInterval: any; ws.onmessage = (e) => { incomingMessageQueue.push(e.data); if (!handleNextInterval) { @@ -388,7 +104,7 @@ export function relayInit(url: string, options: SimplePoolOptions = {}) { } var json = incomingMessageQueue.shift(); if (!json) return; - let subid = getSubscriptionId(json); + let subid: any = getSubscriptionId(json); if (subid) { let so = openSubs[subid]; if (so && so.alreadyHaveEvent && so.alreadyHaveEvent(getHex64(json, "id"), url)) { @@ -407,13 +123,13 @@ export function relayInit(url: string, options: SimplePoolOptions = {}) { (openSubs[id].skipVerification || (await verifySignature(event))) ) { openSubs[id]; - (subListeners[id]?.event || []).forEach((cb) => cb(event)); + (subListeners[id]?.event || []).forEach((cb: any) => cb(event)); } return; case "EOSE": { let id2 = data[1]; if (id2 in subListeners) { - subListeners[id2].eose.forEach((cb) => cb()); + subListeners[id2].eose.forEach((cb: any) => cb()); subListeners[id2].eose = []; } return; @@ -423,8 +139,8 @@ export function relayInit(url: string, options: SimplePoolOptions = {}) { let ok = data[2]; let reason = data[3] || ""; if (id2 in pubListeners) { - if (ok) pubListeners[id2].ok.forEach((cb) => cb()); - else pubListeners[id2].failed.forEach((cb) => cb(reason)); + if (ok) pubListeners[id2].ok.forEach((cb: any) => cb()); + else pubListeners[id2].failed.forEach((cb: any) => cb(reason)); pubListeners[id2].ok = []; pubListeners[id2].failed = []; } @@ -432,7 +148,7 @@ export function relayInit(url: string, options: SimplePoolOptions = {}) { } case "NOTICE": let notice = data[1]; - listeners.notice.forEach((cb) => cb(notice)); + listeners.notice.forEach((cb: any) => cb(notice)); return; } } catch (err) { @@ -449,7 +165,7 @@ export function relayInit(url: string, options: SimplePoolOptions = {}) { if (connected()) return; await connectRelay(); } - async function trySend(params) { + async function trySend(params: any) { let msg = JSON.stringify(params); if (!connected()) { await new Promise((resolve) => setTimeout(resolve, 1e3)); @@ -464,7 +180,7 @@ export function relayInit(url: string, options: SimplePoolOptions = {}) { } } const sub = ( - filters, + filters: any[], { skipVerification = false, alreadyHaveEvent = null, @@ -480,7 +196,7 @@ export function relayInit(url: string, options: SimplePoolOptions = {}) { }; trySend(["REQ", subid, ...filters]); return { - sub: (newFilters, newOpts: SubscriptionOptions = {}) => + sub: (newFilters: any, newOpts: SubscriptionOptions = {}) => sub(newFilters || filters, { skipVerification: newOpts.skipVerification || skipVerification, alreadyHaveEvent: newOpts.alreadyHaveEvent || alreadyHaveEvent, @@ -491,14 +207,14 @@ export function relayInit(url: string, options: SimplePoolOptions = {}) { delete subListeners[subid]; trySend(["CLOSE", subid]); }, - on: (type, cb) => { + on: (type: string | number, cb: any) => { subListeners[subid] = subListeners[subid] || { event: [], eose: [] }; subListeners[subid][type].push(cb); }, - off: (type, cb) => { + off: (type: string | number, cb: any) => { let listeners2 = subListeners[subid]; let idx = listeners2[type].indexOf(cb); if (idx >= 0) listeners2[type].splice(idx, 1); @@ -508,20 +224,29 @@ export function relayInit(url: string, options: SimplePoolOptions = {}) { return { url, sub, - on: (type, cb) => { + on: (type: string, cb: () => void) => { listeners[type].push(cb); if (type === "connect" && ws?.readyState === 1) { cb(); } }, - off: (type, cb) => { + off: (type: string | number, cb: any) => { let index = listeners[type].indexOf(cb); if (index !== -1) listeners[type].splice(index, 1); }, - list: (filters, opts) => + list: ( + filters: any[], + opts: + | { + skipVerification?: boolean | undefined; + alreadyHaveEvent?: null | undefined; + id?: string | undefined; + } + | undefined + ) => new Promise((resolve) => { let s = sub(filters, opts); - let events = []; + let events: any = []; let timeout = setTimeout(() => { s.unsub(); resolve(events); @@ -531,36 +256,45 @@ export function relayInit(url: string, options: SimplePoolOptions = {}) { clearTimeout(timeout); resolve(events); }); - s.on("event", (event) => { + s.on("event", (event: any) => { events.push(event); }); }), - get: (filter, opts) => + get: ( + filter: any, + opts: + | { + skipVerification?: boolean | undefined; + alreadyHaveEvent?: null | undefined; + id?: string | undefined; + } + | undefined + ) => new Promise((resolve) => { let s = sub([filter], opts); let timeout = setTimeout(() => { s.unsub(); resolve(null); }, getTimeout); - s.on("event", (event) => { + s.on("event", (event: any) => { s.unsub(); clearTimeout(timeout); resolve(event); }); }), - publish(event) { + publish(event: any) { if (!event.id) throw new Error(`event ${event} has no id`); let id = event.id; trySend(["EVENT", event]); return { - on: (type, cb) => { + on: (type: string | number, cb: any) => { pubListeners[id] = pubListeners[id] || { ok: [], failed: [] }; pubListeners[id][type].push(cb); }, - off: (type, cb) => { + off: (type: string | number, cb: any) => { let listeners2 = pubListeners[id]; if (!listeners2) return; let idx = listeners2[type].indexOf(cb); diff --git a/src/lib/nostr-tools/tsconfig.json b/src/lib/nostr-tools/tsconfig.json deleted file mode 100644 index 22bbdb23d4d..00000000000 --- a/src/lib/nostr-tools/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "module": "esnext", - "target": "esnext", - "lib": ["dom", "dom.iterable", "esnext"], - "declaration": true, - "strict": true, - "moduleResolution": "node", - "skipLibCheck": true, - "esModuleInterop": true, - "emitDeclarationOnly": true, - "outDir": "dist", - "rootDir": "." - } -} diff --git a/src/providers/message-provider.tsx b/src/providers/message-provider.tsx index 58daae77edf..b7588af1de9 100644 --- a/src/providers/message-provider.tsx +++ b/src/providers/message-provider.tsx @@ -100,7 +100,7 @@ const MessageProvider = (props: Props) => { const append = data.filter( (x) => props.chat.directMessages.find((y) => y.id === x.id) === undefined ); - + raven?.loadProfiles(append.map((x) => x.peer)); const result = [...props.chat.directMessages, ...append]; props.addDirectMessages(result); @@ -116,7 +116,6 @@ const MessageProvider = (props: Props) => { const handleProfileUpdate = (data: Profile[]) => { const result = [...props.chat.directContacts]; - data.forEach(({ name, creator }) => { const isPresent = props.chat.directContacts.some( (obj) => obj.name === name && obj.creator === creator @@ -138,6 +137,14 @@ const MessageProvider = (props: Props) => { }; }, [raven]); + useEffect(() => { + return () => { + raven?.removeListener(RavenEvents.Ready, handleReadyState); + raven?.removeListener(RavenEvents.ProfileUpdate, handleProfileUpdate); + raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); + }; + }, [raven]); + return <>{}; }; From 56ec652ef43eea98f11f66dd43478281bb2eb71d Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Wed, 21 Jun 2023 18:50:07 +0500 Subject: [PATCH 014/179] Remove extra code --- src/common/helper/message-helper.ts | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/common/helper/message-helper.ts b/src/common/helper/message-helper.ts index 0c6449a9012..9dcef190871 100644 --- a/src/common/helper/message-helper.ts +++ b/src/common/helper/message-helper.ts @@ -31,27 +31,13 @@ const relays = { export enum RavenEvents { Ready = "ready", ProfileUpdate = "profile_update", - ChannelCreation = "channel_creation", - ChannelUpdate = "channel_update", - EventDeletion = "event_deletion", - PublicMessage = "public_message", - DirectMessage = "direct_message", - ChannelMessageHide = "channel_message_hide", - ChannelUserMute = "channel_user_mute", - MuteList = "mute_list" + DirectMessage = "direct_message" } type EventHandlerMap = { [RavenEvents.Ready]: () => void; [RavenEvents.ProfileUpdate]: (data: Profile[]) => void; - [RavenEvents.ChannelCreation]: (data: Channel[]) => void; - [RavenEvents.ChannelUpdate]: (data: ChannelUpdate[]) => void; - [RavenEvents.EventDeletion]: (data: EventDeletion[]) => void; - [RavenEvents.PublicMessage]: (data: PublicMessage[]) => void; [RavenEvents.DirectMessage]: (data: DirectMessage[]) => void; - [RavenEvents.ChannelMessageHide]: (data: ChannelMessageHide[]) => void; - [RavenEvents.ChannelUserMute]: (data: ChannelUserMute[]) => void; - [RavenEvents.MuteList]: (data: MuteList) => void; }; class Raven extends TypedEventEmitter { From 4e4f407c871494630de0fe1dce342128c5bbd2aa Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Thu, 6 Jul 2023 11:17:14 +0500 Subject: [PATCH 015/179] Optimize the chat --- src/common/components/chat-box/index.tsx | 51 +++--- src/common/components/login/index.tsx | 4 +- src/common/helper/chat-utils.ts | 3 +- src/common/helper/message-helper.ts | 205 +++++++++++------------ src/common/store/chat/index.ts | 34 ++-- src/common/store/chat/types.ts | 10 +- src/providers/message-provider-types.ts | 2 + src/providers/message-provider.tsx | 93 ++++++---- 8 files changed, 230 insertions(+), 172 deletions(-) diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index ba3fb063500..cd40bb9dbff 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -5,7 +5,6 @@ import { Link } from "react-router-dom"; import { ActiveUser } from "../../store/active-user/types"; import { Chat, DirectContactsType } from "../../store/chat/types"; import { DirectMessage } from "../../../providers/message-provider-types"; -// import { useMappedStore } from "../../store/use-mapped-store"; import Tooltip from "../tooltip"; import UserAvatar from "../user-avatar"; @@ -31,7 +30,7 @@ import { getProfileMetaData, NostrKeys, setProfileMetaData, - correctProfile + resetProfile } from "../../helper/chat-utils"; import { _t } from "../../i18n"; import { getAccountFull } from "../../api/hive"; @@ -69,16 +68,18 @@ export default function ChatBox(props: Props) { const [showSpinner, setShowSpinner] = useState(false); const [messagesList, setMessagesList] = useState([]); const [isUserFromSearch, setIsUserFromSearch] = useState(false); + const [isCurrentUserJoined, setIsCurrentUserJoined] = useState(true); useEffect(() => { - // correctProfile(props.activeUser); + // resetProfile(props.activeUser); fetchProfileData(); setShow(!!props.activeUser?.username); }, []); useEffect(() => { - const msgsList = getDirectMessages(props.chat.directMessages, receiverPubKey!); - setMessagesList(msgsList); + const msgsList = fetchMessages(receiverPubKey!); + const messages = msgsList.sort((a, b) => a.created - b.created); + setMessagesList(messages); }, [props.chat.directMessages]); useEffect(() => { @@ -94,21 +95,24 @@ export default function ChatBox(props: Props) { useEffect(() => { fetchProfileData(); setShow(!!props.activeUser?.username); - const msgsList = getDirectMessages(props.chat.directMessages, receiverPubKey!); - setMessagesList(msgsList); + const msgsList = fetchMessages(receiverPubKey!); + const messages = msgsList.sort((a, b) => a.created - b.created); + setMessagesList(messages); }, [props.activeUser]); useEffect(() => { if (currentUser) { fetchCurrentUserData(); - const peer = props.chat.directContacts.find((x) => x.name === currentUser)?.creator ?? null; + const peer = props.chat.directContacts.find((x) => x.name === currentUser)?.pubkey ?? null; setReceiverPubKey(peer!); - const msgsList = getDirectMessages(props.chat.directMessages, peer!); - setMessagesList(msgsList); + const msgsList = fetchMessages(peer!); + const messages = msgsList.sort((a, b) => a.created - b.created); + setMessagesList(messages); if (!window.raven) { setNostrkeys(activeUserKeys!); } } else { + setIsCurrentUserJoined(true); setMessage(""); setShowSpinner(false); } @@ -125,6 +129,15 @@ export default function ChatBox(props: Props) { return count; }; + const fetchMessages = (peer: string) => { + for (const item of props.chat.directMessages) { + if (item.peer === peer) { + return item.chat; + } + } + return []; + }; + const fetchCurrentUserData = async () => { setShowSpinner(true); const response = await getAccountFull(currentUser); @@ -133,8 +146,11 @@ export default function ChatBox(props: Props) { about: response.profile?.about, followers: response.follow_stats?.follower_count }); - const currentUserProfile = await getProfileMetaData(currentUser); - setReceiverPubKey(currentUserProfile?.noStrKey?.pub); + const { posting_json_metadata } = response; + const profile = JSON.parse(posting_json_metadata!).profile; + const { noStrKey } = profile || {}; + setReceiverPubKey(noStrKey?.pub); + setIsCurrentUserJoined(!!noStrKey?.pub); setShowSpinner(false); }; @@ -156,7 +172,6 @@ export default function ChatBox(props: Props) { setCurrentUser(username); setExpanded(true); setIsCurrentUser(true); - // fetchCurrentUserData(); setIsUserFromSearch(true); }; @@ -172,7 +187,7 @@ export default function ChatBox(props: Props) { window.raven?.sendDirectMessage(receiverPubKey, message); } if (isUserFromSearch && receiverPubKey) { - window.raven?.addDirectContact(receiverPubKey); + window.raven?.publishContacts(currentUser, receiverPubKey); setIsUserFromSearch(false); } }; @@ -183,8 +198,7 @@ export default function ChatBox(props: Props) { const isScrollToTop = !isCurrentUser && element.scrollTop >= srollHeight; const isScrollToBottom = isCurrentUser && - element.scrollTop <= (element.scrollHeight / 100) * 50 && - element.scrollHeight > 700; + element.scrollTop + chatBodyDivRef?.current?.clientHeight! < element.scrollHeight; setIsScrollToTop(isScrollToTop); setIsScrollToBottom(isScrollToBottom); }; @@ -219,7 +233,6 @@ export default function ChatBox(props: Props) { setHasUserJoinedChat(true); setNostrkeys(keys); window.raven?.updateProfile({ name: props.activeUser?.username!, about: "", picture: "" }); - // fetchProfileData(); setActiveUserKeys(keys); setInProgress(false); }; @@ -329,7 +342,7 @@ export default function ChatBox(props: Props) {
- {receiverPubKey === null || receiverPubKey === undefined ? ( + {!isCurrentUserJoined ? (

{_t("chat.not-joined")}

) : ( <> @@ -378,7 +391,7 @@ export default function ChatBox(props: Props) { {props.chat.directContacts.map((user: DirectContactsType) => { return ( -
+
diff --git a/src/common/components/login/index.tsx b/src/common/components/login/index.tsx index ee0ad3314d5..351541d06bc 100644 --- a/src/common/components/login/index.tsx +++ b/src/common/components/login/index.tsx @@ -773,7 +773,9 @@ class LoginDialog extends Component { resetChat(); //create new raven instance for newly logged in user - setNostrkeys(profile.noStrKey); + if (profile && profile.noStrKey) { + setNostrkeys(profile.noStrKey); + } // activate user setActiveUser(user.username); diff --git a/src/common/helper/chat-utils.ts b/src/common/helper/chat-utils.ts index 6ac87fdd8a1..9530da60353 100644 --- a/src/common/helper/chat-utils.ts +++ b/src/common/helper/chat-utils.ts @@ -20,7 +20,7 @@ export const getProfileMetaData = async (username: string) => { } }; -export const correctProfile = async (activeUser: ActiveUser | null) => { +export const resetProfile = async (activeUser: ActiveUser | null) => { const profile = await getProfileMetaData(activeUser?.username!); delete profile.noStrKey; delete profile.profile; @@ -37,7 +37,6 @@ export const correctProfile = async (activeUser: ActiveUser | null) => { version: 2 }; const updatedProfile = await updateProfile(response, { ...profileC }); - console.log(updatedProfile); }; export const setProfileMetaData = async (activeUser: ActiveUser | null, keys: NostrKeys) => { diff --git a/src/common/helper/message-helper.ts b/src/common/helper/message-helper.ts index 9dcef190871..9c5a64b192c 100644 --- a/src/common/helper/message-helper.ts +++ b/src/common/helper/message-helper.ts @@ -3,17 +3,10 @@ import { Kind } from "../../lib/nostr-tools/event"; import { Filter } from "../../lib/nostr-tools/filter"; import { TypedEventEmitter } from "./message-event-emitter"; import { - Channel, - ChannelMessageHide, - ChannelUpdate, - ChannelUserMute, + DirectContact, DirectMessage, - EventDeletion, Keys, - Metadata, - MuteList, - Profile, - PublicMessage + Metadata } from "../../providers/message-provider-types"; import { encrypt, decrypt } from "../../lib/nostr-tools/nip04"; import SimplePool from "../../lib/nostr-tools/pool"; @@ -30,14 +23,14 @@ const relays = { export enum RavenEvents { Ready = "ready", - ProfileUpdate = "profile_update", - DirectMessage = "direct_message" + DirectMessage = "direct_message", + DirectContact = "direct_contact" } type EventHandlerMap = { [RavenEvents.Ready]: () => void; - [RavenEvents.ProfileUpdate]: (data: Profile[]) => void; [RavenEvents.DirectMessage]: (data: DirectMessage[]) => void; + [RavenEvents.DirectContact]: (data: DirectContact[]) => void; }; class Raven extends TypedEventEmitter { @@ -54,8 +47,7 @@ class Raven extends TypedEventEmitter { private eventQueueTimer: any; private eventQueueFlag = true; private eventQueueBuffer: Event[] = []; - public directContacts: string[] = []; - private isActiveUserPushed: boolean = false; + public directContacts: any = []; private nameCache: Record = {}; @@ -81,66 +73,86 @@ class Raven extends TypedEventEmitter { const filters: Filter[] = [ { - kinds: [Kind.Metadata], + kinds: [Kind.Contacts], authors: [this.pub] - }, - { - kinds: [Kind.EncryptedDirectMessage], - authors: [this.pub] - }, - { - kinds: [Kind.EncryptedDirectMessage], - "#p": [this.pub] } ]; this.fetchP(filters).then((resp) => { const events = resp.sort((a, b) => b.created_at - a.created_at); - const profile = events.find((x) => x.kind === Kind.Metadata); - if (profile) this.pushToEventBuffer(profile); - const directContacts = Array.from( - new Set( - events.map((x) => { - if (x.kind === Kind.EncryptedDirectMessage) { - const receiver = Raven.findTagValue(x, "p"); - if (!receiver) return undefined; - return receiver === this.pub ? x.pubkey : receiver; - } - return undefined; - }) - ) - ).filter(notEmpty); - - this.directContacts.push(...directContacts); + const profile = events.find((x) => x.kind === Kind.Contacts); + if (profile && profile?.tags.length !== 0) { + this.directContacts = profile?.tags; + this.pushToEventBuffer(profile!); + } + this.fetchMessages(); + this.emit(RavenEvents.Ready); + }); + } + public fetchMessages() { + this.directContacts.map((contact: any) => { const filters: Filter[] = [ { - kinds: [Kind.Metadata], - authors: directContacts + kinds: [Kind.EncryptedDirectMessage], + "#p": [contact[0]], + authors: [this.pub] }, - ...directContacts.map((x) => ({ + { kinds: [Kind.EncryptedDirectMessage], "#p": [this.pub], - authors: [x] - })), - ...directContacts.map((x) => ({ - kinds: [Kind.EncryptedDirectMessage], - "#p": [x], - authors: [this.pub] - })) + authors: [contact[0]] + } ]; - filters.forEach((c) => { - this.fetch([c]); + this.fetch(filters); + }); + } + + public publishContacts(username: string, pubkey: string) { + const newUser = [pubkey, username]; + newUser.forEach((element) => { + let nameExist = false; + + // Check if the name exists in the first array + this.directContacts.forEach((existingElement: string[]) => { + if (existingElement[0] === element || existingElement[1] === element) { + nameExist = true; + return; + } }); - this.emit(RavenEvents.Ready); + + // If the name doesn't exist, add the element to the direct contacts array + if (!nameExist) { + this.directContacts.push(newUser); + this.publish(Kind.Contacts, this.directContacts, ""); + return; + } }); } - public loadProfiles(pubs: string[]) { + public getContacts() { + const filters: Filter[] = [ + { + kinds: [Kind.Contacts], + authors: [this.pub] + } + ]; + this.fetch(filters); + } + + public checkProfiles(pubs: string[]) { pubs.forEach((p) => { - if (!this.directContacts.includes(p)) { - if (p !== this.pub) { - this.directContacts.push(p); - } + if (this.directContacts.length !== 0) { + this.directContacts.forEach((c: any) => { + if (p !== c[0]) { + this.fetch([ + { + kinds: [Kind.Metadata], + authors: [p] + } + ]); + } + }); + } else { this.fetch([ { kinds: [Kind.Metadata], @@ -151,45 +163,31 @@ class Raven extends TypedEventEmitter { }); } - public directContactForActiveUser(event: Event) { - if (!this.isActiveUserPushed && !this.directContacts.includes(event.pubkey)) { - const activeUserProfile = { - id: event.id, - creator: event.pubkey, - created: event.created_at, - name: JSON.parse(event.content).name, - about: JSON.parse(event.content).about || "", - picture: JSON.parse(event.content).picture || "" - }; - - this.emit(RavenEvents.ProfileUpdate, [activeUserProfile]); - this.isActiveUserPushed = true; - this.directContacts.push(event.pubkey); - } - } - - public addDirectContact(directContact: string) { - const filters: Filter[] = [ - { - kinds: [Kind.Metadata], - authors: [directContact] - } - ]; - - if (!this.directContacts.includes(directContact) && this.pub !== directContact) { - this.directContacts.push(directContact); + private addDirectContact(event: Event) { + let isAdded = false; + const user = { + id: event.id, + creator: event.pubkey, + created: event.created_at, + name: JSON.parse(event.content).name, + about: JSON.parse(event.content).about || "", + picture: JSON.parse(event.content).picture || "" + }; + const { creator, name } = user; + if (!isAdded) { + this.publishContacts(name, creator); + isAdded = true; } - this.fetch(filters); } private fetch(filters: Filter[], unsub: boolean = true) { const sub = this.pool.sub(this.readRelays, filters); sub.on("event", (event: Event) => { - this.pushToEventBuffer(event); - if (event.kind === Kind.Metadata && event.pubkey === this.pub) { - this.directContactForActiveUser(event); + if (event.kind === Kind.Metadata) { + this.addDirectContact(event); } + this.pushToEventBuffer(event); }); sub.on("eose", () => { @@ -284,6 +282,9 @@ class Raven extends TypedEventEmitter { const pub = this.pool.publish(this.writeRelays, event); pub.on("ok", () => { resolve(event); + if (event.kind === Kind.Contacts) { + this.getContacts(); + } }); pub.on("failed", () => { @@ -331,28 +332,20 @@ class Raven extends TypedEventEmitter { async processEventQueue() { this.eventQueueFlag = false; - - const profileUpdates: Profile[] = this.eventQueue - .filter((x) => x.kind === Kind.Metadata) - .map((ev) => { - const content = Raven.parseJson(ev.content); - return content - ? { - id: ev.id, - creator: ev.pubkey, - created: ev.created_at, - ...Raven.normalizeMetadata(content) - } - : null; + const data = this.eventQueue + .filter((x) => x.kind === Kind.Contacts) + .map((e: any) => { + const profiles: Array<[string, string]> = e.tags; + return profiles.map(([pubkey, name]) => ({ pubkey, name })); }) .filter(notEmpty); - const directContacts = profileUpdates.filter((obj) => - this.directContacts.includes(obj.creator) - ); + if (data.length > 0) { + const directContactsProfile: Array<{ pubkey: string; name: string }> = data[0]; - if (profileUpdates.length > 0) { - this.emit(RavenEvents.ProfileUpdate, directContacts); + if (directContactsProfile.length > 0) { + this.emit(RavenEvents.DirectContact, directContactsProfile); + } } Promise.all( diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts index 55af8c96eb6..8eccd7a3b5f 100644 --- a/src/common/store/chat/index.ts +++ b/src/common/store/chat/index.ts @@ -8,7 +8,8 @@ import { DirectContactsAction, DirectContactsType, DirectMessagesAction, - ResetChatAction + ResetChatAction, + directMessagesList } from "./types"; export const initialState: Chat = { @@ -20,19 +21,30 @@ export default (state: Chat = initialState, action: Actions): Chat => { switch (action.type) { case ActionTypes.DIRECTCONTACTS: { const { data } = action; - const uniqueDirectContacts = data.filter( - (contact) => !state.directContacts.includes(contact) - ); + const uniqueDirectContacts = data.filter((contact) => { + return !state.directContacts.some((existingContact) => { + return existingContact.name === contact.name && existingContact.pubkey === contact.pubkey; + }); + }); + // console.log(uniqueDirectContacts, "uniqueDirectContacts"); return { ...state, - directContacts: [...state.directContacts, ...uniqueDirectContacts] + directContacts: [...state.directContacts, ...uniqueDirectContacts], + directMessages: [ + ...state.directMessages, + ...uniqueDirectContacts.map((contact) => ({ chat: [], peer: contact.pubkey })) + ] }; } case ActionTypes.DIRECTMESSAGES: { - const { data } = action; + const { peer, data } = action; return { ...state, - directMessages: [...state.directMessages, ...data] + directMessages: [ + ...state.directMessages.map((contact) => + contact.peer === peer ? { peer: peer, chat: [...contact.chat, ...data] } : contact + ) + ] }; } @@ -49,13 +61,14 @@ export const addDirectContacts = (data: DirectContactsType[]) => (dispatch: Disp dispatch(addDirectContactsAct(data)); }; -export const addDirectMessages = (data: DirectMessage[]) => (dispatch: Dispatch) => { - dispatch(addDirectMessagesAct(data)); +export const addDirectMessages = (peer: string, data: DirectMessage[]) => (dispatch: Dispatch) => { + dispatch(addDirectMessagesAct(peer, data)); }; export const resetChat = () => (dispatch: Dispatch) => { dispatch(resetChatAct()); }; + /* Action Creators */ export const addDirectContactsAct = (data: DirectContactsType[]): DirectContactsAction => { @@ -65,9 +78,10 @@ export const addDirectContactsAct = (data: DirectContactsType[]): DirectContacts }; }; -export const addDirectMessagesAct = (data: DirectMessage[]): DirectMessagesAction => { +export const addDirectMessagesAct = (peer: string, data: DirectMessage[]): DirectMessagesAction => { return { type: ActionTypes.DIRECTMESSAGES, + peer, data }; }; diff --git a/src/common/store/chat/types.ts b/src/common/store/chat/types.ts index 57e6c3ddded..cbd48ae10bf 100644 --- a/src/common/store/chat/types.ts +++ b/src/common/store/chat/types.ts @@ -2,12 +2,17 @@ import { DirectMessage } from "./../../../providers/message-provider-types"; export interface DirectContactsType { name: string; - creator: string; + pubkey: string; +} + +export interface directMessagesList { + peer: string; + chat: DirectMessage[]; } export interface Chat { directContacts: DirectContactsType[]; - directMessages: DirectMessage[]; + directMessages: directMessagesList[]; } export enum ActionTypes { @@ -28,6 +33,7 @@ export interface ResetChatAction { export interface DirectMessagesAction { type: ActionTypes.DIRECTMESSAGES; data: DirectMessage[]; + peer: string; } export type Actions = DirectContactsAction | DirectMessagesAction | ResetChatAction; diff --git a/src/providers/message-provider-types.ts b/src/providers/message-provider-types.ts index a553c7d16c2..2a71c9d2149 100644 --- a/src/providers/message-provider-types.ts +++ b/src/providers/message-provider-types.ts @@ -17,6 +17,8 @@ export type Metadata = { export type Profile = { id: string; creator: string; created: number } & Metadata; +export type DirectContact = { name: string; pubkey: string }; + export type Channel = { id: string; creator: string; created: number } & Metadata; export type ChannelUpdate = { channelId: string } & Channel; diff --git a/src/providers/message-provider.tsx b/src/providers/message-provider.tsx index b7588af1de9..7a8bc98e157 100644 --- a/src/providers/message-provider.tsx +++ b/src/providers/message-provider.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import { ActiveUser } from "../common/store/active-user/types"; import { Chat, DirectContactsType } from "../common/store/chat/types"; -import { DirectMessage, Profile, Keys } from "./message-provider-types"; +import { DirectMessage, Profile, Keys, DirectContact } from "./message-provider-types"; import { initRaven, RavenEvents } from "../common/helper/message-helper"; import { getProfileMetaData, NostrKeys } from "../common/helper/chat-utils"; @@ -19,18 +19,17 @@ export const setNostrkeys = (keys: NostrKeys) => { }; interface Props { - activeUser: ActiveUser; - chat: Chat; addDirectContacts: (data?: DirectContactsType[]) => void; - addDirectMessages: (data?: DirectMessage[]) => void; + addDirectMessages: (peer: string, data?: DirectMessage[]) => void; } const MessageProvider = (props: Props) => { - const { activeUser } = useMappedStore(); + const { activeUser, chat } = useMappedStore(); const [ravenReady, setRavenReady] = useState(false); const [since, setSince] = useState(0); const [keys, setKeys] = useState(); const [raven, setRaven] = useState(); + const [messageBuffer, setMessageBuffer] = useState([]); useEffect(() => { window.addEventListener("createRavenInstance", createRavenInstance); @@ -95,52 +94,82 @@ const MessageProvider = (props: Props) => { }; }, [ravenReady, raven]); - // // Direct message handler - const handleDirectMessage = (data: DirectMessage[]) => { - const append = data.filter( - (x) => props.chat.directMessages.find((y) => y.id === x.id) === undefined - ); - raven?.loadProfiles(append.map((x) => x.peer)); - const result = [...props.chat.directMessages, ...append]; - - props.addDirectMessages(result); + const handleProfileUpdate = (data: DirectContact[]) => { + const result = [...chat.directContacts]; + data.forEach(({ name, pubkey }) => { + const isPresent = chat.directContacts.some( + (obj) => obj.name === name && obj.pubkey === pubkey + ); + if (!isPresent) { + //Add the object to the result array if it's not already present in store state. + result.push({ name, pubkey }); + } + }); + if (result.length !== 0) { + props.addDirectContacts(result); + } }; useEffect(() => { - raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); - raven?.addListener(RavenEvents.DirectMessage, handleDirectMessage); + raven?.removeListener(RavenEvents.DirectContact, handleProfileUpdate); + raven?.addListener(RavenEvents.DirectContact, handleProfileUpdate); return () => { - raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); + raven?.removeListener(RavenEvents.DirectContact, handleProfileUpdate); }; }, [raven]); - const handleProfileUpdate = (data: Profile[]) => { - const result = [...props.chat.directContacts]; - data.forEach(({ name, creator }) => { - const isPresent = props.chat.directContacts.some( - (obj) => obj.name === name && obj.creator === creator - ); - if (!isPresent) { - //Add the object to the result array if it's not already present in store state. - result.push({ name, creator }); + // // Direct message handler + const handleDirectMessage = (data: DirectMessage[]) => { + setMessageBuffer((messageBuffer) => [...messageBuffer!, ...data]); + raven?.checkProfiles(data.map((x) => x.peer)); + }; + + const setMessages = () => { + messageBuffer.forEach((obj) => { + const { peer } = obj; + const matchingStateItem = chat.directMessages.find((stateItem) => stateItem.peer === peer); + if (matchingStateItem) { + if (matchingStateItem.chat.length === 0) { + props.addDirectMessages(peer, [obj]); + setMessageBuffer((prevMessageBuffer) => + prevMessageBuffer.filter((message) => message.id !== obj.id) + ); + } else { + let itemExists = false; + matchingStateItem.chat.forEach((item) => { + if (item.id === obj.id) { + itemExists = true; + } + }); + if (!itemExists) { + props.addDirectMessages(peer, [obj]); + setMessageBuffer((prevMessageBuffer) => + prevMessageBuffer.filter((message) => message.id !== obj.id) + ); + } + } } }); - - props.addDirectContacts(result); }; useEffect(() => { - raven?.removeListener(RavenEvents.ProfileUpdate, handleProfileUpdate); - raven?.addListener(RavenEvents.ProfileUpdate, handleProfileUpdate); + if (chat.directContacts.length !== 0) { + setMessages(); + } + }, [chat.directContacts, messageBuffer]); + + useEffect(() => { + raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); + raven?.addListener(RavenEvents.DirectMessage, handleDirectMessage); return () => { - raven?.removeListener(RavenEvents.ProfileUpdate, handleProfileUpdate); + raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); }; }, [raven]); useEffect(() => { return () => { raven?.removeListener(RavenEvents.Ready, handleReadyState); - raven?.removeListener(RavenEvents.ProfileUpdate, handleProfileUpdate); + raven?.removeListener(RavenEvents.DirectContact, handleProfileUpdate); raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); }; }, [raven]); From 0d52de95c1fef421316eaf7c76b3722c13d3ec50 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Thu, 6 Jul 2023 18:30:38 +0500 Subject: [PATCH 016/179] Add emojis in chat feature and also show last message in contact list --- src/common/components/chat-box/index.scss | 40 +++++++++++--- src/common/components/chat-box/index.tsx | 55 +++++++++++++++++-- .../components/emoji-picker/_index.scss | 9 +-- src/common/components/emoji-picker/index.tsx | 31 ++++++++++- src/common/img/svg.tsx | 7 +-- 5 files changed, 115 insertions(+), 27 deletions(-) diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss index fd4c41bc385..71874f19027 100644 --- a/src/common/components/chat-box/index.scss +++ b/src/common/components/chat-box/index.scss @@ -72,7 +72,7 @@ margin-top: 0.8rem; font-weight: 700; line-height: 24px; - font-size: 20px; + font-size: 21px; font-family: $font-family-sans-serif; @include themify(day) { @@ -172,7 +172,7 @@ padding: 18px 16px; } .user-title { - padding-top: 26px; + padding-top: 19px; width: -webkit-fill-available; .username { @@ -184,14 +184,17 @@ color: $white-two; } - font-size: 16px; - + font-size: 17px; font-weight: 700; margin-bottom: 0.2rem !important; } .last-message { - font-size: 15px; - margin-bottom: 0 !important; + max-width: 320px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-bottom: 0; + font-size: 14px; } } } @@ -390,6 +393,7 @@ } } .chat { + display: flex; margin: 4px 8px; padding: 4px; min-height: 45px; @@ -401,9 +405,31 @@ background: #cee2ff; } + .chatbox-emoji-picker { + .chatbox-emoji { + .emoji-picker { + display: none; + } + margin: 8px 0 0 7px; + cursor: pointer; + + svg { + height: 22px; + fill: rgb(60, 68, 73); + } + } + &:hover { + .chatbox-emoji { + .emoji-picker { + display: block; + } + } + } + } + .chat-input-group { .chat-input { - padding-top: 13px; + padding-top: 10px; border: none; @include themify(day) { background: #e4e6eb; diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index cd40bb9dbff..4d430ca2dbf 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -10,6 +10,8 @@ import Tooltip from "../tooltip"; import UserAvatar from "../user-avatar"; import SeachUser from "../search-user"; import LinearProgress from "../linear-progress"; +import EmojiPicker from "../emoji-picker"; +import ClickAwayListener from "../clickaway-listener"; import { setNostrkeys } from "../../../providers/message-provider"; import { @@ -19,7 +21,8 @@ import { arrowBackSvg, messageSendSvg, chevronUpSvg, - chevronDownSvgForSlider + chevronDownSvgForSlider, + emoticonHappyOutlineSvg } from "../../img/svg"; import { dateToFormatted } from "../../helper/parse-date"; import { @@ -102,7 +105,16 @@ export default function ChatBox(props: Props) { useEffect(() => { if (currentUser) { - fetchCurrentUserData(); + const isCurrentUserFound = props.chat.directContacts.some( + (contact) => contact.name === currentUser + ); + if (isCurrentUserFound) { + fetchCurrentUserData(); + } else { + setShowSpinner(true); + fetchCurrentUserData(); + } + const peer = props.chat.directContacts.find((x) => x.name === currentUser)?.pubkey ?? null; setReceiverPubKey(peer!); const msgsList = fetchMessages(peer!); @@ -139,7 +151,6 @@ export default function ChatBox(props: Props) { }; const fetchCurrentUserData = async () => { - setShowSpinner(true); const response = await getAccountFull(currentUser); setProfileData({ joiningData: response.created, @@ -168,7 +179,6 @@ export default function ChatBox(props: Props) { }; const setCurrentUserFromSearch = (username: string) => { - setShowSpinner(true); setCurrentUser(username); setExpanded(true); setIsCurrentUser(true); @@ -205,8 +215,7 @@ export default function ChatBox(props: Props) { const scrollerClicked = () => { chatBodyDivRef?.current?.scrollTo({ - top: isCurrentUser ? chatBodyDivRef?.current?.scrollHeight : 0, - behavior: "smooth" + top: isCurrentUser ? chatBodyDivRef?.current?.scrollHeight : 0 }); }; @@ -255,6 +264,17 @@ export default function ChatBox(props: Props) { return <>; }; + const handleEmojiSelection = (emoji: string) => { + setMessage((prevMessage) => prevMessage + emoji); + }; + + const getLastMessage = (pubkey: string) => { + const msgsList = fetchMessages(pubkey!); + const messages = msgsList.sort((a, b) => a.created - b.created); + const lastMessage = messages.slice(-1); + return lastMessage[0]?.content; + }; + return ( <> {show && ( @@ -402,6 +422,7 @@ export default function ChatBox(props: Props) {
userClicked(user.name)}>

{user.name}

+

{getLastMessage(user.pubkey)}

); @@ -444,12 +465,34 @@ export default function ChatBox(props: Props) { {showSpinner && } {currentUser && (
+
+
+ +
{emoticonHappyOutlineSvg}
+
+ { + handleEmojiSelection(e); + }} + /> +
+
+ { e.preventDefault(); e.stopPropagation(); sendMessage(); }} + style={{ width: "100%" }} > void; + style?: { + top: string; + left: string | number; + marginLeft: string; + borderTopLeftRadius: string; + borderTopRightRadius: string; + borderBottomLeftRadius: string; + }; } interface State { @@ -47,6 +55,16 @@ interface State { filter: string; } +interface EmojiPickerStyle { + width: string; + position?: "absolute" | "relative" | "fixed" | "static"; + right: string | number; + zIndex: number; + borderBottomRightRadius: string; + borderBottomLeftRadius: string; + padding: string; +} + export default class EmojiPicker extends BaseComponent { state: State = { data: null, @@ -144,8 +162,19 @@ export default class EmojiPicker extends BaseComponent { const recent: string[] = ls.get("recent-emoji", []); + const emojiPickerStyle: EmojiPickerStyle = { + width: "280px", + position: "absolute", + right: "0", + zIndex: 1000, + borderBottomRightRadius: "8px", + borderBottomLeftRadius: "8px", + padding: "14px 6px", + ...(this.props.style && this.props.style) // Merge the passed style props if available + }; + return ( -
+
- + + ); From 90211cdff4a6af086882fb6850c4fda91fa197ca Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Mon, 10 Jul 2023 14:25:05 +0500 Subject: [PATCH 017/179] Add feature to send gifs and image in chat --- src/common/components/chat-box/index.scss | 52 ++++- src/common/components/chat-box/index.tsx | 225 ++++++++++++++++++- src/common/components/emoji-picker/index.tsx | 1 + src/common/components/gif-picker/_index.scss | 8 - src/common/components/gif-picker/index.tsx | 44 +++- src/common/img/svg.tsx | 18 +- 6 files changed, 329 insertions(+), 19 deletions(-) diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss index 71874f19027..960b7e668d2 100644 --- a/src/common/components/chat-box/index.scss +++ b/src/common/components/chat-box/index.scss @@ -5,7 +5,7 @@ .chatbox-container { position: fixed; - z-index: 99; + // z-index: 99; right: 15rem; bottom: 0rem; height: 53px; @@ -349,6 +349,20 @@ @include themify(night) { background: #cee2ff; } + + &.gif { + background: none; + img { + max-width: 100%; + } + } + + &.chat-image { + background: none; + img { + max-width: 100%; + } + } color: #050505; max-width: 280px; word-wrap: break-word; @@ -378,12 +392,38 @@ margin-right: 15px; .sender-message-content { + @include themify(day) { + // background: rgb(204, 247, 255); + background: $light-periwinkle; + // background-color: rgb(0, 132, 255); + color: $dark-indigo; + // color: $white; + } + @include themify(night) { + background: $dark-indigo; + color: $white; + } + &.gif { + background: none; + img { + max-width: 100%; + } + } + + &.chat-image { + background: none; + img { + max-width: 100%; + } + } + border-radius: 10px 10px 0px; max-width: 280px; - background-color: rgb(0, 132, 255); + // background-color: rgb(0, 132, 255); + word-wrap: break-word; margin-bottom: 0; - color: $white; + color: $charcoal-grey; font-size: 16px; font-weight: 400; padding: 8px 12px 8px 12px; @@ -426,6 +466,12 @@ } } } + .chatbox-image { + cursor: pointer; + .chatbox-image-icon { + margin: 7px 0 0 7px; + } + } .chat-input-group { .chat-input { diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 4d430ca2dbf..b2e5c242f50 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -1,9 +1,12 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { Button, Form, FormControl, InputGroup, Spinner } from "react-bootstrap"; import { Link } from "react-router-dom"; +import axios from "axios"; +import mediumZoom, { Zoom } from "medium-zoom"; import { ActiveUser } from "../../store/active-user/types"; import { Chat, DirectContactsType } from "../../store/chat/types"; +import { Global, Theme } from "../../store/global/types"; import { DirectMessage } from "../../../providers/message-provider-types"; import Tooltip from "../tooltip"; @@ -11,7 +14,8 @@ import UserAvatar from "../user-avatar"; import SeachUser from "../search-user"; import LinearProgress from "../linear-progress"; import EmojiPicker from "../emoji-picker"; -import ClickAwayListener from "../clickaway-listener"; +import GifPicker from "../gif-picker"; +import { error } from "../feedback"; import { setNostrkeys } from "../../../providers/message-provider"; import { @@ -22,8 +26,11 @@ import { messageSendSvg, chevronUpSvg, chevronDownSvgForSlider, - emoticonHappyOutlineSvg + emoticonHappyOutlineSvg, + gifIcon, + chatBoxImageSvg } from "../../img/svg"; + import { dateToFormatted } from "../../helper/parse-date"; import { createNoStrAccount, @@ -35,8 +42,13 @@ import { setProfileMetaData, resetProfile } from "../../helper/chat-utils"; +import { renderPostBody } from "@ecency/render-helper"; +import { getAccessToken } from "../../helper/user-token"; import { _t } from "../../i18n"; + import { getAccountFull } from "../../api/hive"; +import { uploadImage } from "../../api/misc"; +import { addImage } from "../../api/private-api"; import "./index.scss"; @@ -48,12 +60,17 @@ export interface profileData { interface Props { activeUser: ActiveUser | null; + global: Global; chat: Chat; resetChat: () => void; } +let zoom: Zoom | null = null; + export default function ChatBox(props: Props) { + const prevPropsRef = useRef(props); const chatBodyDivRef = React.createRef(); + const fileInput = React.createRef(); const [expanded, setExpanded] = useState(false); const [currentUser, setCurrentUser] = useState(""); const [isCurrentUser, setIsCurrentUser] = useState(false); @@ -72,6 +89,7 @@ export default function ChatBox(props: Props) { const [messagesList, setMessagesList] = useState([]); const [isUserFromSearch, setIsUserFromSearch] = useState(false); const [isCurrentUserJoined, setIsCurrentUserJoined] = useState(true); + const [shGif, setShGif] = useState(false); useEffect(() => { // resetProfile(props.activeUser); @@ -85,12 +103,25 @@ export default function ChatBox(props: Props) { setMessagesList(messages); }, [props.chat.directMessages]); + useEffect(() => { + const prevProps = prevPropsRef.current; + if (prevProps.global.theme !== props.global.theme) { + setBackground(); + } + prevPropsRef.current = props; + }, [props.global.theme]); + useEffect(() => { scrollToBottom(); + if (messagesList.length !== 0) { + //Initialize the zooming effect + zoomInitializer(); + } }, [messagesList]); useEffect(() => { if (isCurrentUser) { + zoomInitializer(); scrollToBottom(); } }, [isCurrentUser]); @@ -141,6 +172,22 @@ export default function ChatBox(props: Props) { return count; }; + const zoomInitializer = () => { + const elements: HTMLElement[] = [ + ...document.querySelectorAll(".chat-image img") + ].filter((x) => x.parentNode?.nodeName !== "A"); + zoom = mediumZoom(elements); + setBackground(); + }; + + const setBackground = () => { + if (props.global.theme === Theme.day) { + zoom?.update({ background: "#ffffff" }); + } else { + zoom?.update({ background: "#131111" }); + } + }; + const fetchMessages = (peer: string) => { for (const item of props.chat.directMessages) { if (item.peer === peer) { @@ -275,6 +322,91 @@ export default function ChatBox(props: Props) { return lastMessage[0]?.content; }; + const handleGifSelection = (gif: string) => { + window.raven?.sendDirectMessage(receiverPubKey, gif); + }; + + const toggleGif = (e?: React.MouseEvent) => { + if (e) { + e.stopPropagation(); + } + setShGif(!shGif); + }; + + const isMessageGif = (content: string) => { + return content.includes("giphy"); + }; + + const checkFile = (filename: string) => { + const filenameLow = filename.toLowerCase(); + return ["jpg", "jpeg", "gif", "png"].some((el) => filenameLow.endsWith(el)); + }; + + const fileInputChanged = (e: React.ChangeEvent): void => { + let files = [...(e.target.files as FileList)].filter((i) => checkFile(i.name)).filter((i) => i); + + const { + global: { isElectron } + } = props; + + if (files.length > 0) { + e.stopPropagation(); + e.preventDefault(); + } + + if (files.length > 1 && isElectron) { + let isWindows = process.platform === "win32"; + if (isWindows) { + files = files.reverse(); + } + } + + files.forEach((file) => upload(file)); + + // reset input + e.target.value = ""; + }; + + const upload = async (file: File) => { + const { activeUser, global } = props; + + const username = activeUser?.username!; + + const tempImgTag = `![Uploading ${file.name} #${Math.floor(Math.random() * 99)}]()\n\n`; + + setMessage(tempImgTag); + + let imageUrl: string; + try { + let token = getAccessToken(username); + if (token) { + const resp = await uploadImage(file, token); + imageUrl = resp.url; + + if (global.usePrivate && imageUrl.length > 0) { + addImage(username, imageUrl).then(); + } + + const imgTag = imageUrl.length > 0 && `![](${imageUrl})\n\n`; + + imgTag && setMessage(imgTag); + } else { + error(_t("editor-toolbar.image-error-cache")); + } + } catch (e) { + if (axios.isAxiosError(e) && e.response?.status === 413) { + error(_t("editor-toolbar.image-error-size")); + } else { + error(_t("editor-toolbar.image-error")); + } + return; + } + }; + + const isMessageImage = (content: string) => { + return content.includes("https://images.ecency.com"); + }; + return ( <> {show && ( @@ -368,6 +500,18 @@ export default function ChatBox(props: Props) { <> {messagesList.map((msg, i) => { const dayAndMonth = getFormattedDateAndDay(msg, i); + let renderedPreview = renderPostBody( + msg.content, + false, + props.global.canUseWebp + ); + + renderedPreview = renderedPreview.replace(/]*>/g, ""); + renderedPreview = renderedPreview.replace(/<\/p>/g, ""); + + const isGif = isMessageGif(msg.content); + + const isImage = isMessageImage(msg.content); return ( @@ -385,7 +529,12 @@ export default function ChatBox(props: Props) {

{formatMessageTime(msg.created)}

-

{msg.content}

+
) : ( @@ -394,7 +543,12 @@ export default function ChatBox(props: Props) { {formatMessageTime(msg.created)}

-

{msg.content}

+
)} @@ -472,6 +626,7 @@ export default function ChatBox(props: Props) {
+ {message.length === 0 && ( + +
+
+ +
+ {" "} + {gifIcon} +
+
+ {shGif && ( + { + setShGif(gifState!); + }} + fallback={(e) => { + handleGifSelection(e); + }} + /> + )} +
+
+ +
) => { + e.stopPropagation(); + const el = fileInput.current; + if (el) el.click(); + }} + > +
{chatBoxImageSvg}
+
+
+ + +
+ )} + { e.preventDefault(); diff --git a/src/common/components/emoji-picker/index.tsx b/src/common/components/emoji-picker/index.tsx index a59e48a0749..f2b08aba0ab 100644 --- a/src/common/components/emoji-picker/index.tsx +++ b/src/common/components/emoji-picker/index.tsx @@ -40,6 +40,7 @@ interface EmojiCacheItem { interface Props { fallback?: (e: string) => void; style?: { + width: string; top: string; left: string | number; marginLeft: string; diff --git a/src/common/components/gif-picker/_index.scss b/src/common/components/gif-picker/_index.scss index ce529fa762c..2c94546b951 100644 --- a/src/common/components/gif-picker/_index.scss +++ b/src/common/components/gif-picker/_index.scss @@ -4,14 +4,6 @@ @import "src/style/mixins"; .emoji-picker.gif { - // width: 280px; - width: 430px !important; - position: absolute; - right: 0; - z-index: 12; - border-bottom-right-radius: 8px; - border-bottom-left-radius: 8px; - padding: 14px 6px; @media (max-width: $sm-break) { width: 160px !important; diff --git a/src/common/components/gif-picker/index.tsx b/src/common/components/gif-picker/index.tsx index 20932b84f98..774c6f481a5 100644 --- a/src/common/components/gif-picker/index.tsx +++ b/src/common/components/gif-picker/index.tsx @@ -11,6 +11,18 @@ interface Props { fallback?: (e: string) => void; shGif: boolean; changeState: (gifState?: boolean) => void; + style?: { + width: string; + top: string; + left: string | number; + marginLeft: string; + borderTopLeftRadius: string; + borderTopRightRadius: string; + borderBottomLeftRadius: string; + }; + gifImagesStyle?: { + width: string; + }; } interface State { @@ -22,6 +34,20 @@ interface State { total_count: number; } +interface GifPickerStyle { + width: string; + position?: "absolute" | "relative" | "fixed" | "static"; + right: string | number; + zIndex: number; + borderBottomRightRadius: string; + borderBottomLeftRadius: string; + padding: string; +} + +interface GifImageStyle { + width: string; +} + export default class GifPicker extends BaseComponent { state: State = { data: [], @@ -172,10 +198,15 @@ export default class GifPicker extends BaseComponent { }; renderEmoji = (gifData: any[] | null) => { + const gifImageStyle: GifImageStyle = { + width: "200px", + ...(this.props.gifImagesStyle && this.props.gifImagesStyle) + }; return gifData?.map((_gif, i) => { return (
can't fetch :( { return null; } + const gifPickerStyle: GifPickerStyle = { + width: "430px", + position: "absolute", + right: "0", + zIndex: 12, + borderBottomRightRadius: "8px", + borderBottomLeftRadius: "8px", + padding: "14px 6px", + ...(this.props.style && this.props.style) + }; + return ( -
+
); + +export const chatBoxImageSvg = ( + + + + +); From 70d39729ab890241d0ccd8c131fc9d336364fef5 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Mon, 17 Jul 2023 18:30:52 +0500 Subject: [PATCH 018/179] Add public chat feature in chat --- src/common/components/chat-box/index.scss | 116 ++- src/common/components/chat-box/index.tsx | 690 ++++++++++++++---- .../components/community-cover/index.tsx | 6 +- .../components/community-settings/index.tsx | 2 + .../join-community-chat-btn/index.tsx | 171 +++++ src/common/helper/chat-utils.ts | 23 +- src/common/helper/message-helper.ts | 273 ++++++- src/common/i18n/locales/en-US.json | 8 +- src/common/img/svg.tsx | 17 + src/common/store/actions.ts | 14 +- src/common/store/chat/index.ts | 91 ++- src/common/store/chat/types.ts | 44 +- src/providers/message-provider-types.ts | 1 + src/providers/message-provider.tsx | 157 +++- 14 files changed, 1411 insertions(+), 202 deletions(-) create mode 100644 src/common/components/join-community-chat-btn/index.tsx diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss index 960b7e668d2..e95cc1f7c74 100644 --- a/src/common/components/chat-box/index.scss +++ b/src/common/components/chat-box/index.scss @@ -5,7 +5,7 @@ .chatbox-container { position: fixed; - // z-index: 99; + z-index: 99; right: 15rem; bottom: 0rem; height: 53px; @@ -75,6 +75,11 @@ font-size: 21px; font-family: $font-family-sans-serif; + max-width: 183px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + @include themify(day) { color: $charcoal-grey; } @@ -90,15 +95,15 @@ justify-content: center; margin-top: 1.1rem; .message-image { - margin: 0.2rem 0.3rem 0 0; + margin-top: 2px; .message-svg { - width: 40px; - height: 40px; + width: 32px; + height: 32px; justify-content: center; text-align: center; border-radius: 50%; - padding-top: 11px; + padding-top: 9px; display: flex; svg { @@ -116,9 +121,9 @@ display: flex; justify-content: center; text-align: center; - padding-top: 6px; - width: 40px; - height: 40px; + padding-top: 1px; + width: 28px; + height: 28px; border-radius: 50%; } @@ -126,13 +131,36 @@ background: rgba(29, 155, 240, 0.1); } } + .community-menu { + margin-top: -15px; + svg { + @include themify(day) { + color: $charcoal-grey; + } + + @include themify(night) { + color: $white-two; + } + } + } } } .chat-body { position: relative; height: 470px; - overflow-y: auto; + overflow: scroll; + overflow-x: hidden; + + .community-header, + .dm-header { + padding: 10px; + border-bottom: 1px solid #e7e7e7; + padding-left: 14px; + font-family: Faktum, sans-serif; + font-weight: 700; + // background: #e4e6eb; + } &.current-user { padding-bottom: 10px; @@ -146,6 +174,22 @@ } } + &.community { + padding-bottom: 10px; + height: 412px; + margin-bottom: 8px; + @include themify(day) { + border-bottom: 1px solid $white-three; + } + @include themify(night) { + border-bottom: 1px solid #172b44; + } + } + + &.no-scroll { + overflow: hidden; + } + &.join-chat { display: flex; justify-content: center; @@ -169,10 +213,10 @@ border-bottom: 0.5px solid rgba(134, 150, 160, 0.15); .user-img { - padding: 18px 16px; + padding: 13px 16px; } .user-title { - padding-top: 19px; + padding-top: 13px; width: -webkit-fill-available; .username { @@ -184,7 +228,7 @@ color: $white-two; } - font-size: 17px; + font-size: 18px; font-weight: 700; margin-bottom: 0.2rem !important; } @@ -318,7 +362,7 @@ margin-top: 14%; } .custom-divider { - margin: 0 24px; + margin: 12px 24px 0px 24px; font-size: 0.7em; color: $dark; .custom-divider-text { @@ -332,6 +376,12 @@ .user-img { padding: 18px 8px 8px 16px; } + .community-user-img { + padding: 8px 8px 8px 16px; + .user-avatar.medium { + cursor: pointer; + } + } .user-info { padding-top: 8px; .user-msg-time { @@ -341,6 +391,9 @@ font-size: 12px; margin-left: 9px; margin-bottom: 2px; + .username-community { + margin-right: 8px; + } } .receiver-message-content { @include themify(day) { @@ -416,7 +469,7 @@ max-width: 100%; } } - + border-radius: 10px 10px 0px; max-width: 280px; // background-color: rgb(0, 132, 255); @@ -498,3 +551,38 @@ } } } + +.chats-dialog { + .chat-modals-body { + padding: 2rem; + + .leave-dialog-body { + font-size: 18px; + margin-top: 12px; + } + .leave-confirm-buttons { + text-align: right !important; + .close-btn, + .confirm-btn { + margin-right: 20px; + } + } + } +} + +.profile-box { + padding: 15px; + + .user-avatar.large { + margin-left: 73px; + margin-bottom: 20px; + } + .profile-name { + margin-bottom: 10px; + display: flex; + justify-content: center; + text-align: center; + font-size: 18px; + font-weight: 800; + } +} diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index b2e5c242f50..84220d79e03 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -1,5 +1,15 @@ -import React, { useEffect, useRef, useState } from "react"; -import { Button, Form, FormControl, InputGroup, Spinner } from "react-bootstrap"; +import React, { useEffect, useMemo, useRef, useState } from "react"; +import { History } from "history"; +import { + Button, + Form, + FormControl, + InputGroup, + Modal, + OverlayTrigger, + Popover, + Spinner +} from "react-bootstrap"; import { Link } from "react-router-dom"; import axios from "axios"; import mediumZoom, { Zoom } from "medium-zoom"; @@ -7,7 +17,7 @@ import mediumZoom, { Zoom } from "medium-zoom"; import { ActiveUser } from "../../store/active-user/types"; import { Chat, DirectContactsType } from "../../store/chat/types"; import { Global, Theme } from "../../store/global/types"; -import { DirectMessage } from "../../../providers/message-provider-types"; +import { Channel, DirectMessage, PublicMessage } from "../../../providers/message-provider-types"; import Tooltip from "../tooltip"; import UserAvatar from "../user-avatar"; @@ -15,7 +25,7 @@ import SeachUser from "../search-user"; import LinearProgress from "../linear-progress"; import EmojiPicker from "../emoji-picker"; import GifPicker from "../gif-picker"; -import { error } from "../feedback"; +import { error, success } from "../feedback"; import { setNostrkeys } from "../../../providers/message-provider"; import { @@ -28,7 +38,11 @@ import { chevronDownSvgForSlider, emoticonHappyOutlineSvg, gifIcon, - chatBoxImageSvg + chatBoxImageSvg, + linkSvg, + keyOutlineSvg, + KebabMenu, + chatLeaveSvg } from "../../img/svg"; import { dateToFormatted } from "../../helper/parse-date"; @@ -36,7 +50,6 @@ import { createNoStrAccount, formatMessageDate, formatMessageTime, - getDirectMessages, getProfileMetaData, NostrKeys, setProfileMetaData, @@ -51,6 +64,10 @@ import { uploadImage } from "../../api/misc"; import { addImage } from "../../api/private-api"; import "./index.scss"; +import { getCommunity } from "../../api/bridge"; +import { Community } from "../../store/communities/types"; +import { npubEncode } from "../../../lib/nostr-tools/nip19"; +import DropDown, { MenuItem } from "../dropdown"; export interface profileData { joiningData: string; @@ -59,6 +76,7 @@ export interface profileData { } interface Props { + history: History; activeUser: ActiveUser | null; global: Global; chat: Chat; @@ -69,12 +87,14 @@ let zoom: Zoom | null = null; export default function ChatBox(props: Props) { const prevPropsRef = useRef(props); + const popoverRef = useRef(null); const chatBodyDivRef = React.createRef(); const fileInput = React.createRef(); const [expanded, setExpanded] = useState(false); const [currentUser, setCurrentUser] = useState(""); const [isCurrentUser, setIsCurrentUser] = useState(false); const [message, setMessage] = useState(""); + const [dMMessage, setDMMessage] = useState(""); const [isMessageText, setIsMessageText] = useState(false); const [profileData, setProfileData] = useState(); const [isScrollToTop, setIsScrollToTop] = useState(false); @@ -86,10 +106,48 @@ export default function ChatBox(props: Props) { const [activeUserKeys, setActiveUserKeys] = useState(); const [receiverPubKey, setReceiverPubKey] = useState(""); const [showSpinner, setShowSpinner] = useState(false); - const [messagesList, setMessagesList] = useState([]); + const [directMessagesList, setDirectMessagesList] = useState([]); const [isUserFromSearch, setIsUserFromSearch] = useState(false); const [isCurrentUserJoined, setIsCurrentUserJoined] = useState(true); const [shGif, setShGif] = useState(false); + const [isCommunity, setIsCommunity] = useState(false); + const [communityName, setCommunityName] = useState(""); + const [currentCommunity, setCurrentCommunity] = useState(); + const [currentChannel, setCurrentChannel] = useState(); + const [publicMessages, setPublicMessages] = useState([]); + const [activeMessage, setActiveMessage] = useState(""); + const [keyDialog, setKeyDialog] = useState(false); + const [step, setStep] = useState(0); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + const clickedElement = event.target as HTMLElement; + const isAvatarClicked = + clickedElement.classList.contains("user-avatar") && + clickedElement.classList.contains("medium"); + if ( + popoverRef.current && + !(popoverRef.current as HTMLElement).contains(event.target as Node) && + !isAvatarClicked + ) { + setActiveMessage(""); + } + }; + + if (chatBodyDivRef.current) { + chatBodyDivRef.current.addEventListener("mousedown", handleClickOutside); + } + + return () => { + if (chatBodyDivRef.current) { + chatBodyDivRef.current.removeEventListener("mousedown", handleClickOutside); + } + }; + }, [activeMessage]); + + useEffect(() => { + console.log(currentChannel, "currentChannel"); + }, [currentChannel]); useEffect(() => { // resetProfile(props.activeUser); @@ -97,10 +155,16 @@ export default function ChatBox(props: Props) { setShow(!!props.activeUser?.username); }, []); + // useEffect(() => { + // if (window.raven) { + // setHasUserJoinedChat(true); + // } + // }, [window?.raven]); + useEffect(() => { - const msgsList = fetchMessages(receiverPubKey!); + const msgsList = fetchDirectMessages(receiverPubKey!); const messages = msgsList.sort((a, b) => a.created - b.created); - setMessagesList(messages); + setDirectMessagesList(messages); }, [props.chat.directMessages]); useEffect(() => { @@ -112,28 +176,47 @@ export default function ChatBox(props: Props) { }, [props.global.theme]); useEffect(() => { - scrollToBottom(); - if (messagesList.length !== 0) { + scrollerClicked(); + if (directMessagesList.length !== 0 || publicMessages.length !== 0) { //Initialize the zooming effect zoomInitializer(); } - }, [messagesList]); + }, [directMessagesList, publicMessages]); + + useEffect(() => { + if (currentChannel && isCommunity) { + const publicMessages = fetchCommunityMessages(currentChannel.id); + const messages = publicMessages.sort((a, b) => a.created - b.created); + setPublicMessages(messages); + } + scrollerClicked(); + }, [currentChannel, isCommunity, props.chat.publicMessages]); useEffect(() => { if (isCurrentUser) { zoomInitializer(); - scrollToBottom(); + scrollerClicked(); + } else { + scrollerClicked(); } }, [isCurrentUser]); useEffect(() => { fetchProfileData(); setShow(!!props.activeUser?.username); - const msgsList = fetchMessages(receiverPubKey!); + const msgsList = fetchDirectMessages(receiverPubKey!); const messages = msgsList.sort((a, b) => a.created - b.created); - setMessagesList(messages); + setDirectMessagesList(messages); }, [props.activeUser]); + useEffect(() => { + if (isCommunity) { + fetchCommunity(); + scrollerClicked(); + fetchCurrentChannel(communityName); + } + }, [isCommunity, communityName]); + useEffect(() => { if (currentUser) { const isCurrentUserFound = props.chat.directContacts.some( @@ -148,9 +231,9 @@ export default function ChatBox(props: Props) { const peer = props.chat.directContacts.find((x) => x.name === currentUser)?.pubkey ?? null; setReceiverPubKey(peer!); - const msgsList = fetchMessages(peer!); + const msgsList = fetchDirectMessages(peer!); const messages = msgsList.sort((a, b) => a.created - b.created); - setMessagesList(messages); + setDirectMessagesList(messages); if (!window.raven) { setNostrkeys(activeUserKeys!); } @@ -158,9 +241,30 @@ export default function ChatBox(props: Props) { setIsCurrentUserJoined(true); setMessage(""); setShowSpinner(false); + scrollerClicked(); } }, [currentUser]); + const fetchCommunity = async () => { + const community = await getCommunity(communityName, props.activeUser?.username); + setCurrentCommunity(community!); + setProfileData({ + joiningData: community?.created_at!, + about: community?.about, + followers: community?.subscribers + }); + }; + + const fetchCurrentChannel = (communityName: string) => { + for (const item of props.chat.channels) { + if (item.communityName === communityName) { + setCurrentChannel(item); + return item; + } + } + return {}; + }; + const formatFollowers = (count: number | undefined) => { if (count) { return count >= 1e6 @@ -188,7 +292,7 @@ export default function ChatBox(props: Props) { } }; - const fetchMessages = (peer: string) => { + const fetchDirectMessages = (peer: string) => { for (const item of props.chat.directMessages) { if (item.peer === peer) { return item.chat; @@ -197,6 +301,15 @@ export default function ChatBox(props: Props) { return []; }; + const fetchCommunityMessages = (channelId: string) => { + for (const item of props.chat.publicMessages) { + if (item.channelId === channelId) { + return item.PublicMessage; + } + } + return []; + }; + const fetchCurrentUserData = async () => { const response = await getAccountFull(currentUser); setProfileData({ @@ -215,7 +328,7 @@ export default function ChatBox(props: Props) { const fetchProfileData = async () => { const profileData = await getProfileMetaData(props.activeUser?.username!); setActiveUserKeys(profileData?.noStrKey); - const hasNoStrKey = "noStrKey" in profileData; + const hasNoStrKey = profileData.hasOwnProperty("noStrKey"); setHasUserJoinedChat(hasNoStrKey); setShow(!!props.activeUser?.username); }; @@ -238,10 +351,12 @@ export default function ChatBox(props: Props) { }; const sendMessage = () => { - if (message.length !== 0) { + if (message.length !== 0 && !message.includes("Uploading")) { setMessage(""); setIsMessageText(false); - window.raven?.sendDirectMessage(receiverPubKey, message); + isCommunity + ? window?.raven?.sendPublicMessage(currentChannel!, message, [], "") + : window.raven?.sendDirectMessage(receiverPubKey, message); } if (isUserFromSearch && receiverPubKey) { window.raven?.publishContacts(currentUser, receiverPubKey); @@ -252,23 +367,25 @@ export default function ChatBox(props: Props) { const handleScroll = (event: React.UIEvent) => { var element = event.currentTarget; let srollHeight: number = (element.scrollHeight / 100) * 25; - const isScrollToTop = !isCurrentUser && element.scrollTop >= srollHeight; + const isScrollToTop = !isCurrentUser && !isCommunity && element.scrollTop >= srollHeight; const isScrollToBottom = - isCurrentUser && + (isCurrentUser || isCommunity) && element.scrollTop + chatBodyDivRef?.current?.clientHeight! < element.scrollHeight; setIsScrollToTop(isScrollToTop); setIsScrollToBottom(isScrollToBottom); }; const scrollerClicked = () => { - chatBodyDivRef?.current?.scrollTo({ - top: isCurrentUser ? chatBodyDivRef?.current?.scrollHeight : 0 + chatBodyDivRef?.current?.scroll({ + top: isCurrentUser || isCommunity ? chatBodyDivRef?.current?.scrollHeight : 0, + behavior: "auto" }); }; const scrollToBottom = () => { - chatBodyDivRef?.current?.scrollTo({ - top: chatBodyDivRef?.current?.scrollHeight + chatBodyDivRef?.current?.scroll({ + top: chatBodyDivRef?.current?.scrollHeight, + behavior: "auto" }); }; @@ -297,8 +414,8 @@ export default function ChatBox(props: Props) { ); - const getFormattedDateAndDay = (msg: DirectMessage, i: number) => { - const prevMsg = messagesList[i - 1]; + const getFormattedDateAndDay = (msg: DirectMessage | PublicMessage, i: number) => { + const prevMsg = isCurrentUser ? directMessagesList[i - 1] : publicMessages[i - 1]; const msgDate = formatMessageDate(msg.created); const prevMsgDate = prevMsg ? formatMessageDate(prevMsg.created) : null; if (msgDate !== prevMsgDate) { @@ -316,14 +433,16 @@ export default function ChatBox(props: Props) { }; const getLastMessage = (pubkey: string) => { - const msgsList = fetchMessages(pubkey!); + const msgsList = fetchDirectMessages(pubkey!); const messages = msgsList.sort((a, b) => a.created - b.created); const lastMessage = messages.slice(-1); return lastMessage[0]?.content; }; const handleGifSelection = (gif: string) => { - window.raven?.sendDirectMessage(receiverPubKey, gif); + isCurrentUser + ? window.raven?.sendDirectMessage(receiverPubKey, gif) + : window?.raven?.sendPublicMessage(currentChannel!, gif, [], ""); }; const toggleGif = (e?: React.MouseEvent) => { @@ -390,6 +509,7 @@ export default function ChatBox(props: Props) { const imgTag = imageUrl.length > 0 && `![](${imageUrl})\n\n`; imgTag && setMessage(imgTag); + setIsMessageText(true); } else { error(_t("editor-toolbar.image-error-cache")); } @@ -407,12 +527,124 @@ export default function ChatBox(props: Props) { return content.includes("https://images.ecency.com"); }; + const communityClicked = (community: string, name: string) => { + setIsCommunity(true); + setCommunityName(community); + }; + + const getProfileName = (creator: string) => { + const profile = props.chat.profiles.find((x) => x.creator === creator); + return profile?.name; + }; + + const sendDM = (name: string, pubkey: string) => { + if (dMMessage) { + window.raven?.sendDirectMessage(pubkey, dMMessage); + + setIsCurrentUser(true); + setCurrentUser(name); + setIsCommunity(false); + setCommunityName(""); + setActiveMessage(""); + setDMMessage(""); + } + }; + + const handleDMChange = (e: React.ChangeEvent) => { + setDMMessage(e.target.value); + }; + + const handleImageClick = (id: string) => { + if (activeMessage === id) { + popoverRef.current = null; + setActiveMessage(""); + } else { + popoverRef.current = null; + setActiveMessage(id); + } + }; + + const inviteClicked = () => { + const textField = document.createElement("textarea"); + textField.innerText = currentChannel?.id!; + document.body.appendChild(textField); + textField.select(); + document.execCommand("copy"); + textField.remove(); + success("Channel Id copied into clipboard"); + }; + + const LeaveModal = () => { + return ( + <> +
+
+

Confirmaton

+
+
+
Are you sure?
+

+ {/* { + e.preventDefault(); + setKeyDialog(false); + }} + > + {_t("g.back")} + */} + + +

+ + ); + }; + const LeaveClicked = () => { + setKeyDialog(true); + setStep(1); + }; + + const toggleKeyDialog = () => { + setKeyDialog(!keyDialog); + }; + + const menuItems: MenuItem[] = [ + { + label: _t("chat.invite"), + onClick: inviteClicked, + icon: linkSvg + }, + { + label: "Leave", + onClick: LeaveClicked, + icon: chatLeaveSvg + } + ]; + + const menuConfig = { + history: props.history, + label: "", + icon: KebabMenu, + items: menuItems + }; + return ( <> {show && (
- {currentUser && expanded && ( + {(currentUser || communityName) && expanded && (
{ setCurrentUser(""); setIsCurrentUser(false); + setCommunityName(""); + setIsCommunity(false); + setActiveMessage(""); }} > {" "} @@ -429,13 +664,24 @@ export default function ChatBox(props: Props) { )}
setExpanded(!expanded)}> - {currentUser && ( + {(currentUser || isCommunity) && (

- +

)} -

{currentUser ? currentUser : _t("chat.messages")}

+

+ {currentUser + ? currentUser + : isCommunity + ? currentCommunity?.title + : _t("chat.messages")} +

{!currentUser && hasUserJoinedChat && ( @@ -452,117 +698,289 @@ export default function ChatBox(props: Props) {

+ {isCommunity && ( +
+ +
+ )}
{hasUserJoinedChat ? ( <> - {currentUser.length !== 0 ? ( + {currentUser.length !== 0 || communityName.length !== 0 ? ( <> - -
- {profileData?.joiningData && ( -
- - - -

{currentUser}

- {profileData.about && ( -

{profileData.about}

- )} - -
-

- {" "} - {_t("chat.joined")}{" "} - {dateToFormatted(profileData!.joiningData, "LL")} -

-

- {" "} - {formatFollowers(profileData!.followers)} {_t("chat.followers")} -

-
-
- )} -
-
{!isCurrentUserJoined ? (

{_t("chat.not-joined")}

) : ( <> - {messagesList.map((msg, i) => { - const dayAndMonth = getFormattedDateAndDay(msg, i); - let renderedPreview = renderPostBody( - msg.content, - false, - props.global.canUseWebp - ); - - renderedPreview = renderedPreview.replace(/]*>/g, ""); - renderedPreview = renderedPreview.replace(/<\/p>/g, ""); - - const isGif = isMessageGif(msg.content); - - const isImage = isMessageImage(msg.content); - - return ( - - {dayAndMonth} - {msg.creator !== activeUserKeys?.pub ? ( -
-
- - - - - -
-
-

- {formatMessageTime(msg.created)} -

-
-
-
- ) : ( -
-

- {formatMessageTime(msg.created)} + {" "} + +

+ {profileData?.joiningData && ( +
+ + + +

+ {isCurrentUser + ? currentUser + : (isCommunity && currentCommunity?.title) || ""} +

+ {profileData.about && ( +

{profileData.about}

+ )} + +
+

+ {" "} + {_t("chat.joined")}{" "} + {dateToFormatted(profileData!.joiningData, "LL")} +

+

+ {" "} + {formatFollowers(profileData!.followers)}{" "} + {isCommunity ? _t("chat.subscribers") : _t("chat.followers")}

-
-
-
- )} - - ); - })} +
+ )} +
+ + {isCurrentUser + ? directMessagesList.map((msg, i) => { + const dayAndMonth = getFormattedDateAndDay(msg, i); + let renderedPreview = renderPostBody( + msg.content, + false, + props.global.canUseWebp + ); + + renderedPreview = renderedPreview.replace(/]*>/g, ""); + renderedPreview = renderedPreview.replace(/<\/p>/g, ""); + + const isGif = isMessageGif(msg.content); + + const isImage = isMessageImage(msg.content); + + return ( + + {dayAndMonth} + {msg.creator !== activeUserKeys?.pub ? ( +
+
+ + + + + +
+
+

+ {formatMessageTime(msg.created)} +

+
+
+
+ ) : ( +
+

+ {formatMessageTime(msg.created)} +

+
+
+
+
+ )} + + ); + }) + : publicMessages.map((pMsg, i) => { + const dayAndMonth = getFormattedDateAndDay(pMsg, i); + let renderedPreview = renderPostBody( + pMsg.content, + false, + props.global.canUseWebp + ); + + renderedPreview = renderedPreview.replace(/]*>/g, ""); + renderedPreview = renderedPreview.replace(/<\/p>/g, ""); + + const isGif = isMessageGif(pMsg.content); + + const isImage = isMessageImage(pMsg.content); + + const name = getProfileName(pMsg.creator); + + const popover = ( + + +
+ + +

{name!}

+ + { + e.preventDefault(); + e.stopPropagation(); + sendDM(name!, pMsg.creator); + }} + > + + + + +
+
+
+ ); + + return ( + + {dayAndMonth} + {pMsg.creator !== activeUserKeys?.pub ? ( +
+
+ handleImageClick(pMsg.id)} + > + + + + +
+ +
+

+ {name} + {formatMessageTime(pMsg.created)} +

+
+
+
+ ) : ( +
+

+ {formatMessageTime(pMsg.created)} +

+
+
+
+
+ )} + + ); + })} )}
) : ( <> - {props.chat.directContacts.length !== 0 ? ( + {props.chat.directContacts.length !== 0 || props.chat.channels.length !== 0 ? ( + {props.chat.channels.length !== 0 && ( + <> +
Communities
+ {props.chat.channels.map((channel) => { + return ( +
+ +
+ + + +
+ + +
+ communityClicked(channel.communityName!, channel.name) + } + > +

+ {channel.name} +

+
+
+ ); + })} + {props.chat.directContacts.length !== 0 && ( +
DMs
+ )} + + )} {props.chat.directContacts.map((user: DirectContactsType) => { return (
@@ -602,7 +1020,8 @@ export default function ChatBox(props: Props) { )} - {((isScrollToTop && !isCurrentUser) || (isCurrentUser && isScrollToBottom)) && ( + {((isScrollToTop && !isCurrentUser) || + ((isCurrentUser || isCommunity) && isScrollToBottom)) && ( @@ -611,13 +1030,13 @@ export default function ChatBox(props: Props) { style={{ bottom: isCurrentUser && isScrollToBottom ? "20px" : "55px" }} onClick={scrollerClicked} > - {isCurrentUser ? chevronDownSvgForSlider : chevronUpSvg} + {isCurrentUser || isCommunity ? chevronDownSvgForSlider : chevronUpSvg}
)}
{showSpinner && } - {currentUser && ( + {(currentUser || isCommunity) && (
@@ -712,6 +1131,7 @@ export default function ChatBox(props: Props) { {messageSendSvg} @@ -742,6 +1162,24 @@ export default function ChatBox(props: Props) { setCurrentUserFromSearch={setCurrentUserFromSearch} /> )} + + {keyDialog && ( + + + + {step === 1 && LeaveModal()} + {/* {step === 2 && successModal()} */} + + + )} ); } diff --git a/src/common/components/community-cover/index.tsx b/src/common/components/community-cover/index.tsx index 992d624d229..3805477505e 100644 --- a/src/common/components/community-cover/index.tsx +++ b/src/common/components/community-cover/index.tsx @@ -21,6 +21,7 @@ setProxyBase(defaults.imageServer); import BaseComponent from "../base"; import SubscriptionBtn from "../subscription-btn"; import CommunityPostBtn from "../community-post-btn"; +import JoinCommunityChatBtn from "../join-community-chat-btn"; import Tooltip from "../tooltip"; import ImageUploadDialog from "../image-upload"; @@ -138,6 +139,7 @@ interface Props { toggleUIProp: (what: ToggleType) => void; updateSubscriptions: (list: Subscription[]) => void; addAccount: (data: Account) => void; + addCommunity: (data: Community) => void; } export class CommunityCover extends Component { @@ -203,6 +205,7 @@ export class CommunityCover extends Component {
{CommunityPostBtn({ ...this.props })} +
{canUpdateCoverImage && ( { deleteUser: p.deleteUser, toggleUIProp: p.toggleUIProp, updateSubscriptions: p.updateSubscriptions, - addAccount: p.addAccount + addAccount: p.addAccount, + addCommunity: p.addCommunity }; return ; diff --git a/src/common/components/community-settings/index.tsx b/src/common/components/community-settings/index.tsx index 29ac5931536..a491319ec3c 100644 --- a/src/common/components/community-settings/index.tsx +++ b/src/common/components/community-settings/index.tsx @@ -180,9 +180,11 @@ export class CommunitySettings extends BaseComponent { }; this.stateSet({ inProgress: true }); + console.log("Submit run"); return updateCommunity(activeUser.username, community.name, newProps) .then(() => { const nCom: Community = { ...clone(community), ...newProps }; + console.log(nCom, "New dara"); addCommunity(nCom); onHide(); }) diff --git a/src/common/components/join-community-chat-btn/index.tsx b/src/common/components/join-community-chat-btn/index.tsx new file mode 100644 index 00000000000..a275bc322ab --- /dev/null +++ b/src/common/components/join-community-chat-btn/index.tsx @@ -0,0 +1,171 @@ +import React, { useEffect, useState } from "react"; +import { Button, Spinner } from "react-bootstrap"; +import { History } from "history"; + +import { Community } from "../../store/communities/types"; +import { ActiveUser } from "../../store/active-user/types"; + +import { _t } from "../../i18n"; + +import { useMappedStore } from "../../store/use-mapped-store"; +import { Channel } from "../../../providers/message-provider-types"; +import { + createNoStrAccount, + getProfileMetaData, + setChannelMetaData, + setProfileMetaData +} from "../../helper/chat-utils"; +import { setNostrkeys } from "../../../providers/message-provider"; + +interface Props { + history: History; + community: Community; + activeUser: ActiveUser | null; + addCommunity: (data: Community) => void; + resetChat: () => void; +} + +export default function JoinCommunityChatBtn(props: Props) { + const { chat } = useMappedStore(); + const [inProgress, setInProgress] = useState(false); + const [isJoinChat, setIsJoinChat] = useState(false); + const [isChatEnabled, setIsChatEnabled] = useState(false); + const [currentChannel, setCurrentChannel] = useState(); + const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); + + useEffect(() => { + fetchCommunityProfile(); + fetchUserProfileData(); + }, [chat.channels, currentChannel]); + + useEffect(() => { + fetchCommunityProfile(); + }, [props.activeUser]); + + // useEffect(() => { + // if (window.raven) { + // setHasUserJoinedChat(true); + // } + // }, [window?.raven]); + + useEffect(() => { + checkIsChatJoined(); + + fetchCurrentChannel(); + }, [isJoinChat, props.community, chat.channels]); + + const fetchUserProfileData = async () => { + const profileData = await getProfileMetaData(props.activeUser?.username!); + const hasNoStrKey = profileData.hasOwnProperty("noStrKey"); + setHasUserJoinedChat(hasNoStrKey); + }; + + const fetchCommunityProfile = async () => { + const communityProfile = await getProfileMetaData(props.community?.name); + const haschannelMetaData = communityProfile.hasOwnProperty("channel"); + setIsChatEnabled(haschannelMetaData); + const hasNoStrKey = communityProfile.hasOwnProperty("noStrKey"); + if (!currentChannel) { + setCurrentChannel(communityProfile.channel); + } + + if (!haschannelMetaData && currentChannel) { + await setChannelMetaData(props.community.name, currentChannel!); + } + }; + + const checkIsChatJoined = () => { + for (const item of chat.channels) { + if (item.communityName === props.community.name) { + setIsJoinChat(true); + } + } + return []; + }; + + const createCommunityChat = async () => { + const { community } = props; + try { + setInProgress(true); + const data = await window?.raven?.createChannel({ + name: community.title, + about: community.description, + communityName: community.name, + picture: "" + }); + + const content = JSON.parse(data?.content!); + const channelMetaData = { + id: data?.id as string, + creator: data?.pubkey as string, + created: data?.created_at!, + communityName: content.communityName, + name: content.name, + about: content.about, + picture: content.picture + }; + await setChannelMetaData(community.name, currentChannel!); + setCurrentChannel(channelMetaData); + } finally { + setInProgress(false); + setIsJoinChat(true); + } + }; + + const joinCommunityChat = () => { + window?.raven?.loadChannel(currentChannel?.id!); + setIsJoinChat(true); + }; + + const fetchCurrentChannel = () => { + for (const item of chat.channels) { + if (item.communityName === props.community.name) { + setCurrentChannel(item); + return item; + } + } + return {}; + }; + + const handleJoinChat = async () => { + const { resetChat } = props; + setInProgress(true); + const keys = createNoStrAccount(); + await setProfileMetaData(props.activeUser, keys); + setHasUserJoinedChat(true); + setNostrkeys(keys); + window.raven?.updateProfile({ name: props.activeUser?.username!, about: "", picture: "" }); + setInProgress(false); + fetchCommunityProfile(); + resetChat(); + }; + + const chatButtonSpinner = ( + + ); + + return ( + <> + {props.community["context"].role === "owner" ? ( + isJoinChat ? ( + + ) : hasUserJoinedChat ? ( + + ) : ( + + ) + ) : isChatEnabled && !isJoinChat && hasUserJoinedChat ? ( + + ) : !hasUserJoinedChat ? ( + + ) : isJoinChat ? ( + + ) : ( + <> + )} + + ); +} diff --git a/src/common/helper/chat-utils.ts b/src/common/helper/chat-utils.ts index 9530da60353..5f55e4fdcc6 100644 --- a/src/common/helper/chat-utils.ts +++ b/src/common/helper/chat-utils.ts @@ -1,6 +1,6 @@ import moment from "moment"; import { generatePrivateKey, getPublicKey } from "../../lib/nostr-tools/keys"; -import { DirectMessage } from "../../providers/message-provider-types"; +import { Channel } from "../../providers/message-provider-types"; import { getAccountFull } from "../api/hive"; import { updateProfile } from "../api/operations"; import { ActiveUser } from "../store/active-user/types"; @@ -37,6 +37,7 @@ export const resetProfile = async (activeUser: ActiveUser | null) => { version: 2 }; const updatedProfile = await updateProfile(response, { ...profileC }); + console.log(updatedProfile); }; export const setProfileMetaData = async (activeUser: ActiveUser | null, keys: NostrKeys) => { @@ -52,6 +53,18 @@ export const setProfileMetaData = async (activeUser: ActiveUser | null, keys: No return updatedProfile; }; +export const setChannelMetaData = async (username: string, channel: Channel) => { + const response = await getAccountFull(username!); + const { posting_json_metadata } = response; + const profile = JSON.parse(posting_json_metadata!).profile; + const newProfile = { + channel: channel + }; + console.log({ ...profile, ...newProfile }); + const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); + return updatedProfile; +}; + export const createNoStrAccount = () => { const priv = generatePrivateKey(); const pub = getPublicKey(priv); @@ -62,14 +75,6 @@ export function notEmpty(value: TValue | null | undefined): value is TVa return value !== null && value !== undefined; } -export const getDirectMessages = (messages: DirectMessage[], peer?: string) => { - const clean = messages.filter((x) => x.peer === peer).sort((a, b) => a.created - b.created); - - return clean - .map((c) => ({ ...c, children: clean.filter((x) => x.root === c.id) })) - .filter((x) => !x.root); -}; - export const formatMessageTime = (unixTs: number) => moment.unix(unixTs).format("h:mm a"); export const formatMessageDate = (unixTs: number) => moment.unix(unixTs).format("dddd, MMMM Do"); diff --git a/src/common/helper/message-helper.ts b/src/common/helper/message-helper.ts index 9c5a64b192c..6d1938f80e2 100644 --- a/src/common/helper/message-helper.ts +++ b/src/common/helper/message-helper.ts @@ -3,10 +3,13 @@ import { Kind } from "../../lib/nostr-tools/event"; import { Filter } from "../../lib/nostr-tools/filter"; import { TypedEventEmitter } from "./message-event-emitter"; import { + Channel, DirectContact, DirectMessage, Keys, - Metadata + Metadata, + Profile, + PublicMessage } from "../../providers/message-provider-types"; import { encrypt, decrypt } from "../../lib/nostr-tools/nip04"; import SimplePool from "../../lib/nostr-tools/pool"; @@ -21,15 +24,26 @@ const relays = { "wss://nos.lol": { read: true, write: true } }; +enum NewKinds { + MuteList = 10000, + Arbitrary = 30078 +} + export enum RavenEvents { Ready = "ready", + ProfileUpdate = "profile_update", + ChannelCreation = "channel_creation", DirectMessage = "direct_message", - DirectContact = "direct_contact" + DirectContact = "direct_contact", + PublicMessage = "public_message" } type EventHandlerMap = { [RavenEvents.Ready]: () => void; + [RavenEvents.ProfileUpdate]: (data: Profile[]) => void; [RavenEvents.DirectMessage]: (data: DirectMessage[]) => void; + [RavenEvents.ChannelCreation]: (data: Channel[]) => void; + [RavenEvents.PublicMessage]: (data: PublicMessage[]) => void; [RavenEvents.DirectContact]: (data: DirectContact[]) => void; }; @@ -75,20 +89,91 @@ class Raven extends TypedEventEmitter { { kinds: [Kind.Contacts], authors: [this.pub] + }, + { + kinds: [Kind.ChannelCreation, Kind.EventDeletion], + authors: [this.pub] + }, + { + kinds: [Kind.ChannelMessage], + authors: [this.pub] + }, + { + kinds: [NewKinds.Arbitrary], + authors: [this.pub], + "#d": ["left-channel-list", "read-mark-map"] } ]; this.fetchP(filters).then((resp) => { const events = resp.sort((a, b) => b.created_at - a.created_at); + const deletions = resp + .filter((x) => x.kind === Kind.EventDeletion) + .map((x) => Raven.findTagValue(x, "e")); const profile = events.find((x) => x.kind === Kind.Contacts); if (profile && profile?.tags.length !== 0) { this.directContacts = profile?.tags; this.pushToEventBuffer(profile!); } + + const leftChannelList = events.find( + (x) => + x.kind.toString() === NewKinds.Arbitrary.toString() && + Raven.findTagValue(x, "d") === "left-channel-list" + ); + console.log(leftChannelList, "leftChannelList"); + if (leftChannelList) { + this.pushToEventBuffer(leftChannelList); + } + + const channels = Array.from( + new Set( + events + .map((x) => { + if (x.kind === Kind.ChannelCreation) { + return x.id; + } + + if (x.kind === Kind.ChannelMessage) { + return Raven.findTagValue(x, "e"); + } + + return undefined; + }) + .filter((x) => !deletions.includes(x)) + .filter(notEmpty) + ) + ); + if (channels.length !== 0) { + this.fetchChannels(channels); + } + this.fetchMessages(); this.emit(RavenEvents.Ready); }); } + public fetchChannels(channels: string[]) { + const filters: Filter[] = [ + { + kinds: [Kind.ChannelCreation], + ids: channels + }, + ...channels.map((c) => ({ + kinds: [Kind.ChannelMetadata, Kind.EventDeletion], + "#e": [c] + })), + ...channels.map((c) => ({ + kinds: [Kind.ChannelMessage], + "#e": [c], + limit: 30 + })) + ]; + + filters.forEach((c) => { + this.fetch([c]); + }); + } + public fetchMessages() { this.directContacts.map((contact: any) => { const filters: Filter[] = [ @@ -139,26 +224,71 @@ class Raven extends TypedEventEmitter { this.fetch(filters); } + public loadChannel(id: string) { + // console.log(id, "id"); + const filters: Filter[] = [ + { + kinds: [Kind.ChannelCreation], + ids: [id] + }, + { + kinds: [Kind.ChannelMetadata, Kind.EventDeletion], + "#e": [id] + }, + { + kinds: [Kind.ChannelMessage], + "#e": [id], + limit: 30 + } + ]; + + this.fetch(filters); + } + + public loadProfiles(pubs: string[]) { + console.log("load profiles run"); + pubs.forEach((p) => { + this.fetch( + [ + { + kinds: [Kind.Metadata], + authors: [p] + } + ], + true, + false + ); + }); + } + public checkProfiles(pubs: string[]) { pubs.forEach((p) => { if (this.directContacts.length !== 0) { this.directContacts.forEach((c: any) => { if (p !== c[0]) { - this.fetch([ - { - kinds: [Kind.Metadata], - authors: [p] - } - ]); + this.fetch( + [ + { + kinds: [Kind.Metadata], + authors: [p] + } + ], + true, + true + ); } }); } else { - this.fetch([ - { - kinds: [Kind.Metadata], - authors: [p] - } - ]); + this.fetch( + [ + { + kinds: [Kind.Metadata], + authors: [p] + } + ], + true, + true + ); } }); } @@ -180,14 +310,14 @@ class Raven extends TypedEventEmitter { } } - private fetch(filters: Filter[], unsub: boolean = true) { + private fetch(filters: Filter[], unsub: boolean = true, isDirectContact: boolean = false) { + // console.log("Fetch run", filters); const sub = this.pool.sub(this.readRelays, filters); sub.on("event", (event: Event) => { - if (event.kind === Kind.Metadata) { - this.addDirectContact(event); - } - this.pushToEventBuffer(event); + event.kind === Kind.Metadata && isDirectContact + ? this.addDirectContact(event) + : this.pushToEventBuffer(event); }); sub.on("eose", () => { @@ -215,7 +345,7 @@ class Raven extends TypedEventEmitter { }); } - public listen(since: number) { + public listen(channels: string[], since: number) { if (this.listenerSub) { this.listenerSub.unsub(); } @@ -226,6 +356,11 @@ class Raven extends TypedEventEmitter { authors: [this.pub], since }, + { + kinds: [Kind.EventDeletion, Kind.ChannelMetadata, Kind.ChannelMessage], + "#e": channels, + since + }, { kinds: [Kind.EncryptedDirectMessage], "#p": [this.pub], @@ -236,6 +371,11 @@ class Raven extends TypedEventEmitter { ); } + public async createChannel(meta: Metadata) { + // console.log("create channel run"); + return this.publish(Kind.ChannelCreation, [], JSON.stringify(meta)); + } + private async findHealthyRelay(relays: string[]) { for (const relay of relays) { try { @@ -263,6 +403,21 @@ class Raven extends TypedEventEmitter { return this.publish(Kind.EncryptedDirectMessage, tags, encrypted); } + public async sendPublicMessage( + channel: Channel, + message: string, + mentions?: string[], + parent?: string + ) { + const root = parent || channel.id; + const relay = await this.findHealthyRelay(this.pool.seenOn(root) as string[]); + const tags = [["e", root, relay, "root"]]; + if (mentions) { + mentions.forEach((m) => tags.push(["p", m])); + } + return this.publish(Kind.ChannelMessage, tags, message); + } + private publish(kind: number, tags: Array[], content: string): Promise { return new Promise((resolve, reject) => { this.signEvent({ @@ -276,22 +431,26 @@ class Raven extends TypedEventEmitter { }) .then((event) => { if (!event) { + console.log("Event not found"); reject("Couldn't sign event!"); return; } const pub = this.pool.publish(this.writeRelays, event); pub.on("ok", () => { resolve(event); + console.log("event is send", event); if (event.kind === Kind.Contacts) { this.getContacts(); } }); pub.on("failed", () => { + console.log("Failed to sign event"); reject("Couldn't sign event!"); }); }) .catch(() => { + console.log("Catch run event not"); reject("Couldn't sign event!"); }); }); @@ -332,7 +491,7 @@ class Raven extends TypedEventEmitter { async processEventQueue() { this.eventQueueFlag = false; - const data = this.eventQueue + const directContacts = this.eventQueue .filter((x) => x.kind === Kind.Contacts) .map((e: any) => { const profiles: Array<[string, string]> = e.tags; @@ -340,14 +499,77 @@ class Raven extends TypedEventEmitter { }) .filter(notEmpty); - if (data.length > 0) { - const directContactsProfile: Array<{ pubkey: string; name: string }> = data[0]; + if (directContacts.length > 0) { + const directContactsProfile: Array<{ pubkey: string; name: string }> = directContacts[0]; if (directContactsProfile.length > 0) { this.emit(RavenEvents.DirectContact, directContactsProfile); } } + const profileUpdates: Profile[] = this.eventQueue + .filter((x) => x.kind === Kind.Metadata) + .map((ev) => { + const content = Raven.parseJson(ev.content); + return content + ? { + id: ev.id, + creator: ev.pubkey, + created: ev.created_at, + ...Raven.normalizeMetadata(content) + } + : null; + }) + .filter(notEmpty); + if (profileUpdates.length > 0) { + this.emit(RavenEvents.ProfileUpdate, profileUpdates); + } + + const channelCreations: Channel[] = this.eventQueue + .filter((x) => x.kind === Kind.ChannelCreation) + .map((ev) => { + const content = Raven.parseJson(ev.content); + // console.log(content,"events") + return content + ? { + id: ev.id, + creator: ev.pubkey, + created: ev.created_at, + communityName: content.communityName, + ...Raven.normalizeMetadata(content) + } + : null; + }) + .filter(notEmpty); + if (channelCreations.length > 0) { + this.emit(RavenEvents.ChannelCreation, channelCreations); + } + + const publicMessages: PublicMessage[] = this.eventQueue + .filter((x) => x.kind === Kind.ChannelMessage) + .map((ev) => { + const eTags = Raven.filterTagValue(ev, "e"); + const root = eTags.find((x) => x[3] === "root")?.[1]; + const mentions = Raven.filterTagValue(ev, "p") + .map((x) => x?.[1]) + .filter(notEmpty); + if (!root) return null; + return ev.content + ? { + id: ev.id, + root, + content: ev.content, + creator: ev.pubkey, + mentions, + created: ev.created_at + } + : null; + }) + .filter(notEmpty); + if (publicMessages.length > 0) { + this.emit(RavenEvents.PublicMessage, publicMessages); + } + Promise.all( this.eventQueue .filter((x) => x.kind === Kind.EncryptedDirectMessage) @@ -410,11 +632,11 @@ class Raven extends TypedEventEmitter { } } - static findTagValue(ev: Event, tag: "e" | "p") { + static findTagValue(ev: Event, tag: "e" | "p" | "d") { return ev.tags.find(([t]) => t === tag)?.[1]; } - static filterTagValue(ev: Event, tag: "e" | "p") { + static filterTagValue(ev: Event, tag: "e" | "p" | "d") { return ev.tags.filter(([t]) => t === tag); } } @@ -429,6 +651,7 @@ export const initRaven = (keys: Keys): Raven | undefined => { if (keys) { window.raven = new Raven(keys.priv, keys.pub); + console.log("window raven", window.raven); } return window.raven; diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index d6f5d52715a..215fe18b70e 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -644,7 +644,8 @@ "roles-account": "Account", "roles-role": "Role", "roles-account-title": "Title", - "roles-add": "Add User" + "roles-add": "Add User", + "join-community-chat": "Join Community Chat" }, "community-cover": { "rewards": "rewards", @@ -1661,7 +1662,10 @@ "back": "Back", "no-chat": "No conversation found", "start-chat": "Start chat", - "not-joined": "This user hasn't joined the chat yet" + "not-joined": "This user hasn't joined the chat yet", + "subscribers": "Subscribers", + "invite": "Invite", + "leave:": "Leave" }, "add-image": { "title": "Add Image", diff --git a/src/common/img/svg.tsx b/src/common/img/svg.tsx index 320a12a284b..45dbddc5aa5 100644 --- a/src/common/img/svg.tsx +++ b/src/common/img/svg.tsx @@ -2082,3 +2082,20 @@ export const chatBoxImageSvg = ( ); + +export const chatLeaveSvg = ( + +); diff --git a/src/common/store/actions.ts b/src/common/store/actions.ts index 27d8a7a87f2..ac342178407 100644 --- a/src/common/store/actions.ts +++ b/src/common/store/actions.ts @@ -41,7 +41,14 @@ import { fetchPoints, resetPoints } from "./points"; import { setSigningKey } from "./signing-key"; import { setEntryPin, trackEntryPin } from "./entry-pin-tracker"; import { savePageScroll } from "./persistent-page-scroll"; -import { addDirectContacts, addDirectMessages, resetChat } from "./chat"; +import { + addDirectContacts, + addDirectMessages, + resetChat, + addChannels, + addPublicMessage, + addProfile +} from "./chat"; // @note Do not use it directly export const ACTIONS = { @@ -93,7 +100,10 @@ export const ACTIONS = { setNotificationsSettingsItem, addDirectContacts, addDirectMessages, - resetChat + resetChat, + addChannels, + addPublicMessage, + addProfile }; export const getActions = () => ({ diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts index 8eccd7a3b5f..d130c988be3 100644 --- a/src/common/store/chat/index.ts +++ b/src/common/store/chat/index.ts @@ -1,4 +1,9 @@ -import { DirectMessage } from "./../../../providers/message-provider-types"; +import { + Channel, + DirectMessage, + PublicMessage, + Profile +} from "./../../../providers/message-provider-types"; import { Dispatch } from "redux"; import { @@ -9,12 +14,18 @@ import { DirectContactsType, DirectMessagesAction, ResetChatAction, - directMessagesList + directMessagesList, + ChannelsAction, + PublicMessagesAction, + ProfilesAction } from "./types"; export const initialState: Chat = { directContacts: [], - directMessages: [] + directMessages: [], + channels: [], + publicMessages: [], + profiles: [] }; export default (state: Chat = initialState, action: Actions): Chat => { @@ -51,6 +62,41 @@ export default (state: Chat = initialState, action: Actions): Chat => { case ActionTypes.RESET: return initialState; + case ActionTypes.CHANNELS: { + const { data } = action; + + return { + ...state, + channels: [...state.channels, ...data], + publicMessages: [ + ...state.publicMessages, + ...data.map((channel) => ({ channelId: channel.id, PublicMessage: [] })) + ] + }; + } + + case ActionTypes.PUBLICMESSAGES: { + const { channelId, data } = action; + return { + ...state, + publicMessages: [ + ...state.publicMessages.map((obj) => + obj.channelId === channelId + ? { channelId: channelId, PublicMessage: [...obj.PublicMessage, ...data] } + : obj + ) + ] + }; + } + + case ActionTypes.PROFILES: { + const { data } = action; + return { + ...state, + profiles: [...state.profiles, ...data] + }; + } + default: return state; } @@ -66,9 +112,23 @@ export const addDirectMessages = (peer: string, data: DirectMessage[]) => (dispa }; export const resetChat = () => (dispatch: Dispatch) => { + console.log("Reset chat run in store"); dispatch(resetChatAct()); }; +export const addChannels = (data: Channel[]) => (dispatch: Dispatch) => { + dispatch(addChannelsAct(data)); +}; + +export const addPublicMessage = + (channelId: string, data: PublicMessage[]) => (dispatch: Dispatch) => { + dispatch(addPublicMessagesAct(channelId, data)); + }; + +export const addProfile = (data: Profile[]) => (dispatch: Dispatch) => { + dispatch(addProfilesAct(data)); +}; + /* Action Creators */ export const addDirectContactsAct = (data: DirectContactsType[]): DirectContactsAction => { @@ -86,8 +146,33 @@ export const addDirectMessagesAct = (peer: string, data: DirectMessage[]): Direc }; }; +export const addPublicMessagesAct = ( + channelId: string, + data: PublicMessage[] +): PublicMessagesAction => { + return { + type: ActionTypes.PUBLICMESSAGES, + channelId, + data + }; +}; + export const resetChatAct = (): ResetChatAction => { return { type: ActionTypes.RESET }; }; + +export const addChannelsAct = (data: Channel[]): ChannelsAction => { + return { + type: ActionTypes.CHANNELS, + data + }; +}; + +export const addProfilesAct = (data: Profile[]): ProfilesAction => { + return { + type: ActionTypes.PROFILES, + data + }; +}; diff --git a/src/common/store/chat/types.ts b/src/common/store/chat/types.ts index cbd48ae10bf..5cb02068eb1 100644 --- a/src/common/store/chat/types.ts +++ b/src/common/store/chat/types.ts @@ -1,4 +1,9 @@ -import { DirectMessage } from "./../../../providers/message-provider-types"; +import { + Channel, + DirectMessage, + Profile, + PublicMessage +} from "./../../../providers/message-provider-types"; export interface DirectContactsType { name: string; @@ -10,15 +15,26 @@ export interface directMessagesList { chat: DirectMessage[]; } +export interface publicMessagesList { + channelId: string; + PublicMessage: PublicMessage[]; +} + export interface Chat { directContacts: DirectContactsType[]; directMessages: directMessagesList[]; + channels: Channel[]; + publicMessages: publicMessagesList[]; + profiles: Profile[]; } export enum ActionTypes { DIRECTCONTACTS = "@chat/DIRECTCONTACTS", DIRECTMESSAGES = "@chat/DIRECTMESSAGES", - RESET = "@chat/RESET" + RESET = "@chat/RESET", + CHANNELS = "@chat/CHANNELS", + PUBLICMESSAGES = "@chat/PUBLICMESSAGES", + PROFILES = "@chat/PROFILES" } export interface DirectContactsAction { @@ -36,4 +52,26 @@ export interface DirectMessagesAction { peer: string; } -export type Actions = DirectContactsAction | DirectMessagesAction | ResetChatAction; +export interface PublicMessagesAction { + type: ActionTypes.PUBLICMESSAGES; + data: PublicMessage[]; + channelId: string; +} + +export interface ChannelsAction { + type: ActionTypes.CHANNELS; + data: Channel[]; +} + +export interface ProfilesAction { + type: ActionTypes.PROFILES; + data: Profile[]; +} + +export type Actions = + | DirectContactsAction + | DirectMessagesAction + | ResetChatAction + | ChannelsAction + | PublicMessagesAction + | ProfilesAction; diff --git a/src/providers/message-provider-types.ts b/src/providers/message-provider-types.ts index 2a71c9d2149..ad0dfd3eb68 100644 --- a/src/providers/message-provider-types.ts +++ b/src/providers/message-provider-types.ts @@ -13,6 +13,7 @@ export type Metadata = { name: string; about: string; picture: string; + communityName?: string; }; export type Profile = { id: string; creator: string; created: number } & Metadata; diff --git a/src/providers/message-provider.tsx b/src/providers/message-provider.tsx index 7a8bc98e157..79a53259b79 100644 --- a/src/providers/message-provider.tsx +++ b/src/providers/message-provider.tsx @@ -2,7 +2,14 @@ import React, { useEffect, useState } from "react"; import { ActiveUser } from "../common/store/active-user/types"; import { Chat, DirectContactsType } from "../common/store/chat/types"; -import { DirectMessage, Profile, Keys, DirectContact } from "./message-provider-types"; +import { + DirectMessage, + Profile, + Keys, + DirectContact, + Channel, + PublicMessage +} from "./message-provider-types"; import { initRaven, RavenEvents } from "../common/helper/message-helper"; import { getProfileMetaData, NostrKeys } from "../common/helper/chat-utils"; @@ -21,6 +28,9 @@ export const setNostrkeys = (keys: NostrKeys) => { interface Props { addDirectContacts: (data?: DirectContactsType[]) => void; addDirectMessages: (peer: string, data?: DirectMessage[]) => void; + addPublicMessage: (channelId: string, data?: PublicMessage[]) => void; + addChannels: (data: Channel[]) => void; + addProfile: (data: Profile[]) => void; } const MessageProvider = (props: Props) => { @@ -29,7 +39,8 @@ const MessageProvider = (props: Props) => { const [since, setSince] = useState(0); const [keys, setKeys] = useState(); const [raven, setRaven] = useState(); - const [messageBuffer, setMessageBuffer] = useState([]); + const [directMessageBuffer, setDirectMessageBuffer] = useState([]); + const [publicMessageBuffer, setPublicMessageBuffer] = useState([]); useEffect(() => { window.addEventListener("createRavenInstance", createRavenInstance); @@ -55,6 +66,7 @@ const MessageProvider = (props: Props) => { const createRavenInstance = (e: Event) => { const detail = (e as CustomEvent).detail as NostrKeys; const ravenInstance = initRaven(detail); + console.log("Create rvan instance run"); setRaven(ravenInstance); }; @@ -69,7 +81,10 @@ const MessageProvider = (props: Props) => { const timer = setTimeout( () => { - raven?.listen(Math.floor((since || Date.now()) / 1000)); + raven?.listen( + chat.channels.map((x) => x.id), + Math.floor((since || Date.now()) / 1000) + ); setSince(Date.now()); }, since === 0 ? 500 : 10000 @@ -78,7 +93,7 @@ const MessageProvider = (props: Props) => { return () => { clearTimeout(timer); }; - }, [since, ravenReady, raven]); + }, [since, ravenReady, raven, chat.channels]); // // Ready state handler const handleReadyState = () => { @@ -94,14 +109,36 @@ const MessageProvider = (props: Props) => { }; }, [ravenReady, raven]); - const handleProfileUpdate = (data: DirectContact[]) => { + // Profile update handler + const handleProfileUpdate = (data: Profile[]) => { + console.log("handleProfileUpdate", data); + props.addProfile(data); + // setProfiles([ + // ...profiles.filter(x => data.find(y => x.creator === y.creator) === undefined), + // ...data + // ]); + // const profile = data.find(x => x.creator === keys!.pub); + // if (profile) { + // setProfile(profile); + // } + }; + + useEffect(() => { + raven?.removeListener(RavenEvents.ProfileUpdate, handleProfileUpdate); + raven?.addListener(RavenEvents.ProfileUpdate, handleProfileUpdate); + return () => { + raven?.removeListener(RavenEvents.ProfileUpdate, handleProfileUpdate); + }; + }, [raven, chat.profiles]); + + //Direct contact handler + const handleDirectContact = (data: DirectContact[]) => { const result = [...chat.directContacts]; data.forEach(({ name, pubkey }) => { const isPresent = chat.directContacts.some( (obj) => obj.name === name && obj.pubkey === pubkey ); if (!isPresent) { - //Add the object to the result array if it's not already present in store state. result.push({ name, pubkey }); } }); @@ -111,27 +148,28 @@ const MessageProvider = (props: Props) => { }; useEffect(() => { - raven?.removeListener(RavenEvents.DirectContact, handleProfileUpdate); - raven?.addListener(RavenEvents.DirectContact, handleProfileUpdate); + raven?.removeListener(RavenEvents.DirectContact, handleDirectContact); + raven?.addListener(RavenEvents.DirectContact, handleDirectContact); return () => { - raven?.removeListener(RavenEvents.DirectContact, handleProfileUpdate); + raven?.removeListener(RavenEvents.DirectContact, handleDirectContact); }; }, [raven]); // // Direct message handler const handleDirectMessage = (data: DirectMessage[]) => { - setMessageBuffer((messageBuffer) => [...messageBuffer!, ...data]); + setDirectMessageBuffer((directMessageBuffer) => [...directMessageBuffer!, ...data]); + console.log("HandleDirectMessage", data); raven?.checkProfiles(data.map((x) => x.peer)); }; - const setMessages = () => { - messageBuffer.forEach((obj) => { + const setDirectMessages = () => { + directMessageBuffer.forEach((obj) => { const { peer } = obj; const matchingStateItem = chat.directMessages.find((stateItem) => stateItem.peer === peer); if (matchingStateItem) { if (matchingStateItem.chat.length === 0) { props.addDirectMessages(peer, [obj]); - setMessageBuffer((prevMessageBuffer) => + setDirectMessageBuffer((prevMessageBuffer) => prevMessageBuffer.filter((message) => message.id !== obj.id) ); } else { @@ -143,7 +181,7 @@ const MessageProvider = (props: Props) => { }); if (!itemExists) { props.addDirectMessages(peer, [obj]); - setMessageBuffer((prevMessageBuffer) => + setDirectMessageBuffer((prevMessageBuffer) => prevMessageBuffer.filter((message) => message.id !== obj.id) ); } @@ -154,9 +192,9 @@ const MessageProvider = (props: Props) => { useEffect(() => { if (chat.directContacts.length !== 0) { - setMessages(); + setDirectMessages(); } - }, [chat.directContacts, messageBuffer]); + }, [chat.directContacts, directMessageBuffer]); useEffect(() => { raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); @@ -166,10 +204,95 @@ const MessageProvider = (props: Props) => { }; }, [raven]); + // Channel creation handler + const handleChannelCreation = (data: Channel[]) => { + console.log("handleChannelCreation", data); + + const append = data.filter((x) => chat.channels.find((y) => y.id === x.id) === undefined); + props.addChannels(append); + }; + + useEffect(() => { + raven?.removeListener(RavenEvents.ChannelCreation, handleChannelCreation); + raven?.addListener(RavenEvents.ChannelCreation, handleChannelCreation); + + return () => { + raven?.removeListener(RavenEvents.ChannelCreation, handleChannelCreation); + }; + }, [raven]); + + //Public Message handler + const handlePublicMessage = (data: PublicMessage[]) => { + console.log("handlePublicMessage", data, chat.profiles); + setPublicMessageBuffer((publicMessageBuffer) => [...publicMessageBuffer!, ...data]); + + for (const item of data) { + const isCreatorMatch = chat.profiles.some((firstItem) => firstItem.creator === item.creator); + + if (!isCreatorMatch) { + let uniqueUsers: string[] = []; + for (const item of data) { + if (!uniqueUsers.includes(item.creator)) { + uniqueUsers.push(item.creator); + } + } + raven?.loadProfiles(uniqueUsers); + } + } + }; + + useEffect(() => { + raven?.removeListener(RavenEvents.PublicMessage, handlePublicMessage); + raven?.addListener(RavenEvents.PublicMessage, handlePublicMessage); + + return () => { + raven?.removeListener(RavenEvents.PublicMessage, handlePublicMessage); + }; + }, [raven, chat.profiles, chat.publicMessages]); + + useEffect(() => { + if (chat.channels.length !== 0) { + setPublicMessages(); + } + }, [chat.publicMessages, publicMessageBuffer]); + + const setPublicMessages = () => { + publicMessageBuffer.forEach((obj) => { + const { root } = obj; + const matchingStateItem = chat.publicMessages.find( + (stateItem) => stateItem.channelId === root + ); + if (matchingStateItem) { + if (matchingStateItem.PublicMessage.length === 0) { + props.addPublicMessage(root, [obj]); + setPublicMessageBuffer((prevMessageBuffer) => + prevMessageBuffer.filter((message) => message.id !== obj.id) + ); + } else { + let itemExists = false; + matchingStateItem.PublicMessage.forEach((item) => { + if (item.id === obj.id) { + itemExists = true; + } + }); + if (!itemExists) { + props.addPublicMessage(root, [obj]); + setPublicMessageBuffer((prevMessageBuffer) => + prevMessageBuffer.filter((message) => message.id !== obj.id) + ); + } + } + } + }); + }; + useEffect(() => { return () => { raven?.removeListener(RavenEvents.Ready, handleReadyState); - raven?.removeListener(RavenEvents.DirectContact, handleProfileUpdate); + raven?.removeListener(RavenEvents.ProfileUpdate, handleProfileUpdate); + raven?.removeListener(RavenEvents.ChannelCreation, handleChannelCreation); + raven?.removeListener(RavenEvents.DirectContact, handleDirectContact); + raven?.removeListener(RavenEvents.PublicMessage, handlePublicMessage); raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); }; }, [raven]); From 176818b0e051369f21e219d5e78fa957dba4fdf2 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Mon, 17 Jul 2023 18:33:23 +0500 Subject: [PATCH 019/179] remove commented code --- src/common/components/chat-box/index.tsx | 10 +++++----- .../components/join-community-chat-btn/index.tsx | 10 +++++----- src/providers/message-provider.tsx | 9 --------- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 84220d79e03..8551e1a5c31 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -155,11 +155,11 @@ export default function ChatBox(props: Props) { setShow(!!props.activeUser?.username); }, []); - // useEffect(() => { - // if (window.raven) { - // setHasUserJoinedChat(true); - // } - // }, [window?.raven]); + useEffect(() => { + if (window.raven) { + setHasUserJoinedChat(true); + } + }, [window?.raven]); useEffect(() => { const msgsList = fetchDirectMessages(receiverPubKey!); diff --git a/src/common/components/join-community-chat-btn/index.tsx b/src/common/components/join-community-chat-btn/index.tsx index a275bc322ab..32379dfae0c 100644 --- a/src/common/components/join-community-chat-btn/index.tsx +++ b/src/common/components/join-community-chat-btn/index.tsx @@ -42,11 +42,11 @@ export default function JoinCommunityChatBtn(props: Props) { fetchCommunityProfile(); }, [props.activeUser]); - // useEffect(() => { - // if (window.raven) { - // setHasUserJoinedChat(true); - // } - // }, [window?.raven]); + useEffect(() => { + if (window.raven) { + setHasUserJoinedChat(true); + } + }, [window?.raven]); useEffect(() => { checkIsChatJoined(); diff --git a/src/providers/message-provider.tsx b/src/providers/message-provider.tsx index 79a53259b79..9b9ba83d57a 100644 --- a/src/providers/message-provider.tsx +++ b/src/providers/message-provider.tsx @@ -66,7 +66,6 @@ const MessageProvider = (props: Props) => { const createRavenInstance = (e: Event) => { const detail = (e as CustomEvent).detail as NostrKeys; const ravenInstance = initRaven(detail); - console.log("Create rvan instance run"); setRaven(ravenInstance); }; @@ -113,14 +112,6 @@ const MessageProvider = (props: Props) => { const handleProfileUpdate = (data: Profile[]) => { console.log("handleProfileUpdate", data); props.addProfile(data); - // setProfiles([ - // ...profiles.filter(x => data.find(y => x.creator === y.creator) === undefined), - // ...data - // ]); - // const profile = data.find(x => x.creator === keys!.pub); - // if (profile) { - // setProfile(profile); - // } }; useEffect(() => { From d871c706909e65fbc703613381bd68a4fda8abce Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Tue, 18 Jul 2023 12:57:12 +0500 Subject: [PATCH 020/179] Add option to leave public chat --- src/common/components/chat-box/index.scss | 1 - src/common/components/chat-box/index.tsx | 48 +++++++++++++++---- .../join-community-chat-btn/index.tsx | 35 ++++++++++---- src/common/helper/chat-utils.ts | 2 + src/common/helper/message-helper.ts | 27 ++++++++++- src/common/store/actions.ts | 6 ++- src/common/store/chat/index.ts | 27 +++++++++-- src/common/store/chat/types.ts | 11 ++++- src/providers/message-provider.tsx | 22 ++++++++- 9 files changed, 147 insertions(+), 32 deletions(-) diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss index e95cc1f7c74..b42c63207c2 100644 --- a/src/common/components/chat-box/index.scss +++ b/src/common/components/chat-box/index.scss @@ -132,7 +132,6 @@ } } .community-menu { - margin-top: -15px; svg { @include themify(day) { color: $charcoal-grey; diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 8551e1a5c31..79be6679bde 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -118,6 +118,7 @@ export default function ChatBox(props: Props) { const [activeMessage, setActiveMessage] = useState(""); const [keyDialog, setKeyDialog] = useState(false); const [step, setStep] = useState(0); + const [communities, setCommunities] = useState([]); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -146,8 +147,8 @@ export default function ChatBox(props: Props) { }, [activeMessage]); useEffect(() => { - console.log(currentChannel, "currentChannel"); - }, [currentChannel]); + console.log(props.chat, "chat in store"); + }, [props.chat]); useEffect(() => { // resetProfile(props.activeUser); @@ -155,11 +156,16 @@ export default function ChatBox(props: Props) { setShow(!!props.activeUser?.username); }, []); + // useEffect(() => { + // if (window.raven) { + // setHasUserJoinedChat(true); + // } + // }, [window?.raven]); + useEffect(() => { - if (window.raven) { - setHasUserJoinedChat(true); - } - }, [window?.raven]); + const communities = getCommunities(); + setCommunities(communities); + }, [props.chat.channels, props.chat.leftChannelsList]); useEffect(() => { const msgsList = fetchDirectMessages(receiverPubKey!); @@ -301,6 +307,10 @@ export default function ChatBox(props: Props) { return []; }; + const getCommunities = () => { + return props.chat.channels.filter((item) => !props.chat.leftChannelsList.includes(item.id)); + }; + const fetchCommunityMessages = (channelId: string) => { for (const item of props.chat.publicMessages) { if (item.channelId === channelId) { @@ -603,7 +613,24 @@ export default function ChatBox(props: Props) { > Close -

@@ -944,12 +971,13 @@ export default function ChatBox(props: Props) { ) : ( <> - {props.chat.directContacts.length !== 0 || props.chat.channels.length !== 0 ? ( + {props.chat.directContacts.length !== 0 || + (props.chat.channels.length !== 0 && communities.length !== 0) ? ( - {props.chat.channels.length !== 0 && ( + {props.chat.channels.length !== 0 && communities.length !== 0 && ( <>
Communities
- {props.chat.channels.map((channel) => { + {communities.map((channel) => { return (
diff --git a/src/common/components/join-community-chat-btn/index.tsx b/src/common/components/join-community-chat-btn/index.tsx index 32379dfae0c..cad3da3c5a6 100644 --- a/src/common/components/join-community-chat-btn/index.tsx +++ b/src/common/components/join-community-chat-btn/index.tsx @@ -36,23 +36,23 @@ export default function JoinCommunityChatBtn(props: Props) { useEffect(() => { fetchCommunityProfile(); fetchUserProfileData(); - }, [chat.channels, currentChannel]); + }, [chat.channels, currentChannel, chat.leftChannelsList]); useEffect(() => { fetchCommunityProfile(); }, [props.activeUser]); - useEffect(() => { - if (window.raven) { - setHasUserJoinedChat(true); - } - }, [window?.raven]); + // useEffect(() => { + // if (window.raven) { + // setHasUserJoinedChat(true); + // } + // }, [window?.raven]); useEffect(() => { checkIsChatJoined(); fetchCurrentChannel(); - }, [isJoinChat, props.community, chat.channels]); + }, [isJoinChat, props.community, chat.channels, chat.leftChannelsList]); const fetchUserProfileData = async () => { const profileData = await getProfileMetaData(props.activeUser?.username!); @@ -76,11 +76,15 @@ export default function JoinCommunityChatBtn(props: Props) { const checkIsChatJoined = () => { for (const item of chat.channels) { - if (item.communityName === props.community.name) { + if ( + item.communityName === props.community.name && + !chat.leftChannelsList.includes(currentChannel?.id!) + ) { setIsJoinChat(true); + } else { + setIsJoinChat(false); } } - return []; }; const createCommunityChat = async () => { @@ -113,6 +117,12 @@ export default function JoinCommunityChatBtn(props: Props) { }; const joinCommunityChat = () => { + if (chat.leftChannelsList.includes(currentChannel?.id!)) { + console.log("If true in join community"); + window?.raven?.updateLeftChannelList( + chat.leftChannelsList.filter((x) => x !== currentChannel?.id) + ); + } window?.raven?.loadChannel(currentChannel?.id!); setIsJoinChat(true); }; @@ -149,11 +159,16 @@ export default function JoinCommunityChatBtn(props: Props) { {props.community["context"].role === "owner" ? ( isJoinChat ? ( - ) : hasUserJoinedChat ? ( + ) : hasUserJoinedChat && !isChatEnabled ? ( + ) : !isJoinChat && isChatEnabled ? ( + ) : ( ) diff --git a/src/common/helper/chat-utils.ts b/src/common/helper/chat-utils.ts index 5f55e4fdcc6..9fd09850b6d 100644 --- a/src/common/helper/chat-utils.ts +++ b/src/common/helper/chat-utils.ts @@ -78,3 +78,5 @@ export function notEmpty(value: TValue | null | undefined): value is TVa export const formatMessageTime = (unixTs: number) => moment.unix(unixTs).format("h:mm a"); export const formatMessageDate = (unixTs: number) => moment.unix(unixTs).format("dddd, MMMM Do"); + +export const isSha256 = (s: string) => /^[a-f0-9]{64}$/gi.test(s); diff --git a/src/common/helper/message-helper.ts b/src/common/helper/message-helper.ts index 6d1938f80e2..5f9956a86a4 100644 --- a/src/common/helper/message-helper.ts +++ b/src/common/helper/message-helper.ts @@ -14,7 +14,7 @@ import { import { encrypt, decrypt } from "../../lib/nostr-tools/nip04"; import SimplePool from "../../lib/nostr-tools/pool"; import { signEvent, getEventHash, Event } from "../../lib/nostr-tools/event"; -import { notEmpty } from "./chat-utils"; +import { isSha256, notEmpty } from "./chat-utils"; const relays = { "wss://relay1.nostrchat.io": { read: true, write: true }, @@ -32,6 +32,7 @@ enum NewKinds { export enum RavenEvents { Ready = "ready", ProfileUpdate = "profile_update", + LeftChannelList = "left_channel_list", ChannelCreation = "channel_creation", DirectMessage = "direct_message", DirectContact = "direct_contact", @@ -44,6 +45,7 @@ type EventHandlerMap = { [RavenEvents.DirectMessage]: (data: DirectMessage[]) => void; [RavenEvents.ChannelCreation]: (data: Channel[]) => void; [RavenEvents.PublicMessage]: (data: PublicMessage[]) => void; + [RavenEvents.LeftChannelList]: (data: string[]) => void; [RavenEvents.DirectContact]: (data: DirectContact[]) => void; }; @@ -174,6 +176,12 @@ class Raven extends TypedEventEmitter { }); } + public async updateLeftChannelList(channelIds: string[]) { + console.log(channelIds, "channelIds"); + const tags = [["d", "left-channel-list"]]; + return this.publish(NewKinds.Arbitrary, tags, JSON.stringify(channelIds)); + } + public fetchMessages() { this.directContacts.map((contact: any) => { const filters: Filter[] = [ @@ -225,7 +233,7 @@ class Raven extends TypedEventEmitter { } public loadChannel(id: string) { - // console.log(id, "id"); + console.log(id, "id"); const filters: Filter[] = [ { kinds: [Kind.ChannelCreation], @@ -525,6 +533,21 @@ class Raven extends TypedEventEmitter { this.emit(RavenEvents.ProfileUpdate, profileUpdates); } + const leftChannelListEv = this.eventQueue + .filter( + (x) => + x.kind.toString() === NewKinds.Arbitrary.toString() && + Raven.findTagValue(x, "d") === "left-channel-list" + ) + .sort((a, b) => b.created_at - a.created_at)[0]; + + if (leftChannelListEv) { + const content = Raven.parseJson(leftChannelListEv.content); + if (Array.isArray(content) && content.every((x) => isSha256(x))) { + this.emit(RavenEvents.LeftChannelList, content); + } + } + const channelCreations: Channel[] = this.eventQueue .filter((x) => x.kind === Kind.ChannelCreation) .map((ev) => { diff --git a/src/common/store/actions.ts b/src/common/store/actions.ts index ac342178407..ada5bbba07f 100644 --- a/src/common/store/actions.ts +++ b/src/common/store/actions.ts @@ -47,7 +47,8 @@ import { resetChat, addChannels, addPublicMessage, - addProfile + addProfile, + addleftChannels } from "./chat"; // @note Do not use it directly @@ -103,7 +104,8 @@ export const ACTIONS = { resetChat, addChannels, addPublicMessage, - addProfile + addProfile, + addleftChannels }; export const getActions = () => ({ diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts index d130c988be3..af83baa5514 100644 --- a/src/common/store/chat/index.ts +++ b/src/common/store/chat/index.ts @@ -17,7 +17,8 @@ import { directMessagesList, ChannelsAction, PublicMessagesAction, - ProfilesAction + ProfilesAction, + LeftChannelsAction } from "./types"; export const initialState: Chat = { @@ -25,7 +26,8 @@ export const initialState: Chat = { directMessages: [], channels: [], publicMessages: [], - profiles: [] + profiles: [], + leftChannelsList: [] }; export default (state: Chat = initialState, action: Actions): Chat => { @@ -97,6 +99,15 @@ export default (state: Chat = initialState, action: Actions): Chat => { }; } + case ActionTypes.LEFTCHANNELLIST: { + const { data } = action; + console.log("data in store", data); + return { + ...state, + leftChannelsList: [...data] + }; + } + default: return state; } @@ -112,7 +123,6 @@ export const addDirectMessages = (peer: string, data: DirectMessage[]) => (dispa }; export const resetChat = () => (dispatch: Dispatch) => { - console.log("Reset chat run in store"); dispatch(resetChatAct()); }; @@ -129,6 +139,10 @@ export const addProfile = (data: Profile[]) => (dispatch: Dispatch) => { dispatch(addProfilesAct(data)); }; +export const addleftChannels = (data: string[]) => (dispatch: Dispatch) => { + dispatch(addleftChannelsAct(data)); +}; + /* Action Creators */ export const addDirectContactsAct = (data: DirectContactsType[]): DirectContactsAction => { @@ -176,3 +190,10 @@ export const addProfilesAct = (data: Profile[]): ProfilesAction => { data }; }; + +export const addleftChannelsAct = (data: string[]): LeftChannelsAction => { + return { + type: ActionTypes.LEFTCHANNELLIST, + data + }; +}; diff --git a/src/common/store/chat/types.ts b/src/common/store/chat/types.ts index 5cb02068eb1..cf1f82266dd 100644 --- a/src/common/store/chat/types.ts +++ b/src/common/store/chat/types.ts @@ -26,6 +26,7 @@ export interface Chat { channels: Channel[]; publicMessages: publicMessagesList[]; profiles: Profile[]; + leftChannelsList: string[]; } export enum ActionTypes { @@ -34,7 +35,8 @@ export enum ActionTypes { RESET = "@chat/RESET", CHANNELS = "@chat/CHANNELS", PUBLICMESSAGES = "@chat/PUBLICMESSAGES", - PROFILES = "@chat/PROFILES" + PROFILES = "@chat/PROFILES", + LEFTCHANNELLIST = "@chat/LEFTCHANNELLIST" } export interface DirectContactsAction { @@ -67,6 +69,10 @@ export interface ProfilesAction { type: ActionTypes.PROFILES; data: Profile[]; } +export interface LeftChannelsAction { + type: ActionTypes.LEFTCHANNELLIST; + data: string[]; +} export type Actions = | DirectContactsAction @@ -74,4 +80,5 @@ export type Actions = | ResetChatAction | ChannelsAction | PublicMessagesAction - | ProfilesAction; + | ProfilesAction + | LeftChannelsAction; diff --git a/src/providers/message-provider.tsx b/src/providers/message-provider.tsx index 9b9ba83d57a..8281a71a2e8 100644 --- a/src/providers/message-provider.tsx +++ b/src/providers/message-provider.tsx @@ -31,6 +31,7 @@ interface Props { addPublicMessage: (channelId: string, data?: PublicMessage[]) => void; addChannels: (data: Channel[]) => void; addProfile: (data: Profile[]) => void; + addleftChannels: (data: string[]) => void; } const MessageProvider = (props: Props) => { @@ -110,7 +111,7 @@ const MessageProvider = (props: Props) => { // Profile update handler const handleProfileUpdate = (data: Profile[]) => { - console.log("handleProfileUpdate", data); + // console.log("handleProfileUpdate", data); props.addProfile(data); }; @@ -214,7 +215,7 @@ const MessageProvider = (props: Props) => { //Public Message handler const handlePublicMessage = (data: PublicMessage[]) => { - console.log("handlePublicMessage", data, chat.profiles); + console.log("handlePublicMessage", data); setPublicMessageBuffer((publicMessageBuffer) => [...publicMessageBuffer!, ...data]); for (const item of data) { @@ -277,12 +278,29 @@ const MessageProvider = (props: Props) => { }); }; + // Left channel handler + const handleLeftChannelList = (data: string[]) => { + console.log("handleLeftChannelList", data); + // setLeftChannelList(data); + props.addleftChannels(data); + }; + + useEffect(() => { + raven?.removeListener(RavenEvents.LeftChannelList, handleLeftChannelList); + raven?.addListener(RavenEvents.LeftChannelList, handleLeftChannelList); + + return () => { + raven?.removeListener(RavenEvents.LeftChannelList, handleLeftChannelList); + }; + }, [raven, chat.leftChannelsList]); + useEffect(() => { return () => { raven?.removeListener(RavenEvents.Ready, handleReadyState); raven?.removeListener(RavenEvents.ProfileUpdate, handleProfileUpdate); raven?.removeListener(RavenEvents.ChannelCreation, handleChannelCreation); raven?.removeListener(RavenEvents.DirectContact, handleDirectContact); + raven?.removeListener(RavenEvents.LeftChannelList, handleLeftChannelList); raven?.removeListener(RavenEvents.PublicMessage, handlePublicMessage); raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); }; From cb4fdffd4d7105adaf48ef542d5a8f751ed4d27a Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Tue, 18 Jul 2023 16:06:37 +0500 Subject: [PATCH 021/179] Remove commented code --- src/common/components/chat-box/index.tsx | 33 +++++++------------ .../join-community-chat-btn/index.tsx | 11 +++---- src/providers/message-provider.tsx | 8 ++--- 3 files changed, 20 insertions(+), 32 deletions(-) diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 79be6679bde..e28bfafd5df 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -146,9 +146,9 @@ export default function ChatBox(props: Props) { }; }, [activeMessage]); - useEffect(() => { - console.log(props.chat, "chat in store"); - }, [props.chat]); + // useEffect(() => { + // console.log(props.chat, "chat in store"); + // }, [props.chat]); useEffect(() => { // resetProfile(props.activeUser); @@ -156,11 +156,11 @@ export default function ChatBox(props: Props) { setShow(!!props.activeUser?.username); }, []); - // useEffect(() => { - // if (window.raven) { - // setHasUserJoinedChat(true); - // } - // }, [window?.raven]); + useEffect(() => { + if (window.raven) { + setHasUserJoinedChat(true); + } + }, [window?.raven]); useEffect(() => { const communities = getCommunities(); @@ -576,12 +576,13 @@ export default function ChatBox(props: Props) { const inviteClicked = () => { const textField = document.createElement("textarea"); - textField.innerText = currentChannel?.id!; + const url = `https://ecency.com/community/join?communityId=${currentChannel?.id}`; + textField.innerText = url; document.body.appendChild(textField); textField.select(); document.execCommand("copy"); textField.remove(); - success("Channel Id copied into clipboard"); + success("Link copied into clipboard"); }; const LeaveModal = () => { @@ -594,15 +595,6 @@ export default function ChatBox(props: Props) {
Are you sure?

- {/* { - e.preventDefault(); - setKeyDialog(false); - }} - > - {_t("g.back")} - */} )} @@ -1052,7 +1104,7 @@ export default function ChatBox(props: Props) { )}

- {showSpinner && } + {inProgress && } {(currentUser || isCommunity) && (
@@ -1061,15 +1113,7 @@ export default function ChatBox(props: Props) {
{emoticonHappyOutlineSvg}
{ handleEmojiSelection(e); }} @@ -1089,15 +1133,7 @@ export default function ChatBox(props: Props) { {shGif && ( )} - {showSearchUser && ( - - )} - {keyDialog && ( { @@ -112,11 +121,19 @@ const MyDropDown = (props: Props) => { const { label, float, items } = props; + const menuDownStyle: MenuDownStyle = { + width: "28px", + height: "28px", + ...(props.style && props.style) // Merge the passed style props if available + }; + const child: JSX.Element = typeof label === "string" ? (
{label &&
{label}
} -
{props?.icon || menuDownSvg}
+
+ {props?.icon || menuDownSvg} +
) : ( label @@ -203,7 +220,8 @@ export default (p: Props) => { className: p?.className, withPadding: p?.withPadding, menuHide: p?.menuHide, - noMarginTop: p?.noMarginTop + noMarginTop: p?.noMarginTop, + style: p?.style }; return ; diff --git a/src/common/components/join-community-chat-btn/index.tsx b/src/common/components/join-community-chat-btn/index.tsx index 1f54a0893cb..b378282cb92 100644 --- a/src/common/components/join-community-chat-btn/index.tsx +++ b/src/common/components/join-community-chat-btn/index.tsx @@ -43,10 +43,10 @@ export default function JoinCommunityChatBtn(props: Props) { }, [props.activeUser]); useEffect(() => { - if (window.raven) { + if (window.messageService) { setHasUserJoinedChat(true); } - }, [window?.raven]); + }, [window?.messageService]); useEffect(() => { checkIsChatJoined(); @@ -56,15 +56,15 @@ export default function JoinCommunityChatBtn(props: Props) { const fetchUserProfileData = async () => { const profileData = await getProfileMetaData(props.activeUser?.username!); - const hasNoStrKey = profileData.hasOwnProperty("noStrKey"); + const hasNoStrKey = profileData && profileData.hasOwnProperty("noStrKey"); setHasUserJoinedChat(hasNoStrKey); }; const fetchCommunityProfile = async () => { const communityProfile = await getProfileMetaData(props.community?.name); - const haschannelMetaData = communityProfile.hasOwnProperty("channel"); + const haschannelMetaData = communityProfile && communityProfile.hasOwnProperty("channel"); setIsChatEnabled(haschannelMetaData); - const hasNoStrKey = communityProfile.hasOwnProperty("noStrKey"); + const hasNoStrKey = communityProfile && communityProfile.hasOwnProperty("noStrKey"); if (!currentChannel) { setCurrentChannel(communityProfile.channel); } @@ -91,7 +91,7 @@ export default function JoinCommunityChatBtn(props: Props) { const { community } = props; try { setInProgress(true); - const data = await window?.raven?.createChannel({ + const data = await window?.messageService?.createChannel({ name: community.title, about: community.description, communityName: community.name, @@ -118,11 +118,11 @@ export default function JoinCommunityChatBtn(props: Props) { const joinCommunityChat = () => { if (chat.leftChannelsList.includes(currentChannel?.id!)) { - window?.raven?.updateLeftChannelList( + window?.messageService?.updateLeftChannelList( chat.leftChannelsList.filter((x) => x !== currentChannel?.id) ); } - window?.raven?.loadChannel(currentChannel?.id!); + window?.messageService?.loadChannel(currentChannel?.id!); setIsJoinChat(true); }; @@ -143,7 +143,11 @@ export default function JoinCommunityChatBtn(props: Props) { await setProfileMetaData(props.activeUser, keys); setHasUserJoinedChat(true); setNostrkeys(keys); - window.raven?.updateProfile({ name: props.activeUser?.username!, about: "", picture: "" }); + window.messageService?.updateProfile({ + name: props.activeUser?.username!, + about: "", + picture: "" + }); setInProgress(false); fetchCommunityProfile(); resetChat(); @@ -163,7 +167,7 @@ export default function JoinCommunityChatBtn(props: Props) { {inProgress && chatButtonSpinner} Start Community Chat - ) : !isJoinChat && isChatEnabled ? ( + ) : !isJoinChat && isChatEnabled && hasUserJoinedChat ? ( + +

+ + ); + } else { + if (isCommunityAlreadyJoined) { + return ( + <> +
+
+

You have already joined this community

+
+
+

+ +

+ + ); + } else { + return ( + <> +
+
+

Please Join the Chat to Continue

+
+
+ {inProgress && } +

+ +

+ + ); + } + } + }; + return community && account ? ( <> + {isJoinCommunity && ( + + setIsJoinCommunity(false)} + keyboard={false} + className="authorities-dialog modal-thin-header" + size="lg" + > + + {joinCommunityModal()} + + + )} diff --git a/src/common/routes.ts b/src/common/routes.ts index 4bf63ecfe99..a479d0d98ba 100644 --- a/src/common/routes.ts +++ b/src/common/routes.ts @@ -27,7 +27,9 @@ export default { COMMUNITIES: `/communities`, COMMUNITIES_CREATE: `/communities/create`, COMMUNITIES_CREATE_HS: `/communities/create-hs`, - COMMUNITY: `/:filter(${entryFilters.join("|")}|subscribers|activities|roles)/:name(hive-[\\d]+)`, + COMMUNITY: `/:filter(${entryFilters.join( + "|" + )}|subscribers|activities|roles)/:name(hive-[\\d]+)(/:communityid)?`, SUBMIT: `/submit`, EDIT: `/:username(@[\\w\\.\\d-]+)/:permlink/edit`, EDIT_DRAFT: `/draft/:draftId`, diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts index af83baa5514..2b452360728 100644 --- a/src/common/store/chat/index.ts +++ b/src/common/store/chat/index.ts @@ -101,7 +101,6 @@ export default (state: Chat = initialState, action: Actions): Chat => { case ActionTypes.LEFTCHANNELLIST: { const { data } = action; - console.log("data in store", data); return { ...state, leftChannelsList: [...data] diff --git a/src/providers/message-provider.tsx b/src/providers/message-provider.tsx index 88f9c7ee875..e169032a40c 100644 --- a/src/providers/message-provider.tsx +++ b/src/providers/message-provider.tsx @@ -11,7 +11,7 @@ import { PublicMessage } from "./message-provider-types"; -import { initRaven, RavenEvents } from "../common/helper/message-helper"; +import { initMessageService, MessageEvents } from "../common/helper/message-service"; import { getProfileMetaData, NostrKeys } from "../common/helper/chat-utils"; import { useMappedStore } from "../common/store/use-mapped-store"; @@ -21,7 +21,7 @@ export const setNostrkeys = (keys: NostrKeys) => { pub: keys.pub, priv: keys.priv }; - const ev = new CustomEvent("createRavenInstance", { detail }); + const ev = new CustomEvent("createMSInstance", { detail }); window.dispatchEvent(ev); }; @@ -36,25 +36,25 @@ interface Props { const MessageProvider = (props: Props) => { const { activeUser, chat } = useMappedStore(); - const [ravenReady, setRavenReady] = useState(false); + const [messageServiceReady, setMessageServiceReady] = useState(false); const [since, setSince] = useState(0); const [keys, setKeys] = useState(); - const [raven, setRaven] = useState(); + const [messageService, setMessageService] = useState(); const [directMessageBuffer, setDirectMessageBuffer] = useState([]); const [publicMessageBuffer, setPublicMessageBuffer] = useState([]); useEffect(() => { - window.addEventListener("createRavenInstance", createRavenInstance); + window.addEventListener("createMSInstance", createMSInstance); return () => { - window.removeEventListener("createRavenInstance", createRavenInstance); + window.removeEventListener("createMSInstance", createMSInstance); }; }, []); useEffect(() => { - if (!window.raven && keys) { - const ravenInstance = initRaven(keys); - setRaven(ravenInstance); + if (!window.messageService && keys) { + const messageServiceInstance = initMessageService(keys); + setMessageService(messageServiceInstance); } }, [keys]); @@ -64,10 +64,10 @@ const MessageProvider = (props: Props) => { } }, [activeUser]); - const createRavenInstance = (e: Event) => { + const createMSInstance = (e: Event) => { const detail = (e as CustomEvent).detail as NostrKeys; - const ravenInstance = initRaven(detail); - setRaven(ravenInstance); + const messageServiceInstance = initMessageService(detail); + setMessageService(messageServiceInstance); }; const getNostrKeys = async (activeUser: ActiveUser) => { @@ -77,11 +77,11 @@ const MessageProvider = (props: Props) => { //Listen for events in an interval. useEffect(() => { - if (!ravenReady) return; + if (!messageServiceReady) return; const timer = setTimeout( () => { - raven?.listen( + messageService?.listen( chat.channels.map((x) => x.id), Math.floor((since || Date.now()) / 1000) ); @@ -93,21 +93,21 @@ const MessageProvider = (props: Props) => { return () => { clearTimeout(timer); }; - }, [since, ravenReady, raven, chat.channels]); + }, [since, messageServiceReady, messageService, chat.channels]); // // Ready state handler const handleReadyState = () => { - setRavenReady(true); + setMessageServiceReady(true); }; useEffect(() => { - raven?.removeListener(RavenEvents.Ready, handleReadyState); - raven?.addListener(RavenEvents.Ready, handleReadyState); + messageService?.removeListener(MessageEvents.Ready, handleReadyState); + messageService?.addListener(MessageEvents.Ready, handleReadyState); return () => { - raven?.removeListener(RavenEvents.Ready, handleReadyState); + messageService?.removeListener(MessageEvents.Ready, handleReadyState); }; - }, [ravenReady, raven]); + }, [messageServiceReady, messageService]); // Profile update handler const handleProfileUpdate = (data: Profile[]) => { @@ -116,12 +116,12 @@ const MessageProvider = (props: Props) => { }; useEffect(() => { - raven?.removeListener(RavenEvents.ProfileUpdate, handleProfileUpdate); - raven?.addListener(RavenEvents.ProfileUpdate, handleProfileUpdate); + messageService?.removeListener(MessageEvents.ProfileUpdate, handleProfileUpdate); + messageService?.addListener(MessageEvents.ProfileUpdate, handleProfileUpdate); return () => { - raven?.removeListener(RavenEvents.ProfileUpdate, handleProfileUpdate); + messageService?.removeListener(MessageEvents.ProfileUpdate, handleProfileUpdate); }; - }, [raven, chat.profiles]); + }, [messageService, chat.profiles]); //Direct contact handler const handleDirectContact = (data: DirectContact[]) => { @@ -140,18 +140,18 @@ const MessageProvider = (props: Props) => { }; useEffect(() => { - raven?.removeListener(RavenEvents.DirectContact, handleDirectContact); - raven?.addListener(RavenEvents.DirectContact, handleDirectContact); + messageService?.removeListener(MessageEvents.DirectContact, handleDirectContact); + messageService?.addListener(MessageEvents.DirectContact, handleDirectContact); return () => { - raven?.removeListener(RavenEvents.DirectContact, handleDirectContact); + messageService?.removeListener(MessageEvents.DirectContact, handleDirectContact); }; - }, [raven]); + }, [messageService]); // // Direct message handler const handleDirectMessage = (data: DirectMessage[]) => { setDirectMessageBuffer((directMessageBuffer) => [...directMessageBuffer!, ...data]); // console.log("HandleDirectMessage", data); - raven?.checkProfiles(data.map((x) => x.peer)); + messageService?.checkProfiles(data.map((x) => x.peer)); }; const setDirectMessages = () => { @@ -189,12 +189,12 @@ const MessageProvider = (props: Props) => { }, [chat.directContacts, directMessageBuffer]); useEffect(() => { - raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); - raven?.addListener(RavenEvents.DirectMessage, handleDirectMessage); + messageService?.removeListener(MessageEvents.DirectMessage, handleDirectMessage); + messageService?.addListener(MessageEvents.DirectMessage, handleDirectMessage); return () => { - raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); + messageService?.removeListener(MessageEvents.DirectMessage, handleDirectMessage); }; - }, [raven]); + }, [messageService]); // Channel creation handler const handleChannelCreation = (data: Channel[]) => { @@ -205,13 +205,13 @@ const MessageProvider = (props: Props) => { }; useEffect(() => { - raven?.removeListener(RavenEvents.ChannelCreation, handleChannelCreation); - raven?.addListener(RavenEvents.ChannelCreation, handleChannelCreation); + messageService?.removeListener(MessageEvents.ChannelCreation, handleChannelCreation); + messageService?.addListener(MessageEvents.ChannelCreation, handleChannelCreation); return () => { - raven?.removeListener(RavenEvents.ChannelCreation, handleChannelCreation); + messageService?.removeListener(MessageEvents.ChannelCreation, handleChannelCreation); }; - }, [raven]); + }, [messageService]); //Public Message handler const handlePublicMessage = (data: PublicMessage[]) => { @@ -228,19 +228,19 @@ const MessageProvider = (props: Props) => { uniqueUsers.push(item.creator); } } - raven?.loadProfiles(uniqueUsers); + messageService?.loadProfiles(uniqueUsers); } } }; useEffect(() => { - raven?.removeListener(RavenEvents.PublicMessage, handlePublicMessage); - raven?.addListener(RavenEvents.PublicMessage, handlePublicMessage); + messageService?.removeListener(MessageEvents.PublicMessage, handlePublicMessage); + messageService?.addListener(MessageEvents.PublicMessage, handlePublicMessage); return () => { - raven?.removeListener(RavenEvents.PublicMessage, handlePublicMessage); + messageService?.removeListener(MessageEvents.PublicMessage, handlePublicMessage); }; - }, [raven, chat.profiles, chat.publicMessages]); + }, [messageService, chat.profiles, chat.publicMessages]); useEffect(() => { if (chat.channels.length !== 0) { @@ -286,25 +286,25 @@ const MessageProvider = (props: Props) => { }; useEffect(() => { - raven?.removeListener(RavenEvents.LeftChannelList, handleLeftChannelList); - raven?.addListener(RavenEvents.LeftChannelList, handleLeftChannelList); + messageService?.removeListener(MessageEvents.LeftChannelList, handleLeftChannelList); + messageService?.addListener(MessageEvents.LeftChannelList, handleLeftChannelList); return () => { - raven?.removeListener(RavenEvents.LeftChannelList, handleLeftChannelList); + messageService?.removeListener(MessageEvents.LeftChannelList, handleLeftChannelList); }; - }, [raven, chat.leftChannelsList]); + }, [messageService, chat.leftChannelsList]); useEffect(() => { return () => { - raven?.removeListener(RavenEvents.Ready, handleReadyState); - raven?.removeListener(RavenEvents.ProfileUpdate, handleProfileUpdate); - raven?.removeListener(RavenEvents.ChannelCreation, handleChannelCreation); - raven?.removeListener(RavenEvents.DirectContact, handleDirectContact); - raven?.removeListener(RavenEvents.LeftChannelList, handleLeftChannelList); - raven?.removeListener(RavenEvents.PublicMessage, handlePublicMessage); - raven?.removeListener(RavenEvents.DirectMessage, handleDirectMessage); + messageService?.removeListener(MessageEvents.Ready, handleReadyState); + messageService?.removeListener(MessageEvents.ProfileUpdate, handleProfileUpdate); + messageService?.removeListener(MessageEvents.ChannelCreation, handleChannelCreation); + messageService?.removeListener(MessageEvents.DirectContact, handleDirectContact); + messageService?.removeListener(MessageEvents.LeftChannelList, handleLeftChannelList); + messageService?.removeListener(MessageEvents.PublicMessage, handlePublicMessage); + messageService?.removeListener(MessageEvents.DirectMessage, handleDirectMessage); }; - }, [raven]); + }, [messageService]); return <>{}; }; From e18f2c61c7cdaf2c17cd5f4d4d9071a778ecf378 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Wed, 19 Jul 2023 16:45:35 +0500 Subject: [PATCH 023/179] Remove commented code --- src/common/components/community-settings/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/common/components/community-settings/index.tsx b/src/common/components/community-settings/index.tsx index a491319ec3c..29ac5931536 100644 --- a/src/common/components/community-settings/index.tsx +++ b/src/common/components/community-settings/index.tsx @@ -180,11 +180,9 @@ export class CommunitySettings extends BaseComponent { }; this.stateSet({ inProgress: true }); - console.log("Submit run"); return updateCommunity(activeUser.username, community.name, newProps) .then(() => { const nCom: Community = { ...clone(community), ...newProps }; - console.log(nCom, "New dara"); addCommunity(nCom); onHide(); }) From 544b837948358bc70c8c4ebae0be8f04b45a5f57 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Wed, 19 Jul 2023 17:09:39 +0500 Subject: [PATCH 024/179] Remove default styling from ts file and move it into scss file of some components --- .../components/chat-box/chat-constants.ts | 4 ++++ src/common/components/chat-box/index.tsx | 7 +++---- src/common/components/dropdown/_index.scss | 3 +++ src/common/components/dropdown/index.tsx | 4 +--- .../components/emoji-picker/_index.scss | 9 ++++++++- src/common/components/emoji-picker/index.tsx | 20 +------------------ src/common/components/gif-picker/_index.scss | 8 ++++++++ src/common/components/gif-picker/index.tsx | 19 +----------------- 8 files changed, 29 insertions(+), 45 deletions(-) diff --git a/src/common/components/chat-box/chat-constants.ts b/src/common/components/chat-box/chat-constants.ts index a03c1a7d895..f0605f2bae8 100644 --- a/src/common/components/chat-box/chat-constants.ts +++ b/src/common/components/chat-box/chat-constants.ts @@ -23,6 +23,10 @@ export const DropDownStyle = { height: "40px" }; +export const GifImagesStyle = { + width: "170px" +}; + export const NOSTRKEY = "noStrKey"; export const UPLOADING = "Uploading"; export const GIPHGY = "giphy"; diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 802d39c4a14..524f9d3a350 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -52,7 +52,8 @@ import { GifPickerStyle, GIPHGY, NOSTRKEY, - UPLOADING + UPLOADING, + GifImagesStyle } from "./chat-constants"; import { dateToFormatted } from "../../helper/parse-date"; @@ -1134,9 +1135,7 @@ export default function ChatBox(props: Props) { {shGif && ( { setShGif(gifState!); diff --git a/src/common/components/dropdown/_index.scss b/src/common/components/dropdown/_index.scss index 0a212a14c76..b8191971127 100644 --- a/src/common/components/dropdown/_index.scss +++ b/src/common/components/dropdown/_index.scss @@ -25,7 +25,10 @@ background: transparent; border-radius: 50%; display: flex; + height: 28px; + height: 28px; justify-content: center; + width: 28px; user-select: none; svg { diff --git a/src/common/components/dropdown/index.tsx b/src/common/components/dropdown/index.tsx index 9aa17da3f0e..ab6399aec24 100644 --- a/src/common/components/dropdown/index.tsx +++ b/src/common/components/dropdown/index.tsx @@ -121,9 +121,7 @@ const MyDropDown = (props: Props) => { const { label, float, items } = props; - const menuDownStyle: MenuDownStyle = { - width: "28px", - height: "28px", + const menuDownStyle = { ...(props.style && props.style) // Merge the passed style props if available }; diff --git a/src/common/components/emoji-picker/_index.scss b/src/common/components/emoji-picker/_index.scss index 4f179d5c809..c478b0df909 100644 --- a/src/common/components/emoji-picker/_index.scss +++ b/src/common/components/emoji-picker/_index.scss @@ -4,7 +4,14 @@ @import "src/style/mixins"; .emoji-picker { - + width: 280px; + position: absolute; + right: 0; + z-index: 100; + border-bottom-right-radius: 8px; + border-bottom-left-radius: 8px; + padding: 14px 6px; + @media (max-width: $sm-break) { width: 160px !important; } diff --git a/src/common/components/emoji-picker/index.tsx b/src/common/components/emoji-picker/index.tsx index f2b08aba0ab..24e92f44267 100644 --- a/src/common/components/emoji-picker/index.tsx +++ b/src/common/components/emoji-picker/index.tsx @@ -55,17 +55,6 @@ interface State { cache: EmojiCacheItem[] | null; filter: string; } - -interface EmojiPickerStyle { - width: string; - position?: "absolute" | "relative" | "fixed" | "static"; - right: string | number; - zIndex: number; - borderBottomRightRadius: string; - borderBottomLeftRadius: string; - padding: string; -} - export default class EmojiPicker extends BaseComponent { state: State = { data: null, @@ -163,14 +152,7 @@ export default class EmojiPicker extends BaseComponent { const recent: string[] = ls.get("recent-emoji", []); - const emojiPickerStyle: EmojiPickerStyle = { - width: "280px", - position: "absolute", - right: "0", - zIndex: 1000, - borderBottomRightRadius: "8px", - borderBottomLeftRadius: "8px", - padding: "14px 6px", + const emojiPickerStyle = { ...(this.props.style && this.props.style) // Merge the passed style props if available }; diff --git a/src/common/components/gif-picker/_index.scss b/src/common/components/gif-picker/_index.scss index 2c94546b951..edba9650abc 100644 --- a/src/common/components/gif-picker/_index.scss +++ b/src/common/components/gif-picker/_index.scss @@ -4,6 +4,14 @@ @import "src/style/mixins"; .emoji-picker.gif { + // width: 280px; + width: 430px; + position: absolute; + right: 0; + z-index: 12; + border-bottom-right-radius: 8px; + border-bottom-left-radius: 8px; + padding: 14px 6px; @media (max-width: $sm-break) { width: 160px !important; diff --git a/src/common/components/gif-picker/index.tsx b/src/common/components/gif-picker/index.tsx index 774c6f481a5..729bdbea4cc 100644 --- a/src/common/components/gif-picker/index.tsx +++ b/src/common/components/gif-picker/index.tsx @@ -34,16 +34,6 @@ interface State { total_count: number; } -interface GifPickerStyle { - width: string; - position?: "absolute" | "relative" | "fixed" | "static"; - right: string | number; - zIndex: number; - borderBottomRightRadius: string; - borderBottomLeftRadius: string; - padding: string; -} - interface GifImageStyle { width: string; } @@ -225,14 +215,7 @@ export default class GifPicker extends BaseComponent { return null; } - const gifPickerStyle: GifPickerStyle = { - width: "430px", - position: "absolute", - right: "0", - zIndex: 12, - borderBottomRightRadius: "8px", - borderBottomLeftRadius: "8px", - padding: "14px 6px", + const gifPickerStyle = { ...(this.props.style && this.props.style) }; From ebb60f1e04dc2658d5a1da9b808a6201b12facfb Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Wed, 19 Jul 2023 19:12:03 +0500 Subject: [PATCH 025/179] change color scheme for message sender --- src/common/components/chat-box/index.scss | 30 +++++++++---------- .../join-community-chat-btn/index.tsx | 5 ++-- src/common/pages/community-functional.tsx | 3 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss index 32ff6ccb3c2..9fb5d7a12b5 100644 --- a/src/common/components/chat-box/index.scss +++ b/src/common/components/chat-box/index.scss @@ -483,12 +483,21 @@ margin-right: 15px; .sender-message-content { + border-radius: 10px 10px 0px; + max-width: 280px; + word-wrap: break-word; + margin-bottom: 0; + color: $charcoal-grey; + font-size: 16px; + font-weight: 400; + padding: 8px 12px 8px 12px; + a { + text-decoration: underline; + color: white; + } @include themify(day) { - // background: rgb(204, 247, 255); - background: $light-periwinkle; - // background-color: rgb(0, 132, 255); - color: $dark-indigo; - // color: $white; + background-color: rgb(0, 132, 255); + color: $white; } @include themify(night) { background: $dark-indigo; @@ -507,17 +516,6 @@ max-width: 100%; } } - - border-radius: 10px 10px 0px; - max-width: 280px; - // background-color: rgb(0, 132, 255); - - word-wrap: break-word; - margin-bottom: 0; - color: $charcoal-grey; - font-size: 16px; - font-weight: 400; - padding: 8px 12px 8px 12px; } } } diff --git a/src/common/components/join-community-chat-btn/index.tsx b/src/common/components/join-community-chat-btn/index.tsx index b378282cb92..629967a07b4 100644 --- a/src/common/components/join-community-chat-btn/index.tsx +++ b/src/common/components/join-community-chat-btn/index.tsx @@ -16,6 +16,7 @@ import { setProfileMetaData } from "../../helper/chat-utils"; import { setNostrkeys } from "../../../providers/message-provider"; +import { NOSTRKEY } from "../chat-box/chat-constants"; interface Props { history: History; @@ -56,7 +57,7 @@ export default function JoinCommunityChatBtn(props: Props) { const fetchUserProfileData = async () => { const profileData = await getProfileMetaData(props.activeUser?.username!); - const hasNoStrKey = profileData && profileData.hasOwnProperty("noStrKey"); + const hasNoStrKey = profileData && profileData.hasOwnProperty(NOSTRKEY); setHasUserJoinedChat(hasNoStrKey); }; @@ -64,7 +65,7 @@ export default function JoinCommunityChatBtn(props: Props) { const communityProfile = await getProfileMetaData(props.community?.name); const haschannelMetaData = communityProfile && communityProfile.hasOwnProperty("channel"); setIsChatEnabled(haschannelMetaData); - const hasNoStrKey = communityProfile && communityProfile.hasOwnProperty("noStrKey"); + const hasNoStrKey = communityProfile && communityProfile.hasOwnProperty(NOSTRKEY); if (!currentChannel) { setCurrentChannel(communityProfile.channel); } diff --git a/src/common/pages/community-functional.tsx b/src/common/pages/community-functional.tsx index b8344b6b722..ba3006c1696 100644 --- a/src/common/pages/community-functional.tsx +++ b/src/common/pages/community-functional.tsx @@ -12,6 +12,7 @@ import ScrollToTop from "../components/scroll-to-top"; import Theme from "../components/theme"; import Feedback from "../components/feedback"; import defaults from "../constants/defaults.json"; +import { NOSTRKEY } from "../components/chat-box/chat-constants"; import CommunitySubscribers from "../components/community-subscribers"; import CommunityActivities from "../components/community-activities"; import LinearProgress from "../components/linear-progress"; @@ -218,7 +219,7 @@ export const CommunityPage = (props: Props) => { const fetchUserProfileData = async () => { if (props.activeUser) { const profileData = await getProfileMetaData(props.activeUser?.username!); - const hasNoStrKey = profileData && profileData.hasOwnProperty("noStrKey"); + const hasNoStrKey = profileData && profileData.hasOwnProperty(NOSTRKEY); setHasUserJoinedChat(hasNoStrKey); } }; From 088b0eecc73b9fcc8afcc268052e05e0af9cef1c Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Fri, 21 Jul 2023 15:50:25 +0500 Subject: [PATCH 026/179] Community chat authorities | WIP --- src/common/components/chat-box/index.scss | 22 +- src/common/components/chat-box/index.tsx | 227 ++++++++++++++++-- .../join-community-chat-btn/index.tsx | 45 +++- src/common/helper/message-service.ts | 41 +++- src/common/img/svg.tsx | 16 ++ src/common/pages/community-functional.tsx | 2 +- src/providers/message-provider-types.ts | 7 + src/providers/message-provider.tsx | 24 +- 8 files changed, 350 insertions(+), 34 deletions(-) diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss index 9fb5d7a12b5..dd5a689a4c2 100644 --- a/src/common/components/chat-box/index.scss +++ b/src/common/components/chat-box/index.scss @@ -69,7 +69,7 @@ } .message-header-content { - margin-top: 0.8rem; + margin-top: 0.9rem; font-weight: 700; line-height: 24px; font-size: 21px; @@ -103,7 +103,7 @@ justify-content: center; text-align: center; border-radius: 50%; - padding-top: 11px; + padding-top: 13px; display: flex; svg { @@ -121,7 +121,7 @@ display: flex; justify-content: center; text-align: center; - padding-top: 6px; + padding-top: 7px; width: 40px; height: 40px; border-radius: 50%; @@ -427,7 +427,7 @@ color: rgb(138, 141, 145); font-weight: 400; font-size: 12px; - margin-left: 9px; + margin-left: 4px; margin-bottom: 2px; .username-community { margin-right: 8px; @@ -604,6 +604,20 @@ } } } + .add-dialog-header { + margin-bottom: 20px; + } + .community-chat-role-edit-dialog-content { + .add-user-role-form { + margin: 15px 0; + } + .user { + display: flex; + } + .username { + margin: 10px 0 0 10px; + } + } } .profile-box { diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 524f9d3a350..2d7c39b7131 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -3,12 +3,14 @@ import useDebounce from "react-use/lib/useDebounce"; import { History } from "history"; import { Button, + Col, Form, FormControl, InputGroup, Modal, OverlayTrigger, Popover, + Row, Spinner } from "react-bootstrap"; import { Link } from "react-router-dom"; @@ -17,9 +19,14 @@ import mediumZoom, { Zoom } from "medium-zoom"; import { ActiveUser } from "../../store/active-user/types"; import { Chat, DirectContactsType } from "../../store/chat/types"; -import { Community } from "../../store/communities/types"; +import { Community, ROLES } from "../../store/communities/types"; import { Global, Theme } from "../../store/global/types"; -import { Channel, DirectMessage, PublicMessage } from "../../../providers/message-provider-types"; +import { + Channel, + communityModerator, + DirectMessage, + PublicMessage +} from "../../../providers/message-provider-types"; import Tooltip from "../tooltip"; import UserAvatar from "../user-avatar"; @@ -43,7 +50,8 @@ import { chatBoxImageSvg, linkSvg, KebabMenu, - chatLeaveSvg + chatLeaveSvg, + editSVG } from "../../img/svg"; import { @@ -93,6 +101,7 @@ interface Props { } let zoom: Zoom | null = null; +const roles = [ROLES.ADMIN, ROLES.MOD]; export default function ChatBox(props: Props) { const prevPropsRef = useRef(props); @@ -129,6 +138,10 @@ export default function ChatBox(props: Props) { const [communities, setCommunities] = useState([]); const [searchtext, setSearchText] = useState(""); const [userList, setUserList] = useState([]); + const [user, setUser] = useState(""); + const [role, setRole] = useState("admin"); + const [addRoleError, setAddRoleError] = useState(""); + const [moderator, setModerator] = useState(); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -166,11 +179,11 @@ export default function ChatBox(props: Props) { setShow(!!props.activeUser?.username); }, []); - useEffect(() => { - if (window.messageService) { - setHasUserJoinedChat(true); - } - }, [window?.messageService]); + // useEffect(() => { + // if (window.messageService) { + // setHasUserJoinedChat(true); + // } + // }, [window?.messageService]); useEffect(() => { const communities = getCommunities(props.chat.channels, props.chat.leftChannelsList); @@ -263,8 +276,12 @@ export default function ChatBox(props: Props) { useDebounce( async () => { - const resp = await lookupAccounts(searchtext, 7); - setUserList(resp); + if (searchtext.length !== 0) { + const resp = await lookupAccounts(searchtext, 7); + setUserList(resp); + } else { + setUserList([]); + } }, 500, [searchtext] @@ -605,7 +622,7 @@ export default function ChatBox(props: Props) {

Confirmaton

-
Are you sure?
+
Are you sure to leave this community?

+

+
+ + + + + + + + + {currentChannel?.communityModerators!.map((t, i) => { + // const [username, role, title] = t; + // const canEdit = roles && roles.includes(role); + return ( + + + + + ); + })} + +
{_t("community.roles-account")}{_t("community.roles-role")}
+ + {" "} + @{t.name} + + + + {roles.map((r, i) => ( + + ))} + +
+
+ + ); + }; + + useDebounce( + async () => { + if (user.length === 0) { + setAddRoleError(""); + setInProgress(false); + return; + } + + try { + const profileData = await getProfileMetaData(user); + if (profileData && profileData.hasOwnProperty(NOSTRKEY)) { + const alreadyExists = currentChannel?.communityModerators?.some( + (moderator) => moderator.name === profileData.name + ); + if (alreadyExists) { + setAddRoleError("You have already assigned some rule to this user."); + setInProgress(false); + return; + } + const moderator = { + name: profileData.name, + pubkey: profileData.noStrKey.pub, + role: role + }; + setModerator(moderator); + setAddRoleError(""); + } else { + setAddRoleError("You cannot set this user because this user hasn't joined the chat yet."); + } + } catch (err) { + error(err as string); + } + + setInProgress(false); + }, + 200, + [user, role] + ); + + const userChanged = (e: React.ChangeEvent) => { + const { value: user } = e.target; + setUser(user); + console.log(); + setInProgress(true); + }; + + const addNewRole = () => { + const updatedRoles = [...(currentChannel?.communityModerators || []), moderator!]; + + console.log("updatedRoles", currentChannel); + const updatedMetaData = { + name: currentChannel?.name!, + about: currentChannel?.about!, + picture: "", + communityName: currentChannel?.communityName!, + communityModerators: updatedRoles + }; + + console.log(updatedMetaData, "updatedMetaData"); + window.messageService?.updateChannel(currentChannel!, updatedMetaData).then((resp) => { + console.log(resp, "resp"); + }); + }; + + const roleChanged = (e: React.ChangeEvent) => { + const { value: role } = e.target; + setRole(role); + }; + + const leaveClicked = () => { setKeyDialog(true); setStep(1); }; + const editRolesClicked = () => { + setKeyDialog(true); + setStep(2); + }; + const toggleKeyDialog = () => { setKeyDialog(!keyDialog); + setUser(""); }; const handleBackArrowSvg = () => { @@ -665,9 +853,18 @@ export default function ChatBox(props: Props) { }, { label: "Leave", - onClick: LeaveClicked, + onClick: leaveClicked, icon: chatLeaveSvg - } + }, + ...(props.activeUser?.username === currentChannel?.communityName + ? [ + { + label: "Edit Roles", + onClick: editRolesClicked, + icon: editSVG + } + ] + : []) ]; const menuConfig = { @@ -1219,7 +1416,7 @@ export default function ChatBox(props: Props) { {step === 1 && LeaveModal()} - {/* {step === 2 && successModal()} */} + {step === 2 && EditRolesModal()} )} diff --git a/src/common/components/join-community-chat-btn/index.tsx b/src/common/components/join-community-chat-btn/index.tsx index 629967a07b4..d387a454164 100644 --- a/src/common/components/join-community-chat-btn/index.tsx +++ b/src/common/components/join-community-chat-btn/index.tsx @@ -2,13 +2,13 @@ import React, { useEffect, useState } from "react"; import { Button, Spinner } from "react-bootstrap"; import { History } from "history"; -import { Community } from "../../store/communities/types"; +import { Community, ROLES } from "../../store/communities/types"; import { ActiveUser } from "../../store/active-user/types"; import { _t } from "../../i18n"; import { useMappedStore } from "../../store/use-mapped-store"; -import { Channel } from "../../../providers/message-provider-types"; +import { Channel, communityModerator } from "../../../providers/message-provider-types"; import { createNoStrAccount, getProfileMetaData, @@ -26,6 +26,8 @@ interface Props { resetChat: () => void; } +type CommunityRoles = [string, string, string]; + export default function JoinCommunityChatBtn(props: Props) { const { chat } = useMappedStore(); const [inProgress, setInProgress] = useState(false); @@ -33,6 +35,11 @@ export default function JoinCommunityChatBtn(props: Props) { const [isChatEnabled, setIsChatEnabled] = useState(false); const [currentChannel, setCurrentChannel] = useState(); const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); + const [communityRoles, setCommunityRoles] = useState([]); + + useEffect(() => { + getCommunityRoles(); + }, []); useEffect(() => { fetchCommunityProfile(); @@ -43,11 +50,11 @@ export default function JoinCommunityChatBtn(props: Props) { fetchCommunityProfile(); }, [props.activeUser]); - useEffect(() => { - if (window.messageService) { - setHasUserJoinedChat(true); - } - }, [window?.messageService]); + // useEffect(() => { + // if (window.messageService) { + // setHasUserJoinedChat(true); + // } + // }, [window?.messageService]); useEffect(() => { checkIsChatJoined(); @@ -88,6 +95,27 @@ export default function JoinCommunityChatBtn(props: Props) { } }; + const getCommunityRoles = async () => { + let communityRoles: communityModerator[] = []; + const { community } = props; + for (let i = 0; i < community.team.length; i++) { + const item = community.team[i]; + if (item[1] === ROLES.ADMIN || item[1] === ROLES.MOD || item[1] === ROLES.OWNER) { + const profileData = await getProfileMetaData(item[0]); + if (profileData && profileData.hasOwnProperty(NOSTRKEY)) { + const roleInfo: communityModerator = { + name: item[0], + pubkey: profileData.noStrKey.pub, + role: item[1] + }; + + communityRoles.push(roleInfo); + } + } + } + setCommunityRoles(communityRoles); + }; + const createCommunityChat = async () => { const { community } = props; try { @@ -96,7 +124,8 @@ export default function JoinCommunityChatBtn(props: Props) { name: community.title, about: community.description, communityName: community.name, - picture: "" + picture: "", + communityModerators: communityRoles }); const content = JSON.parse(data?.content!); diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts index f2505d15cec..a1e2ed41634 100644 --- a/src/common/helper/message-service.ts +++ b/src/common/helper/message-service.ts @@ -4,6 +4,7 @@ import { Filter } from "../../lib/nostr-tools/filter"; import { TypedEventEmitter } from "./message-event-emitter"; import { Channel, + ChannelUpdate, DirectContact, DirectMessage, Keys, @@ -33,6 +34,7 @@ export enum MessageEvents { Ready = "ready", ProfileUpdate = "profile_update", LeftChannelList = "left_channel_list", + ChannelUpdate = "channel_update", ChannelCreation = "channel_creation", DirectMessage = "direct_message", DirectContact = "direct_contact", @@ -44,6 +46,7 @@ type EventHandlerMap = { [MessageEvents.ProfileUpdate]: (data: Profile[]) => void; [MessageEvents.DirectMessage]: (data: DirectMessage[]) => void; [MessageEvents.ChannelCreation]: (data: Channel[]) => void; + [MessageEvents.ChannelUpdate]: (data: ChannelUpdate[]) => void; [MessageEvents.PublicMessage]: (data: PublicMessage[]) => void; [MessageEvents.LeftChannelList]: (data: string[]) => void; [MessageEvents.DirectContact]: (data: DirectContact[]) => void; @@ -325,6 +328,7 @@ class MessageService extends TypedEventEmitter { const sub = this.pool.sub(this.readRelays, filters); sub.on("event", (event: Event) => { + console.log(event, "channel event"); event.kind === Kind.Metadata && isDirectContact ? this.addDirectContact(event) : this.pushToEventBuffer(event); @@ -382,10 +386,17 @@ class MessageService extends TypedEventEmitter { } public async createChannel(meta: Metadata) { - // console.log("create channel run"); + // console.log("create channel run", meta); return this.publish(Kind.ChannelCreation, [], JSON.stringify(meta)); } + public async updateChannel(channel: Channel, meta: Metadata) { + console.log(channel, meta, "Update channel run"); + return this.findHealthyRelay(this.pool.seenOn(channel.id) as string[]).then((relay) => { + return this.publish(Kind.ChannelMetadata, [["e", channel.id, relay]], JSON.stringify(meta)); + }); + } + private async findHealthyRelay(relays: string[]) { for (const relay of relays) { try { @@ -513,7 +524,7 @@ class MessageService extends TypedEventEmitter { const directContactsProfile: Array<{ pubkey: string; name: string }> = directContacts[0]; if (directContactsProfile.length > 0) { - console.log(directContactsProfile, "directContactsProfile"); + // console.log(directContactsProfile, "directContactsProfile"); this.emit(MessageEvents.DirectContact, directContactsProfile); } } @@ -555,13 +566,13 @@ class MessageService extends TypedEventEmitter { .filter((x) => x.kind === Kind.ChannelCreation) .map((ev) => { const content = MessageService.parseJson(ev.content); - // console.log(content,"events") return content ? { id: ev.id, creator: ev.pubkey, created: ev.created_at, communityName: content.communityName, + communityModerators: content.communityModerators, ...MessageService.normalizeMetadata(content) } : null; @@ -571,6 +582,30 @@ class MessageService extends TypedEventEmitter { this.emit(MessageEvents.ChannelCreation, channelCreations); } + const channelUpdates: ChannelUpdate[] = this.eventQueue + .filter((x) => x.kind === Kind.ChannelMetadata) + .map((ev) => { + const content = MessageService.parseJson(ev.content); + console.log("content", content); + const channelId = MessageService.findTagValue(ev, "e"); + if (!channelId) return null; + return content + ? { + id: ev.id, + creator: ev.pubkey, + created: ev.created_at, + communityName: content.communityName, + communityModerators: content.communityModerators, + channelId, + ...MessageService.normalizeMetadata(content) + } + : null; + }) + .filter(notEmpty); + if (channelUpdates.length > 0) { + this.emit(MessageEvents.ChannelUpdate, channelUpdates); + } + const publicMessages: PublicMessage[] = this.eventQueue .filter((x) => x.kind === Kind.ChannelMessage) .map((ev) => { diff --git a/src/common/img/svg.tsx b/src/common/img/svg.tsx index 45dbddc5aa5..4fe1e7da76b 100644 --- a/src/common/img/svg.tsx +++ b/src/common/img/svg.tsx @@ -2099,3 +2099,19 @@ export const chatLeaveSvg = ( > ); + +export const editSVG = ( + + + +); diff --git a/src/common/pages/community-functional.tsx b/src/common/pages/community-functional.tsx index ba3006c1696..d6f67e474eb 100644 --- a/src/common/pages/community-functional.tsx +++ b/src/common/pages/community-functional.tsx @@ -300,7 +300,7 @@ export const CommunityPage = (props: Props) => { Close

diff --git a/src/providers/message-provider-types.ts b/src/providers/message-provider-types.ts index ad0dfd3eb68..8fd832af5d5 100644 --- a/src/providers/message-provider-types.ts +++ b/src/providers/message-provider-types.ts @@ -14,6 +14,13 @@ export type Metadata = { about: string; picture: string; communityName?: string; + communityModerators?: communityModerator[]; +}; + +export type communityModerator = { + name: string; + pubkey: string; + role: string; }; export type Profile = { id: string; creator: string; created: number } & Metadata; diff --git a/src/providers/message-provider.tsx b/src/providers/message-provider.tsx index e169032a40c..5640fae9055 100644 --- a/src/providers/message-provider.tsx +++ b/src/providers/message-provider.tsx @@ -8,7 +8,8 @@ import { Keys, DirectContact, Channel, - PublicMessage + PublicMessage, + ChannelUpdate } from "./message-provider-types"; import { initMessageService, MessageEvents } from "../common/helper/message-service"; @@ -198,7 +199,7 @@ const MessageProvider = (props: Props) => { // Channel creation handler const handleChannelCreation = (data: Channel[]) => { - // console.log("handleChannelCreation", data); + console.log("handleChannelCreation", data); const append = data.filter((x) => chat.channels.find((y) => y.id === x.id) === undefined); props.addChannels(append); @@ -213,9 +214,25 @@ const MessageProvider = (props: Props) => { }; }, [messageService]); + // Channel update handler + const handleChannelUpdate = (data: ChannelUpdate[]) => { + console.log("handleChannelUpdate", data); + // const append = data.filter(x => channelUpdates.find(y => y.id === x.id) === undefined); + // setChannelUpdates([...channelUpdates, ...append]); + }; + + useEffect(() => { + messageService?.removeListener(MessageEvents.ChannelUpdate, handleChannelUpdate); + messageService?.addListener(MessageEvents.ChannelUpdate, handleChannelUpdate); + + return () => { + messageService?.removeListener(MessageEvents.ChannelUpdate, handleChannelUpdate); + }; + }, [messageService]); + //Public Message handler const handlePublicMessage = (data: PublicMessage[]) => { - // console.log("handlePublicMessage", data); + console.log("handlePublicMessage", data); setPublicMessageBuffer((publicMessageBuffer) => [...publicMessageBuffer!, ...data]); for (const item of data) { @@ -301,6 +318,7 @@ const MessageProvider = (props: Props) => { messageService?.removeListener(MessageEvents.ChannelCreation, handleChannelCreation); messageService?.removeListener(MessageEvents.DirectContact, handleDirectContact); messageService?.removeListener(MessageEvents.LeftChannelList, handleLeftChannelList); + messageService?.removeListener(MessageEvents.ChannelUpdate, handleChannelUpdate); messageService?.removeListener(MessageEvents.PublicMessage, handlePublicMessage); messageService?.removeListener(MessageEvents.DirectMessage, handleDirectMessage); }; From 3bde88f5a37752e89bff9de2011efc70f22e58a0 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Tue, 25 Jul 2023 19:41:19 +0500 Subject: [PATCH 027/179] Add moderators in public chat --- src/common/components/chat-box/index.scss | 37 +- src/common/components/chat-box/index.tsx | 385 ++++++++++++++---- .../join-community-chat-btn/index.tsx | 37 +- src/common/components/login/index.tsx | 10 +- src/common/helper/chat-utils.ts | 14 +- src/common/helper/message-service.ts | 31 +- src/common/i18n/locales/en-US.json | 3 +- src/common/store/actions.ts | 6 +- src/common/store/chat/index.ts | 32 +- src/common/store/chat/types.ts | 17 +- src/common/store/users/types.ts | 2 - src/providers/message-provider.tsx | 35 +- 12 files changed, 478 insertions(+), 131 deletions(-) diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss index dd5a689a4c2..261c6c21ebc 100644 --- a/src/common/components/chat-box/index.scss +++ b/src/common/components/chat-box/index.scss @@ -75,7 +75,7 @@ font-size: 21px; font-family: $font-family-sans-serif; - max-width: 200px; + max-width: 190px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -121,7 +121,7 @@ display: flex; justify-content: center; text-align: center; - padding-top: 7px; + padding-top: 6px; width: 40px; height: 40px; border-radius: 50%; @@ -131,8 +131,10 @@ background: rgba(29, 155, 240, 0.1); } } - .community-menu { - margin-bottom: 12px; + .community-menu, + .simple-menu { + border: none; + margin-bottom: 16px; svg { @include themify(day) { color: $charcoal-grey; @@ -455,7 +457,7 @@ } } color: #050505; - max-width: 280px; + max-width: 290px; word-wrap: break-word; padding: 10px; border-radius: 0px 10px 10px; @@ -475,7 +477,7 @@ margin-bottom: 2px; } .sender-message { - max-width: 280px; + max-width: 290px; margin-left: auto; display: flex; @@ -484,7 +486,7 @@ .sender-message-content { border-radius: 10px 10px 0px; - max-width: 280px; + max-width: 290px; word-wrap: break-word; margin-bottom: 0; color: $charcoal-grey; @@ -603,6 +605,27 @@ margin-right: 20px; } } + + .import-chat-dialog-header { + display: flex; + .import-chat-dialog-titles { + margin-top: 9px; + } + .import-chat-main-title { + font-weight: bold; + } + } + .success-dialog-body { + margin: 2rem 0 1.4rem 0; + .success-dialog-content { + text-align: center; + margin-bottom: 1.4rem; + } + } + + .private-key { + margin: 2rem 0; + } } .add-dialog-header { margin-bottom: 20px; diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 2d7c39b7131..7d66fb3632e 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -51,7 +51,9 @@ import { linkSvg, KebabMenu, chatLeaveSvg, - editSVG + editSVG, + keySvg, + syncSvg } from "../../img/svg"; import { @@ -64,17 +66,20 @@ import { GifImagesStyle } from "./chat-constants"; +import { getPublicKey } from "../../../lib/nostr-tools/keys"; import { dateToFormatted } from "../../helper/parse-date"; import { createNoStrAccount, formatMessageDate, formatMessageTime, getProfileMetaData, - NostrKeys, + NostrKeysType, setProfileMetaData, resetProfile, - getCommunities + getCommunities, + getPrivateKey } from "../../helper/chat-utils"; +import * as ls from "../../util/local-storage"; import { renderPostBody } from "@ecency/render-helper"; import { getAccessToken } from "../../helper/user-token"; import { _t } from "../../i18n"; @@ -101,7 +106,7 @@ interface Props { } let zoom: Zoom | null = null; -const roles = [ROLES.ADMIN, ROLES.MOD]; +const roles = [ROLES.ADMIN, ROLES.MOD, ROLES.GUEST]; export default function ChatBox(props: Props) { const prevPropsRef = useRef(props); @@ -121,7 +126,7 @@ export default function ChatBox(props: Props) { const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); const [inProgress, setInProgress] = useState(false); const [show, setShow] = useState(false); - const [activeUserKeys, setActiveUserKeys] = useState(); + const [activeUserKeys, setActiveUserKeys] = useState(); const [receiverPubKey, setReceiverPubKey] = useState(""); const [showSpinner, setShowSpinner] = useState(false); const [directMessagesList, setDirectMessagesList] = useState([]); @@ -142,6 +147,8 @@ export default function ChatBox(props: Props) { const [role, setRole] = useState("admin"); const [addRoleError, setAddRoleError] = useState(""); const [moderator, setModerator] = useState(); + const [noStrPrivKey, setNoStrPrivKey] = useState(""); + const [chatPrivKey, setChatPrivkey] = useState(""); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -170,13 +177,15 @@ export default function ChatBox(props: Props) { }, [clickedMessage]); useEffect(() => { - console.log(props.chat, "chat in store"); + setInProgress(false); }, [props.chat]); useEffect(() => { // resetProfile(props.activeUser); fetchProfileData(); setShow(!!props.activeUser?.username); + const noStrPrivKey = getPrivateKey(props.activeUser?.username!); + setNoStrPrivKey(noStrPrivKey); }, []); // useEffect(() => { @@ -201,8 +210,14 @@ export default function ChatBox(props: Props) { if (prevProps.global.theme !== props.global.theme) { setBackground(); } + if (prevProps.activeUser?.username !== props.activeUser?.username) { + setIsCommunity(false); + setIsCurrentUser(false); + setCurrentUser(""); + setCommunityName(""); + } prevPropsRef.current = props; - }, [props.global.theme]); + }, [props.global.theme, props.activeUser]); useEffect(() => { scrollerClicked(); @@ -214,6 +229,7 @@ export default function ChatBox(props: Props) { useEffect(() => { if (currentChannel && isCommunity) { + window?.messageService?.fetchChannel(currentChannel.id); const publicMessages = fetchCommunityMessages(currentChannel.id); const messages = publicMessages.sort((a, b) => a.created - b.created); setPublicMessages(messages); @@ -236,6 +252,8 @@ export default function ChatBox(props: Props) { const msgsList = fetchDirectMessages(receiverPubKey!); const messages = msgsList.sort((a, b) => a.created - b.created); setDirectMessagesList(messages); + const noStrPrivKey = getPrivateKey(props.activeUser?.username!); + setNoStrPrivKey(noStrPrivKey); }, [props.activeUser]); useEffect(() => { @@ -279,7 +297,9 @@ export default function ChatBox(props: Props) { if (searchtext.length !== 0) { const resp = await lookupAccounts(searchtext, 7); setUserList(resp); + setInProgress(false); } else { + setInProgress(false); setUserList([]); } }, @@ -298,13 +318,27 @@ export default function ChatBox(props: Props) { }; const fetchCurrentChannel = (communityName: string) => { - for (const item of props.chat.channels) { - if (item.communityName === communityName) { + const item = props.chat.channels.find((channel) => channel.communityName === communityName); + if (item) { + const updated = props.chat.updatedChannel + .filter((x) => x.channelId === item.id) + .sort((a, b) => b.created - a.created)[0]; + if (updated && currentChannel) { + const channel = { + name: updated.name, + about: updated.about, + picture: updated.picture, + communityName: updated.communityName, + communityModerators: updated.communityModerators, + id: updated.channelId, + creator: currentChannel.creator, + created: currentChannel.created + }; + setCurrentChannel(channel); + } else { setCurrentChannel(item); - return item; } } - return {}; }; const formatFollowers = (count: number | undefined) => { @@ -362,14 +396,19 @@ export default function ChatBox(props: Props) { const { posting_json_metadata } = response; const profile = JSON.parse(posting_json_metadata!).profile; const { noStrKey } = profile || {}; - setReceiverPubKey(noStrKey?.pub); - setIsCurrentUserJoined(!!noStrKey?.pub); + setReceiverPubKey(noStrKey); + setIsCurrentUserJoined(!!noStrKey); setInProgress(false); }; const fetchProfileData = async () => { const profileData = await getProfileMetaData(props.activeUser?.username!); - setActiveUserKeys(profileData?.noStrKey); + const noStrPrivKey = getPrivateKey(props.activeUser?.username!); + const activeUserKeys = { + pub: profileData?.noStrKey, + priv: noStrPrivKey + }; + setActiveUserKeys(activeUserKeys); const hasNoStrKey = profileData && profileData.hasOwnProperty(NOSTRKEY); setHasUserJoinedChat(hasNoStrKey); setShow(!!props.activeUser?.username); @@ -398,7 +437,6 @@ export default function ChatBox(props: Props) { !props.chat.directContacts.some((contact) => contact.name === currentUser) && isCurrentUser ) { - console.log("Publish contacts run from chatbox component."); window.messageService?.publishContacts(currentUser, receiverPubKey); } }; @@ -432,12 +470,27 @@ export default function ChatBox(props: Props) { setShowSearchUser(!showSearchUser); }; + const handleRefreshSvgClick = () => { + if (getPrivateKey(props.activeUser?.username!)) { + setInProgress(true); + const keys = { + pub: activeUserKeys?.pub!, + priv: getPrivateKey(props.activeUser?.username!) + }; + setNostrkeys(keys); + } else { + setNoStrPrivKey(""); + } + }; + const handleJoinChat = async () => { const { resetChat } = props; setShowSpinner(true); resetChat(); const keys = createNoStrAccount(); - await setProfileMetaData(props.activeUser, keys); + ls.set(`${props.activeUser?.username}_noStrPrivKey`, keys.priv); + setNoStrPrivKey(keys.priv); + await setProfileMetaData(props.activeUser, keys.pub); setHasUserJoinedChat(true); setNostrkeys(keys); window.messageService?.updateProfile({ @@ -603,15 +656,59 @@ export default function ChatBox(props: Props) { } }; - const inviteClicked = () => { + const inviteClicked = (content: string) => { const textField = document.createElement("textarea"); - const url = `http://localhost:3000/created/${currentCommunity?.name}?communityid=${currentChannel?.id}`; - textField.innerText = url; + textField.innerText = content; document.body.appendChild(textField); textField.select(); document.execCommand("copy"); textField.remove(); - success("Link copied into clipboard"); + success(`${isCommunity ? "Link" : "Key"} copied into clipboard`); + }; + + const handleImportChatSubmit = () => { + try { + const pubKey = getPublicKey(chatPrivKey); + if (pubKey === activeUserKeys?.pub) { + setNoStrPrivKey(chatPrivKey); + ls.set(`${props.activeUser?.username}_noStrPrivKey`, chatPrivKey); + const keys = { + pub: activeUserKeys?.pub!, + priv: chatPrivKey + }; + setNostrkeys(keys); + setStep(4); + setChatPrivkey(""); + } else { + setAddRoleError("Invalid Private key"); + } + } catch (error) { + setAddRoleError("Invalid Private key"); + } + }; + + const finish = () => { + setStep(0); + setKeyDialog(false); + }; + + const updateRole = ( + event: React.ChangeEvent, + moderator: communityModerator + ) => { + const selectedRole = event.target.value; + const moderatorIndex = currentChannel?.communityModerators?.findIndex( + (mod) => mod.name === moderator.name + ); + if (moderatorIndex !== -1 && currentChannel) { + const newUpdatedChannel: Channel = { ...currentChannel }; + const newUpdatedModerator = { ...newUpdatedChannel?.communityModerators![moderatorIndex!] }; + newUpdatedModerator.role = selectedRole; + newUpdatedChannel!.communityModerators![moderatorIndex!] = newUpdatedModerator; + setCurrentChannel(newUpdatedChannel); + window.messageService?.updateChannel(currentChannel, newUpdatedChannel); + success("Roles updated succesfully"); + } }; const LeaveModal = () => { @@ -707,45 +804,126 @@ export default function ChatBox(props: Props) {
- - - - - - - - - {currentChannel?.communityModerators!.map((t, i) => { - // const [username, role, title] = t; - // const canEdit = roles && roles.includes(role); - return ( - - - + {currentChannel?.communityModerators?.length !== 0 ? ( + <> +
{_t("community.roles-account")}{_t("community.roles-role")}
- - {" "} - @{t.name} - - - - {roles.map((r, i) => ( - - ))} - -
+ + + + - ); - })} - -
{_t("community.roles-account")}{_t("community.roles-role")}
+ + + {currentChannel?.communityModerators && + currentChannel?.communityModerators!.map((moderator, i) => { + return ( + + + + {" "} + @{moderator.name} + + + + {moderator.name === props.activeUser?.username ? ( +

{moderator.role}

+ ) : ( + ) => + updateRole(e, moderator) + } + > + {roles.map((r, i) => ( + + ))} + + )} + + + ); + })} + + + + ) : ( +
+

No admin or moderator for this community chat.

+
+ )} +
+ + ); + }; + + const ImportChatModal = () => { + return ( + <> +
+
1
+
+
Enter Chat private key
+
Enter chat private key to import all chats
+
+
+
+
{ + e.preventDefault(); + }} + > + + + {keySvg} + + setChatPrivkey(e.target.value)} + /> + + + + + {addRoleError && {addRoleError}} +
+
+ + ); + }; + + const chatSuccessModal = () => { + return ( + <> +
+
2
+
+
{_t("manage-authorities.success-title")}
+
+ {_t("manage-authorities.success-sub-title")} +
+
+
+
+
+ Chats imported successfully +
+
+ + +
); @@ -771,8 +949,8 @@ export default function ChatBox(props: Props) { return; } const moderator = { - name: profileData.name, - pubkey: profileData.noStrKey.pub, + name: user, + pubkey: profileData.noStrKey, role: role }; setModerator(moderator); @@ -793,14 +971,11 @@ export default function ChatBox(props: Props) { const userChanged = (e: React.ChangeEvent) => { const { value: user } = e.target; setUser(user); - console.log(); setInProgress(true); }; const addNewRole = () => { const updatedRoles = [...(currentChannel?.communityModerators || []), moderator!]; - - console.log("updatedRoles", currentChannel); const updatedMetaData = { name: currentChannel?.name!, about: currentChannel?.about!, @@ -809,10 +984,14 @@ export default function ChatBox(props: Props) { communityModerators: updatedRoles }; - console.log(updatedMetaData, "updatedMetaData"); - window.messageService?.updateChannel(currentChannel!, updatedMetaData).then((resp) => { - console.log(resp, "resp"); - }); + window.messageService?.updateChannel(currentChannel!, updatedMetaData); + currentChannel?.communityModerators?.push(moderator!); + const updatedChannel: Channel = { + ...currentChannel!, + communityModerators: updatedRoles + }; + setUser(""); + setCurrentChannel(updatedChannel); }; const roleChanged = (e: React.ChangeEvent) => { @@ -833,6 +1012,7 @@ export default function ChatBox(props: Props) { const toggleKeyDialog = () => { setKeyDialog(!keyDialog); setUser(""); + setAddRoleError(""); }; const handleBackArrowSvg = () => { @@ -845,10 +1025,19 @@ export default function ChatBox(props: Props) { setSearchText(""); }; - const menuItems: MenuItem[] = [ + const handleImportChat = () => { + setKeyDialog(true); + + setStep(3); + }; + + const communityMenuItems: MenuItem[] = [ { label: _t("chat.invite"), - onClick: inviteClicked, + onClick: () => + inviteClicked( + `http://localhost:3000/created/${currentCommunity?.name}?communityid=${currentChannel?.id}` + ), icon: linkSvg }, { @@ -867,6 +1056,21 @@ export default function ChatBox(props: Props) { : []) ]; + const communityMenuConfig = { + history: props.history, + label: "", + icon: KebabMenu, + items: communityMenuItems + }; + + const menuItems: MenuItem[] = [ + { + label: "Copy private key", + onClick: () => inviteClicked(noStrPrivKey), + icon: linkSvg + } + ]; + const menuConfig = { history: props.history, label: "", @@ -912,10 +1116,21 @@ export default function ChatBox(props: Props) {

- {!currentUser && hasUserJoinedChat && ( -
- -

{addMessageSVG}

+ {!currentUser && hasUserJoinedChat && noStrPrivKey && !isCommunity && ( + <> +
+ +

{addMessageSVG}

+
+
+ + )} + {!currentUser && hasUserJoinedChat && noStrPrivKey && ( +
+ +

+ {syncSvg} +

)} @@ -928,6 +1143,17 @@ export default function ChatBox(props: Props) {
{isCommunity && (
+ +
+ )}{" "} + {!isCommunity && !isCurrentUser && noStrPrivKey && ( +
+ {inProgress && !isCommunity && !isCurrentUser && } +
{ setSearchText(e.target.value); + setInProgress(true); }} /> @@ -1210,8 +1439,9 @@ export default function ChatBox(props: Props) { ) : ( <> - {props.chat.directContacts.length !== 0 || - (props.chat.channels.length !== 0 && communities.length !== 0) ? ( + {(props.chat.directContacts.length !== 0 || + (props.chat.channels.length !== 0 && communities.length !== 0)) && + noStrPrivKey ? ( {props.chat.channels.length !== 0 && communities.length !== 0 && ( <> @@ -1267,15 +1497,24 @@ export default function ChatBox(props: Props) { ); })} + ) : !noStrPrivKey || noStrPrivKey.length === 0 || noStrPrivKey === null ? ( + <> + {/*

{_t("chat.no-chat")}

*/} +
+ +
+ ) : ( - + <>

{_t("chat.no-chat")}

-
+ )} )} @@ -1417,6 +1656,8 @@ export default function ChatBox(props: Props) { {step === 1 && LeaveModal()} {step === 2 && EditRolesModal()} + {step === 3 && ImportChatModal()} + {step === 4 && chatSuccessModal()} )} diff --git a/src/common/components/join-community-chat-btn/index.tsx b/src/common/components/join-community-chat-btn/index.tsx index d387a454164..400280b205a 100644 --- a/src/common/components/join-community-chat-btn/index.tsx +++ b/src/common/components/join-community-chat-btn/index.tsx @@ -12,9 +12,11 @@ import { Channel, communityModerator } from "../../../providers/message-provider import { createNoStrAccount, getProfileMetaData, + NostrKeysType, setChannelMetaData, setProfileMetaData } from "../../helper/chat-utils"; +import * as ls from "../../util/local-storage"; import { setNostrkeys } from "../../../providers/message-provider"; import { NOSTRKEY } from "../chat-box/chat-constants"; @@ -26,8 +28,6 @@ interface Props { resetChat: () => void; } -type CommunityRoles = [string, string, string]; - export default function JoinCommunityChatBtn(props: Props) { const { chat } = useMappedStore(); const [inProgress, setInProgress] = useState(false); @@ -36,6 +36,7 @@ export default function JoinCommunityChatBtn(props: Props) { const [currentChannel, setCurrentChannel] = useState(); const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); const [communityRoles, setCommunityRoles] = useState([]); + const [activeUserKeys, setActiveUserKeys] = useState(); useEffect(() => { getCommunityRoles(); @@ -50,11 +51,12 @@ export default function JoinCommunityChatBtn(props: Props) { fetchCommunityProfile(); }, [props.activeUser]); - // useEffect(() => { - // if (window.messageService) { - // setHasUserJoinedChat(true); - // } - // }, [window?.messageService]); + useEffect(() => { + if (window.messageService && activeUserKeys) { + getCommunityRoles(); + setHasUserJoinedChat(true); + } + }, [window?.messageService, activeUserKeys]); useEffect(() => { checkIsChatJoined(); @@ -97,15 +99,24 @@ export default function JoinCommunityChatBtn(props: Props) { const getCommunityRoles = async () => { let communityRoles: communityModerator[] = []; - const { community } = props; + const { community, activeUser } = props; + const ownerData = await getProfileMetaData(community.name); + const ownerRole = { + name: activeUser!.username, + pubkey: ownerData.noStrKey || activeUserKeys?.pub, + role: "owner" + }; + + communityRoles.push(ownerRole); + for (let i = 0; i < community.team.length; i++) { const item = community.team[i]; - if (item[1] === ROLES.ADMIN || item[1] === ROLES.MOD || item[1] === ROLES.OWNER) { + if (item[1] === ROLES.ADMIN || item[1] === ROLES.MOD) { const profileData = await getProfileMetaData(item[0]); if (profileData && profileData.hasOwnProperty(NOSTRKEY)) { const roleInfo: communityModerator = { name: item[0], - pubkey: profileData.noStrKey.pub, + pubkey: profileData.noStrKey, role: item[1] }; @@ -167,10 +178,12 @@ export default function JoinCommunityChatBtn(props: Props) { }; const handleJoinChat = async () => { - const { resetChat } = props; + const { resetChat, activeUser } = props; setInProgress(true); const keys = createNoStrAccount(); - await setProfileMetaData(props.activeUser, keys); + setActiveUserKeys(keys); + ls.set(`${activeUser?.username}_noStrPrivKey`, keys.priv); + await setProfileMetaData(props.activeUser, keys.pub); setHasUserJoinedChat(true); setNostrkeys(keys); window.messageService?.updateProfile({ diff --git a/src/common/components/login/index.tsx b/src/common/components/login/index.tsx index 8a04894f563..23d4b8141c6 100644 --- a/src/common/components/login/index.tsx +++ b/src/common/components/login/index.tsx @@ -36,7 +36,7 @@ import { hsTokenRenew } from "../../api/auth-api"; import { formatError, grantPostingPermission, revokePostingPermission } from "../../api/operations"; import { getRefreshToken } from "../../helper/user-token"; -import { getProfileMetaData } from "../../helper/chat-utils"; +import { getPrivateKey, getProfileMetaData } from "../../helper/chat-utils"; import ReCAPTCHA from "react-google-recaptcha"; @@ -773,8 +773,12 @@ class LoginDialog extends Component { resetChat(); //create new message service instance for newly logged in user - if (profile && profile.noStrKey) { - setNostrkeys(profile.noStrKey); + if (profile && profile.noStrKey && getPrivateKey(account.name)) { + const keys = { + pub: profile.noStrKey, + priv: getPrivateKey(account.name) + }; + setNostrkeys(keys); } // activate user setActiveUser(user.username); diff --git a/src/common/helper/chat-utils.ts b/src/common/helper/chat-utils.ts index 6bfb1f510a8..67ba159bff1 100644 --- a/src/common/helper/chat-utils.ts +++ b/src/common/helper/chat-utils.ts @@ -4,8 +4,9 @@ import { Channel } from "../../providers/message-provider-types"; import { getAccountFull } from "../api/hive"; import { updateProfile } from "../api/operations"; import { ActiveUser } from "../store/active-user/types"; +import * as ls from "../util/local-storage"; -export interface NostrKeys { +export interface NostrKeysType { pub: string; priv: string; } @@ -24,18 +25,19 @@ export const resetProfile = async (activeUser: ActiveUser | null) => { const profile = await getProfileMetaData(activeUser?.username!); delete profile.noStrKey; delete profile.channel; + ls.remove(`${activeUser?.username}_noStrPrivKey`); const response = await getAccountFull(activeUser?.username!); const updatedProfile = await updateProfile(response, { ...profile }); - console.log(updatedProfile); + console.log(updatedProfile, profile); }; -export const setProfileMetaData = async (activeUser: ActiveUser | null, keys: NostrKeys) => { +export const setProfileMetaData = async (activeUser: ActiveUser | null, noStrPubKey: string) => { const response = await getAccountFull(activeUser?.username!); const { posting_json_metadata } = response; const profile = JSON.parse(posting_json_metadata!).profile; const newProfile = { - noStrKey: keys + noStrKey: noStrPubKey }; const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); @@ -73,3 +75,7 @@ export const isSha256 = (s: string) => /^[a-f0-9]{64}$/gi.test(s); export const getCommunities = (channels: Channel[], leftChannels: string[]) => { return channels.filter((item) => !leftChannels.includes(item.id)); }; + +export const getPrivateKey = (username: string) => { + return ls.get(`${username}_noStrPrivKey`); +}; diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts index a1e2ed41634..80536ad083f 100644 --- a/src/common/helper/message-service.ts +++ b/src/common/helper/message-service.ts @@ -158,6 +158,7 @@ class MessageService extends TypedEventEmitter { } public fetchChannels(channels: string[]) { + // console.log("fetch channels run"); const filters: Filter[] = [ { kinds: [Kind.ChannelCreation], @@ -179,8 +180,24 @@ class MessageService extends TypedEventEmitter { }); } + public fetchChannel(id: string) { + // console.log("fetchChannel run", id); + const filters: Filter[] = [ + { + kinds: [Kind.ChannelCreation], + ids: [id] + }, + { + kinds: [Kind.ChannelMetadata, Kind.EventDeletion], + "#e": [id] + } + ]; + + this.fetch(filters); + } + public async updateLeftChannelList(channelIds: string[]) { - console.log(channelIds, "channelIds"); + // console.log(channelIds, "channelIds"); const tags = [["d", "left-channel-list"]]; return this.publish(NewKinds.Arbitrary, tags, JSON.stringify(channelIds)); } @@ -204,7 +221,6 @@ class MessageService extends TypedEventEmitter { } public publishContacts(username: string, pubkey: string) { - console.log("Publish contact run", username, pubkey); const newUser = [pubkey, username]; newUser.forEach((element) => { let nameExist = false; @@ -220,7 +236,7 @@ class MessageService extends TypedEventEmitter { // If the name doesn't exist, add the element to the direct contacts array if (!nameExist) { this.directContacts.push(newUser); - console.log("Event is fired"); + // console.log("Event is fired"); this.publish(Kind.Contacts, this.directContacts, ""); return; } @@ -238,7 +254,7 @@ class MessageService extends TypedEventEmitter { } public loadChannel(id: string) { - console.log(id, "id"); + // console.log(id, "id"); const filters: Filter[] = [ { kinds: [Kind.ChannelCreation], @@ -259,7 +275,6 @@ class MessageService extends TypedEventEmitter { } public loadProfiles(pubs: string[]) { - console.log("load profiles run"); pubs.forEach((p) => { this.fetch( [ @@ -328,7 +343,6 @@ class MessageService extends TypedEventEmitter { const sub = this.pool.sub(this.readRelays, filters); sub.on("event", (event: Event) => { - console.log(event, "channel event"); event.kind === Kind.Metadata && isDirectContact ? this.addDirectContact(event) : this.pushToEventBuffer(event); @@ -490,6 +504,7 @@ class MessageService extends TypedEventEmitter { } pushToEventBuffer(event: Event) { + // console.log("event reaches in event buffer"); const cacheKey = `${event.id}_emitted`; if (this.nameCache[cacheKey] === undefined) { if (this.eventQueueFlag) { @@ -511,6 +526,7 @@ class MessageService extends TypedEventEmitter { } async processEventQueue() { + // console.log("Event reaches in process queue"); this.eventQueueFlag = false; const directContacts = this.eventQueue .filter((x) => x.kind === Kind.Contacts) @@ -705,6 +721,7 @@ class MessageService extends TypedEventEmitter { export default MessageService; export const initMessageService = (keys: Keys): MessageService | undefined => { + // console.log("keys in raven instance", keys) if (window.messageService) { window.messageService.close(); window.messageService = undefined; @@ -712,7 +729,7 @@ export const initMessageService = (keys: Keys): MessageService | undefined => { if (keys) { window.messageService = new MessageService(keys.priv, keys.pub); - console.log("window messageService", window.messageService); + // console.log("window messageService", window.messageService); } return window.messageService; diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 215fe18b70e..64b5d753799 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1665,7 +1665,8 @@ "not-joined": "This user hasn't joined the chat yet", "subscribers": "Subscribers", "invite": "Invite", - "leave:": "Leave" + "leave:": "Leave", + "refresh": "Refresh" }, "add-image": { "title": "Add Image", diff --git a/src/common/store/actions.ts b/src/common/store/actions.ts index ada5bbba07f..da195eab229 100644 --- a/src/common/store/actions.ts +++ b/src/common/store/actions.ts @@ -48,7 +48,8 @@ import { addChannels, addPublicMessage, addProfile, - addleftChannels + addleftChannels, + UpdateChannels } from "./chat"; // @note Do not use it directly @@ -105,7 +106,8 @@ export const ACTIONS = { addChannels, addPublicMessage, addProfile, - addleftChannels + addleftChannels, + UpdateChannels }; export const getActions = () => ({ diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts index 2b452360728..6d09ef6d6ce 100644 --- a/src/common/store/chat/index.ts +++ b/src/common/store/chat/index.ts @@ -2,7 +2,8 @@ import { Channel, DirectMessage, PublicMessage, - Profile + Profile, + ChannelUpdate } from "./../../../providers/message-provider-types"; import { Dispatch } from "redux"; @@ -15,10 +16,11 @@ import { DirectMessagesAction, ResetChatAction, directMessagesList, - ChannelsAction, + AddChannelsAction, PublicMessagesAction, ProfilesAction, - LeftChannelsAction + LeftChannelsAction, + UpdateChannelAction } from "./types"; export const initialState: Chat = { @@ -27,7 +29,8 @@ export const initialState: Chat = { channels: [], publicMessages: [], profiles: [], - leftChannelsList: [] + leftChannelsList: [], + updatedChannel: [] }; export default (state: Chat = initialState, action: Actions): Chat => { @@ -107,6 +110,14 @@ export default (state: Chat = initialState, action: Actions): Chat => { }; } + case ActionTypes.UPDATEDCHANNEL: { + const { data } = action; + return { + ...state, + updatedChannel: [...state.updatedChannel, ...data] + }; + } + default: return state; } @@ -142,6 +153,10 @@ export const addleftChannels = (data: string[]) => (dispatch: Dispatch) => { dispatch(addleftChannelsAct(data)); }; +export const UpdateChannels = (data: ChannelUpdate[]) => (dispatch: Dispatch) => { + dispatch(UpdateChannelsAct(data)); +}; + /* Action Creators */ export const addDirectContactsAct = (data: DirectContactsType[]): DirectContactsAction => { @@ -176,7 +191,7 @@ export const resetChatAct = (): ResetChatAction => { }; }; -export const addChannelsAct = (data: Channel[]): ChannelsAction => { +export const addChannelsAct = (data: Channel[]): AddChannelsAction => { return { type: ActionTypes.CHANNELS, data @@ -196,3 +211,10 @@ export const addleftChannelsAct = (data: string[]): LeftChannelsAction => { data }; }; + +export const UpdateChannelsAct = (data: ChannelUpdate[]): UpdateChannelAction => { + return { + type: ActionTypes.UPDATEDCHANNEL, + data + }; +}; diff --git a/src/common/store/chat/types.ts b/src/common/store/chat/types.ts index cf1f82266dd..3b795570922 100644 --- a/src/common/store/chat/types.ts +++ b/src/common/store/chat/types.ts @@ -1,5 +1,6 @@ import { Channel, + ChannelUpdate, DirectMessage, Profile, PublicMessage @@ -27,6 +28,7 @@ export interface Chat { publicMessages: publicMessagesList[]; profiles: Profile[]; leftChannelsList: string[]; + updatedChannel: ChannelUpdate[]; } export enum ActionTypes { @@ -36,7 +38,8 @@ export enum ActionTypes { CHANNELS = "@chat/CHANNELS", PUBLICMESSAGES = "@chat/PUBLICMESSAGES", PROFILES = "@chat/PROFILES", - LEFTCHANNELLIST = "@chat/LEFTCHANNELLIST" + LEFTCHANNELLIST = "@chat/LEFTCHANNELLIST", + UPDATEDCHANNEL = "@chat/UPDATEDCHANNEL" } export interface DirectContactsAction { @@ -60,7 +63,7 @@ export interface PublicMessagesAction { channelId: string; } -export interface ChannelsAction { +export interface AddChannelsAction { type: ActionTypes.CHANNELS; data: Channel[]; } @@ -74,11 +77,17 @@ export interface LeftChannelsAction { data: string[]; } +export interface UpdateChannelAction { + type: ActionTypes.UPDATEDCHANNEL; + data: ChannelUpdate[]; +} + export type Actions = | DirectContactsAction | DirectMessagesAction | ResetChatAction - | ChannelsAction + | AddChannelsAction | PublicMessagesAction | ProfilesAction - | LeftChannelsAction; + | LeftChannelsAction + | UpdateChannelAction; diff --git a/src/common/store/users/types.ts b/src/common/store/users/types.ts index 23057f4c0f7..407ea2a6f95 100644 --- a/src/common/store/users/types.ts +++ b/src/common/store/users/types.ts @@ -1,5 +1,3 @@ -import { PrivateKey } from "@hiveio/dhive"; - export interface UserKeys { owner?: string; active?: string; diff --git a/src/providers/message-provider.tsx b/src/providers/message-provider.tsx index 5640fae9055..d46124bfe34 100644 --- a/src/providers/message-provider.tsx +++ b/src/providers/message-provider.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import { ActiveUser } from "../common/store/active-user/types"; -import { Chat, DirectContactsType } from "../common/store/chat/types"; +import { DirectContactsType } from "../common/store/chat/types"; import { DirectMessage, Profile, @@ -13,15 +13,17 @@ import { } from "./message-provider-types"; import { initMessageService, MessageEvents } from "../common/helper/message-service"; -import { getProfileMetaData, NostrKeys } from "../common/helper/chat-utils"; +import { getProfileMetaData, NostrKeysType, getPrivateKey } from "../common/helper/chat-utils"; +import * as ls from "../common/util/local-storage"; import { useMappedStore } from "../common/store/use-mapped-store"; -export const setNostrkeys = (keys: NostrKeys) => { - const detail: NostrKeys = { +export const setNostrkeys = (keys: NostrKeysType) => { + const detail: NostrKeysType = { pub: keys.pub, priv: keys.priv }; + // console.log("detail", detail) const ev = new CustomEvent("createMSInstance", { detail }); window.dispatchEvent(ev); }; @@ -33,6 +35,7 @@ interface Props { addChannels: (data: Channel[]) => void; addProfile: (data: Profile[]) => void; addleftChannels: (data: string[]) => void; + UpdateChannels: (data: ChannelUpdate[]) => void; } const MessageProvider = (props: Props) => { @@ -53,7 +56,8 @@ const MessageProvider = (props: Props) => { }, []); useEffect(() => { - if (!window.messageService && keys) { + if (!window.messageService && keys?.priv) { + // console.log(keys, "keys") const messageServiceInstance = initMessageService(keys); setMessageService(messageServiceInstance); } @@ -66,14 +70,21 @@ const MessageProvider = (props: Props) => { }, [activeUser]); const createMSInstance = (e: Event) => { - const detail = (e as CustomEvent).detail as NostrKeys; + const detail = (e as CustomEvent).detail as NostrKeysType; + // console.log(detail, "details") const messageServiceInstance = initMessageService(detail); setMessageService(messageServiceInstance); }; const getNostrKeys = async (activeUser: ActiveUser) => { const profile = await getProfileMetaData(activeUser.username); - setKeys(profile.noStrKey); + const noStrPrivKey = getPrivateKey(activeUser.username); + const keys = { + pub: profile.noStrKey, + priv: noStrPrivKey + }; + // console.log('keys', keys) + setKeys(keys); }; //Listen for events in an interval. @@ -126,6 +137,7 @@ const MessageProvider = (props: Props) => { //Direct contact handler const handleDirectContact = (data: DirectContact[]) => { + // console.log("handleDirectContact", data) const result = [...chat.directContacts]; data.forEach(({ name, pubkey }) => { const isPresent = chat.directContacts.some( @@ -199,7 +211,7 @@ const MessageProvider = (props: Props) => { // Channel creation handler const handleChannelCreation = (data: Channel[]) => { - console.log("handleChannelCreation", data); + // console.log("handleChannelCreation", data); const append = data.filter((x) => chat.channels.find((y) => y.id === x.id) === undefined); props.addChannels(append); @@ -216,9 +228,8 @@ const MessageProvider = (props: Props) => { // Channel update handler const handleChannelUpdate = (data: ChannelUpdate[]) => { - console.log("handleChannelUpdate", data); - // const append = data.filter(x => channelUpdates.find(y => y.id === x.id) === undefined); - // setChannelUpdates([...channelUpdates, ...append]); + // console.log("handleChannelUpdate", data); + props.UpdateChannels(data); }; useEffect(() => { @@ -232,7 +243,7 @@ const MessageProvider = (props: Props) => { //Public Message handler const handlePublicMessage = (data: PublicMessage[]) => { - console.log("handlePublicMessage", data); + // console.log("handlePublicMessage", data); setPublicMessageBuffer((publicMessageBuffer) => [...publicMessageBuffer!, ...data]); for (const item of data) { From a6c679fece78ac706ecf71cdafb1c9f5399eb7c8 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Thu, 27 Jul 2023 18:22:20 +0500 Subject: [PATCH 028/179] Removed users from community completed --- .../components/chat-box/chat-constants.ts | 4 + src/common/components/chat-box/index.scss | 43 +- src/common/components/chat-box/index.tsx | 422 +++++++++++++----- .../components/follow-controls/index.tsx | 8 + .../join-community-chat-btn/index.tsx | 4 +- src/common/helper/message-service.ts | 11 +- src/common/img/svg.tsx | 17 + src/common/pages/community-functional.tsx | 8 +- src/common/store/chat/index.ts | 7 +- src/providers/message-provider-types.ts | 2 + src/providers/message-provider.tsx | 10 +- 11 files changed, 405 insertions(+), 131 deletions(-) diff --git a/src/common/components/chat-box/chat-constants.ts b/src/common/components/chat-box/chat-constants.ts index f0605f2bae8..68cb398cae4 100644 --- a/src/common/components/chat-box/chat-constants.ts +++ b/src/common/components/chat-box/chat-constants.ts @@ -30,3 +30,7 @@ export const GifImagesStyle = { export const NOSTRKEY = "noStrKey"; export const UPLOADING = "Uploading"; export const GIPHGY = "giphy"; +export const HIDEMESSAGE = "hideMessage"; +export const ADDROLE = "addRole"; +export const REMOVEUSER = "removeUser"; +export const LEAVECOMMUNITY = "leaveCommunity"; diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss index 261c6c21ebc..eb5d5f9fc75 100644 --- a/src/common/components/chat-box/index.scss +++ b/src/common/components/chat-box/index.scss @@ -477,7 +477,7 @@ margin-bottom: 2px; } .sender-message { - max-width: 290px; + max-width: 320px; margin-left: auto; display: flex; @@ -521,6 +521,17 @@ } } } + .hide-msg { + margin-left: 6px; + margin-top: 22px; + svg { + height: 14px; + width: 14px; + } + .hide-msg-svg { + cursor: pointer; + } + } } } .chat { @@ -536,6 +547,11 @@ background: #cee2ff; } + &.disable { + pointer-events: none; + opacity: 0.5; + } + .chatbox-emoji-picker { .chatbox-emoji { .emoji-picker { @@ -645,17 +661,18 @@ .profile-box { padding: 15px; - - .user-avatar.large { - margin-left: 73px; - margin-bottom: 20px; - } - .profile-name { - margin-bottom: 10px; - display: flex; - justify-content: center; - text-align: center; - font-size: 18px; - font-weight: 800; + .profile-box-content { + .user-avatar.large { + margin-bottom: 20px; + } + .profile-name { + margin-bottom: 10px; + font-size: 18px; + font-weight: 800; + } + .profile-box-buttons { + margin-bottom: 15px; + padding: 0 5px; + } } } diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 7d66fb3632e..9487b62276c 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -21,8 +21,12 @@ import { ActiveUser } from "../../store/active-user/types"; import { Chat, DirectContactsType } from "../../store/chat/types"; import { Community, ROLES } from "../../store/communities/types"; import { Global, Theme } from "../../store/global/types"; +import { User } from "../../store/users/types"; +import { ToggleType, UI } from "../../store/ui/types"; +import { Account } from "../../store/accounts/types"; import { Channel, + ChannelUpdate, communityModerator, DirectMessage, PublicMessage @@ -36,6 +40,7 @@ import GifPicker from "../gif-picker"; import { error, success } from "../feedback"; import { setNostrkeys } from "../../../providers/message-provider"; import DropDown, { MenuItem } from "../dropdown"; +import FollowControls from "../follow-controls"; import { addMessageSVG, @@ -53,7 +58,8 @@ import { chatLeaveSvg, editSVG, keySvg, - syncSvg + syncSvg, + hideSvg } from "../../img/svg"; import { @@ -63,7 +69,11 @@ import { GIPHGY, NOSTRKEY, UPLOADING, - GifImagesStyle + GifImagesStyle, + ADDROLE, + HIDEMESSAGE, + REMOVEUSER, + LEAVECOMMUNITY } from "./chat-constants"; import { getPublicKey } from "../../../lib/nostr-tools/keys"; @@ -98,11 +108,19 @@ export interface profileData { } interface Props { - history: History; + users: User[]; activeUser: ActiveUser | null; + ui: UI; + targetUsername: string; + where?: string; + history: History; global: Global; chat: Chat; resetChat: () => void; + setActiveUser: (username: string | null) => void; + updateActiveUser: (data?: Account) => void; + deleteUser: (username: string) => void; + toggleUIProp: (what: ToggleType) => void; } let zoom: Zoom | null = null; @@ -149,6 +167,47 @@ export default function ChatBox(props: Props) { const [moderator, setModerator] = useState(); const [noStrPrivKey, setNoStrPrivKey] = useState(""); const [chatPrivKey, setChatPrivkey] = useState(""); + const [hoveredMessageId, setHoveredMessageId] = useState(""); + const [privilegedUsers, setPrivilegedUsers] = useState([]); + const [hiddenMsgId, setHiddenMsgId] = useState(""); + const [removedUserId, setRemovedUserID] = useState(""); + const [removedUsers, setRemovedUsers] = useState([]); + const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); + + useEffect(() => { + console.log(isActveUserRemoved, "isActveUserRemoved"); + }, [isActveUserRemoved]); + + useEffect(() => { + console.log(currentChannel, "currentChannel"); + }, [currentChannel]); + + useEffect(() => { + const updated: ChannelUpdate = props.chat.updatedChannel + .filter((x) => x.channelId === currentChannel?.id!) + .sort((a, b) => b.created - a.created)[0]; + if (currentChannel) { + const publicMessages: PublicMessage[] = fetchCommunityMessages( + currentChannel.id, + updated.hiddenMessageIds + ); + const messages = publicMessages.sort((a, b) => a.created - b.created); + setPublicMessages(messages); + const channel = { + name: updated.name, + about: updated.about, + picture: updated.picture, + communityName: updated.communityName, + communityModerators: updated.communityModerators, + id: updated.channelId, + creator: updated.creator, + created: currentChannel?.created!, + hiddenMessageIds: updated.hiddenMessageIds, + removedUserIds: updated.removedUserIds + }; + setCurrentChannel(channel); + } + }, [props.chat.updatedChannel]); useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -178,8 +237,20 @@ export default function ChatBox(props: Props) { useEffect(() => { setInProgress(false); + console.log("chat in store", props.chat); }, [props.chat]); + useEffect(() => { + currentChannel?.communityModerators && getPrivilegedUsers(currentChannel?.communityModerators!); + currentChannel?.removedUserIds && setRemovedUsers(currentChannel.removedUserIds); + currentChannel && scrollerClicked(); + if (removedUsers) { + const removed = removedUsers.includes(activeUserKeys?.pub!); + setIsActiveUserRemoved(removed); + } + scrollerClicked(); + }, [currentChannel, removedUsers]); + useEffect(() => { // resetProfile(props.activeUser); fetchProfileData(); @@ -191,6 +262,7 @@ export default function ChatBox(props: Props) { // useEffect(() => { // if (window.messageService) { // setHasUserJoinedChat(true); + // setInProgress(false); // } // }, [window?.messageService]); @@ -230,7 +302,7 @@ export default function ChatBox(props: Props) { useEffect(() => { if (currentChannel && isCommunity) { window?.messageService?.fetchChannel(currentChannel.id); - const publicMessages = fetchCommunityMessages(currentChannel.id); + const publicMessages: PublicMessage[] = fetchCommunityMessages(currentChannel.id); const messages = publicMessages.sort((a, b) => a.created - b.created); setPublicMessages(messages); } @@ -307,6 +379,14 @@ export default function ChatBox(props: Props) { [searchtext] ); + const getPrivilegedUsers = (communityModerators: communityModerator[]) => { + const filteredUsers = + communityModerators && + communityModerators.filter((user) => ["owner", "admin", "mod"].includes(user.role)); + const privilegedUsersName = filteredUsers?.map((user) => user.name); + setPrivilegedUsers(privilegedUsersName!); + }; + const fetchCommunity = async () => { const community = await getCommunity(communityName, props.activeUser?.username); setCurrentCommunity(community!); @@ -318,12 +398,14 @@ export default function ChatBox(props: Props) { }; const fetchCurrentChannel = (communityName: string) => { - const item = props.chat.channels.find((channel) => channel.communityName === communityName); - if (item) { - const updated = props.chat.updatedChannel - .filter((x) => x.channelId === item.id) + const channel = props.chat.channels.find((channel) => channel.communityName === communityName); + console.log("fetch current chanel", channel); + if (channel) { + const updated: ChannelUpdate = props.chat.updatedChannel + .filter((x) => x.channelId === channel.id) .sort((a, b) => b.created - a.created)[0]; - if (updated && currentChannel) { + if (updated) { + console.log("updated", updated); const channel = { name: updated.name, about: updated.about, @@ -331,12 +413,14 @@ export default function ChatBox(props: Props) { communityName: updated.communityName, communityModerators: updated.communityModerators, id: updated.channelId, - creator: currentChannel.creator, - created: currentChannel.created + creator: updated.creator, + created: currentChannel?.created!, + hiddenMessageIds: updated.hiddenMessageIds, + removedUserIds: updated.removedUserIds }; setCurrentChannel(channel); } else { - setCurrentChannel(item); + setCurrentChannel(channel); } } }; @@ -377,10 +461,11 @@ export default function ChatBox(props: Props) { return []; }; - const fetchCommunityMessages = (channelId: string) => { + const fetchCommunityMessages = (channelId: string, hiddenMessageIds?: string[]) => { + const hideMessageIds = hiddenMessageIds || currentChannel?.hiddenMessageIds || []; for (const item of props.chat.publicMessages) { if (item.channelId === channelId) { - return item.PublicMessage; + return item.PublicMessage.filter((message) => !hideMessageIds.includes(message.id)); } } return []; @@ -459,13 +544,6 @@ export default function ChatBox(props: Props) { }); }; - const scrollToBottom = () => { - chatBodyDivRef?.current?.scroll({ - top: chatBodyDivRef?.current?.scrollHeight, - behavior: "auto" - }); - }; - const handleMessageSvgClick = () => { setShowSearchUser(!showSearchUser); }; @@ -478,6 +556,9 @@ export default function ChatBox(props: Props) { priv: getPrivateKey(props.activeUser?.username!) }; setNostrkeys(keys); + if (isCommunity && communityName) { + fetchCurrentChannel(communityName); + } } else { setNoStrPrivKey(""); } @@ -646,13 +727,14 @@ export default function ChatBox(props: Props) { setDMMessage(e.target.value); }; - const handleImageClick = (id: string) => { - if (clickedMessage === id) { + const handleImageClick = (msgId: string, pubkey: string) => { + if (clickedMessage === msgId) { popoverRef.current = null; setClickedMessage(""); } else { popoverRef.current = null; - setClickedMessage(id); + setClickedMessage(msgId); + setRemovedUserID(pubkey); } }; @@ -705,52 +787,29 @@ export default function ChatBox(props: Props) { const newUpdatedModerator = { ...newUpdatedChannel?.communityModerators![moderatorIndex!] }; newUpdatedModerator.role = selectedRole; newUpdatedChannel!.communityModerators![moderatorIndex!] = newUpdatedModerator; + console.log("Update role", newUpdatedChannel); setCurrentChannel(newUpdatedChannel); window.messageService?.updateChannel(currentChannel, newUpdatedChannel); success("Roles updated succesfully"); } }; - const LeaveModal = () => { - return ( - <> -
-
-

Confirmaton

-
-
-
Are you sure to leave this community?
-

- - -

- - ); + const handleConfirmaButton = (actionType: string) => { + if (actionType === HIDEMESSAGE) { + handleChannelUpdate(HIDEMESSAGE); + } else if (actionType === REMOVEUSER) { + handleChannelUpdate(REMOVEUSER); + } else if (actionType === LEAVECOMMUNITY) { + window?.messageService + ?.updateLeftChannelList([...props.chat.leftChannelsList!, currentChannel?.id!]) + .then(() => {}) + .finally(() => { + setKeyDialog(false); + setStep(0); + setIsCommunity(false); + setCommunityName(""); + }); + } }; const EditRolesModal = () => { @@ -803,7 +862,7 @@ export default function ChatBox(props: Props) {
+ +

+ + ); + }; + useDebounce( async () => { if (user.length === 0) { @@ -974,24 +1068,53 @@ export default function ChatBox(props: Props) { setInProgress(true); }; - const addNewRole = () => { - const updatedRoles = [...(currentChannel?.communityModerators || []), moderator!]; - const updatedMetaData = { + const handleChannelUpdate = (operationType: string) => { + let updatedMetaData = { name: currentChannel?.name!, about: currentChannel?.about!, picture: "", communityName: currentChannel?.communityName!, - communityModerators: updatedRoles + communityModerators: currentChannel?.communityModerators, + hiddenMessageIds: currentChannel?.hiddenMessageIds, + removedUserIds: currentChannel?.removedUserIds }; - window.messageService?.updateChannel(currentChannel!, updatedMetaData); - currentChannel?.communityModerators?.push(moderator!); - const updatedChannel: Channel = { - ...currentChannel!, - communityModerators: updatedRoles - }; - setUser(""); - setCurrentChannel(updatedChannel); + switch (operationType) { + case ADDROLE: + const updatedRoles = [...(currentChannel?.communityModerators || []), moderator!]; + updatedMetaData.communityModerators = updatedRoles; + break; + case HIDEMESSAGE: + const updatedHiddenMessages = [...(currentChannel?.hiddenMessageIds || []), hiddenMsgId!]; + updatedMetaData.hiddenMessageIds = updatedHiddenMessages; + break; + case REMOVEUSER: + const updatedRemovedUsers = [...(currentChannel?.removedUserIds || []), removedUserId!]; + updatedMetaData.removedUserIds = updatedRemovedUsers; + break; + default: + break; + } + + console.log(updatedMetaData, "updatedMetaData"); + try { + window.messageService?.updateChannel(currentChannel!, updatedMetaData); + setCurrentChannel({ ...currentChannel!, ...updatedMetaData }); + if (operationType === HIDEMESSAGE) { + setStep(0); + setKeyDialog(false); + fetchCommunityMessages(currentChannel?.id!); + setHiddenMsgId(""); + } + if (operationType === REMOVEUSER) { + setStep(0); + setKeyDialog(false); + // fetchCommunityMessages(currentChannel?.id!); + setRemovedUserID(""); + } + } catch (err) { + error("Error occurred while updating community"); + } }; const roleChanged = (e: React.ChangeEvent) => { @@ -1125,7 +1248,7 @@ export default function ChatBox(props: Props) {
)} - {!currentUser && hasUserJoinedChat && noStrPrivKey && ( + {hasUserJoinedChat && noStrPrivKey && (

@@ -1314,30 +1437,54 @@ export default function ChatBox(props: Props) { >

- - -

{name!}

- -
{ - e.preventDefault(); - e.stopPropagation(); - sendDM(name!, pMsg.creator); - }} - > - - +
+ +
+ +

+ {name!} +

+
+ - - + + +
+ +
{ + e.preventDefault(); + e.stopPropagation(); + sendDM(name!, pMsg.creator); + }} + > + + + +
+
@@ -1347,7 +1494,12 @@ export default function ChatBox(props: Props) { {dayAndMonth} {pMsg.creator !== activeUserKeys?.pub ? ( -
+
setHoveredMessageId(pMsg.id)} + onMouseLeave={() => setHoveredMessageId("")} + >
handleImageClick(pMsg.id)} + onToggle={() => handleImageClick(pMsg.id, pMsg.creator)} > @@ -1375,13 +1527,56 @@ export default function ChatBox(props: Props) { dangerouslySetInnerHTML={{ __html: renderedPreview }} />
+ {hoveredMessageId === pMsg.id && + privilegedUsers.includes(props.activeUser?.username!) && ( + +
+

{ + setClickedMessage(""); + setKeyDialog(true); + setStep(5); + setHiddenMsgId(pMsg.id); + }} + > + {hideSvg} +

+
+
+ )}
) : ( -
+
setHoveredMessageId(pMsg.id)} + onMouseLeave={() => setHoveredMessageId("")} + >

{formatMessageTime(pMsg.created)}

+ {hoveredMessageId === pMsg.id && ( + +
+

{ + setClickedMessage(""); + setKeyDialog(true); + setStep(5); + setHiddenMsgId(pMsg.id); + }} + > + {hideSvg} +

+
+
+ )}
)} + {isActveUserRemoved && isCommunity && ( +

+ You have been removed from this community +

+ )}
{inProgress && } + {(currentUser || isCommunity) && ( -
+
@@ -1627,7 +1828,12 @@ export default function ChatBox(props: Props) { autoComplete="off" className="chat-input" style={{ maxWidth: "100%", overflowWrap: "break-word" }} - disabled={inProgress || receiverPubKey === null || receiverPubKey === undefined} + disabled={ + inProgress || + receiverPubKey === null || + receiverPubKey === undefined || + isActveUserRemoved + } /> - {step === 1 && LeaveModal()} + {step === 1 && confirmationModal(LEAVECOMMUNITY)} + {step === 5 && confirmationModal(HIDEMESSAGE)} + {step === 6 && confirmationModal(REMOVEUSER)} {step === 2 && EditRolesModal()} {step === 3 && ImportChatModal()} {step === 4 && chatSuccessModal()} diff --git a/src/common/components/follow-controls/index.tsx b/src/common/components/follow-controls/index.tsx index 4b370863468..8efae611fbb 100644 --- a/src/common/components/follow-controls/index.tsx +++ b/src/common/components/follow-controls/index.tsx @@ -191,6 +191,14 @@ export default class FollowControls extends BaseComponent { ) }); + if (where && where === "chat-box" && following) { + return <>{btnUnfollow}; + } + + if (where && where === "chat-box" && !following) { + return <>{btnFollow}; + } + if (fetching) { return ( <> diff --git a/src/common/components/join-community-chat-btn/index.tsx b/src/common/components/join-community-chat-btn/index.tsx index 400280b205a..a67d415a94b 100644 --- a/src/common/components/join-community-chat-btn/index.tsx +++ b/src/common/components/join-community-chat-btn/index.tsx @@ -136,7 +136,9 @@ export default function JoinCommunityChatBtn(props: Props) { about: community.description, communityName: community.name, picture: "", - communityModerators: communityRoles + communityModerators: communityRoles, + hiddenMessageIds: [], + removedUserIds: [] }); const content = JSON.parse(data?.content!); diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts index 80536ad083f..8185497409f 100644 --- a/src/common/helper/message-service.ts +++ b/src/common/helper/message-service.ts @@ -275,6 +275,7 @@ class MessageService extends TypedEventEmitter { } public loadProfiles(pubs: string[]) { + console.log("lod profiles"); pubs.forEach((p) => { this.fetch( [ @@ -400,7 +401,7 @@ class MessageService extends TypedEventEmitter { } public async createChannel(meta: Metadata) { - // console.log("create channel run", meta); + console.log("create channel run", meta); return this.publish(Kind.ChannelCreation, [], JSON.stringify(meta)); } @@ -423,6 +424,7 @@ class MessageService extends TypedEventEmitter { } public async updateProfile(profile: Metadata) { + console.log("Update profile run", profile); return this.publish(Kind.Metadata, [], JSON.stringify(profile)); } @@ -560,6 +562,7 @@ class MessageService extends TypedEventEmitter { }) .filter(notEmpty); if (profileUpdates.length > 0) { + console.log("Profile Updates", profileUpdates); this.emit(MessageEvents.ProfileUpdate, profileUpdates); } @@ -589,6 +592,8 @@ class MessageService extends TypedEventEmitter { created: ev.created_at, communityName: content.communityName, communityModerators: content.communityModerators, + hiddenMessageIds: content.hiddenMessageIds, + removedUserIds: content.removedUserIds, ...MessageService.normalizeMetadata(content) } : null; @@ -602,7 +607,6 @@ class MessageService extends TypedEventEmitter { .filter((x) => x.kind === Kind.ChannelMetadata) .map((ev) => { const content = MessageService.parseJson(ev.content); - console.log("content", content); const channelId = MessageService.findTagValue(ev, "e"); if (!channelId) return null; return content @@ -612,6 +616,8 @@ class MessageService extends TypedEventEmitter { created: ev.created_at, communityName: content.communityName, communityModerators: content.communityModerators, + hiddenMessageIds: content.hiddenMessageIds, + removedUserIds: content.removedUserIds, channelId, ...MessageService.normalizeMetadata(content) } @@ -619,6 +625,7 @@ class MessageService extends TypedEventEmitter { }) .filter(notEmpty); if (channelUpdates.length > 0) { + console.log("channelUpdates", channelUpdates); this.emit(MessageEvents.ChannelUpdate, channelUpdates); } diff --git a/src/common/img/svg.tsx b/src/common/img/svg.tsx index 4fe1e7da76b..8ef29a4ef61 100644 --- a/src/common/img/svg.tsx +++ b/src/common/img/svg.tsx @@ -2115,3 +2115,20 @@ export const editSVG = ( ); + +export const hideSvg = ( + +); diff --git a/src/common/pages/community-functional.tsx b/src/common/pages/community-functional.tsx index d6f67e474eb..4bc4d51ec34 100644 --- a/src/common/pages/community-functional.tsx +++ b/src/common/pages/community-functional.tsx @@ -4,7 +4,9 @@ import React, { Fragment, useEffect, useState } from "react"; import { search as searchApi, SearchResult } from "../api/search-api"; import { getCommunity, getSubscriptions } from "../api/bridge"; import { EntryFilter, ListStyle } from "../store/global/types"; +import { Channel } from "../../providers/message-provider-types"; import { usePrevious } from "../util/use-previous"; +import * as ls from "../util/local-storage"; import { makeGroupKey } from "../store/entries"; import _ from "lodash"; import Meta from "../components/meta"; @@ -45,7 +47,7 @@ import { setProfileMetaData } from "../helper/chat-utils"; import { useMappedStore } from "../store/use-mapped-store"; -import { Channel } from "../../providers/message-provider-types"; +import { setNostrkeys } from "../../providers/message-provider"; interface MatchParams { filter: string; @@ -249,7 +251,9 @@ export const CommunityPage = (props: Props) => { const handleJoinChat = async () => { setInProgress(true); const keys = createNoStrAccount(); - await setProfileMetaData(props.activeUser, keys); + ls.set(`${props.activeUser?.username}_noStrPrivKey`, keys.priv); + await setProfileMetaData(props.activeUser, keys.pub); + setNostrkeys(keys); setHasUserJoinedChat(true); window.messageService?.updateProfile({ name: props.activeUser?.username!, diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts index 6d09ef6d6ce..53570eacf5e 100644 --- a/src/common/store/chat/index.ts +++ b/src/common/store/chat/index.ts @@ -96,9 +96,14 @@ export default (state: Chat = initialState, action: Actions): Chat => { case ActionTypes.PROFILES: { const { data } = action; + + const filteredProfiles = data.filter((profile) => { + return !state.profiles.some((existingProfile) => existingProfile.id === profile.id); + }); + return { ...state, - profiles: [...state.profiles, ...data] + profiles: [...state.profiles, ...filteredProfiles] }; } diff --git a/src/providers/message-provider-types.ts b/src/providers/message-provider-types.ts index 8fd832af5d5..9ed1b199a02 100644 --- a/src/providers/message-provider-types.ts +++ b/src/providers/message-provider-types.ts @@ -15,6 +15,8 @@ export type Metadata = { picture: string; communityName?: string; communityModerators?: communityModerator[]; + hiddenMessageIds?: string[]; + removedUserIds?: string[]; }; export type communityModerator = { diff --git a/src/providers/message-provider.tsx b/src/providers/message-provider.tsx index d46124bfe34..4f78ea8d3fa 100644 --- a/src/providers/message-provider.tsx +++ b/src/providers/message-provider.tsx @@ -123,7 +123,7 @@ const MessageProvider = (props: Props) => { // Profile update handler const handleProfileUpdate = (data: Profile[]) => { - // console.log("handleProfileUpdate", data); + console.log("handleProfileUpdate", data); props.addProfile(data); }; @@ -137,7 +137,7 @@ const MessageProvider = (props: Props) => { //Direct contact handler const handleDirectContact = (data: DirectContact[]) => { - // console.log("handleDirectContact", data) + console.log("handleDirectContact", data); const result = [...chat.directContacts]; data.forEach(({ name, pubkey }) => { const isPresent = chat.directContacts.some( @@ -211,7 +211,7 @@ const MessageProvider = (props: Props) => { // Channel creation handler const handleChannelCreation = (data: Channel[]) => { - // console.log("handleChannelCreation", data); + console.log("handleChannelCreation", data); const append = data.filter((x) => chat.channels.find((y) => y.id === x.id) === undefined); props.addChannels(append); @@ -228,7 +228,7 @@ const MessageProvider = (props: Props) => { // Channel update handler const handleChannelUpdate = (data: ChannelUpdate[]) => { - // console.log("handleChannelUpdate", data); + console.log("handleChannelUpdate", data); props.UpdateChannels(data); }; @@ -247,7 +247,7 @@ const MessageProvider = (props: Props) => { setPublicMessageBuffer((publicMessageBuffer) => [...publicMessageBuffer!, ...data]); for (const item of data) { - const isCreatorMatch = chat.profiles.some((firstItem) => firstItem.creator === item.creator); + const isCreatorMatch = chat.profiles.some((profile) => profile.creator === item.creator); if (!isCreatorMatch) { let uniqueUsers: string[] = []; From 63e0c228eded55a17f8b421be46909f0987e4195 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Tue, 1 Aug 2023 15:23:15 +0500 Subject: [PATCH 029/179] show warning modal if user click on create new account button --- .../components/chat-box/chat-constants.ts | 5 +- src/common/components/chat-box/index.tsx | 308 ++++++++++++++---- .../join-community-chat-btn/index.tsx | 13 +- src/common/helper/chat-utils.ts | 47 ++- src/common/helper/message-service.ts | 17 +- src/common/img/svg.tsx | 23 +- src/providers/message-provider.tsx | 8 +- 7 files changed, 324 insertions(+), 97 deletions(-) diff --git a/src/common/components/chat-box/chat-constants.ts b/src/common/components/chat-box/chat-constants.ts index 68cb398cae4..f947ea75c46 100644 --- a/src/common/components/chat-box/chat-constants.ts +++ b/src/common/components/chat-box/chat-constants.ts @@ -32,5 +32,8 @@ export const UPLOADING = "Uploading"; export const GIPHGY = "giphy"; export const HIDEMESSAGE = "hideMessage"; export const ADDROLE = "addRole"; -export const REMOVEUSER = "removeUser"; +export const BLOCKUSER = "blockUser"; export const LEAVECOMMUNITY = "leaveCommunity"; +export const UNBLOCKUSER = "unblockUser"; +export const NEWCHATACCOUNT = "newChatAccount"; +export const CHATIMPORT = "chatImport"; diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 9487b62276c..8b992bd05bb 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -41,6 +41,7 @@ import { error, success } from "../feedback"; import { setNostrkeys } from "../../../providers/message-provider"; import DropDown, { MenuItem } from "../dropdown"; import FollowControls from "../follow-controls"; +import OrDivider from "../or-divider"; import { addMessageSVG, @@ -59,7 +60,8 @@ import { editSVG, keySvg, syncSvg, - hideSvg + hideSvg, + removeUserSvg } from "../../img/svg"; import { @@ -72,8 +74,11 @@ import { GifImagesStyle, ADDROLE, HIDEMESSAGE, - REMOVEUSER, - LEAVECOMMUNITY + BLOCKUSER, + LEAVECOMMUNITY, + UNBLOCKUSER, + NEWCHATACCOUNT, + CHATIMPORT } from "./chat-constants"; import { getPublicKey } from "../../../lib/nostr-tools/keys"; @@ -173,13 +178,15 @@ export default function ChatBox(props: Props) { const [removedUserId, setRemovedUserID] = useState(""); const [removedUsers, setRemovedUsers] = useState([]); const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); + const [communityAdmins, setCommunityAdmins] = useState([]); + const [blockedUsers, setBlockedUsers] = useState<{ name: string; pubkey: string }[]>([]); useEffect(() => { - console.log(isActveUserRemoved, "isActveUserRemoved"); - }, [isActveUserRemoved]); + // console.log("communityAdmins", communityAdmins); + }, [communityAdmins]); useEffect(() => { - console.log(currentChannel, "currentChannel"); + // console.log(currentChannel, "currentChannel"); }, [currentChannel]); useEffect(() => { @@ -242,6 +249,7 @@ export default function ChatBox(props: Props) { useEffect(() => { currentChannel?.communityModerators && getPrivilegedUsers(currentChannel?.communityModerators!); + currentChannel?.removedUserIds && getBlockedUsers(currentChannel?.removedUserIds!); currentChannel?.removedUserIds && setRemovedUsers(currentChannel.removedUserIds); currentChannel && scrollerClicked(); if (removedUsers) { @@ -380,11 +388,29 @@ export default function ChatBox(props: Props) { ); const getPrivilegedUsers = (communityModerators: communityModerator[]) => { - const filteredUsers = - communityModerators && - communityModerators.filter((user) => ["owner", "admin", "mod"].includes(user.role)); - const privilegedUsersName = filteredUsers?.map((user) => user.name); - setPrivilegedUsers(privilegedUsersName!); + const privilegedRoles = ["owner", "admin", "mod"]; + const communityAdminRoles = ["owner", "admin"]; + + const privilegedUsers = communityModerators.filter((user) => + privilegedRoles.includes(user.role) + ); + const communityAdmins = communityModerators.filter((user) => + communityAdminRoles.includes(user.role) + ); + + const privilegedUserNames = privilegedUsers.map((user) => user.name); + const communityAdminNames = communityAdmins.map((user) => user.name); + + setPrivilegedUsers(privilegedUserNames); + setCommunityAdmins(communityAdminNames); + }; + + const getBlockedUsers = (blockedUser: string[]) => { + const blockedUsers = props.chat.profiles + .filter((item) => blockedUser.includes(item.creator)) + .map((item) => ({ name: item.name, pubkey: item.creator })); + // console.log("blockedUsers", blockedUsers); + setBlockedUsers(blockedUsers); }; const fetchCommunity = async () => { @@ -399,13 +425,13 @@ export default function ChatBox(props: Props) { const fetchCurrentChannel = (communityName: string) => { const channel = props.chat.channels.find((channel) => channel.communityName === communityName); - console.log("fetch current chanel", channel); + // console.log("fetch current chanel", channel); if (channel) { const updated: ChannelUpdate = props.chat.updatedChannel .filter((x) => x.channelId === channel.id) .sort((a, b) => b.created - a.created)[0]; if (updated) { - console.log("updated", updated); + // console.log("updated", updated); const channel = { name: updated.name, about: updated.about, @@ -787,28 +813,50 @@ export default function ChatBox(props: Props) { const newUpdatedModerator = { ...newUpdatedChannel?.communityModerators![moderatorIndex!] }; newUpdatedModerator.role = selectedRole; newUpdatedChannel!.communityModerators![moderatorIndex!] = newUpdatedModerator; - console.log("Update role", newUpdatedChannel); setCurrentChannel(newUpdatedChannel); window.messageService?.updateChannel(currentChannel, newUpdatedChannel); success("Roles updated succesfully"); } }; - const handleConfirmaButton = (actionType: string) => { - if (actionType === HIDEMESSAGE) { - handleChannelUpdate(HIDEMESSAGE); - } else if (actionType === REMOVEUSER) { - handleChannelUpdate(REMOVEUSER); - } else if (actionType === LEAVECOMMUNITY) { - window?.messageService - ?.updateLeftChannelList([...props.chat.leftChannelsList!, currentChannel?.id!]) - .then(() => {}) - .finally(() => { - setKeyDialog(false); - setStep(0); - setIsCommunity(false); - setCommunityName(""); - }); + const handleConfirmButton = (actionType: string) => { + switch (actionType) { + case HIDEMESSAGE: + handleChannelUpdate(HIDEMESSAGE); + break; + case BLOCKUSER: + handleChannelUpdate(BLOCKUSER); + break; + case UNBLOCKUSER: + handleChannelUpdate(UNBLOCKUSER); + break; + case LEAVECOMMUNITY: + window?.messageService + ?.updateLeftChannelList([...props.chat.leftChannelsList!, currentChannel?.id!]) + .then(() => {}) + .finally(() => { + setKeyDialog(false); + setStep(0); + setIsCommunity(false); + setCommunityName(""); + }); + break; + case NEWCHATACCOUNT: + setInProgress(true); + resetProfile(props.activeUser) + .then((updatedProfile) => { + handleJoinChat(); + setStep(10); + setInProgress(false); + }) + .catch((err) => { + error(err); + console.error("Error:", error); + }); + + break; + default: + break; } }; @@ -839,7 +887,6 @@ export default function ChatBox(props: Props) { value={user} onChange={userChanged} className={addRoleError ? "is-invalid" : ""} - // ref={this._input} /> {addRoleError && {addRoleError}} @@ -874,8 +921,8 @@ export default function ChatBox(props: Props) { - - + + @@ -924,6 +971,62 @@ export default function ChatBox(props: Props) { ); }; + const blockedUsersModal = () => { + return ( + <> +
+

Blocked Users

+
+ + {blockedUsers.length !== 0 ? ( + <> +
{_t("community.roles-account")}{_t("community.roles-role")}{_t("community.roles-account")}{_t("community.roles-role")}
+ + + + + + + + {blockedUsers && + blockedUsers.map((user, i) => { + return ( + + + + + ); + })} + +
{_t("community.roles-account")}Action
+ + {" "} + + @{user.name} + + + + +
+ + ) : ( +
+

No Blocked user yet.

+
+ )} + + ); + }; + const ImportChatModal = () => { return ( <> @@ -963,7 +1066,7 @@ export default function ChatBox(props: Props) { ); }; - const chatSuccessModal = () => { + const successModal = (message: string) => { return ( <>
@@ -977,7 +1080,13 @@ export default function ChatBox(props: Props) {
- Chats imported successfully + + {message === CHATIMPORT + ? "Chats imported successfully" + : message === NEWCHATACCOUNT + ? "New Account created successfully" + : ""} +
@@ -993,11 +1102,16 @@ export default function ChatBox(props: Props) { <>
-

Confirmaton

+

+ {actionType === NEWCHATACCOUNT ? "Warning" : "Confirmation"} +

+ {inProgress && actionType === NEWCHATACCOUNT && }
- Are you sure? + {actionType === NEWCHATACCOUNT + ? "creating new account will reset your chats" + : "Are you sure?"}

@@ -1088,15 +1202,33 @@ export default function ChatBox(props: Props) { const updatedHiddenMessages = [...(currentChannel?.hiddenMessageIds || []), hiddenMsgId!]; updatedMetaData.hiddenMessageIds = updatedHiddenMessages; break; - case REMOVEUSER: + case BLOCKUSER: const updatedRemovedUsers = [...(currentChannel?.removedUserIds || []), removedUserId!]; updatedMetaData.removedUserIds = updatedRemovedUsers; + + const isRemovedUserModerator = currentChannel?.communityModerators?.find( + (x) => x.pubkey === removedUserId + ); + const isModerator = !!isRemovedUserModerator; + if (isModerator) { + const NewUpdatedRoles = currentChannel?.communityModerators?.filter( + (item) => item.pubkey !== removedUserId + ); + updatedMetaData.communityModerators = NewUpdatedRoles; + } + break; + case UNBLOCKUSER: + const NewUpdatedRemovedUsers = currentChannel?.removedUserIds?.filter( + (item) => item !== removedUserId + ); + updatedMetaData.removedUserIds = NewUpdatedRemovedUsers; + // console.log("NewUpdatedRemovedUsers", NewUpdatedRemovedUsers); break; default: break; } - console.log(updatedMetaData, "updatedMetaData"); + // console.log(updatedMetaData, "updatedMetaData"); try { window.messageService?.updateChannel(currentChannel!, updatedMetaData); setCurrentChannel({ ...currentChannel!, ...updatedMetaData }); @@ -1106,7 +1238,7 @@ export default function ChatBox(props: Props) { fetchCommunityMessages(currentChannel?.id!); setHiddenMsgId(""); } - if (operationType === REMOVEUSER) { + if (operationType === BLOCKUSER || operationType === UNBLOCKUSER) { setStep(0); setKeyDialog(false); // fetchCommunityMessages(currentChannel?.id!); @@ -1127,11 +1259,16 @@ export default function ChatBox(props: Props) { setStep(1); }; - const editRolesClicked = () => { + const handleEditRoles = () => { setKeyDialog(true); setStep(2); }; + const handleBlockedUsers = () => { + setKeyDialog(true); + setStep(8); + }; + const toggleKeyDialog = () => { setKeyDialog(!keyDialog); setUser(""); @@ -1172,10 +1309,19 @@ export default function ChatBox(props: Props) { ? [ { label: "Edit Roles", - onClick: editRolesClicked, + onClick: handleEditRoles, icon: editSVG } ] + : []), + ...(communityAdmins.includes(props.activeUser?.username!) + ? [ + { + label: "Blocked Users", + onClick: handleBlockedUsers, + icon: removeUserSvg + } + ] : []) ]; @@ -1443,25 +1589,54 @@ export default function ChatBox(props: Props) {

- {name!} + {`@${name!}`}

-
+
- + {communityAdmins.includes(props.activeUser?.username!) && ( + <> + {currentChannel?.removedUserIds?.includes( + pMsg.creator + ) ? ( + <> + + + ) : ( + <> + + + )} + + )}
- {hoveredMessageId === pMsg.id && ( + {hoveredMessageId === pMsg.id && !isActveUserRemoved && (
) : !noStrPrivKey || noStrPrivKey.length === 0 || noStrPrivKey === null ? ( <> - {/*

{_t("chat.no-chat")}

*/}
+ {} +
+ +
) : ( <> @@ -1737,7 +1923,7 @@ export default function ChatBox(props: Props) { )} {isActveUserRemoved && isCommunity && (

- You have been removed from this community + You have been blocked from this community

)}
@@ -1862,10 +2048,14 @@ export default function ChatBox(props: Props) { {step === 1 && confirmationModal(LEAVECOMMUNITY)} {step === 5 && confirmationModal(HIDEMESSAGE)} - {step === 6 && confirmationModal(REMOVEUSER)} + {step === 6 && confirmationModal(BLOCKUSER)} + {step === 7 && confirmationModal(UNBLOCKUSER)} + {step === 9 && confirmationModal(NEWCHATACCOUNT)} {step === 2 && EditRolesModal()} {step === 3 && ImportChatModal()} - {step === 4 && chatSuccessModal()} + {step === 4 && successModal(CHATIMPORT)} + {step === 10 && successModal(NEWCHATACCOUNT)} + {step === 8 && blockedUsersModal()} )} diff --git a/src/common/components/join-community-chat-btn/index.tsx b/src/common/components/join-community-chat-btn/index.tsx index a67d415a94b..1e2e98b9f49 100644 --- a/src/common/components/join-community-chat-btn/index.tsx +++ b/src/common/components/join-community-chat-btn/index.tsx @@ -79,9 +79,9 @@ export default function JoinCommunityChatBtn(props: Props) { setCurrentChannel(communityProfile.channel); } - if (!haschannelMetaData && currentChannel) { - await setChannelMetaData(props.community.name, currentChannel!); - } + // if (!haschannelMetaData && currentChannel) { + // // await setChannelMetaData(props.community.name, currentChannel!); + // } }; const checkIsChatJoined = () => { @@ -103,7 +103,7 @@ export default function JoinCommunityChatBtn(props: Props) { const ownerData = await getProfileMetaData(community.name); const ownerRole = { name: activeUser!.username, - pubkey: ownerData.noStrKey || activeUserKeys?.pub, + pubkey: activeUserKeys?.pub || ownerData.noStrKey, role: "owner" }; @@ -151,8 +151,9 @@ export default function JoinCommunityChatBtn(props: Props) { about: content.about, picture: content.picture }; - await setChannelMetaData(community.name, currentChannel!); - setCurrentChannel(channelMetaData); + setChannelMetaData(community.name, channelMetaData).then(() => + setCurrentChannel(channelMetaData) + ); } finally { setInProgress(false); setIsJoinChat(true); diff --git a/src/common/helper/chat-utils.ts b/src/common/helper/chat-utils.ts index 67ba159bff1..f78325b0e75 100644 --- a/src/common/helper/chat-utils.ts +++ b/src/common/helper/chat-utils.ts @@ -21,14 +21,20 @@ export const getProfileMetaData = async (username: string) => { } }; -export const resetProfile = async (activeUser: ActiveUser | null) => { - const profile = await getProfileMetaData(activeUser?.username!); - delete profile.noStrKey; - delete profile.channel; - ls.remove(`${activeUser?.username}_noStrPrivKey`); - const response = await getAccountFull(activeUser?.username!); - const updatedProfile = await updateProfile(response, { ...profile }); - console.log(updatedProfile, profile); +export const resetProfile = (activeUser: ActiveUser | null) => { + return new Promise(async (resolve, reject) => { + try { + const profile = await getProfileMetaData(activeUser?.username!); + delete profile.noStrKey; + delete profile.channel; + ls.remove(`${activeUser?.username}_noStrPrivKey`); + const response = await getAccountFull(activeUser?.username!); + const updatedProfile = await updateProfile(response, { ...profile }); + resolve(updatedProfile); + } catch (error) { + reject(error); + } + }); }; export const setProfileMetaData = async (activeUser: ActiveUser | null, noStrPubKey: string) => { @@ -44,16 +50,21 @@ export const setProfileMetaData = async (activeUser: ActiveUser | null, noStrPub return updatedProfile; }; -export const setChannelMetaData = async (username: string, channel: Channel) => { - const response = await getAccountFull(username!); - const { posting_json_metadata } = response; - const profile = JSON.parse(posting_json_metadata!).profile; - const newProfile = { - channel: channel - }; - console.log({ ...profile, ...newProfile }); - const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); - return updatedProfile; +export const setChannelMetaData = (username: string, channel: Channel) => { + return new Promise(async (resolve, reject) => { + try { + const response = await getAccountFull(username!); + const { posting_json_metadata } = response; + const profile = JSON.parse(posting_json_metadata!).profile; + const newProfile = { + channel: channel + }; + const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); + resolve(updatedProfile); + } catch (error) { + reject(error); + } + }); }; export const createNoStrAccount = () => { diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts index 8185497409f..cd85cca4bee 100644 --- a/src/common/helper/message-service.ts +++ b/src/common/helper/message-service.ts @@ -275,7 +275,7 @@ class MessageService extends TypedEventEmitter { } public loadProfiles(pubs: string[]) { - console.log("lod profiles"); + // console.log("lod profiles"); pubs.forEach((p) => { this.fetch( [ @@ -344,6 +344,7 @@ class MessageService extends TypedEventEmitter { const sub = this.pool.sub(this.readRelays, filters); sub.on("event", (event: Event) => { + console.log("Event in fetch", event); event.kind === Kind.Metadata && isDirectContact ? this.addDirectContact(event) : this.pushToEventBuffer(event); @@ -401,12 +402,12 @@ class MessageService extends TypedEventEmitter { } public async createChannel(meta: Metadata) { - console.log("create channel run", meta); + // console.log("create channel run", meta); return this.publish(Kind.ChannelCreation, [], JSON.stringify(meta)); } public async updateChannel(channel: Channel, meta: Metadata) { - console.log(channel, meta, "Update channel run"); + // console.log(channel, meta, "Update channel run"); return this.findHealthyRelay(this.pool.seenOn(channel.id) as string[]).then((relay) => { return this.publish(Kind.ChannelMetadata, [["e", channel.id, relay]], JSON.stringify(meta)); }); @@ -424,7 +425,7 @@ class MessageService extends TypedEventEmitter { } public async updateProfile(profile: Metadata) { - console.log("Update profile run", profile); + // console.log("Update profile run", profile); return this.publish(Kind.Metadata, [], JSON.stringify(profile)); } @@ -464,7 +465,8 @@ class MessageService extends TypedEventEmitter { content, created_at: Math.floor(Date.now() / 1000), id: "", - sig: "" + sig: "", + sent: false }) .then((event) => { if (!event) { @@ -562,7 +564,7 @@ class MessageService extends TypedEventEmitter { }) .filter(notEmpty); if (profileUpdates.length > 0) { - console.log("Profile Updates", profileUpdates); + // console.log("Profile Updates", profileUpdates); this.emit(MessageEvents.ProfileUpdate, profileUpdates); } @@ -625,7 +627,7 @@ class MessageService extends TypedEventEmitter { }) .filter(notEmpty); if (channelUpdates.length > 0) { - console.log("channelUpdates", channelUpdates); + // console.log("channelUpdates", channelUpdates); this.emit(MessageEvents.ChannelUpdate, channelUpdates); } @@ -638,6 +640,7 @@ class MessageService extends TypedEventEmitter { .map((x) => x?.[1]) .filter(notEmpty); if (!root) return null; + console.log(ev, "evenet"); return ev.content ? { id: ev.id, diff --git a/src/common/img/svg.tsx b/src/common/img/svg.tsx index 8ef29a4ef61..8daf25b4214 100644 --- a/src/common/img/svg.tsx +++ b/src/common/img/svg.tsx @@ -2103,8 +2103,8 @@ export const chatLeaveSvg = ( export const editSVG = ( ); + +export const removeUserSvg = ( + + + + + + +); diff --git a/src/providers/message-provider.tsx b/src/providers/message-provider.tsx index 4f78ea8d3fa..8b6e082bdd9 100644 --- a/src/providers/message-provider.tsx +++ b/src/providers/message-provider.tsx @@ -123,7 +123,7 @@ const MessageProvider = (props: Props) => { // Profile update handler const handleProfileUpdate = (data: Profile[]) => { - console.log("handleProfileUpdate", data); + // console.log("handleProfileUpdate", data); props.addProfile(data); }; @@ -137,7 +137,7 @@ const MessageProvider = (props: Props) => { //Direct contact handler const handleDirectContact = (data: DirectContact[]) => { - console.log("handleDirectContact", data); + // console.log("handleDirectContact", data); const result = [...chat.directContacts]; data.forEach(({ name, pubkey }) => { const isPresent = chat.directContacts.some( @@ -211,7 +211,7 @@ const MessageProvider = (props: Props) => { // Channel creation handler const handleChannelCreation = (data: Channel[]) => { - console.log("handleChannelCreation", data); + // console.log("handleChannelCreation", data); const append = data.filter((x) => chat.channels.find((y) => y.id === x.id) === undefined); props.addChannels(append); @@ -228,7 +228,7 @@ const MessageProvider = (props: Props) => { // Channel update handler const handleChannelUpdate = (data: ChannelUpdate[]) => { - console.log("handleChannelUpdate", data); + // console.log("handleChannelUpdate", data); props.UpdateChannels(data); }; From da64edd1bf3875f02cc4078e3dca0e438b757542 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Mon, 7 Aug 2023 18:07:28 +0500 Subject: [PATCH 030/179] Add pagination in public chat and add spinner feature in public chat --- src/common/components/chat-box/index.scss | 2 +- src/common/components/chat-box/index.tsx | 172 ++++++++++++++++++++-- src/common/helper/chat-utils.ts | 1 + src/common/helper/message-service.ts | 89 ++++++++--- src/common/img/svg.tsx | 2 +- src/common/store/actions.ts | 8 +- src/common/store/chat/index.ts | 87 ++++++++++- src/common/store/chat/types.ts | 20 ++- src/providers/message-provider-types.ts | 2 + src/providers/message-provider.tsx | 123 ++++++++++++---- 10 files changed, 438 insertions(+), 68 deletions(-) diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss index eb5d5f9fc75..9008afe51ed 100644 --- a/src/common/components/chat-box/index.scss +++ b/src/common/components/chat-box/index.scss @@ -477,7 +477,7 @@ margin-bottom: 2px; } .sender-message { - max-width: 320px; + // max-width: 320px; margin-left: auto; display: flex; diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 8b992bd05bb..43da0b8e0a3 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -61,7 +61,8 @@ import { keySvg, syncSvg, hideSvg, - removeUserSvg + removeUserSvg, + alertCircleSvg } from "../../img/svg"; import { @@ -92,7 +93,8 @@ import { setProfileMetaData, resetProfile, getCommunities, - getPrivateKey + getPrivateKey, + notEmpty } from "../../helper/chat-utils"; import * as ls from "../../util/local-storage"; import { renderPostBody } from "@ecency/render-helper"; @@ -180,10 +182,12 @@ export default function ChatBox(props: Props) { const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); const [communityAdmins, setCommunityAdmins] = useState([]); const [blockedUsers, setBlockedUsers] = useState<{ name: string; pubkey: string }[]>([]); + const [isTop, setIsTop] = useState(false); + const [hasMore, setHasMore] = useState(true); useEffect(() => { - // console.log("communityAdmins", communityAdmins); - }, [communityAdmins]); + console.log("isTop", isTop); + }, [isTop]); useEffect(() => { // console.log(currentChannel, "currentChannel"); @@ -193,13 +197,14 @@ export default function ChatBox(props: Props) { const updated: ChannelUpdate = props.chat.updatedChannel .filter((x) => x.channelId === currentChannel?.id!) .sort((a, b) => b.created - a.created)[0]; - if (currentChannel) { + if (currentChannel && updated) { const publicMessages: PublicMessage[] = fetchCommunityMessages( currentChannel.id, updated.hiddenMessageIds ); const messages = publicMessages.sort((a, b) => a.created - b.created); setPublicMessages(messages); + console.log("community messages", messages); const channel = { name: updated.name, about: updated.about, @@ -216,6 +221,14 @@ export default function ChatBox(props: Props) { } }, [props.chat.updatedChannel]); + useEffect(() => { + console.log("isTop", isTop); + if (isTop) { + fetchPrevMessages(); + console.log("event is dispatched"); + } + }, [isTop]); + useEffect(() => { const handleClickOutside = (event: MouseEvent) => { const clickedElement = event.target as HTMLElement; @@ -243,7 +256,11 @@ export default function ChatBox(props: Props) { }, [clickedMessage]); useEffect(() => { - setInProgress(false); + //what if there is no channel or there is no direct contact in store. handle this thing too. + if (props.chat.channels.length !== 0 || props.chat.directContacts.length !== 0) { + setInProgress(false); + } + console.log("chat in store", props.chat); }, [props.chat]); @@ -256,7 +273,7 @@ export default function ChatBox(props: Props) { const removed = removedUsers.includes(activeUserKeys?.pub!); setIsActiveUserRemoved(removed); } - scrollerClicked(); + // scrollerClicked(); }, [currentChannel, removedUsers]); useEffect(() => { @@ -283,6 +300,7 @@ export default function ChatBox(props: Props) { const msgsList = fetchDirectMessages(receiverPubKey!); const messages = msgsList.sort((a, b) => a.created - b.created); setDirectMessagesList(messages); + // if(messages) }, [props.chat.directMessages]); useEffect(() => { @@ -300,11 +318,18 @@ export default function ChatBox(props: Props) { }, [props.global.theme, props.activeUser]); useEffect(() => { - scrollerClicked(); if (directMessagesList.length !== 0 || publicMessages.length !== 0) { //Initialize the zooming effect zoomInitializer(); } + if (directMessagesList.length !== 0) { + scrollerClicked(); + } + + if (!isTop && !isScrollToTop && publicMessages.length !== 0) { + console.log("I want this"); + scrollerClicked(); + } }, [directMessagesList, publicMessages]); useEffect(() => { @@ -313,8 +338,9 @@ export default function ChatBox(props: Props) { const publicMessages: PublicMessage[] = fetchCommunityMessages(currentChannel.id); const messages = publicMessages.sort((a, b) => a.created - b.created); setPublicMessages(messages); + console.log("community messages", messages); } - scrollerClicked(); + // scrollerClicked(); }, [currentChannel, isCommunity, props.chat.publicMessages]); useEffect(() => { @@ -368,7 +394,7 @@ export default function ChatBox(props: Props) { setIsCurrentUserJoined(true); setMessage(""); setInProgress(false); - scrollerClicked(); + // scrollerClicked(); } }, [currentUser]); @@ -537,11 +563,18 @@ export default function ChatBox(props: Props) { const sendMessage = () => { if (message.length !== 0 && !message.includes(UPLOADING)) { + if (isCommunity) { + if (!isActveUserRemoved) { + window?.messageService?.sendPublicMessage(currentChannel!, message, [], ""); + } else { + error("You cannot send messages in this group."); + } + } + if (isCurrentUser) { + window.messageService?.sendDirectMessage(receiverPubKey, message); + } setMessage(""); setIsMessageText(false); - isCommunity - ? window?.messageService?.sendPublicMessage(currentChannel!, message, [], "") - : window.messageService?.sendDirectMessage(receiverPubKey, message); } if ( receiverPubKey && @@ -552,7 +585,67 @@ export default function ChatBox(props: Props) { } }; + const fetchPrevMessages = () => { + if (!hasMore || inProgress) return; + + setInProgress(true); + window.messageService + ?.fetchPrevMessages(currentChannel!.id, publicMessages[0].created) + .then((events) => { + console.log("events in chatbox", events); + + const newMessages = events + .map((ev) => { + const eTags = ev.tags.filter(([t]) => t === "e"); + const root = eTags.find((x) => x[3] === "root")?.[1]; + const mentions = ev.tags + .filter(([t]) => t === "p") + .map((x) => x?.[1]) + .filter(notEmpty); + + if (!root) return null; + + return ev.content + ? { + id: ev.id, + root, + content: ev.content, + creator: ev.pubkey, + mentions, + created: ev.created_at, + sent: 1 + } + : null; + }) + .filter(notEmpty); + + const filteredNewMessages = newMessages.filter((newMsg) => { + return !publicMessages.some((pubMsg) => pubMsg.id === newMsg.id); + }); + + const messages = filteredNewMessages.sort((a, b) => a.created - b.created); + + setPublicMessages((prevMessages) => [...messages, ...prevMessages]); + + setInProgress(false); + console.log("num", events.length); + if (events.length < 30 - 5) { + setHasMore(false); + } + console.log("number", events.length); + }) + .finally(() => { + setInProgress(false); + setIsTop(false); + }); + }; + const handleScroll = (event: React.UIEvent) => { + // if (isTop) { + // console.log("Hurrah"); + // event.preventDefault(); + // return; + // } var element = event.currentTarget; let srollHeight: number = (element.scrollHeight / 100) * 25; const isScrollToTop = !isCurrentUser && !isCommunity && element.scrollTop >= srollHeight; @@ -561,9 +654,17 @@ export default function ChatBox(props: Props) { element.scrollTop + chatBodyDivRef?.current?.clientHeight! < element.scrollHeight; setIsScrollToTop(isScrollToTop); setIsScrollToBottom(isScrollToBottom); + const scrollerTop = element.scrollTop <= 600; + console.log("scrollerTop", scrollerTop); + if (isCommunity && scrollerTop) { + setIsTop(true); + } else { + setIsTop(false); // Set isTop to false when the condition is not met + } }; const scrollerClicked = () => { + console.log("scroller clicked"); chatBodyDivRef?.current?.scroll({ top: isCurrentUser || isCommunity ? chatBodyDivRef?.current?.scrollHeight : 0, behavior: "auto" @@ -575,6 +676,9 @@ export default function ChatBox(props: Props) { }; const handleRefreshSvgClick = () => { + const { resetChat } = props; + resetChat(); + handleBackArrowSvg(); if (getPrivateKey(props.activeUser?.username!)) { setInProgress(true); const keys = { @@ -1283,6 +1387,8 @@ export default function ChatBox(props: Props) { setClickedMessage(""); setShowSearchUser(false); setSearchText(""); + setMessage(""); + setHasMore(true); }; const handleImportChat = () => { @@ -1405,7 +1511,12 @@ export default function ChatBox(props: Props) { )}
-

setExpanded(!expanded)}> +

{ + setExpanded(!expanded); + }} + > {expanded ? expandArrow : collapseArrow}

@@ -1440,7 +1551,13 @@ export default function ChatBox(props: Props) {
@@ -1758,6 +1875,29 @@ export default function ChatBox(props: Props) { }`} dangerouslySetInnerHTML={{ __html: renderedPreview }} /> + {pMsg.sent === 0 && ( + + + + )} + {pMsg.sent === 2 && ( + + + {/* */} +
+ + + )}
)} @@ -1887,6 +2027,8 @@ export default function ChatBox(props: Props) {
+ ) : inProgress ? ( +

Loading...

) : ( <>

{_t("chat.no-chat")}

diff --git a/src/common/helper/chat-utils.ts b/src/common/helper/chat-utils.ts index f78325b0e75..68f46d77891 100644 --- a/src/common/helper/chat-utils.ts +++ b/src/common/helper/chat-utils.ts @@ -30,6 +30,7 @@ export const resetProfile = (activeUser: ActiveUser | null) => { ls.remove(`${activeUser?.username}_noStrPrivKey`); const response = await getAccountFull(activeUser?.username!); const updatedProfile = await updateProfile(response, { ...profile }); + console.log("Updated profile", updatedProfile); resolve(updatedProfile); } catch (error) { reject(error); diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts index cd85cca4bee..78c6c85d283 100644 --- a/src/common/helper/message-service.ts +++ b/src/common/helper/message-service.ts @@ -24,6 +24,7 @@ const relays = { "wss://relay.snort.social": { read: true, write: true }, "wss://nos.lol": { read: true, write: true } }; +const MESSAGE_PAGE_SIZE = 30; enum NewKinds { MuteList = 10000, @@ -38,7 +39,8 @@ export enum MessageEvents { ChannelCreation = "channel_creation", DirectMessage = "direct_message", DirectContact = "direct_contact", - PublicMessage = "public_message" + PublicMessageBeforeSent = "public_message_before_sent", + PublicMessageAfterSent = "public_message_after_sent" } type EventHandlerMap = { @@ -47,7 +49,8 @@ type EventHandlerMap = { [MessageEvents.DirectMessage]: (data: DirectMessage[]) => void; [MessageEvents.ChannelCreation]: (data: Channel[]) => void; [MessageEvents.ChannelUpdate]: (data: ChannelUpdate[]) => void; - [MessageEvents.PublicMessage]: (data: PublicMessage[]) => void; + [MessageEvents.PublicMessageBeforeSent]: (data: PublicMessage[]) => void; + [MessageEvents.PublicMessageAfterSent]: (data: PublicMessage[]) => void; [MessageEvents.LeftChannelList]: (data: string[]) => void; [MessageEvents.DirectContact]: (data: DirectContact[]) => void; }; @@ -148,6 +151,7 @@ class MessageService extends TypedEventEmitter { .filter(notEmpty) ) ); + console.log("channels", channels); if (channels.length !== 0) { this.fetchChannels(channels); } @@ -157,6 +161,29 @@ class MessageService extends TypedEventEmitter { }); } + public fetchPrevMessages(channel: string, until: number) { + console.log("check plz", channel, until); + return this.fetchP( + [ + { + kinds: [Kind.ChannelMessage], + "#e": [channel], + until, + limit: MESSAGE_PAGE_SIZE + } + ], + false + ).then((events) => { + console.log("events", events); + return events; + // events.forEach((ev) => { + // this.pushToEventBuffer(ev); + // }); + // console.log('events ki length',events.length) + // return events.length; + }); + } + public fetchChannels(channels: string[]) { // console.log("fetch channels run"); const filters: Filter[] = [ @@ -171,7 +198,7 @@ class MessageService extends TypedEventEmitter { ...channels.map((c) => ({ kinds: [Kind.ChannelMessage], "#e": [c], - limit: 30 + limit: MESSAGE_PAGE_SIZE })) ]; @@ -267,7 +294,7 @@ class MessageService extends TypedEventEmitter { { kinds: [Kind.ChannelMessage], "#e": [id], - limit: 30 + limit: MESSAGE_PAGE_SIZE } ]; @@ -344,7 +371,7 @@ class MessageService extends TypedEventEmitter { const sub = this.pool.sub(this.readRelays, filters); sub.on("event", (event: Event) => { - console.log("Event in fetch", event); + // console.log("Event in fetch", event); event.kind === Kind.Metadata && isDirectContact ? this.addDirectContact(event) : this.pushToEventBuffer(event); @@ -465,15 +492,41 @@ class MessageService extends TypedEventEmitter { content, created_at: Math.floor(Date.now() / 1000), id: "", - sig: "", - sent: false + sig: "" }) .then((event) => { if (!event) { - console.log("Event not found"); + // console.log("Event not found"); reject("Couldn't sign event!"); return; } + if (event.kind === Kind.ChannelMessage) { + let eventArray = []; + + const eTags = MessageService.filterTagValue(event, "e"); + const root = eTags.find((x) => x[3] === "root")?.[1]; + const mentions = MessageService.filterTagValue(event, "p") + .map((x) => x?.[1]) + .filter(notEmpty); + const formattedEvent = event.content + ? { + id: event.id, + root, + content: event.content, + creator: event.pubkey, + mentions, + created: event.created_at, + sent: 0 + } + : null; + // console.log("mery mtlb ka event", event); + eventArray.push(formattedEvent); + const filteredEventArray = eventArray.filter( + (item) => item !== null + ) as PublicMessage[]; + this.emit(MessageEvents.PublicMessageBeforeSent, filteredEventArray); + } + const pub = this.pool.publish(this.writeRelays, event); pub.on("ok", () => { resolve(event); @@ -484,12 +537,12 @@ class MessageService extends TypedEventEmitter { }); pub.on("failed", () => { - console.log("Failed to sign event"); + // console.log("Failed to sign event"); reject("Couldn't sign event!"); }); }) .catch(() => { - console.log("Catch run event not"); + // console.log("Catch run event not"); reject("Couldn't sign event!"); }); }); @@ -508,7 +561,7 @@ class MessageService extends TypedEventEmitter { } pushToEventBuffer(event: Event) { - // console.log("event reaches in event buffer"); + // console.log("event reaches in event buffer", event); const cacheKey = `${event.id}_emitted`; if (this.nameCache[cacheKey] === undefined) { if (this.eventQueueFlag) { @@ -640,7 +693,6 @@ class MessageService extends TypedEventEmitter { .map((x) => x?.[1]) .filter(notEmpty); if (!root) return null; - console.log(ev, "evenet"); return ev.content ? { id: ev.id, @@ -648,13 +700,15 @@ class MessageService extends TypedEventEmitter { content: ev.content, creator: ev.pubkey, mentions, - created: ev.created_at + created: ev.created_at, + sent: 1 } : null; }) .filter(notEmpty); if (publicMessages.length > 0) { - this.emit(MessageEvents.PublicMessage, publicMessages); + // console.log("publicMessages", publicMessages); + this.emit(MessageEvents.PublicMessageAfterSent, publicMessages); } Promise.all( @@ -674,7 +728,8 @@ class MessageService extends TypedEventEmitter { peer, creator: ev.pubkey, created: ev.created_at, - decrypted: false + decrypted: false, + sent: 1 }; if (this.priv === "nip07") { @@ -731,7 +786,7 @@ class MessageService extends TypedEventEmitter { export default MessageService; export const initMessageService = (keys: Keys): MessageService | undefined => { - // console.log("keys in raven instance", keys) + // console.log("keys in raven instance", keys); if (window.messageService) { window.messageService.close(); window.messageService = undefined; @@ -739,7 +794,7 @@ export const initMessageService = (keys: Keys): MessageService | undefined => { if (keys) { window.messageService = new MessageService(keys.priv, keys.pub); - // console.log("window messageService", window.messageService); + console.log("window messageService", window.messageService); } return window.messageService; diff --git a/src/common/img/svg.tsx b/src/common/img/svg.tsx index 8daf25b4214..6ba12684e14 100644 --- a/src/common/img/svg.tsx +++ b/src/common/img/svg.tsx @@ -2096,7 +2096,7 @@ export const chatLeaveSvg = ( strokeLinecap="round" strokeLinejoin="round" d="M15.75 9V5.25A2.25 2.25 0 0013.5 3h-6a2.25 2.25 0 00-2.25 2.25v13.5A2.25 2.25 0 007.5 21h6a2.25 2.25 0 002.25-2.25V15M12 9l-3 3m0 0l3 3m-3-3h12.75" - > + /> ); diff --git a/src/common/store/actions.ts b/src/common/store/actions.ts index da195eab229..66d26fe90f5 100644 --- a/src/common/store/actions.ts +++ b/src/common/store/actions.ts @@ -49,7 +49,9 @@ import { addPublicMessage, addProfile, addleftChannels, - UpdateChannels + UpdateChannels, + replacePublicMessage, + verifyMessageSending } from "./chat"; // @note Do not use it directly @@ -107,7 +109,9 @@ export const ACTIONS = { addPublicMessage, addProfile, addleftChannels, - UpdateChannels + UpdateChannels, + replacePublicMessage, + verifyMessageSending }; export const getActions = () => ({ diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts index 53570eacf5e..4913987211c 100644 --- a/src/common/store/chat/index.ts +++ b/src/common/store/chat/index.ts @@ -20,7 +20,9 @@ import { PublicMessagesAction, ProfilesAction, LeftChannelsAction, - UpdateChannelAction + UpdateChannelAction, + ReplacePublicMessagesAction, + VerifySendingMessageAction } from "./types"; export const initialState: Chat = { @@ -82,6 +84,7 @@ export default (state: Chat = initialState, action: Actions): Chat => { case ActionTypes.PUBLICMESSAGES: { const { channelId, data } = action; + return { ...state, publicMessages: [ @@ -123,6 +126,56 @@ export default (state: Chat = initialState, action: Actions): Chat => { }; } + case ActionTypes.REPLACEPUBLICMESSAGE: { + const { channelId, data } = action; + const publicChatObject = state.publicMessages.find((item) => item.channelId === channelId); + const publicChat = publicChatObject?.PublicMessage!; + + const updatedMainArray = publicChat?.map((message) => { + const matchingMessage = data.find((subMessage) => subMessage.id === message.id); + return matchingMessage ? matchingMessage : message; + }); + + // Add new messages from subArray that do not exist in the mainArray + data.forEach((subMessage) => { + const existsInMainArray = publicChat.some((message) => message.id === subMessage.id); + if (!existsInMainArray) { + updatedMainArray.push(subMessage); + } + }); + + return { + ...state, + publicMessages: [ + ...state.publicMessages.map((obj) => + obj.channelId === channelId + ? { channelId: channelId, PublicMessage: [...updatedMainArray] } + : obj + ) + ] + }; + } + + case ActionTypes.VERIFYMESSAGESENDING: { + const { channelId, data } = action; + + const publicChatObject = state.publicMessages.find((item) => item.channelId === channelId); + const publicChat = publicChatObject?.PublicMessage || []; + + const updatedPublicChat = publicChat.map((item) => + data.some((object) => object.id === item.id && item.sent === 0) + ? { ...item, sent: 2 } + : item + ); + console.log("updatedPublicChat", updatedPublicChat); + return { + ...state, + publicMessages: state.publicMessages.map((obj) => + obj.channelId === channelId ? { channelId, PublicMessage: updatedPublicChat } : obj + ) + }; + } + default: return state; } @@ -162,6 +215,16 @@ export const UpdateChannels = (data: ChannelUpdate[]) => (dispatch: Dispatch) => dispatch(UpdateChannelsAct(data)); }; +export const replacePublicMessage = + (channelId: string, data: PublicMessage[]) => (dispatch: Dispatch) => { + dispatch(replacePublicMessagesAct(channelId, data)); + }; + +export const verifyMessageSending = + (channelId: string, data: PublicMessage[]) => (dispatch: Dispatch) => { + dispatch(verifyMessageSendingAct(channelId, data)); + }; + /* Action Creators */ export const addDirectContactsAct = (data: DirectContactsType[]): DirectContactsAction => { @@ -190,6 +253,17 @@ export const addPublicMessagesAct = ( }; }; +export const replacePublicMessagesAct = ( + channelId: string, + data: PublicMessage[] +): ReplacePublicMessagesAction => { + return { + type: ActionTypes.REPLACEPUBLICMESSAGE, + channelId, + data + }; +}; + export const resetChatAct = (): ResetChatAction => { return { type: ActionTypes.RESET @@ -223,3 +297,14 @@ export const UpdateChannelsAct = (data: ChannelUpdate[]): UpdateChannelAction => data }; }; + +export const verifyMessageSendingAct = ( + channelId: string, + data: PublicMessage[] +): VerifySendingMessageAction => { + return { + type: ActionTypes.VERIFYMESSAGESENDING, + channelId, + data + }; +}; diff --git a/src/common/store/chat/types.ts b/src/common/store/chat/types.ts index 3b795570922..04ed357ce67 100644 --- a/src/common/store/chat/types.ts +++ b/src/common/store/chat/types.ts @@ -39,7 +39,9 @@ export enum ActionTypes { PUBLICMESSAGES = "@chat/PUBLICMESSAGES", PROFILES = "@chat/PROFILES", LEFTCHANNELLIST = "@chat/LEFTCHANNELLIST", - UPDATEDCHANNEL = "@chat/UPDATEDCHANNEL" + UPDATEDCHANNEL = "@chat/UPDATEDCHANNEL", + REPLACEPUBLICMESSAGE = "@chat/REPLACEPUBLICMESSAGE", + VERIFYMESSAGESENDING = "@chat/VERIFYMESSAGESENDING" } export interface DirectContactsAction { @@ -82,6 +84,18 @@ export interface UpdateChannelAction { data: ChannelUpdate[]; } +export interface ReplacePublicMessagesAction { + type: ActionTypes.REPLACEPUBLICMESSAGE; + data: PublicMessage[]; + channelId: string; +} + +export interface VerifySendingMessageAction { + type: ActionTypes.VERIFYMESSAGESENDING; + data: PublicMessage[]; + channelId: string; +} + export type Actions = | DirectContactsAction | DirectMessagesAction @@ -90,4 +104,6 @@ export type Actions = | PublicMessagesAction | ProfilesAction | LeftChannelsAction - | UpdateChannelAction; + | UpdateChannelAction + | ReplacePublicMessagesAction + | VerifySendingMessageAction; diff --git a/src/providers/message-provider-types.ts b/src/providers/message-provider-types.ts index 9ed1b199a02..509fde38dae 100644 --- a/src/providers/message-provider-types.ts +++ b/src/providers/message-provider-types.ts @@ -43,6 +43,7 @@ export type PublicMessage = { created: number; children?: PublicMessage[]; mentions: string[]; + sent?: number; }; export type DirectMessage = { @@ -54,6 +55,7 @@ export type DirectMessage = { created: number; children?: DirectMessage[]; decrypted: boolean; + sent?: number; }; export type Message = PublicMessage | DirectMessage; diff --git a/src/providers/message-provider.tsx b/src/providers/message-provider.tsx index 8b6e082bdd9..b147060b650 100644 --- a/src/providers/message-provider.tsx +++ b/src/providers/message-provider.tsx @@ -14,7 +14,6 @@ import { import { initMessageService, MessageEvents } from "../common/helper/message-service"; import { getProfileMetaData, NostrKeysType, getPrivateKey } from "../common/helper/chat-utils"; -import * as ls from "../common/util/local-storage"; import { useMappedStore } from "../common/store/use-mapped-store"; @@ -36,6 +35,8 @@ interface Props { addProfile: (data: Profile[]) => void; addleftChannels: (data: string[]) => void; UpdateChannels: (data: ChannelUpdate[]) => void; + replacePublicMessage: (channelId: string, data?: PublicMessage[]) => void; + verifyMessageSending: (channelId: string, data?: PublicMessage[]) => void; } const MessageProvider = (props: Props) => { @@ -46,6 +47,18 @@ const MessageProvider = (props: Props) => { const [messageService, setMessageService] = useState(); const [directMessageBuffer, setDirectMessageBuffer] = useState([]); const [publicMessageBuffer, setPublicMessageBuffer] = useState([]); + const [isCommunityCreated, setIsCommunityCreated] = useState(false); + const [replacedMessagesBuffer, setReplacedMessagesBuffer] = useState([]); + + useEffect(() => { + if (chat.channels.length !== 0) { + setIsCommunityCreated(true); + } + }, [chat.channels]); + + useEffect(() => { + // console.log("replacedMessagesBuffer", replacedMessagesBuffer); + }, [replacedMessagesBuffer]); useEffect(() => { window.addEventListener("createMSInstance", createMSInstance); @@ -55,6 +68,10 @@ const MessageProvider = (props: Props) => { }; }, []); + useEffect(() => { + // console.log("publicMessageBuffer", publicMessageBuffer); + }, [publicMessageBuffer]); + useEffect(() => { if (!window.messageService && keys?.priv) { // console.log(keys, "keys") @@ -241,10 +258,60 @@ const MessageProvider = (props: Props) => { }; }, [messageService]); - //Public Message handler - const handlePublicMessage = (data: PublicMessage[]) => { - // console.log("handlePublicMessage", data); - setPublicMessageBuffer((publicMessageBuffer) => [...publicMessageBuffer!, ...data]); + const checkMessageSending = (channelId: string, data: PublicMessage[]) => { + setTimeout(() => { + props.verifyMessageSending(channelId, data); + }, 20000); + }; + + //public message handle before sent + + const handlePublicMessageBeforeSent = (data: PublicMessage[]) => { + console.log("handlePublicMessageBeeforeSent", data); + data.map((m) => { + const { id, root } = m; + setReplacedMessagesBuffer((prevBuffer) => [...prevBuffer, id]); + props.addPublicMessage(root, [m]); + checkMessageSending(root, [m]); + }); + }; + + useEffect(() => { + messageService?.removeListener( + MessageEvents.PublicMessageBeforeSent, + handlePublicMessageBeforeSent + ); + messageService?.addListener( + MessageEvents.PublicMessageBeforeSent, + handlePublicMessageBeforeSent + ); + + return () => { + messageService?.removeListener( + MessageEvents.PublicMessageBeforeSent, + handlePublicMessageBeforeSent + ); + }; + }, [messageService, chat.profiles, chat.publicMessages]); + + //Public Message handler after sent + const handlePublicMessageAfterSent = (data: PublicMessage[]) => { + console.log("handlePublicMessageAfterSent", data, chat); + if (isCommunityCreated) { + data.forEach((message) => { + const { root, id } = message; + if (replacedMessagesBuffer.includes(id)) { + setReplacedMessagesBuffer((prevBuffer) => + prevBuffer.filter((messageId) => messageId !== id) + ); + props.replacePublicMessage(root, [message]); + } else { + props.addPublicMessage(root, [message]); + } + }); + } else { + setPublicMessageBuffer((publicMessageBuffer) => [...publicMessageBuffer, ...data]); + } for (const item of data) { const isCreatorMatch = chat.profiles.some((profile) => profile.creator === item.creator); @@ -262,16 +329,22 @@ const MessageProvider = (props: Props) => { }; useEffect(() => { - messageService?.removeListener(MessageEvents.PublicMessage, handlePublicMessage); - messageService?.addListener(MessageEvents.PublicMessage, handlePublicMessage); + messageService?.removeListener( + MessageEvents.PublicMessageAfterSent, + handlePublicMessageAfterSent + ); + messageService?.addListener(MessageEvents.PublicMessageAfterSent, handlePublicMessageAfterSent); return () => { - messageService?.removeListener(MessageEvents.PublicMessage, handlePublicMessage); + messageService?.removeListener( + MessageEvents.PublicMessageAfterSent, + handlePublicMessageAfterSent + ); }; }, [messageService, chat.profiles, chat.publicMessages]); useEffect(() => { - if (chat.channels.length !== 0) { + if (chat.channels.length !== 0 && publicMessageBuffer.length !== 0) { setPublicMessages(); } }, [chat.publicMessages, publicMessageBuffer]); @@ -283,25 +356,10 @@ const MessageProvider = (props: Props) => { (stateItem) => stateItem.channelId === root ); if (matchingStateItem) { - if (matchingStateItem.PublicMessage.length === 0) { - props.addPublicMessage(root, [obj]); - setPublicMessageBuffer((prevMessageBuffer) => - prevMessageBuffer.filter((message) => message.id !== obj.id) - ); - } else { - let itemExists = false; - matchingStateItem.PublicMessage.forEach((item) => { - if (item.id === obj.id) { - itemExists = true; - } - }); - if (!itemExists) { - props.addPublicMessage(root, [obj]); - setPublicMessageBuffer((prevMessageBuffer) => - prevMessageBuffer.filter((message) => message.id !== obj.id) - ); - } - } + props.addPublicMessage(root, [obj]); + setPublicMessageBuffer((prevMessageBuffer) => + prevMessageBuffer.filter((message) => message.id !== obj.id) + ); } }); }; @@ -330,7 +388,14 @@ const MessageProvider = (props: Props) => { messageService?.removeListener(MessageEvents.DirectContact, handleDirectContact); messageService?.removeListener(MessageEvents.LeftChannelList, handleLeftChannelList); messageService?.removeListener(MessageEvents.ChannelUpdate, handleChannelUpdate); - messageService?.removeListener(MessageEvents.PublicMessage, handlePublicMessage); + messageService?.removeListener( + MessageEvents.PublicMessageBeforeSent, + handlePublicMessageBeforeSent + ); + messageService?.removeListener( + MessageEvents.PublicMessageAfterSent, + handlePublicMessageAfterSent + ); messageService?.removeListener(MessageEvents.DirectMessage, handleDirectMessage); }; }, [messageService]); From b6c5f3334dcdd0be3024a5d7163777b0c09b0a48 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Thu, 10 Aug 2023 12:50:17 +0500 Subject: [PATCH 031/179] add resend functionality and also add the previous messages handler for public chat --- .../components/chat-box/chat-constants.ts | 1 + src/common/components/chat-box/index.scss | 36 ++- src/common/components/chat-box/index.tsx | 200 ++++++------ src/common/helper/message-service.ts | 147 ++++++--- src/common/img/svg.tsx | 12 + src/common/store/actions.ts | 14 +- src/common/store/chat/index.ts | 304 ++++++++++++++---- src/common/store/chat/types.ts | 60 +++- src/providers/message-provider-types.ts | 4 + src/providers/message-provider.tsx | 190 ++++++++--- 10 files changed, 705 insertions(+), 263 deletions(-) diff --git a/src/common/components/chat-box/chat-constants.ts b/src/common/components/chat-box/chat-constants.ts index f947ea75c46..f70fbf93bff 100644 --- a/src/common/components/chat-box/chat-constants.ts +++ b/src/common/components/chat-box/chat-constants.ts @@ -37,3 +37,4 @@ export const LEAVECOMMUNITY = "leaveCommunity"; export const UNBLOCKUSER = "unblockUser"; export const NEWCHATACCOUNT = "newChatAccount"; export const CHATIMPORT = "chatImport"; +export const RESENDMESSAGE = "resendMessage"; diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss index 9008afe51ed..9aa7a44914e 100644 --- a/src/common/components/chat-box/index.scss +++ b/src/common/components/chat-box/index.scss @@ -482,7 +482,36 @@ display: flex; justify-content: flex-end; - margin-right: 15px; + margin-right: 17px; + + .resend-svg { + margin: 5px 15px 0 0; + cursor: pointer; + svg { + @include themify(day) { + fill: $light-black; + } + @include themify(night) { + fill: $white; + } + } + } + + .failed-svg { + margin: 8px 0 0 5px; + svg { + width: 16px; + height: 16px; + fill: $red !important; + } + } + + &.sending { + margin-right: 5px; + } + &.failed { + margin-right: 7px; + } .sender-message-content { border-radius: 10px 10px 0px; @@ -522,14 +551,15 @@ } } .hide-msg { - margin-left: 6px; - margin-top: 22px; + margin-right: 15px; + margin-top: 5px; svg { height: 14px; width: 14px; } .hide-msg-svg { cursor: pointer; + margin-bottom: 0; } } } diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 43da0b8e0a3..da68d889903 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -62,7 +62,8 @@ import { syncSvg, hideSvg, removeUserSvg, - alertCircleSvg + resendMessageSvg, + failedMessageSvg } from "../../img/svg"; import { @@ -79,7 +80,8 @@ import { LEAVECOMMUNITY, UNBLOCKUSER, NEWCHATACCOUNT, - CHATIMPORT + CHATIMPORT, + RESENDMESSAGE } from "./chat-constants"; import { getPublicKey } from "../../../lib/nostr-tools/keys"; @@ -128,6 +130,8 @@ interface Props { updateActiveUser: (data?: Account) => void; deleteUser: (username: string) => void; toggleUIProp: (what: ToggleType) => void; + deletePublicMessage: (channelId: string, msgId: string) => void; + deleteDirectMessage: (peer: string, msgId: string) => void; } let zoom: Zoom | null = null; @@ -142,7 +146,7 @@ export default function ChatBox(props: Props) { const [currentUser, setCurrentUser] = useState(""); const [isCurrentUser, setIsCurrentUser] = useState(false); const [message, setMessage] = useState(""); - const [dMMessage, setDMMessage] = useState(""); + const [dmMessage, setDmMessage] = useState(""); const [isMessageText, setIsMessageText] = useState(false); const [profileData, setProfileData] = useState(); const [isScrollToTop, setIsScrollToTop] = useState(false); @@ -184,10 +188,11 @@ export default function ChatBox(props: Props) { const [blockedUsers, setBlockedUsers] = useState<{ name: string; pubkey: string }[]>([]); const [isTop, setIsTop] = useState(false); const [hasMore, setHasMore] = useState(true); + const [resendMessage, setResendMessage] = useState(); useEffect(() => { - console.log("isTop", isTop); - }, [isTop]); + console.log("publicMessages", publicMessages); + }, [publicMessages]); useEffect(() => { // console.log(currentChannel, "currentChannel"); @@ -204,7 +209,7 @@ export default function ChatBox(props: Props) { ); const messages = publicMessages.sort((a, b) => a.created - b.created); setPublicMessages(messages); - console.log("community messages", messages); + // console.log("community messages", messages); const channel = { name: updated.name, about: updated.about, @@ -222,10 +227,10 @@ export default function ChatBox(props: Props) { }, [props.chat.updatedChannel]); useEffect(() => { - console.log("isTop", isTop); + // console.log("isTop", isTop); if (isTop) { fetchPrevMessages(); - console.log("event is dispatched"); + // console.log("event is dispatched"); } }, [isTop]); @@ -285,10 +290,16 @@ export default function ChatBox(props: Props) { }, []); // useEffect(() => { + // console.log("Use effect run"); // if (window.messageService) { // setHasUserJoinedChat(true); - // setInProgress(false); + // // setInProgress(false); // } + // setTimeout(() => { + // if (props.chat.channels.length === 0 && props.chat.directContacts.length === 0) { + // setInProgress(false); + // } + // }, 5000); // }, [window?.messageService]); useEffect(() => { @@ -300,7 +311,6 @@ export default function ChatBox(props: Props) { const msgsList = fetchDirectMessages(receiverPubKey!); const messages = msgsList.sort((a, b) => a.created - b.created); setDirectMessagesList(messages); - // if(messages) }, [props.chat.directMessages]); useEffect(() => { @@ -327,7 +337,6 @@ export default function ChatBox(props: Props) { } if (!isTop && !isScrollToTop && publicMessages.length !== 0) { - console.log("I want this"); scrollerClicked(); } }, [directMessagesList, publicMessages]); @@ -338,7 +347,7 @@ export default function ChatBox(props: Props) { const publicMessages: PublicMessage[] = fetchCommunityMessages(currentChannel.id); const messages = publicMessages.sort((a, b) => a.created - b.created); setPublicMessages(messages); - console.log("community messages", messages); + // console.log("community messages", messages); } // scrollerClicked(); }, [currentChannel, isCommunity, props.chat.publicMessages]); @@ -507,7 +516,7 @@ export default function ChatBox(props: Props) { const fetchDirectMessages = (peer: string) => { for (const item of props.chat.directMessages) { if (item.peer === peer) { - return item.chat; + return Object.values(item.chat); } } return []; @@ -517,7 +526,10 @@ export default function ChatBox(props: Props) { const hideMessageIds = hiddenMessageIds || currentChannel?.hiddenMessageIds || []; for (const item of props.chat.publicMessages) { if (item.channelId === channelId) { - return item.PublicMessage.filter((message) => !hideMessageIds.includes(message.id)); + const filteredPublicMessages = Object.values(item.PublicMessage).filter( + (message) => !hideMessageIds.includes(message.id) + ); + return filteredPublicMessages; } } return []; @@ -591,48 +603,10 @@ export default function ChatBox(props: Props) { setInProgress(true); window.messageService ?.fetchPrevMessages(currentChannel!.id, publicMessages[0].created) - .then((events) => { - console.log("events in chatbox", events); - - const newMessages = events - .map((ev) => { - const eTags = ev.tags.filter(([t]) => t === "e"); - const root = eTags.find((x) => x[3] === "root")?.[1]; - const mentions = ev.tags - .filter(([t]) => t === "p") - .map((x) => x?.[1]) - .filter(notEmpty); - - if (!root) return null; - - return ev.content - ? { - id: ev.id, - root, - content: ev.content, - creator: ev.pubkey, - mentions, - created: ev.created_at, - sent: 1 - } - : null; - }) - .filter(notEmpty); - - const filteredNewMessages = newMessages.filter((newMsg) => { - return !publicMessages.some((pubMsg) => pubMsg.id === newMsg.id); - }); - - const messages = filteredNewMessages.sort((a, b) => a.created - b.created); - - setPublicMessages((prevMessages) => [...messages, ...prevMessages]); - - setInProgress(false); - console.log("num", events.length); - if (events.length < 30 - 5) { + .then((num) => { + if (num < 25) { setHasMore(false); } - console.log("number", events.length); }) .finally(() => { setInProgress(false); @@ -641,11 +615,6 @@ export default function ChatBox(props: Props) { }; const handleScroll = (event: React.UIEvent) => { - // if (isTop) { - // console.log("Hurrah"); - // event.preventDefault(); - // return; - // } var element = event.currentTarget; let srollHeight: number = (element.scrollHeight / 100) * 25; const isScrollToTop = !isCurrentUser && !isCommunity && element.scrollTop >= srollHeight; @@ -655,16 +624,14 @@ export default function ChatBox(props: Props) { setIsScrollToTop(isScrollToTop); setIsScrollToBottom(isScrollToBottom); const scrollerTop = element.scrollTop <= 600; - console.log("scrollerTop", scrollerTop); if (isCommunity && scrollerTop) { setIsTop(true); } else { - setIsTop(false); // Set isTop to false when the condition is not met + setIsTop(false); } }; const scrollerClicked = () => { - console.log("scroller clicked"); chatBodyDivRef?.current?.scroll({ top: isCurrentUser || isCommunity ? chatBodyDivRef?.current?.scrollHeight : 0, behavior: "auto" @@ -841,20 +808,20 @@ export default function ChatBox(props: Props) { }; const sendDM = (name: string, pubkey: string) => { - if (dMMessage) { - window.messageService?.sendDirectMessage(pubkey, dMMessage); + if (dmMessage) { + window.messageService?.sendDirectMessage(pubkey, dmMessage); setIsCurrentUser(true); setCurrentUser(name); setIsCommunity(false); setCommunityName(""); setClickedMessage(""); - setDMMessage(""); + setDmMessage(""); } }; const handleDMChange = (e: React.ChangeEvent) => { - setDMMessage(e.target.value); + setDmMessage(e.target.value); }; const handleImageClick = (msgId: string, pubkey: string) => { @@ -923,6 +890,20 @@ export default function ChatBox(props: Props) { } }; + const handleResendMessage = () => { + setKeyDialog(false); + setStep(0); + if (resendMessage) { + if (currentChannel) { + props.deletePublicMessage(currentChannel.id, resendMessage.id); + window?.messageService?.sendPublicMessage(currentChannel!, resendMessage.content, [], ""); + } else if (isCurrentUser && receiverPubKey) { + props.deleteDirectMessage(receiverPubKey, resendMessage.id); + window.messageService?.sendDirectMessage(receiverPubKey, resendMessage.content); + } + } + }; + const handleConfirmButton = (actionType: string) => { switch (actionType) { case HIDEMESSAGE: @@ -955,9 +936,10 @@ export default function ChatBox(props: Props) { }) .catch((err) => { error(err); - console.error("Error:", error); }); - + break; + case RESENDMESSAGE: + handleResendMessage(); break; default: break; @@ -1326,13 +1308,11 @@ export default function ChatBox(props: Props) { (item) => item !== removedUserId ); updatedMetaData.removedUserIds = NewUpdatedRemovedUsers; - // console.log("NewUpdatedRemovedUsers", NewUpdatedRemovedUsers); break; default: break; } - // console.log(updatedMetaData, "updatedMetaData"); try { window.messageService?.updateChannel(currentChannel!, updatedMetaData); setCurrentChannel({ ...currentChannel!, ...updatedMetaData }); @@ -1345,7 +1325,6 @@ export default function ChatBox(props: Props) { if (operationType === BLOCKUSER || operationType === UNBLOCKUSER) { setStep(0); setKeyDialog(false); - // fetchCommunityMessages(currentChannel?.id!); setRemovedUserID(""); } } catch (err) { @@ -1662,13 +1641,41 @@ export default function ChatBox(props: Props) {

{formatMessageTime(msg.created)}

-
+
+ {msg.sent === 2 && ( + + { + setKeyDialog(true); + setStep(11); + setResendMessage(msg); + }} + > + {resendMessageSvg} + + + )}
+ {msg.sent === 0 && ( + + + + )} + {msg.sent === 2 && ( + + {failedMessageSvg} + + )}
)} @@ -1765,7 +1772,7 @@ export default function ChatBox(props: Props) { > {formatMessageTime(pMsg.created)}

-
+
{hoveredMessageId === pMsg.id && !isActveUserRemoved && ( -
+

{ @@ -1869,6 +1881,20 @@ export default function ChatBox(props: Props) {

)} + {pMsg.sent === 2 && ( + + { + setKeyDialog(true); + setStep(11); + setResendMessage(pMsg); + }} + > + {resendMessageSvg} + + + )}
)} {pMsg.sent === 2 && ( - - - {/* */} -
- + + {failedMessageSvg} )}
@@ -2193,6 +2206,7 @@ export default function ChatBox(props: Props) { {step === 6 && confirmationModal(BLOCKUSER)} {step === 7 && confirmationModal(UNBLOCKUSER)} {step === 9 && confirmationModal(NEWCHATACCOUNT)} + {step === 11 && confirmationModal(RESENDMESSAGE)} {step === 2 && EditRolesModal()} {step === 3 && ImportChatModal()} {step === 4 && successModal(CHATIMPORT)} diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts index 78c6c85d283..cb44128d88f 100644 --- a/src/common/helper/message-service.ts +++ b/src/common/helper/message-service.ts @@ -37,20 +37,24 @@ export enum MessageEvents { LeftChannelList = "left_channel_list", ChannelUpdate = "channel_update", ChannelCreation = "channel_creation", - DirectMessage = "direct_message", + DirectMessageBeforeSent = "direct_message_before_sent", + DirectMessageAfterSent = "direct_message_after_sent", DirectContact = "direct_contact", PublicMessageBeforeSent = "public_message_before_sent", - PublicMessageAfterSent = "public_message_after_sent" + PublicMessageAfterSent = "public_message_after_sent", + PreviousPublicMessages = "previous_public_messages" } type EventHandlerMap = { [MessageEvents.Ready]: () => void; [MessageEvents.ProfileUpdate]: (data: Profile[]) => void; - [MessageEvents.DirectMessage]: (data: DirectMessage[]) => void; + [MessageEvents.DirectMessageBeforeSent]: (data: DirectMessage[]) => void; + [MessageEvents.DirectMessageAfterSent]: (data: DirectMessage[]) => void; [MessageEvents.ChannelCreation]: (data: Channel[]) => void; [MessageEvents.ChannelUpdate]: (data: ChannelUpdate[]) => void; [MessageEvents.PublicMessageBeforeSent]: (data: PublicMessage[]) => void; [MessageEvents.PublicMessageAfterSent]: (data: PublicMessage[]) => void; + [MessageEvents.PreviousPublicMessages]: (data: PublicMessage[]) => void; [MessageEvents.LeftChannelList]: (data: string[]) => void; [MessageEvents.DirectContact]: (data: DirectContact[]) => void; }; @@ -174,13 +178,32 @@ class MessageService extends TypedEventEmitter { ], false ).then((events) => { - console.log("events", events); - return events; - // events.forEach((ev) => { - // this.pushToEventBuffer(ev); - // }); - // console.log('events ki length',events.length) - // return events.length; + if (events.length > 0) { + const formattedEvents = events + .map((ev) => { + const eTags = MessageService.filterTagValue(ev, "e"); + const root = eTags.find((x) => x[3] === "root")?.[1]; + const mentions = MessageService.filterTagValue(ev, "p") + .map((x) => x?.[1]) + .filter(notEmpty); + if (!root) return null; + return ev.content + ? { + id: ev.id, + root, + content: ev.content, + creator: ev.pubkey, + mentions, + created: ev.created_at, + sent: 1 + } + : null; + }) + .filter(notEmpty); + this.emit(MessageEvents.PreviousPublicMessages, formattedEvents); + } + + return events.length; }); } @@ -302,7 +325,7 @@ class MessageService extends TypedEventEmitter { } public loadProfiles(pubs: string[]) { - // console.log("lod profiles"); + // console.log("load profiles"); pubs.forEach((p) => { this.fetch( [ @@ -440,6 +463,72 @@ class MessageService extends TypedEventEmitter { }); } + public emitPublicMessageBeforeSent(event: Event) { + let publiMessagesEventArray = []; + + const eTags = MessageService.filterTagValue(event, "e"); + const root = eTags.find((x) => x[3] === "root")?.[1]; + const mentions = MessageService.filterTagValue(event, "p") + .map((x) => x?.[1]) + .filter(notEmpty); + const formattedEvent = event.content + ? { + id: event.id, + root, + content: event.content, + creator: event.pubkey, + mentions, + created: event.created_at, + sent: 0 + } + : null; + publiMessagesEventArray.push(formattedEvent); + const filteredEventArray = publiMessagesEventArray.filter( + (item) => item !== null + ) as PublicMessage[]; + this.emit(MessageEvents.PublicMessageBeforeSent, filteredEventArray); + } + + public async emitDirectMessageBeforeSent(event: Event) { + const eventArray = []; + const receiver = MessageService.findTagValue(event, "p"); + + if (!receiver) { + return; + } + + const eTags = MessageService.filterTagValue(event, "e"); + const root = eTags.find((x) => x[3] === "root")?.[1]; + + const peer = receiver === this.pub ? event.pubkey : receiver; + const msg = { + id: event.id, + root, + content: event.content, + peer, + creator: event.pubkey, + created: event.created_at, + decrypted: false, + sent: 0 + }; + if (this.priv === "nip07") { + eventArray.push(msg); + } + + try { + const content = await decrypt(this.priv, peer, event.content); + const decrypted = { + ...msg, + content, + decrypted: true + }; + eventArray.push(decrypted); + } catch (error) { + console.error("Error decrypting:", error); + } + this.emit(MessageEvents.DirectMessageBeforeSent, eventArray); + } + private async findHealthyRelay(relays: string[]) { for (const relay of relays) { try { @@ -483,7 +572,10 @@ class MessageService extends TypedEventEmitter { return this.publish(Kind.ChannelMessage, tags, message); } + public resendMessage(kind: number, tags: Array[], content: string) {} + private publish(kind: number, tags: Array[], content: string): Promise { + console.log("kind", kind, "tags", tags, "content", content); return new Promise((resolve, reject) => { this.signEvent({ kind, @@ -496,37 +588,14 @@ class MessageService extends TypedEventEmitter { }) .then((event) => { if (!event) { - // console.log("Event not found"); reject("Couldn't sign event!"); return; } if (event.kind === Kind.ChannelMessage) { - let eventArray = []; - - const eTags = MessageService.filterTagValue(event, "e"); - const root = eTags.find((x) => x[3] === "root")?.[1]; - const mentions = MessageService.filterTagValue(event, "p") - .map((x) => x?.[1]) - .filter(notEmpty); - const formattedEvent = event.content - ? { - id: event.id, - root, - content: event.content, - creator: event.pubkey, - mentions, - created: event.created_at, - sent: 0 - } - : null; - // console.log("mery mtlb ka event", event); - eventArray.push(formattedEvent); - const filteredEventArray = eventArray.filter( - (item) => item !== null - ) as PublicMessage[]; - this.emit(MessageEvents.PublicMessageBeforeSent, filteredEventArray); + this.emitPublicMessageBeforeSent(event); + } else if (event.kind === Kind.EncryptedDirectMessage) { + this.emitDirectMessageBeforeSent(event); } - const pub = this.pool.publish(this.writeRelays, event); pub.on("ok", () => { resolve(event); @@ -746,7 +815,9 @@ class MessageService extends TypedEventEmitter { }) .filter(notEmpty) ).then((directMessages: DirectMessage[]) => { - this.emit(MessageEvents.DirectMessage, directMessages); + if (directMessages.length > 0) { + this.emit(MessageEvents.DirectMessageAfterSent, directMessages); + } }); this.eventQueue = []; diff --git a/src/common/img/svg.tsx b/src/common/img/svg.tsx index 6ba12684e14..79f1e1b852b 100644 --- a/src/common/img/svg.tsx +++ b/src/common/img/svg.tsx @@ -2151,3 +2151,15 @@ export const removeUserSvg = ( ); + +export const resendMessageSvg = ( + + + +); + +export const failedMessageSvg = ( + + + +); diff --git a/src/common/store/actions.ts b/src/common/store/actions.ts index 66d26fe90f5..972e04a5886 100644 --- a/src/common/store/actions.ts +++ b/src/common/store/actions.ts @@ -51,7 +51,12 @@ import { addleftChannels, UpdateChannels, replacePublicMessage, - verifyMessageSending + verifyPublicMessageSending, + replaceDirectMessage, + verifyDirectMessageSending, + deletePublicMessage, + deleteDirectMessage, + addPreviousPublicMessages } from "./chat"; // @note Do not use it directly @@ -111,7 +116,12 @@ export const ACTIONS = { addleftChannels, UpdateChannels, replacePublicMessage, - verifyMessageSending + verifyPublicMessageSending, + replaceDirectMessage, + verifyDirectMessageSending, + deletePublicMessage, + deleteDirectMessage, + addPreviousPublicMessages }; export const getActions = () => ({ diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts index 4913987211c..5f1c195e302 100644 --- a/src/common/store/chat/index.ts +++ b/src/common/store/chat/index.ts @@ -3,7 +3,8 @@ import { DirectMessage, PublicMessage, Profile, - ChannelUpdate + ChannelUpdate, + MessagesObject } from "./../../../providers/message-provider-types"; import { Dispatch } from "redux"; @@ -15,14 +16,18 @@ import { DirectContactsType, DirectMessagesAction, ResetChatAction, - directMessagesList, AddChannelsAction, PublicMessagesAction, ProfilesAction, LeftChannelsAction, UpdateChannelAction, ReplacePublicMessagesAction, - VerifySendingMessageAction + VerifyPublicMessageSendingAction, + ReplaceDirectMessagesAction, + VerifyDirectMessageSendingAction, + DeletePublicMessagesAction, + DeleteDirectMessagesAction, + AddPreviousPublicMessagesAction } from "./types"; export const initialState: Chat = { @@ -50,7 +55,7 @@ export default (state: Chat = initialState, action: Actions): Chat => { directContacts: [...state.directContacts, ...uniqueDirectContacts], directMessages: [ ...state.directMessages, - ...uniqueDirectContacts.map((contact) => ({ chat: [], peer: contact.pubkey })) + ...uniqueDirectContacts.map((contact) => ({ chat: {}, peer: contact.pubkey })) ] }; } @@ -58,11 +63,11 @@ export default (state: Chat = initialState, action: Actions): Chat => { const { peer, data } = action; return { ...state, - directMessages: [ - ...state.directMessages.map((contact) => - contact.peer === peer ? { peer: peer, chat: [...contact.chat, ...data] } : contact - ) - ] + directMessages: state.directMessages.map((contact) => + contact.peer === peer + ? { peer: peer, chat: { ...contact.chat, [data.id]: data } } + : contact + ) }; } @@ -72,13 +77,15 @@ export default (state: Chat = initialState, action: Actions): Chat => { case ActionTypes.CHANNELS: { const { data } = action; + const defaultPublicMessages = data.map((channel) => ({ + channelId: channel.id, + PublicMessage: {} + })); + return { ...state, channels: [...state.channels, ...data], - publicMessages: [ - ...state.publicMessages, - ...data.map((channel) => ({ channelId: channel.id, PublicMessage: [] })) - ] + publicMessages: [...state.publicMessages, ...defaultPublicMessages] }; } @@ -87,13 +94,17 @@ export default (state: Chat = initialState, action: Actions): Chat => { return { ...state, - publicMessages: [ - ...state.publicMessages.map((obj) => - obj.channelId === channelId - ? { channelId: channelId, PublicMessage: [...obj.PublicMessage, ...data] } - : obj - ) - ] + publicMessages: state.publicMessages.map((obj) => + obj.channelId === channelId + ? { + channelId: channelId, + PublicMessage: { + ...obj.PublicMessage, + [data.id]: data // Add the new message object with its ID as the key + } + } + : obj + ) }; } @@ -129,49 +140,134 @@ export default (state: Chat = initialState, action: Actions): Chat => { case ActionTypes.REPLACEPUBLICMESSAGE: { const { channelId, data } = action; const publicChatObject = state.publicMessages.find((item) => item.channelId === channelId); - const publicChat = publicChatObject?.PublicMessage!; - - const updatedMainArray = publicChat?.map((message) => { - const matchingMessage = data.find((subMessage) => subMessage.id === message.id); - return matchingMessage ? matchingMessage : message; - }); + const publicChat = publicChatObject?.PublicMessage || {}; - // Add new messages from subArray that do not exist in the mainArray - data.forEach((subMessage) => { - const existsInMainArray = publicChat.some((message) => message.id === subMessage.id); - if (!existsInMainArray) { - updatedMainArray.push(subMessage); - } - }); + if (publicChat.hasOwnProperty(data.id)) { + publicChat[data.id] = data; + } else { + publicChat[data.id] = data; + } return { ...state, publicMessages: [ ...state.publicMessages.map((obj) => obj.channelId === channelId - ? { channelId: channelId, PublicMessage: [...updatedMainArray] } + ? { channelId: channelId, PublicMessage: { ...publicChat } } : obj ) ] }; } - case ActionTypes.VERIFYMESSAGESENDING: { + case ActionTypes.VERIFYPUBLICMESSAGESENDING: { + const { channelId, data } = action; + + const publicChatObject = state.publicMessages.find((item) => item.channelId === channelId); + const publicChat = publicChatObject?.PublicMessage || {}; + + if (publicChat.hasOwnProperty(data.id)) { + if (publicChat[data.id].sent === 0) { + publicChat[data.id].sent = 2; + } + } + return { + ...state, + publicMessages: state.publicMessages.map((obj) => + obj.channelId === channelId ? { channelId, PublicMessage: { ...publicChat } } : obj + ) + }; + } + + case ActionTypes.REPLACEDIRECTMESSAGE: { + const { peer, data } = action; + const directMessagesObject = state.directMessages.find((item) => item.peer === peer); + const directMessages = directMessagesObject?.chat || {}; + + if (directMessages.hasOwnProperty(data.id)) { + directMessages[data.id] = data; + } else { + directMessages[data.id] = data; + } + + return { + ...state, + directMessages: [ + ...state.directMessages.map((obj) => + obj.peer === peer ? { peer, chat: { ...directMessages } } : obj + ) + ] + }; + } + + case ActionTypes.VERIFYDIRECTMESSAGESENDING: { + const { peer, data } = action; + + const directMessagesObject = state.directMessages.find((item) => item.peer === peer); + const directMessages = directMessagesObject?.chat || {}; + + if (directMessages.hasOwnProperty(data.id)) { + if (directMessages[data.id].sent === 0) { + directMessages[data.id].sent = 2; + } + } + return { + ...state, + directMessages: state.directMessages.map((obj) => + obj.peer === peer ? { peer, chat: { ...directMessages } } : obj + ) + }; + } + + case ActionTypes.DELETEPUBLICMESSAGE: { + const { channelId, msgId } = action; + + const publicChatObject = state.publicMessages.find((item) => item.channelId === channelId); + const publicChat = publicChatObject?.PublicMessage || {}; + + if (publicChat[msgId]) { + delete publicChat[msgId]; + } + return { + ...state, + publicMessages: state.publicMessages.map((obj) => + obj.channelId === channelId ? { channelId, PublicMessage: { ...publicChat } } : obj + ) + }; + } + + case ActionTypes.DELETEDIRECTMESSAGE: { + const { peer, msgId } = action; + + const directMessagesObject = state.directMessages.find((item) => item.peer === peer); + const directMessages = directMessagesObject?.chat || {}; + + if (directMessages[msgId]) { + delete directMessages[msgId]; + } + + return { + ...state, + directMessages: [ + ...state.directMessages.map((obj) => + obj.peer === peer ? { peer, chat: { ...directMessages } } : obj + ) + ] + }; + } + + case ActionTypes.ADDPREVIOUSPUBLICMESSAGES: { const { channelId, data } = action; const publicChatObject = state.publicMessages.find((item) => item.channelId === channelId); - const publicChat = publicChatObject?.PublicMessage || []; - - const updatedPublicChat = publicChat.map((item) => - data.some((object) => object.id === item.id && item.sent === 0) - ? { ...item, sent: 2 } - : item - ); - console.log("updatedPublicChat", updatedPublicChat); + const publicChat = publicChatObject?.PublicMessage || {}; + return { ...state, publicMessages: state.publicMessages.map((obj) => - obj.channelId === channelId ? { channelId, PublicMessage: updatedPublicChat } : obj + obj.channelId === channelId + ? { channelId, PublicMessage: { ...data, ...publicChat } } + : obj ) }; } @@ -186,7 +282,7 @@ export const addDirectContacts = (data: DirectContactsType[]) => (dispatch: Disp dispatch(addDirectContactsAct(data)); }; -export const addDirectMessages = (peer: string, data: DirectMessage[]) => (dispatch: Dispatch) => { +export const addDirectMessages = (peer: string, data: DirectMessage) => (dispatch: Dispatch) => { dispatch(addDirectMessagesAct(peer, data)); }; @@ -199,7 +295,7 @@ export const addChannels = (data: Channel[]) => (dispatch: Dispatch) => { }; export const addPublicMessage = - (channelId: string, data: PublicMessage[]) => (dispatch: Dispatch) => { + (channelId: string, data: PublicMessage) => (dispatch: Dispatch) => { dispatch(addPublicMessagesAct(channelId, data)); }; @@ -216,13 +312,35 @@ export const UpdateChannels = (data: ChannelUpdate[]) => (dispatch: Dispatch) => }; export const replacePublicMessage = - (channelId: string, data: PublicMessage[]) => (dispatch: Dispatch) => { + (channelId: string, data: PublicMessage) => (dispatch: Dispatch) => { dispatch(replacePublicMessagesAct(channelId, data)); }; -export const verifyMessageSending = - (channelId: string, data: PublicMessage[]) => (dispatch: Dispatch) => { - dispatch(verifyMessageSendingAct(channelId, data)); +export const verifyPublicMessageSending = + (channelId: string, data: PublicMessage) => (dispatch: Dispatch) => { + dispatch(verifyPublicMessageSendingAct(channelId, data)); + }; + +export const replaceDirectMessage = (peer: string, data: DirectMessage) => (dispatch: Dispatch) => { + dispatch(replaceDirectMessagesAct(peer, data)); +}; + +export const verifyDirectMessageSending = + (peer: string, data: DirectMessage) => (dispatch: Dispatch) => { + dispatch(verifyDirectMessageSendingAct(peer, data)); + }; + +export const deletePublicMessage = (channelId: string, msgId: string) => (dispatch: Dispatch) => { + dispatch(deletePublicMessageAct(channelId, msgId)); +}; + +export const deleteDirectMessage = (peer: string, msgId: string) => (dispatch: Dispatch) => { + dispatch(deleteDirectMessageAct(peer, msgId)); +}; + +export const addPreviousPublicMessages = + (channelId: string, data: MessagesObject) => (dispatch: Dispatch) => { + dispatch(addPreviousPublicMessagesAct(channelId, data)); }; /* Action Creators */ @@ -234,7 +352,7 @@ export const addDirectContactsAct = (data: DirectContactsType[]): DirectContacts }; }; -export const addDirectMessagesAct = (peer: string, data: DirectMessage[]): DirectMessagesAction => { +export const addDirectMessagesAct = (peer: string, data: DirectMessage): DirectMessagesAction => { return { type: ActionTypes.DIRECTMESSAGES, peer, @@ -244,7 +362,7 @@ export const addDirectMessagesAct = (peer: string, data: DirectMessage[]): Direc export const addPublicMessagesAct = ( channelId: string, - data: PublicMessage[] + data: PublicMessage ): PublicMessagesAction => { return { type: ActionTypes.PUBLICMESSAGES, @@ -253,17 +371,6 @@ export const addPublicMessagesAct = ( }; }; -export const replacePublicMessagesAct = ( - channelId: string, - data: PublicMessage[] -): ReplacePublicMessagesAction => { - return { - type: ActionTypes.REPLACEPUBLICMESSAGE, - channelId, - data - }; -}; - export const resetChatAct = (): ResetChatAction => { return { type: ActionTypes.RESET @@ -298,12 +405,75 @@ export const UpdateChannelsAct = (data: ChannelUpdate[]): UpdateChannelAction => }; }; -export const verifyMessageSendingAct = ( +export const replacePublicMessagesAct = ( + channelId: string, + data: PublicMessage +): ReplacePublicMessagesAction => { + return { + type: ActionTypes.REPLACEPUBLICMESSAGE, + channelId, + data + }; +}; + +export const verifyPublicMessageSendingAct = ( + channelId: string, + data: PublicMessage +): VerifyPublicMessageSendingAction => { + return { + type: ActionTypes.VERIFYPUBLICMESSAGESENDING, + channelId, + data + }; +}; + +export const replaceDirectMessagesAct = ( + peer: string, + data: DirectMessage +): ReplaceDirectMessagesAction => { + return { + type: ActionTypes.REPLACEDIRECTMESSAGE, + peer, + data + }; +}; + +export const verifyDirectMessageSendingAct = ( + peer: string, + data: DirectMessage +): VerifyDirectMessageSendingAction => { + return { + type: ActionTypes.VERIFYDIRECTMESSAGESENDING, + peer, + data + }; +}; + +export const deletePublicMessageAct = ( + channelId: string, + msgId: string +): DeletePublicMessagesAction => { + return { + type: ActionTypes.DELETEPUBLICMESSAGE, + channelId, + msgId + }; +}; + +export const deleteDirectMessageAct = (peer: string, msgId: string): DeleteDirectMessagesAction => { + return { + type: ActionTypes.DELETEDIRECTMESSAGE, + peer, + msgId + }; +}; + +export const addPreviousPublicMessagesAct = ( channelId: string, - data: PublicMessage[] -): VerifySendingMessageAction => { + data: MessagesObject +): AddPreviousPublicMessagesAction => { return { - type: ActionTypes.VERIFYMESSAGESENDING, + type: ActionTypes.ADDPREVIOUSPUBLICMESSAGES, channelId, data }; diff --git a/src/common/store/chat/types.ts b/src/common/store/chat/types.ts index 04ed357ce67..f2cda78d603 100644 --- a/src/common/store/chat/types.ts +++ b/src/common/store/chat/types.ts @@ -2,6 +2,7 @@ import { Channel, ChannelUpdate, DirectMessage, + MessagesObject, Profile, PublicMessage } from "./../../../providers/message-provider-types"; @@ -13,12 +14,12 @@ export interface DirectContactsType { export interface directMessagesList { peer: string; - chat: DirectMessage[]; + chat: { [messageId: string]: DirectMessage }; } export interface publicMessagesList { channelId: string; - PublicMessage: PublicMessage[]; + PublicMessage: { [messageId: string]: PublicMessage }; } export interface Chat { @@ -41,7 +42,12 @@ export enum ActionTypes { LEFTCHANNELLIST = "@chat/LEFTCHANNELLIST", UPDATEDCHANNEL = "@chat/UPDATEDCHANNEL", REPLACEPUBLICMESSAGE = "@chat/REPLACEPUBLICMESSAGE", - VERIFYMESSAGESENDING = "@chat/VERIFYMESSAGESENDING" + VERIFYPUBLICMESSAGESENDING = "@chat/VERIFYMESSAGESENDING", + REPLACEDIRECTMESSAGE = "@chat/REPLACEDIRECTMESSAGE", + VERIFYDIRECTMESSAGESENDING = "@chat/VERIFYDIRECTMESSAGESENDING", + DELETEPUBLICMESSAGE = "@chat/DELETEPUBLICMESSAGE", + DELETEDIRECTMESSAGE = "@chat/DELETEDIRECTMESSAGE", + ADDPREVIOUSPUBLICMESSAGES = "@chat/ADDPREVIOUSPUBLICMESSAGES" } export interface DirectContactsAction { @@ -55,13 +61,13 @@ export interface ResetChatAction { export interface DirectMessagesAction { type: ActionTypes.DIRECTMESSAGES; - data: DirectMessage[]; + data: DirectMessage; peer: string; } export interface PublicMessagesAction { type: ActionTypes.PUBLICMESSAGES; - data: PublicMessage[]; + data: PublicMessage; channelId: string; } @@ -86,14 +92,43 @@ export interface UpdateChannelAction { export interface ReplacePublicMessagesAction { type: ActionTypes.REPLACEPUBLICMESSAGE; - data: PublicMessage[]; + data: PublicMessage; channelId: string; } -export interface VerifySendingMessageAction { - type: ActionTypes.VERIFYMESSAGESENDING; - data: PublicMessage[]; +export interface ReplaceDirectMessagesAction { + type: ActionTypes.REPLACEDIRECTMESSAGE; + data: DirectMessage; + peer: string; +} + +export interface VerifyPublicMessageSendingAction { + type: ActionTypes.VERIFYPUBLICMESSAGESENDING; + data: PublicMessage; + channelId: string; +} + +export interface VerifyDirectMessageSendingAction { + type: ActionTypes.VERIFYDIRECTMESSAGESENDING; + data: DirectMessage; + peer: string; +} +export interface DeletePublicMessagesAction { + type: ActionTypes.DELETEPUBLICMESSAGE; + msgId: string; + channelId: string; +} + +export interface DeleteDirectMessagesAction { + type: ActionTypes.DELETEDIRECTMESSAGE; + msgId: string; + peer: string; +} + +export interface AddPreviousPublicMessagesAction { + type: ActionTypes.ADDPREVIOUSPUBLICMESSAGES; channelId: string; + data: MessagesObject; } export type Actions = @@ -106,4 +141,9 @@ export type Actions = | LeftChannelsAction | UpdateChannelAction | ReplacePublicMessagesAction - | VerifySendingMessageAction; + | VerifyPublicMessageSendingAction + | ReplaceDirectMessagesAction + | VerifyDirectMessageSendingAction + | DeletePublicMessagesAction + | DeleteDirectMessagesAction + | AddPreviousPublicMessagesAction; diff --git a/src/providers/message-provider-types.ts b/src/providers/message-provider-types.ts index 509fde38dae..6a434f87608 100644 --- a/src/providers/message-provider-types.ts +++ b/src/providers/message-provider-types.ts @@ -58,6 +58,10 @@ export type DirectMessage = { sent?: number; }; +export type MessagesObject = { + [key: string]: PublicMessage; +}; + export type Message = PublicMessage | DirectMessage; export type ChannelMessageHide = { id: string; reason: string }; diff --git a/src/providers/message-provider.tsx b/src/providers/message-provider.tsx index b147060b650..a0b9a3dc01a 100644 --- a/src/providers/message-provider.tsx +++ b/src/providers/message-provider.tsx @@ -9,7 +9,8 @@ import { DirectContact, Channel, PublicMessage, - ChannelUpdate + ChannelUpdate, + MessagesObject } from "./message-provider-types"; import { initMessageService, MessageEvents } from "../common/helper/message-service"; @@ -29,14 +30,17 @@ export const setNostrkeys = (keys: NostrKeysType) => { interface Props { addDirectContacts: (data?: DirectContactsType[]) => void; - addDirectMessages: (peer: string, data?: DirectMessage[]) => void; - addPublicMessage: (channelId: string, data?: PublicMessage[]) => void; + addDirectMessages: (peer: string, data?: DirectMessage) => void; + addPublicMessage: (channelId: string, data?: PublicMessage) => void; addChannels: (data: Channel[]) => void; addProfile: (data: Profile[]) => void; addleftChannels: (data: string[]) => void; UpdateChannels: (data: ChannelUpdate[]) => void; - replacePublicMessage: (channelId: string, data?: PublicMessage[]) => void; - verifyMessageSending: (channelId: string, data?: PublicMessage[]) => void; + replacePublicMessage: (channelId: string, data?: PublicMessage) => void; + verifyPublicMessageSending: (channelId: string, data?: PublicMessage) => void; + replaceDirectMessage: (peer: string, data: DirectMessage) => void; + verifyDirectMessageSending: (peer: string, data: DirectMessage) => void; + addPreviousPublicMessages: (channelId: string, data: MessagesObject) => void; } const MessageProvider = (props: Props) => { @@ -48,17 +52,23 @@ const MessageProvider = (props: Props) => { const [directMessageBuffer, setDirectMessageBuffer] = useState([]); const [publicMessageBuffer, setPublicMessageBuffer] = useState([]); const [isCommunityCreated, setIsCommunityCreated] = useState(false); - const [replacedMessagesBuffer, setReplacedMessagesBuffer] = useState([]); + const [replacedPublicMessagesBuffer, setReplacedPublicMessagesBuffer] = useState([]); + const [isDirectChatCreated, setIsDirectChatCreated] = useState(false); + const [replacedDirectMessagesBuffer, setReplacedDirectMessagesBuffer] = useState([]); useEffect(() => { if (chat.channels.length !== 0) { setIsCommunityCreated(true); } - }, [chat.channels]); + + if (chat.directMessages.length !== 0) { + setIsDirectChatCreated(true); + } + }, [chat.channels, chat.directMessages]); useEffect(() => { - // console.log("replacedMessagesBuffer", replacedMessagesBuffer); - }, [replacedMessagesBuffer]); + console.log("replacedDirectMessagesBuffer", replacedDirectMessagesBuffer); + }, [replacedDirectMessagesBuffer]); useEffect(() => { window.addEventListener("createMSInstance", createMSInstance); @@ -100,7 +110,6 @@ const MessageProvider = (props: Props) => { pub: profile.noStrKey, priv: noStrPrivKey }; - // console.log('keys', keys) setKeys(keys); }; @@ -177,10 +186,61 @@ const MessageProvider = (props: Props) => { }; }, [messageService]); - // // Direct message handler - const handleDirectMessage = (data: DirectMessage[]) => { - setDirectMessageBuffer((directMessageBuffer) => [...directMessageBuffer!, ...data]); - // console.log("HandleDirectMessage", data); + //Direct message ahandle before sent + + const handleDirectMessageBeforeSent = (data: DirectMessage[]) => { + console.log("handleDirectMessageBeeforeSent", data); + data.map((m) => { + const { peer, id } = m; + setReplacedDirectMessagesBuffer((prevBuffer) => [...prevBuffer, id]); + props.addDirectMessages(peer, m); + checkDirectMessageSending(peer, m); + }); + }; + + useEffect(() => { + messageService?.removeListener( + MessageEvents.DirectMessageBeforeSent, + handleDirectMessageBeforeSent + ); + messageService?.addListener( + MessageEvents.DirectMessageBeforeSent, + handleDirectMessageBeforeSent + ); + + return () => { + messageService?.removeListener( + MessageEvents.DirectMessageBeforeSent, + handleDirectMessageBeforeSent + ); + }; + }, [messageService, chat.directMessages]); + + const checkDirectMessageSending = (peer: string, data: DirectMessage) => { + setTimeout(() => { + props.verifyDirectMessageSending(peer, data); + }, 20000); + }; + + // // Direct message handler after sent + const handleDirectMessageAfterSent = (data: DirectMessage[]) => { + console.log("handleDirectMessageAfterSent", data); + if (isDirectChatCreated) { + data.forEach((message) => { + const { peer, id } = message; + if (replacedDirectMessagesBuffer.includes(id)) { + setReplacedDirectMessagesBuffer((prevBuffer) => + prevBuffer.filter((messageId) => messageId !== id) + ); + console.log("I reached"); + props.replaceDirectMessage(peer, message); + } else { + props.addDirectMessages(peer, message); + } + }); + } else { + setDirectMessageBuffer((directMessageBuffer) => [...directMessageBuffer!, ...data]); + } messageService?.checkProfiles(data.map((x) => x.peer)); }; @@ -189,25 +249,10 @@ const MessageProvider = (props: Props) => { const { peer } = obj; const matchingStateItem = chat.directMessages.find((stateItem) => stateItem.peer === peer); if (matchingStateItem) { - if (matchingStateItem.chat.length === 0) { - props.addDirectMessages(peer, [obj]); - setDirectMessageBuffer((prevMessageBuffer) => - prevMessageBuffer.filter((message) => message.id !== obj.id) - ); - } else { - let itemExists = false; - matchingStateItem.chat.forEach((item) => { - if (item.id === obj.id) { - itemExists = true; - } - }); - if (!itemExists) { - props.addDirectMessages(peer, [obj]); - setDirectMessageBuffer((prevMessageBuffer) => - prevMessageBuffer.filter((message) => message.id !== obj.id) - ); - } - } + props.addDirectMessages(peer, obj); + setDirectMessageBuffer((prevMessageBuffer) => + prevMessageBuffer.filter((message) => message.id !== obj.id) + ); } }); }; @@ -219,12 +264,19 @@ const MessageProvider = (props: Props) => { }, [chat.directContacts, directMessageBuffer]); useEffect(() => { - messageService?.removeListener(MessageEvents.DirectMessage, handleDirectMessage); - messageService?.addListener(MessageEvents.DirectMessage, handleDirectMessage); + messageService?.removeListener( + MessageEvents.DirectMessageAfterSent, + handleDirectMessageAfterSent + ); + messageService?.addListener(MessageEvents.DirectMessageAfterSent, handleDirectMessageAfterSent); + return () => { - messageService?.removeListener(MessageEvents.DirectMessage, handleDirectMessage); + messageService?.removeListener( + MessageEvents.DirectMessageAfterSent, + handleDirectMessageAfterSent + ); }; - }, [messageService]); + }, [messageService, chat.directMessages]); // Channel creation handler const handleChannelCreation = (data: Channel[]) => { @@ -258,9 +310,9 @@ const MessageProvider = (props: Props) => { }; }, [messageService]); - const checkMessageSending = (channelId: string, data: PublicMessage[]) => { + const checkPublicMessageSending = (channelId: string, data: PublicMessage) => { setTimeout(() => { - props.verifyMessageSending(channelId, data); + props.verifyPublicMessageSending(channelId, data); }, 20000); }; @@ -270,9 +322,9 @@ const MessageProvider = (props: Props) => { console.log("handlePublicMessageBeeforeSent", data); data.map((m) => { const { id, root } = m; - setReplacedMessagesBuffer((prevBuffer) => [...prevBuffer, id]); - props.addPublicMessage(root, [m]); - checkMessageSending(root, [m]); + setReplacedPublicMessagesBuffer((prevBuffer) => [...prevBuffer, id]); + props.addPublicMessage(root, m); + checkPublicMessageSending(root, m); }); }; @@ -300,32 +352,32 @@ const MessageProvider = (props: Props) => { if (isCommunityCreated) { data.forEach((message) => { const { root, id } = message; - if (replacedMessagesBuffer.includes(id)) { - setReplacedMessagesBuffer((prevBuffer) => + if (replacedPublicMessagesBuffer.includes(id)) { + setReplacedPublicMessagesBuffer((prevBuffer) => prevBuffer.filter((messageId) => messageId !== id) ); - props.replacePublicMessage(root, [message]); + props.replacePublicMessage(root, message); } else { - props.addPublicMessage(root, [message]); + props.addPublicMessage(root, message); } }); } else { setPublicMessageBuffer((publicMessageBuffer) => [...publicMessageBuffer, ...data]); } + let uniqueUsers: string[] = []; for (const item of data) { const isCreatorMatch = chat.profiles.some((profile) => profile.creator === item.creator); if (!isCreatorMatch) { - let uniqueUsers: string[] = []; for (const item of data) { if (!uniqueUsers.includes(item.creator)) { uniqueUsers.push(item.creator); } } - messageService?.loadProfiles(uniqueUsers); } } + window?.messageService?.loadProfiles(uniqueUsers); }; useEffect(() => { @@ -356,7 +408,7 @@ const MessageProvider = (props: Props) => { (stateItem) => stateItem.channelId === root ); if (matchingStateItem) { - props.addPublicMessage(root, [obj]); + props.addPublicMessage(root, obj); setPublicMessageBuffer((prevMessageBuffer) => prevMessageBuffer.filter((message) => message.id !== obj.id) ); @@ -364,6 +416,33 @@ const MessageProvider = (props: Props) => { }); }; + //previous public messages handler + const handlePreviousPublicMessages = (data: PublicMessage[]) => { + console.log("handlePreviousPublicMessages", data); + const channelId = data[0].root; + const messagesObject: MessagesObject = data.reduce((result, message) => { + result[message.id] = message; + return result; + }, {}); + + props.addPreviousPublicMessages(channelId, messagesObject); + }; + + useEffect(() => { + messageService?.removeListener( + MessageEvents.PreviousPublicMessages, + handlePreviousPublicMessages + ); + messageService?.addListener(MessageEvents.PreviousPublicMessages, handlePreviousPublicMessages); + + return () => { + messageService?.removeListener( + MessageEvents.PreviousPublicMessages, + handlePreviousPublicMessages + ); + }; + }, [messageService, chat.profiles, chat.publicMessages]); + // Left channel handler const handleLeftChannelList = (data: string[]) => { // console.log("handleLeftChannelList", data); @@ -388,6 +467,10 @@ const MessageProvider = (props: Props) => { messageService?.removeListener(MessageEvents.DirectContact, handleDirectContact); messageService?.removeListener(MessageEvents.LeftChannelList, handleLeftChannelList); messageService?.removeListener(MessageEvents.ChannelUpdate, handleChannelUpdate); + messageService?.removeListener( + MessageEvents.PreviousPublicMessages, + handlePreviousPublicMessages + ); messageService?.removeListener( MessageEvents.PublicMessageBeforeSent, handlePublicMessageBeforeSent @@ -396,7 +479,14 @@ const MessageProvider = (props: Props) => { MessageEvents.PublicMessageAfterSent, handlePublicMessageAfterSent ); - messageService?.removeListener(MessageEvents.DirectMessage, handleDirectMessage); + messageService?.removeListener( + MessageEvents.DirectMessageBeforeSent, + handleDirectMessageBeforeSent + ); + messageService?.removeListener( + MessageEvents.DirectMessageAfterSent, + handleDirectMessageAfterSent + ); }; }, [messageService]); From 0cab694b8eec17a428453ee6d8edf5de80b640f8 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Thu, 10 Aug 2023 18:34:05 +0500 Subject: [PATCH 032/179] Show join community chat button on community page instead of join chat button if user hasn't join chat yet --- src/common/components/chat-box/index.tsx | 22 ++++++++++++++----- .../join-community-chat-btn/index.tsx | 21 +++++++++++++----- src/common/helper/message-service.ts | 2 +- 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index da68d889903..3ad06c6fad8 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -227,10 +227,10 @@ export default function ChatBox(props: Props) { }, [props.chat.updatedChannel]); useEffect(() => { - // console.log("isTop", isTop); + console.log("isTop", isTop); if (isTop) { fetchPrevMessages(); - // console.log("event is dispatched"); + console.log("event is dispatched"); } }, [isTop]); @@ -261,11 +261,9 @@ export default function ChatBox(props: Props) { }, [clickedMessage]); useEffect(() => { - //what if there is no channel or there is no direct contact in store. handle this thing too. if (props.chat.channels.length !== 0 || props.chat.directContacts.length !== 0) { setInProgress(false); } - console.log("chat in store", props.chat); }, [props.chat]); @@ -305,6 +303,7 @@ export default function ChatBox(props: Props) { useEffect(() => { const communities = getCommunities(props.chat.channels, props.chat.leftChannelsList); setCommunities(communities); + fetchProfileData(); }, [props.chat.channels, props.chat.leftChannelsList]); useEffect(() => { @@ -336,7 +335,18 @@ export default function ChatBox(props: Props) { scrollerClicked(); } - if (!isTop && !isScrollToTop && publicMessages.length !== 0) { + console.log( + "isTop", + !isTop, + "isScrollToTop", + !isScrollToTop, + "publicMessages", + publicMessages.length, + "isCommunity", + isCommunity + ); + if (!isTop && !isScrollToTop && publicMessages.length !== 0 && isCommunity) { + console.log("yes i run"); scrollerClicked(); } }, [directMessagesList, publicMessages]); @@ -604,6 +614,7 @@ export default function ChatBox(props: Props) { window.messageService ?.fetchPrevMessages(currentChannel!.id, publicMessages[0].created) .then((num) => { + console.log("number", num); if (num < 25) { setHasMore(false); } @@ -632,6 +643,7 @@ export default function ChatBox(props: Props) { }; const scrollerClicked = () => { + console.log("Scroller clicked"); chatBodyDivRef?.current?.scroll({ top: isCurrentUser || isCommunity ? chatBodyDivRef?.current?.scrollHeight : 0, behavior: "auto" diff --git a/src/common/components/join-community-chat-btn/index.tsx b/src/common/components/join-community-chat-btn/index.tsx index 1e2e98b9f49..83696c2d574 100644 --- a/src/common/components/join-community-chat-btn/index.tsx +++ b/src/common/components/join-community-chat-btn/index.tsx @@ -37,6 +37,7 @@ export default function JoinCommunityChatBtn(props: Props) { const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); const [communityRoles, setCommunityRoles] = useState([]); const [activeUserKeys, setActiveUserKeys] = useState(); + const [loadCommunity, setLoadCommunity] = useState(false); useEffect(() => { getCommunityRoles(); @@ -55,8 +56,11 @@ export default function JoinCommunityChatBtn(props: Props) { if (window.messageService && activeUserKeys) { getCommunityRoles(); setHasUserJoinedChat(true); + if (loadCommunity) { + window?.messageService?.loadChannel(currentChannel?.id!); + } } - }, [window?.messageService, activeUserKeys]); + }, [window?.messageService, activeUserKeys, loadCommunity]); useEffect(() => { checkIsChatJoined(); @@ -161,6 +165,12 @@ export default function JoinCommunityChatBtn(props: Props) { }; const joinCommunityChat = () => { + if (!hasUserJoinedChat) { + handleJoinChat(); + setLoadCommunity(true); + setIsJoinChat(true); + return; + } if (chat.leftChannelsList.includes(currentChannel?.id!)) { window?.messageService?.updateLeftChannelList( chat.leftChannelsList.filter((x) => x !== currentChannel?.id) @@ -221,10 +231,11 @@ export default function JoinCommunityChatBtn(props: Props) { ) : ( ) - ) : isChatEnabled && !isJoinChat && hasUserJoinedChat ? ( - - ) : !hasUserJoinedChat ? ( - + ) : isChatEnabled && !isJoinChat ? ( + ) : isJoinChat ? ( ) : ( diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts index cb44128d88f..3d28ecfc511 100644 --- a/src/common/helper/message-service.ts +++ b/src/common/helper/message-service.ts @@ -304,7 +304,7 @@ class MessageService extends TypedEventEmitter { } public loadChannel(id: string) { - // console.log(id, "id"); + // console.log(id, "load channel id"); const filters: Filter[] = [ { kinds: [Kind.ChannelCreation], From 2f3a8f3e4a3f50d170ec4d00583f512de2dadc97 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Fri, 11 Aug 2023 17:46:13 +0500 Subject: [PATCH 033/179] code formatting and cleaning --- src/common/components/chat-box/index.tsx | 91 ++++++------------- .../join-community-chat-btn/index.tsx | 52 +++++++---- src/common/helper/chat-utils.ts | 1 + src/common/helper/message-service.ts | 27 ------ src/common/i18n/locales/en-US.json | 28 +++++- src/common/store/chat/index.ts | 2 +- src/providers/message-provider.tsx | 25 ----- 7 files changed, 89 insertions(+), 137 deletions(-) diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 3ad06c6fad8..52fd916002b 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -95,8 +95,7 @@ import { setProfileMetaData, resetProfile, getCommunities, - getPrivateKey, - notEmpty + getPrivateKey } from "../../helper/chat-utils"; import * as ls from "../../util/local-storage"; import { renderPostBody } from "@ecency/render-helper"; @@ -190,14 +189,6 @@ export default function ChatBox(props: Props) { const [hasMore, setHasMore] = useState(true); const [resendMessage, setResendMessage] = useState(); - useEffect(() => { - console.log("publicMessages", publicMessages); - }, [publicMessages]); - - useEffect(() => { - // console.log(currentChannel, "currentChannel"); - }, [currentChannel]); - useEffect(() => { const updated: ChannelUpdate = props.chat.updatedChannel .filter((x) => x.channelId === currentChannel?.id!) @@ -209,7 +200,6 @@ export default function ChatBox(props: Props) { ); const messages = publicMessages.sort((a, b) => a.created - b.created); setPublicMessages(messages); - // console.log("community messages", messages); const channel = { name: updated.name, about: updated.about, @@ -227,10 +217,8 @@ export default function ChatBox(props: Props) { }, [props.chat.updatedChannel]); useEffect(() => { - console.log("isTop", isTop); if (isTop) { fetchPrevMessages(); - console.log("event is dispatched"); } }, [isTop]); @@ -264,7 +252,6 @@ export default function ChatBox(props: Props) { if (props.chat.channels.length !== 0 || props.chat.directContacts.length !== 0) { setInProgress(false); } - console.log("chat in store", props.chat); }, [props.chat]); useEffect(() => { @@ -276,7 +263,6 @@ export default function ChatBox(props: Props) { const removed = removedUsers.includes(activeUserKeys?.pub!); setIsActiveUserRemoved(removed); } - // scrollerClicked(); }, [currentChannel, removedUsers]); useEffect(() => { @@ -288,7 +274,6 @@ export default function ChatBox(props: Props) { }, []); // useEffect(() => { - // console.log("Use effect run"); // if (window.messageService) { // setHasUserJoinedChat(true); // // setInProgress(false); @@ -328,25 +313,12 @@ export default function ChatBox(props: Props) { useEffect(() => { if (directMessagesList.length !== 0 || publicMessages.length !== 0) { - //Initialize the zooming effect zoomInitializer(); } if (directMessagesList.length !== 0) { scrollerClicked(); } - - console.log( - "isTop", - !isTop, - "isScrollToTop", - !isScrollToTop, - "publicMessages", - publicMessages.length, - "isCommunity", - isCommunity - ); - if (!isTop && !isScrollToTop && publicMessages.length !== 0 && isCommunity) { - console.log("yes i run"); + if (!isScrollToBottom && publicMessages.length !== 0 && isCommunity) { scrollerClicked(); } }, [directMessagesList, publicMessages]); @@ -357,9 +329,7 @@ export default function ChatBox(props: Props) { const publicMessages: PublicMessage[] = fetchCommunityMessages(currentChannel.id); const messages = publicMessages.sort((a, b) => a.created - b.created); setPublicMessages(messages); - // console.log("community messages", messages); } - // scrollerClicked(); }, [currentChannel, isCommunity, props.chat.publicMessages]); useEffect(() => { @@ -454,7 +424,6 @@ export default function ChatBox(props: Props) { const blockedUsers = props.chat.profiles .filter((item) => blockedUser.includes(item.creator)) .map((item) => ({ name: item.name, pubkey: item.creator })); - // console.log("blockedUsers", blockedUsers); setBlockedUsers(blockedUsers); }; @@ -470,13 +439,11 @@ export default function ChatBox(props: Props) { const fetchCurrentChannel = (communityName: string) => { const channel = props.chat.channels.find((channel) => channel.communityName === communityName); - // console.log("fetch current chanel", channel); if (channel) { const updated: ChannelUpdate = props.chat.updatedChannel .filter((x) => x.channelId === channel.id) .sort((a, b) => b.created - a.created)[0]; if (updated) { - // console.log("updated", updated); const channel = { name: updated.name, about: updated.about, @@ -589,7 +556,7 @@ export default function ChatBox(props: Props) { if (!isActveUserRemoved) { window?.messageService?.sendPublicMessage(currentChannel!, message, [], ""); } else { - error("You cannot send messages in this group."); + error(_t("chat.message-warning")); } } if (isCurrentUser) { @@ -614,7 +581,6 @@ export default function ChatBox(props: Props) { window.messageService ?.fetchPrevMessages(currentChannel!.id, publicMessages[0].created) .then((num) => { - console.log("number", num); if (num < 25) { setHasMore(false); } @@ -631,7 +597,7 @@ export default function ChatBox(props: Props) { const isScrollToTop = !isCurrentUser && !isCommunity && element.scrollTop >= srollHeight; const isScrollToBottom = (isCurrentUser || isCommunity) && - element.scrollTop + chatBodyDivRef?.current?.clientHeight! < element.scrollHeight; + element.scrollTop + chatBodyDivRef?.current?.clientHeight! < element.scrollHeight - 200; setIsScrollToTop(isScrollToTop); setIsScrollToBottom(isScrollToBottom); const scrollerTop = element.scrollTop <= 600; @@ -643,7 +609,6 @@ export default function ChatBox(props: Props) { }; const scrollerClicked = () => { - console.log("Scroller clicked"); chatBodyDivRef?.current?.scroll({ top: isCurrentUser || isCommunity ? chatBodyDivRef?.current?.scrollHeight : 0, behavior: "auto" @@ -963,7 +928,7 @@ export default function ChatBox(props: Props) { <>
-

Edit Community Roles

+

{_t("chat.edit-community-roles")}

@@ -1010,7 +975,7 @@ export default function ChatBox(props: Props) { onClick={() => handleChannelUpdate(ADDROLE)} disabled={inProgress || addRoleError.length !== 0 || user.length === 0} > - Add + {_t("chat.add")}
@@ -1061,7 +1026,7 @@ export default function ChatBox(props: Props) { ) : (
-

No admin or moderator for this community chat.

+

{_t("chat.no-admin")}

)}
@@ -1073,7 +1038,7 @@ export default function ChatBox(props: Props) { return ( <>
-

Blocked Users

+

{_t("chat.blocked-users")}

{blockedUsers.length !== 0 ? ( @@ -1082,7 +1047,7 @@ export default function ChatBox(props: Props) { {_t("community.roles-account")} - Action + {_t("chat.action")} @@ -1107,7 +1072,7 @@ export default function ChatBox(props: Props) { setRemovedUserID(user.pubkey); }} > - Unblock + {_t("chat.unblock")} @@ -1118,7 +1083,7 @@ export default function ChatBox(props: Props) { ) : (
-

No Blocked user yet.

+

{_t("chat.no-locked-user")}

)} @@ -1131,8 +1096,8 @@ export default function ChatBox(props: Props) {
1
-
Enter Chat private key
-
Enter chat private key to import all chats
+
{_t("chat.enter-private-key")}
+
{_t("chat.enter-private-key-detail")}
@@ -1221,14 +1186,14 @@ export default function ChatBox(props: Props) { setKeyDialog(false); }} > - Close + {_t("chat.close")}

@@ -1340,7 +1305,7 @@ export default function ChatBox(props: Props) { setRemovedUserID(""); } } catch (err) { - error("Error occurred while updating community"); + error(_t("chat.error-updating-community")); } }; @@ -1398,14 +1363,14 @@ export default function ChatBox(props: Props) { icon: linkSvg }, { - label: "Leave", + label: _t("chat.leave"), onClick: leaveClicked, icon: chatLeaveSvg }, ...(props.activeUser?.username === currentChannel?.communityName ? [ { - label: "Edit Roles", + label: _t("chat.edit-roles"), onClick: handleEditRoles, icon: editSVG } @@ -1414,7 +1379,7 @@ export default function ChatBox(props: Props) { ...(communityAdmins.includes(props.activeUser?.username!) ? [ { - label: "Blocked Users", + label: _t("chat.blocked-users"), onClick: handleBlockedUsers, icon: removeUserSvg } @@ -1431,7 +1396,7 @@ export default function ChatBox(props: Props) { const menuItems: MenuItem[] = [ { - label: "Copy private key", + label: _t("chat.copy-private-key"), onClick: () => inviteClicked(noStrPrivKey), icon: linkSvg } @@ -1754,7 +1719,7 @@ export default function ChatBox(props: Props) { setClickedMessage(""); }} > - Unblock + {_t("chat.unblock")} ) : ( @@ -1767,7 +1732,7 @@ export default function ChatBox(props: Props) { setClickedMessage(""); }} > - Block + {_t("chat.block")} )} @@ -1980,7 +1945,7 @@ export default function ChatBox(props: Props) { {props.chat.channels.length !== 0 && communities.length !== 0 && ( <> -
Communities
+
{_t("chat.communities")}
{communities.map((channel) => { return (
@@ -2009,7 +1974,7 @@ export default function ChatBox(props: Props) { ); })} {props.chat.directContacts.length !== 0 && ( -
DMs
+
{_t("chat.dms")}
)} )} @@ -2036,7 +2001,7 @@ export default function ChatBox(props: Props) { <>
{} @@ -2048,12 +2013,12 @@ export default function ChatBox(props: Props) { setStep(9); }} > - Create New Account + {_t("chat.create-new-account")}
) : inProgress ? ( -

Loading...

+

{_t("chat.loading")}

) : ( <>

{_t("chat.no-chat")}

@@ -2090,7 +2055,7 @@ export default function ChatBox(props: Props) { )} {isActveUserRemoved && isCommunity && (

- You have been blocked from this community + {_t("chat.blocked-user-message")}

)}
diff --git a/src/common/components/join-community-chat-btn/index.tsx b/src/common/components/join-community-chat-btn/index.tsx index 83696c2d574..8f10d83603f 100644 --- a/src/common/components/join-community-chat-btn/index.tsx +++ b/src/common/components/join-community-chat-btn/index.tsx @@ -31,7 +31,7 @@ interface Props { export default function JoinCommunityChatBtn(props: Props) { const { chat } = useMappedStore(); const [inProgress, setInProgress] = useState(false); - const [isJoinChat, setIsJoinChat] = useState(false); + const [isCommunityChatJoined, setIsCommunityChatJoined] = useState(false); const [isChatEnabled, setIsChatEnabled] = useState(false); const [currentChannel, setCurrentChannel] = useState(); const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); @@ -43,6 +43,17 @@ export default function JoinCommunityChatBtn(props: Props) { getCommunityRoles(); }, []); + useEffect(() => { + console.log( + "isCommunityChatJoined", + isCommunityChatJoined, + "isChatEnabled", + isChatEnabled, + "hasUserJoinedChat", + hasUserJoinedChat + ); + }, [isCommunityChatJoined, isChatEnabled, hasUserJoinedChat]); + useEffect(() => { fetchCommunityProfile(); fetchUserProfileData(); @@ -66,7 +77,7 @@ export default function JoinCommunityChatBtn(props: Props) { checkIsChatJoined(); fetchCurrentChannel(); - }, [isJoinChat, props.community, chat.channels, chat.leftChannelsList]); + }, [isCommunityChatJoined, props.community, chat.channels, chat.leftChannelsList]); const fetchUserProfileData = async () => { const profileData = await getProfileMetaData(props.activeUser?.username!); @@ -82,10 +93,6 @@ export default function JoinCommunityChatBtn(props: Props) { if (!currentChannel) { setCurrentChannel(communityProfile.channel); } - - // if (!haschannelMetaData && currentChannel) { - // // await setChannelMetaData(props.community.name, currentChannel!); - // } }; const checkIsChatJoined = () => { @@ -94,9 +101,9 @@ export default function JoinCommunityChatBtn(props: Props) { item.communityName === props.community.name && !chat.leftChannelsList.includes(currentChannel?.id!) ) { - setIsJoinChat(true); + setIsCommunityChatJoined(true); } else { - setIsJoinChat(false); + setIsCommunityChatJoined(false); } } }; @@ -160,7 +167,7 @@ export default function JoinCommunityChatBtn(props: Props) { ); } finally { setInProgress(false); - setIsJoinChat(true); + setIsCommunityChatJoined(true); } }; @@ -168,7 +175,7 @@ export default function JoinCommunityChatBtn(props: Props) { if (!hasUserJoinedChat) { handleJoinChat(); setLoadCommunity(true); - setIsJoinChat(true); + setIsCommunityChatJoined(true); return; } if (chat.leftChannelsList.includes(currentChannel?.id!)) { @@ -177,7 +184,7 @@ export default function JoinCommunityChatBtn(props: Props) { ); } window?.messageService?.loadChannel(currentChannel?.id!); - setIsJoinChat(true); + setIsCommunityChatJoined(true); }; const fetchCurrentChannel = () => { @@ -216,28 +223,33 @@ export default function JoinCommunityChatBtn(props: Props) { return ( <> {props.community["context"].role === "owner" ? ( - isJoinChat ? ( - + isCommunityChatJoined ? ( + ) : hasUserJoinedChat && !isChatEnabled ? ( - ) : !isJoinChat && isChatEnabled && hasUserJoinedChat ? ( + ) : !isCommunityChatJoined && isChatEnabled && hasUserJoinedChat ? ( ) : ( - + ) - ) : isChatEnabled && !isJoinChat ? ( + ) : isChatEnabled && !isCommunityChatJoined ? ( - ) : isJoinChat ? ( - + ) : isCommunityChatJoined ? ( + ) : ( <> )} diff --git a/src/common/helper/chat-utils.ts b/src/common/helper/chat-utils.ts index 68f46d77891..5f7dfe6b9f2 100644 --- a/src/common/helper/chat-utils.ts +++ b/src/common/helper/chat-utils.ts @@ -61,6 +61,7 @@ export const setChannelMetaData = (username: string, channel: Channel) => { channel: channel }; const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); + console.log("Channel metadata updated successfully", updatedProfile); resolve(updatedProfile); } catch (error) { reject(error); diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts index 3d28ecfc511..faaa7fe536f 100644 --- a/src/common/helper/message-service.ts +++ b/src/common/helper/message-service.ts @@ -155,7 +155,6 @@ class MessageService extends TypedEventEmitter { .filter(notEmpty) ) ); - console.log("channels", channels); if (channels.length !== 0) { this.fetchChannels(channels); } @@ -166,7 +165,6 @@ class MessageService extends TypedEventEmitter { } public fetchPrevMessages(channel: string, until: number) { - console.log("check plz", channel, until); return this.fetchP( [ { @@ -208,7 +206,6 @@ class MessageService extends TypedEventEmitter { } public fetchChannels(channels: string[]) { - // console.log("fetch channels run"); const filters: Filter[] = [ { kinds: [Kind.ChannelCreation], @@ -231,7 +228,6 @@ class MessageService extends TypedEventEmitter { } public fetchChannel(id: string) { - // console.log("fetchChannel run", id); const filters: Filter[] = [ { kinds: [Kind.ChannelCreation], @@ -247,7 +243,6 @@ class MessageService extends TypedEventEmitter { } public async updateLeftChannelList(channelIds: string[]) { - // console.log(channelIds, "channelIds"); const tags = [["d", "left-channel-list"]]; return this.publish(NewKinds.Arbitrary, tags, JSON.stringify(channelIds)); } @@ -286,7 +281,6 @@ class MessageService extends TypedEventEmitter { // If the name doesn't exist, add the element to the direct contacts array if (!nameExist) { this.directContacts.push(newUser); - // console.log("Event is fired"); this.publish(Kind.Contacts, this.directContacts, ""); return; } @@ -304,7 +298,6 @@ class MessageService extends TypedEventEmitter { } public loadChannel(id: string) { - // console.log(id, "load channel id"); const filters: Filter[] = [ { kinds: [Kind.ChannelCreation], @@ -325,7 +318,6 @@ class MessageService extends TypedEventEmitter { } public loadProfiles(pubs: string[]) { - // console.log("load profiles"); pubs.forEach((p) => { this.fetch( [ @@ -390,11 +382,9 @@ class MessageService extends TypedEventEmitter { } private fetch(filters: Filter[], unsub: boolean = true, isDirectContact: boolean = false) { - // console.log("Fetch run", filters); const sub = this.pool.sub(this.readRelays, filters); sub.on("event", (event: Event) => { - // console.log("Event in fetch", event); event.kind === Kind.Metadata && isDirectContact ? this.addDirectContact(event) : this.pushToEventBuffer(event); @@ -452,12 +442,10 @@ class MessageService extends TypedEventEmitter { } public async createChannel(meta: Metadata) { - // console.log("create channel run", meta); return this.publish(Kind.ChannelCreation, [], JSON.stringify(meta)); } public async updateChannel(channel: Channel, meta: Metadata) { - // console.log(channel, meta, "Update channel run"); return this.findHealthyRelay(this.pool.seenOn(channel.id) as string[]).then((relay) => { return this.publish(Kind.ChannelMetadata, [["e", channel.id, relay]], JSON.stringify(meta)); }); @@ -541,7 +529,6 @@ class MessageService extends TypedEventEmitter { } public async updateProfile(profile: Metadata) { - // console.log("Update profile run", profile); return this.publish(Kind.Metadata, [], JSON.stringify(profile)); } @@ -572,10 +559,7 @@ class MessageService extends TypedEventEmitter { return this.publish(Kind.ChannelMessage, tags, message); } - public resendMessage(kind: number, tags: Array[], content: string) {} - private publish(kind: number, tags: Array[], content: string): Promise { - console.log("kind", kind, "tags", tags, "content", content); return new Promise((resolve, reject) => { this.signEvent({ kind, @@ -599,19 +583,16 @@ class MessageService extends TypedEventEmitter { const pub = this.pool.publish(this.writeRelays, event); pub.on("ok", () => { resolve(event); - console.log("event is send", event); if (event.kind === Kind.Contacts) { this.getContacts(); } }); pub.on("failed", () => { - // console.log("Failed to sign event"); reject("Couldn't sign event!"); }); }) .catch(() => { - // console.log("Catch run event not"); reject("Couldn't sign event!"); }); }); @@ -630,7 +611,6 @@ class MessageService extends TypedEventEmitter { } pushToEventBuffer(event: Event) { - // console.log("event reaches in event buffer", event); const cacheKey = `${event.id}_emitted`; if (this.nameCache[cacheKey] === undefined) { if (this.eventQueueFlag) { @@ -652,7 +632,6 @@ class MessageService extends TypedEventEmitter { } async processEventQueue() { - // console.log("Event reaches in process queue"); this.eventQueueFlag = false; const directContacts = this.eventQueue .filter((x) => x.kind === Kind.Contacts) @@ -666,7 +645,6 @@ class MessageService extends TypedEventEmitter { const directContactsProfile: Array<{ pubkey: string; name: string }> = directContacts[0]; if (directContactsProfile.length > 0) { - // console.log(directContactsProfile, "directContactsProfile"); this.emit(MessageEvents.DirectContact, directContactsProfile); } } @@ -686,7 +664,6 @@ class MessageService extends TypedEventEmitter { }) .filter(notEmpty); if (profileUpdates.length > 0) { - // console.log("Profile Updates", profileUpdates); this.emit(MessageEvents.ProfileUpdate, profileUpdates); } @@ -749,7 +726,6 @@ class MessageService extends TypedEventEmitter { }) .filter(notEmpty); if (channelUpdates.length > 0) { - // console.log("channelUpdates", channelUpdates); this.emit(MessageEvents.ChannelUpdate, channelUpdates); } @@ -776,7 +752,6 @@ class MessageService extends TypedEventEmitter { }) .filter(notEmpty); if (publicMessages.length > 0) { - // console.log("publicMessages", publicMessages); this.emit(MessageEvents.PublicMessageAfterSent, publicMessages); } @@ -857,7 +832,6 @@ class MessageService extends TypedEventEmitter { export default MessageService; export const initMessageService = (keys: Keys): MessageService | undefined => { - // console.log("keys in raven instance", keys); if (window.messageService) { window.messageService.close(); window.messageService = undefined; @@ -865,7 +839,6 @@ export const initMessageService = (keys: Keys): MessageService | undefined => { if (keys) { window.messageService = new MessageService(keys.priv, keys.pub); - console.log("window messageService", window.messageService); } return window.messageService; diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 64b5d753799..c21fa4d8443 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1666,7 +1666,33 @@ "subscribers": "Subscribers", "invite": "Invite", "leave:": "Leave", - "refresh": "Refresh" + "refresh": "Refresh", + "message-warning": "You cannot send messages in this group.", + "edit-community-roles": "Edit Community Roles", + "add": "Add", + "no-admin": "No admin or moderator for this community chat.", + "blocked-users": "Blocked Users", + "action": "Action", + "unblock": "Unblock", + "no-locked-user": "No Blocked user yet.", + "enter-private-key": "Enter Chat private key", + "enter-private-key-detail": "Enter chat private key to import all chats", + "close": "Close", + "confirm": "Confirm", + "error-updating-community": "Error occurred while updating community", + "leave": "Leave", + "edit-roles": "Edit Roles", + "copy-private-key": "Copy private key", + "block": "Block", + "communities": "Communities", + "dms": "DMs", + "import-chat": "Import Chat", + "create-new-account": "Create New Account", + "loading": "Loading...", + "blocked-user-message": "You have been blocked from this community", + "chat-joined": "Chat Joined", + "start-community-chat": "Start Community Chat", + "join-community-chat": "Join Community Chat" }, "add-image": { "title": "Add Image", diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts index 5f1c195e302..80e4c8088e4 100644 --- a/src/common/store/chat/index.ts +++ b/src/common/store/chat/index.ts @@ -49,7 +49,7 @@ export default (state: Chat = initialState, action: Actions): Chat => { return existingContact.name === contact.name && existingContact.pubkey === contact.pubkey; }); }); - // console.log(uniqueDirectContacts, "uniqueDirectContacts"); + return { ...state, directContacts: [...state.directContacts, ...uniqueDirectContacts], diff --git a/src/providers/message-provider.tsx b/src/providers/message-provider.tsx index a0b9a3dc01a..4b4c0cda2e7 100644 --- a/src/providers/message-provider.tsx +++ b/src/providers/message-provider.tsx @@ -23,7 +23,6 @@ export const setNostrkeys = (keys: NostrKeysType) => { pub: keys.pub, priv: keys.priv }; - // console.log("detail", detail) const ev = new CustomEvent("createMSInstance", { detail }); window.dispatchEvent(ev); }; @@ -60,16 +59,11 @@ const MessageProvider = (props: Props) => { if (chat.channels.length !== 0) { setIsCommunityCreated(true); } - if (chat.directMessages.length !== 0) { setIsDirectChatCreated(true); } }, [chat.channels, chat.directMessages]); - useEffect(() => { - console.log("replacedDirectMessagesBuffer", replacedDirectMessagesBuffer); - }, [replacedDirectMessagesBuffer]); - useEffect(() => { window.addEventListener("createMSInstance", createMSInstance); @@ -78,13 +72,8 @@ const MessageProvider = (props: Props) => { }; }, []); - useEffect(() => { - // console.log("publicMessageBuffer", publicMessageBuffer); - }, [publicMessageBuffer]); - useEffect(() => { if (!window.messageService && keys?.priv) { - // console.log(keys, "keys") const messageServiceInstance = initMessageService(keys); setMessageService(messageServiceInstance); } @@ -98,7 +87,6 @@ const MessageProvider = (props: Props) => { const createMSInstance = (e: Event) => { const detail = (e as CustomEvent).detail as NostrKeysType; - // console.log(detail, "details") const messageServiceInstance = initMessageService(detail); setMessageService(messageServiceInstance); }; @@ -149,7 +137,6 @@ const MessageProvider = (props: Props) => { // Profile update handler const handleProfileUpdate = (data: Profile[]) => { - // console.log("handleProfileUpdate", data); props.addProfile(data); }; @@ -163,7 +150,6 @@ const MessageProvider = (props: Props) => { //Direct contact handler const handleDirectContact = (data: DirectContact[]) => { - // console.log("handleDirectContact", data); const result = [...chat.directContacts]; data.forEach(({ name, pubkey }) => { const isPresent = chat.directContacts.some( @@ -189,7 +175,6 @@ const MessageProvider = (props: Props) => { //Direct message ahandle before sent const handleDirectMessageBeforeSent = (data: DirectMessage[]) => { - console.log("handleDirectMessageBeeforeSent", data); data.map((m) => { const { peer, id } = m; setReplacedDirectMessagesBuffer((prevBuffer) => [...prevBuffer, id]); @@ -224,7 +209,6 @@ const MessageProvider = (props: Props) => { // // Direct message handler after sent const handleDirectMessageAfterSent = (data: DirectMessage[]) => { - console.log("handleDirectMessageAfterSent", data); if (isDirectChatCreated) { data.forEach((message) => { const { peer, id } = message; @@ -232,7 +216,6 @@ const MessageProvider = (props: Props) => { setReplacedDirectMessagesBuffer((prevBuffer) => prevBuffer.filter((messageId) => messageId !== id) ); - console.log("I reached"); props.replaceDirectMessage(peer, message); } else { props.addDirectMessages(peer, message); @@ -280,8 +263,6 @@ const MessageProvider = (props: Props) => { // Channel creation handler const handleChannelCreation = (data: Channel[]) => { - // console.log("handleChannelCreation", data); - const append = data.filter((x) => chat.channels.find((y) => y.id === x.id) === undefined); props.addChannels(append); }; @@ -297,7 +278,6 @@ const MessageProvider = (props: Props) => { // Channel update handler const handleChannelUpdate = (data: ChannelUpdate[]) => { - // console.log("handleChannelUpdate", data); props.UpdateChannels(data); }; @@ -319,7 +299,6 @@ const MessageProvider = (props: Props) => { //public message handle before sent const handlePublicMessageBeforeSent = (data: PublicMessage[]) => { - console.log("handlePublicMessageBeeforeSent", data); data.map((m) => { const { id, root } = m; setReplacedPublicMessagesBuffer((prevBuffer) => [...prevBuffer, id]); @@ -348,7 +327,6 @@ const MessageProvider = (props: Props) => { //Public Message handler after sent const handlePublicMessageAfterSent = (data: PublicMessage[]) => { - console.log("handlePublicMessageAfterSent", data, chat); if (isCommunityCreated) { data.forEach((message) => { const { root, id } = message; @@ -418,7 +396,6 @@ const MessageProvider = (props: Props) => { //previous public messages handler const handlePreviousPublicMessages = (data: PublicMessage[]) => { - console.log("handlePreviousPublicMessages", data); const channelId = data[0].root; const messagesObject: MessagesObject = data.reduce((result, message) => { result[message.id] = message; @@ -445,8 +422,6 @@ const MessageProvider = (props: Props) => { // Left channel handler const handleLeftChannelList = (data: string[]) => { - // console.log("handleLeftChannelList", data); - // setLeftChannelList(data); props.addleftChannels(data); }; From 35f66ce43b5d2846967d6399068b4d62a1ec9de3 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Fri, 11 Aug 2023 17:49:30 +0500 Subject: [PATCH 034/179] Remove console and uncomment the useEffect --- src/common/components/chat-box/index.tsx | 22 +++++++++---------- .../join-community-chat-btn/index.tsx | 11 ---------- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 52fd916002b..e7625517bf3 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -273,17 +273,17 @@ export default function ChatBox(props: Props) { setNoStrPrivKey(noStrPrivKey); }, []); - // useEffect(() => { - // if (window.messageService) { - // setHasUserJoinedChat(true); - // // setInProgress(false); - // } - // setTimeout(() => { - // if (props.chat.channels.length === 0 && props.chat.directContacts.length === 0) { - // setInProgress(false); - // } - // }, 5000); - // }, [window?.messageService]); + useEffect(() => { + if (window.messageService) { + setHasUserJoinedChat(true); + // setInProgress(false); + } + setTimeout(() => { + if (props.chat.channels.length === 0 && props.chat.directContacts.length === 0) { + setInProgress(false); + } + }, 5000); + }, [window?.messageService]); useEffect(() => { const communities = getCommunities(props.chat.channels, props.chat.leftChannelsList); diff --git a/src/common/components/join-community-chat-btn/index.tsx b/src/common/components/join-community-chat-btn/index.tsx index 8f10d83603f..67aafdde63f 100644 --- a/src/common/components/join-community-chat-btn/index.tsx +++ b/src/common/components/join-community-chat-btn/index.tsx @@ -43,17 +43,6 @@ export default function JoinCommunityChatBtn(props: Props) { getCommunityRoles(); }, []); - useEffect(() => { - console.log( - "isCommunityChatJoined", - isCommunityChatJoined, - "isChatEnabled", - isChatEnabled, - "hasUserJoinedChat", - hasUserJoinedChat - ); - }, [isCommunityChatJoined, isChatEnabled, hasUserJoinedChat]); - useEffect(() => { fetchCommunityProfile(); fetchUserProfileData(); From fbb4226b525720860cf40c610b8228a533ca841b Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Fri, 11 Aug 2023 18:36:22 +0500 Subject: [PATCH 035/179] fetch previous message called when current data is gretaer than 25 --- src/common/components/chat-box/index.tsx | 2 +- src/common/helper/chat-utils.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index e7625517bf3..5048a83ad02 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -600,7 +600,7 @@ export default function ChatBox(props: Props) { element.scrollTop + chatBodyDivRef?.current?.clientHeight! < element.scrollHeight - 200; setIsScrollToTop(isScrollToTop); setIsScrollToBottom(isScrollToBottom); - const scrollerTop = element.scrollTop <= 600; + const scrollerTop = element.scrollTop <= 600 && publicMessages.length > 25; if (isCommunity && scrollerTop) { setIsTop(true); } else { diff --git a/src/common/helper/chat-utils.ts b/src/common/helper/chat-utils.ts index 5f7dfe6b9f2..549473a8916 100644 --- a/src/common/helper/chat-utils.ts +++ b/src/common/helper/chat-utils.ts @@ -30,7 +30,7 @@ export const resetProfile = (activeUser: ActiveUser | null) => { ls.remove(`${activeUser?.username}_noStrPrivKey`); const response = await getAccountFull(activeUser?.username!); const updatedProfile = await updateProfile(response, { ...profile }); - console.log("Updated profile", updatedProfile); + // console.log("Updated profile", updatedProfile); resolve(updatedProfile); } catch (error) { reject(error); @@ -61,7 +61,6 @@ export const setChannelMetaData = (username: string, channel: Channel) => { channel: channel }; const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); - console.log("Channel metadata updated successfully", updatedProfile); resolve(updatedProfile); } catch (error) { reject(error); From 835148df52781fabddb04d25139d106b4fd2f8c2 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Tue, 15 Aug 2023 11:13:24 +0500 Subject: [PATCH 036/179] Commenting the unused code for the time --- src/common/components/chat-box/index.tsx | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 5048a83ad02..9e0fbac1300 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -273,17 +273,17 @@ export default function ChatBox(props: Props) { setNoStrPrivKey(noStrPrivKey); }, []); - useEffect(() => { - if (window.messageService) { - setHasUserJoinedChat(true); - // setInProgress(false); - } - setTimeout(() => { - if (props.chat.channels.length === 0 && props.chat.directContacts.length === 0) { - setInProgress(false); - } - }, 5000); - }, [window?.messageService]); + // useEffect(() => { + // if (window.messageService) { + // setHasUserJoinedChat(true); + // // setInProgress(false); + // } + // setTimeout(() => { + // if (props.chat.channels.length === 0 && props.chat.directContacts.length === 0) { + // setInProgress(false); + // } + // }, 5000); + // }, [window?.messageService]); useEffect(() => { const communities = getCommunities(props.chat.channels, props.chat.leftChannelsList); From 8336684462aeeabed663321ff8cb66901c498a6b Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Wed, 16 Aug 2023 12:10:10 +0500 Subject: [PATCH 037/179] Handle useEffect when window object is undefined --- src/common/components/chat-box/index.tsx | 21 +++++++++---------- .../join-community-chat-btn/index.tsx | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 9e0fbac1300..42208750cdb 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -273,17 +273,16 @@ export default function ChatBox(props: Props) { setNoStrPrivKey(noStrPrivKey); }, []); - // useEffect(() => { - // if (window.messageService) { - // setHasUserJoinedChat(true); - // // setInProgress(false); - // } - // setTimeout(() => { - // if (props.chat.channels.length === 0 && props.chat.directContacts.length === 0) { - // setInProgress(false); - // } - // }, 5000); - // }, [window?.messageService]); + useEffect(() => { + if (window.messageService) { + setHasUserJoinedChat(true); + } + setTimeout(() => { + if (props.chat.channels.length === 0 && props.chat.directContacts.length === 0) { + setInProgress(false); + } + }, 5000); + }, [typeof window !== "undefined" && window?.messageService]); useEffect(() => { const communities = getCommunities(props.chat.channels, props.chat.leftChannelsList); diff --git a/src/common/components/join-community-chat-btn/index.tsx b/src/common/components/join-community-chat-btn/index.tsx index 67aafdde63f..7a56ec6562b 100644 --- a/src/common/components/join-community-chat-btn/index.tsx +++ b/src/common/components/join-community-chat-btn/index.tsx @@ -60,7 +60,7 @@ export default function JoinCommunityChatBtn(props: Props) { window?.messageService?.loadChannel(currentChannel?.id!); } } - }, [window?.messageService, activeUserKeys, loadCommunity]); + }, [typeof window !== "undefined" && window?.messageService, activeUserKeys, loadCommunity]); useEffect(() => { checkIsChatJoined(); From 19c5877363bf700e664a77f3a38d519d215a0676 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Wed, 16 Aug 2023 16:07:54 +0500 Subject: [PATCH 038/179] Show import chat modal within chat popup --- src/common/components/chat-box/index.scss | 6 +- src/common/components/chat-box/index.tsx | 171 +++++++++++++--------- src/common/helper/chat-utils.ts | 2 +- src/common/i18n/locales/en-US.json | 2 +- src/common/img/svg.tsx | 17 +++ 5 files changed, 121 insertions(+), 77 deletions(-) diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss index 9aa7a44914e..cef78ee4acc 100644 --- a/src/common/components/chat-box/index.scss +++ b/src/common/components/chat-box/index.scss @@ -69,7 +69,7 @@ } .message-header-content { - margin-top: 0.9rem; + margin: 0.8rem 0; font-weight: 700; line-height: 24px; font-size: 21px; @@ -668,10 +668,6 @@ margin-bottom: 1.4rem; } } - - .private-key { - margin: 2rem 0; - } } .add-dialog-header { margin-bottom: 20px; diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 42208750cdb..ebf63da4632 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -63,7 +63,8 @@ import { hideSvg, removeUserSvg, resendMessageSvg, - failedMessageSvg + failedMessageSvg, + chatKeySvg } from "../../img/svg"; import { @@ -188,6 +189,15 @@ export default function ChatBox(props: Props) { const [isTop, setIsTop] = useState(false); const [hasMore, setHasMore] = useState(true); const [resendMessage, setResendMessage] = useState(); + const [importPrivKey, setImportPrivKey] = useState(false); + + useEffect(() => { + // resetProfile(props.activeUser); + fetchProfileData(); + setShow(!!props.activeUser?.username); + const noStrPrivKey = getPrivateKey(props.activeUser?.username!); + setNoStrPrivKey(noStrPrivKey); + }, []); useEffect(() => { const updated: ChannelUpdate = props.chat.updatedChannel @@ -265,14 +275,6 @@ export default function ChatBox(props: Props) { } }, [currentChannel, removedUsers]); - useEffect(() => { - // resetProfile(props.activeUser); - fetchProfileData(); - setShow(!!props.activeUser?.username); - const noStrPrivKey = getPrivateKey(props.activeUser?.username!); - setNoStrPrivKey(noStrPrivKey); - }, []); - useEffect(() => { if (window.messageService) { setHasUserJoinedChat(true); @@ -642,6 +644,7 @@ export default function ChatBox(props: Props) { setShowSpinner(true); resetChat(); const keys = createNoStrAccount(); + console.log("keys", keys); ls.set(`${props.activeUser?.username}_noStrPrivKey`, keys.priv); setNoStrPrivKey(keys.priv); await setProfileMetaData(props.activeUser, keys.pub); @@ -1089,44 +1092,44 @@ export default function ChatBox(props: Props) { ); }; - const ImportChatModal = () => { - return ( - <> -
-
1
-
-
{_t("chat.enter-private-key")}
-
{_t("chat.enter-private-key-detail")}
-
-
-
- { - e.preventDefault(); - }} - > - - - {keySvg} - - setChatPrivkey(e.target.value)} - /> - - - - - {addRoleError && {addRoleError}} - -
- - ); - }; + // const ImportChatModal = () => { + // return ( + // <> + //
+ //
1
+ //
+ //
{_t("chat.enter-private-key")}
+ //
{_t("chat.enter-private-key-detail")}
+ //
+ //
+ //
+ //
{ + // e.preventDefault(); + // }} + // > + // + // + // {keySvg} + // + // setChatPrivkey(e.target.value)} + // /> + // + // + // + // + // {addRoleError && {addRoleError}} + //
+ //
+ // + // ); + // }; const successModal = (message: string) => { return ( @@ -1346,12 +1349,6 @@ export default function ChatBox(props: Props) { setHasMore(true); }; - const handleImportChat = () => { - setKeyDialog(true); - - setStep(3); - }; - const communityMenuItems: MenuItem[] = [ { label: _t("chat.invite"), @@ -1395,9 +1392,9 @@ export default function ChatBox(props: Props) { const menuItems: MenuItem[] = [ { - label: _t("chat.copy-private-key"), + label: _t("chat.manage-chat-key"), onClick: () => inviteClicked(noStrPrivKey), - icon: linkSvg + icon: chatKeySvg } ]; @@ -1464,18 +1461,6 @@ export default function ChatBox(props: Props) {
)} -
- -

{ - setExpanded(!expanded); - }} - > - {expanded ? expandArrow : collapseArrow} -

-
-
{isCommunity && (
)} +
+ +

{ + setExpanded(!expanded); + }} + > + {expanded ? expandArrow : collapseArrow} +

+
+
@@ -1998,11 +1995,46 @@ export default function ChatBox(props: Props) { ) : !noStrPrivKey || noStrPrivKey.length === 0 || noStrPrivKey === null ? ( <> -
-
+ {importPrivKey && ( +
+
{ + e.preventDefault(); + }} + > + + + {keySvg} + + setChatPrivkey(e.target.value)} + /> + + + + + {addRoleError && ( + {addRoleError} + )} +
+
+ )} + {}
- // - // - // {addRoleError && {addRoleError}} - // - //
- // - // ); - // }; - const successModal = (message: string) => { return ( <> @@ -1347,6 +1334,8 @@ export default function ChatBox(props: Props) { setSearchText(""); setMessage(""); setHasMore(true); + setRevealPrivateKey(false); + setAddRoleError(""); }; const communityMenuItems: MenuItem[] = [ @@ -1393,7 +1382,7 @@ export default function ChatBox(props: Props) { const menuItems: MenuItem[] = [ { label: _t("chat.manage-chat-key"), - onClick: () => inviteClicked(noStrPrivKey), + onClick: () => setRevealPrivateKey(!revelPrivateKey), icon: chatKeySvg } ]; @@ -1408,9 +1397,13 @@ export default function ChatBox(props: Props) { return ( <> {show && ( -
+
- {(currentUser || communityName || showSearchUser) && expanded && ( + {(currentUser || communityName || showSearchUser || revelPrivateKey) && expanded && (
@@ -1439,20 +1432,26 @@ export default function ChatBox(props: Props) { ? currentCommunity?.title : showSearchUser ? "New Message" + : revelPrivateKey + ? "Manage chat key" : "Messages"}

- {!currentUser && hasUserJoinedChat && noStrPrivKey && !isCommunity && ( - <> -
- -

{addMessageSVG}

-
-
- - )} - {hasUserJoinedChat && noStrPrivKey && ( + {!currentUser && + hasUserJoinedChat && + noStrPrivKey && + !isCommunity && + !revelPrivateKey && ( + <> +
+ +

{addMessageSVG}

+
+
+ + )} + {hasUserJoinedChat && noStrPrivKey && !revelPrivateKey && (

@@ -1513,7 +1512,7 @@ export default function ChatBox(props: Props) { ref={chatBodyDivRef} onScroll={handleScroll} > - {hasUserJoinedChat ? ( + {hasUserJoinedChat && !revelPrivateKey ? ( <> {currentUser.length !== 0 || communityName.length !== 0 ? (

@@ -1915,18 +1914,21 @@ export default function ChatBox(props: Props) { key={index} className="search-content" onClick={() => { - setCurrentUser(user); + setCurrentUser(user.account); setIsCurrentUser(true); }} >
- +
-

{user}

+

{user.account}

+

+ ({accountReputation(user.reputation)}) +

); @@ -2063,6 +2065,35 @@ export default function ChatBox(props: Props) { )} + ) : revelPrivateKey ? ( + <> +
+
+ + Private key +
+ + +

{ + inviteClicked(noStrPrivKey); + }} + > + {copyContent} +

+
+
+
+
+ +
+ ) : ( @@ -2032,9 +2036,8 @@ export default function ChatBox(props: Props) {
)} - {} -
+
- ) : hasUserJoinedChat && !isChatEnabled ? ( - @@ -226,11 +246,7 @@ export default function JoinCommunityChatBtn(props: Props) { {_t("chat.join-community-chat")} ) : ( - + <> ) ) : isChatEnabled && !isCommunityChatJoined ? ( -
-
- {currentChannel?.communityModerators?.length !== 0 ? ( - <> - - - - - - - - - {currentChannel?.communityModerators && - currentChannel?.communityModerators!.map((moderator, i) => { - return ( - - - - - ); - })} - -
{_t("community.roles-account")}{_t("community.roles-role")}
- - {" "} - @{moderator.name} - - - {moderator.name === props.activeUser?.username ? ( -

{moderator.role}

- ) : ( - ) => - updateRole(e, moderator) - } - > - {roles.map((r, i) => ( - - ))} - - )} -
- - ) : ( -
-

{_t("chat.no-admin")}

-
- )} -
- - ); - }; - - const blockedUsersModal = () => { - return ( - <> -
-

{_t("chat.blocked-users")}

-
- - {blockedUsers.length !== 0 ? ( - <> - - - - - - - - - {blockedUsers && - blockedUsers.map((user, i) => { - return ( - - - - - ); - })} - -
{_t("community.roles-account")}{_t("chat.action")}
- - {" "} - - @{user.name} - - - - -
- - ) : ( -
-

{_t("chat.no-locked-user")}

-
- )} - - ); - }; - const successModal = (message: string) => { return ( <> @@ -1192,51 +854,6 @@ export default function ChatBox(props: Props) { ); }; - useDebounce( - async () => { - if (user.length === 0) { - setAddRoleError(""); - setInProgress(false); - return; - } - - try { - const profileData = await getProfileMetaData(user); - if (profileData && profileData.hasOwnProperty(NOSTRKEY)) { - const alreadyExists = currentChannel?.communityModerators?.some( - (moderator) => moderator.name === profileData.name - ); - if (alreadyExists) { - setAddRoleError("You have already assigned some rule to this user."); - setInProgress(false); - return; - } - const moderator = { - name: user, - pubkey: profileData.nsKey, - role: role - }; - setModerator(moderator); - setAddRoleError(""); - } else { - setAddRoleError("You cannot set this user because this user hasn't joined the chat yet."); - } - } catch (err) { - error(err as string); - } - - setInProgress(false); - }, - 200, - [user, role] - ); - - const userChanged = (e: React.ChangeEvent) => { - const { value: user } = e.target; - setUser(user); - setInProgress(true); - }; - const handleChannelUpdate = (operationType: string) => { let updatedMetaData = { name: currentChannel?.name!, @@ -1249,10 +866,6 @@ export default function ChatBox(props: Props) { }; switch (operationType) { - case ADDROLE: - const updatedRoles = [...(currentChannel?.communityModerators || []), moderator!]; - updatedMetaData.communityModerators = updatedRoles; - break; case HIDEMESSAGE: const updatedHiddenMessages = [...(currentChannel?.hiddenMessageIds || []), hiddenMsgId!]; updatedMetaData.hiddenMessageIds = updatedHiddenMessages; @@ -1272,12 +885,6 @@ export default function ChatBox(props: Props) { updatedMetaData.communityModerators = NewUpdatedRoles; } break; - case UNBLOCKUSER: - const NewUpdatedRemovedUsers = currentChannel?.removedUserIds?.filter( - (item) => item !== removedUserId - ); - updatedMetaData.removedUserIds = NewUpdatedRemovedUsers; - break; default: break; } @@ -1288,7 +895,11 @@ export default function ChatBox(props: Props) { if (operationType === HIDEMESSAGE) { setStep(0); setKeyDialog(false); - fetchCommunityMessages(currentChannel?.id!); + fetchCommunityMessages( + props.chat.publicMessages, + currentChannel!, + currentChannel?.hiddenMessageIds + ); setHiddenMsgId(""); } if (operationType === BLOCKUSER || operationType === UNBLOCKUSER) { @@ -1301,29 +912,8 @@ export default function ChatBox(props: Props) { } }; - const roleChanged = (e: React.ChangeEvent) => { - const { value: role } = e.target; - setRole(role); - }; - - const leaveClicked = () => { - setKeyDialog(true); - setStep(1); - }; - - const handleEditRoles = () => { - setKeyDialog(true); - setStep(2); - }; - - const handleBlockedUsers = () => { - setKeyDialog(true); - setStep(8); - }; - const toggleKeyDialog = () => { setKeyDialog(!keyDialog); - setUser(""); setAddRoleError(""); }; @@ -1335,68 +925,11 @@ export default function ChatBox(props: Props) { setClickedMessage(""); setShowSearchUser(false); setSearchText(""); - setMessage(""); setHasMore(true); setRevealPrivateKey(false); setAddRoleError(""); }; - const communityMenuItems: MenuItem[] = [ - { - label: _t("chat.invite"), - onClick: () => - copyToClipboard( - `http://localhost:3000/created/${currentCommunity?.name}?communityid=${currentChannel?.id}` - ), - icon: linkSvg - }, - { - label: _t("chat.leave"), - onClick: leaveClicked, - icon: chatLeaveSvg - }, - ...(props.activeUser?.username === currentChannel?.communityName - ? [ - { - label: _t("chat.edit-roles"), - onClick: handleEditRoles, - icon: editSVG - } - ] - : []), - ...(communityAdmins.includes(props.activeUser?.username!) - ? [ - { - label: _t("chat.blocked-users"), - onClick: handleBlockedUsers, - icon: removeUserSvg - } - ] - : []) - ]; - - const communityMenuConfig = { - history: props.history, - label: "", - icon: KebabMenu, - items: communityMenuItems - }; - - const menuItems: MenuItem[] = [ - { - label: _t("chat.manage-chat-key"), - onClick: () => setRevealPrivateKey(!revelPrivateKey), - icon: chatKeySvg - } - ]; - - const menuConfig = { - history: props.history, - label: "", - icon: KebabMenu, - items: menuItems - }; - return ( <> {show && ( @@ -1465,23 +998,16 @@ export default function ChatBox(props: Props) { )} {isCommunity && (
- +
)}{" "} {!isCommunity && !isCurrentUser && noStrPrivKey && (
- { + setRevealPrivateKey(!revelPrivateKey); + }} />
)} @@ -1530,43 +1056,12 @@ export default function ChatBox(props: Props) { : "" } > -
- {profileData?.joiningData && ( -
- - - -

- {isCurrentUser - ? currentUser - : (isCommunity && currentCommunity?.title) || ""} -

- {profileData.about && ( -

{profileData.about}

- )} - -
-

- {" "} - {_t("chat.joined")}{" "} - {dateToFormatted(profileData!.joiningData, "LL")} -

-

- {" "} - {formatFollowers(profileData!.followers)}{" "} - {isCommunity ? _t("chat.subscribers") : _t("chat.followers")} -

-
-
- )} -
+ {!isCurrentUserJoined && (

{_t("chat.not-joined")}

@@ -1672,7 +1167,7 @@ export default function ChatBox(props: Props) { const isImage = isMessageImage(pMsg.content); - const name = getProfileName(pMsg.creator); + const name = getProfileName(pMsg.creator, props.chat.profiles); const popover = (
{isCurrentUser || isCommunity ? chevronDownSvgForSlider : chevronUpSvg} @@ -2103,119 +1600,19 @@ export default function ChatBox(props: Props) { )}
{inProgress && } - - {(currentUser || isCommunity) && ( -
- showEmojiPicker && setShowEmojiPicker(false)}> -
-
- -
setShowEmojiPicker(!showEmojiPicker)} - > - {emoticonHappyOutlineSvg} -
-
- {showEmojiPicker && ( - { - handleEmojiSelection(e); - }} - /> - )} -
-
-
- - {message.length === 0 && ( - - shGif && setShGif(false)}> -
-
- -
- {" "} - {gifIcon} -
-
- {shGif && ( - { - setShGif(gifState!); - }} - fallback={(e) => { - handleGifSelection(e); - }} - /> - )} -
-
-
- - -
) => { - e.stopPropagation(); - const el = fileInput.current; - if (el) el.click(); - }} - > -
{chatBoxImageSvg}
-
-
- - -
- )} - -
{ - e.preventDefault(); - e.stopPropagation(); - sendMessage(); - }} - style={{ width: "100%" }} - > - - - - {messageSendSvg} - - -
-
+ {(isCurrentUser || isCommunity) && ( + )}
)} @@ -2232,15 +1629,11 @@ export default function ChatBox(props: Props) { > - {step === 1 && confirmationModal(LEAVECOMMUNITY)} {step === 5 && confirmationModal(HIDEMESSAGE)} {step === 6 && confirmationModal(BLOCKUSER)} - {step === 7 && confirmationModal(UNBLOCKUSER)} {step === 9 && confirmationModal(NEWCHATACCOUNT)} {step === 11 && confirmationModal(RESENDMESSAGE)} - {step === 2 && EditRolesModal()} {step === 10 && successModal(NEWCHATACCOUNT)} - {step === 8 && blockedUsersModal()} )} diff --git a/src/common/components/chat-input/index.scss b/src/common/components/chat-input/index.scss new file mode 100644 index 00000000000..b5af1d1fa20 --- /dev/null +++ b/src/common/components/chat-input/index.scss @@ -0,0 +1,66 @@ +@import "src/style/colors"; +@import "src/style/variables"; +@import "src/style/bootstrap_vars"; +@import "src/style/mixins"; + +.chat { + grid-row: span 1; + display: flex; + margin: 4px 8px; + padding: 4px; + min-height: 45px; + border-radius: 20px; + @include themify(day) { + background: #e4e6eb; + } + @include themify(night) { + background: #cee2ff; + } + + &.disable { + pointer-events: none; + opacity: 0.5; + } + + .chatbox-emoji-picker { + .chatbox-emoji { + margin: 8px 0 0 7px; + cursor: pointer; + + svg { + height: 22px; + fill: rgb(60, 68, 73); + } + } + } + .chatbox-image { + cursor: pointer; + .chatbox-image-icon { + margin: 7px 0 0 7px; + } + } + + .chat-input-group { + .chat-input { + padding-top: 10px; + border: none; + @include themify(day) { + background: #e4e6eb; + } + @include themify(night) { + background: #cee2ff; + } + } + .msg-svg { + padding: 8px; + + &.active { + cursor: pointer; + } + &.active:hover { + background: rgba(29, 155, 240, 0.1); + border-radius: 50%; + } + } + } +} diff --git a/src/common/components/chat-input/index.tsx b/src/common/components/chat-input/index.tsx new file mode 100644 index 00000000000..7ea44638a51 --- /dev/null +++ b/src/common/components/chat-input/index.tsx @@ -0,0 +1,286 @@ +import React, { useEffect, useState } from "react"; +import { Form, FormControl, InputGroup } from "react-bootstrap"; +import axios from "axios"; + +import { Channel } from "../../../providers/message-provider-types"; +import { Chat } from "../../store/chat/types"; +import { ActiveUser } from "../../store/active-user/types"; +import { Global } from "../../store/global/types"; + +import ClickAwayListener from "../clickaway-listener"; +import EmojiPicker from "../emoji-picker"; +import GifPicker from "../gif-picker"; +import { error } from "../feedback"; +import Tooltip from "../tooltip"; + +import { emoticonHappyOutlineSvg, gifIcon, chatBoxImageSvg, messageSendSvg } from "../../img/svg"; +import { GifImagesStyle, UPLOADING } from "../chat-box/chat-constants"; + +import { _t } from "../../i18n"; +import { getAccessToken } from "../../helper/user-token"; +import { uploadImage } from "../../api/misc"; +import { addImage } from "../../api/private-api"; + +import "./index.scss"; +import { EmojiPickerStyleProps } from "../chat-box"; + +interface Props { + activeUser: ActiveUser | null; + global: Global; + chat: Chat; + isCurrentUser: boolean; + isCommunity: boolean; + receiverPubKey: string | null; + isActveUserRemoved: boolean; + currentChannel: Channel; + currentUser: string; + isCurrentUserJoined: boolean; + emojiPickerStyles: EmojiPickerStyleProps; + gifPickerStyle: EmojiPickerStyleProps; +} + +export default function ChatInput(props: Props) { + const fileInput = React.createRef(); + const [showEmojiPicker, setShowEmojiPicker] = useState(false); + const [message, setMessage] = useState(""); + const [shGif, setShGif] = useState(false); + const [isMessageText, setIsMessageText] = useState(false); + + const { + isCommunity, + isCurrentUser, + isActveUserRemoved, + receiverPubKey, + currentChannel, + currentUser, + isCurrentUserJoined, + emojiPickerStyles, + gifPickerStyle + } = props; + // console.log("currentUser in chat input", currentUser); + + useEffect(() => { + if (!isCurrentUser && !isCommunity) { + setMessage(""); + } + }, [isCommunity, isCurrentUser]); + + const handleEmojiSelection = (emoji: string) => { + setMessage((prevMessage) => prevMessage + emoji); + }; + + const toggleGif = (e?: React.MouseEvent) => { + if (e) { + e.stopPropagation(); + } + setShGif(!shGif); + }; + + const handleGifSelection = (gif: string) => { + isCurrentUser + ? window.messageService?.sendDirectMessage(receiverPubKey!, gif) + : window?.messageService?.sendPublicMessage(currentChannel, gif, [], ""); + }; + + const sendMessage = () => { + if (message.length !== 0 && !message.includes(UPLOADING)) { + if (isCommunity) { + if (!isActveUserRemoved) { + window?.messageService?.sendPublicMessage(currentChannel, message, [], ""); + } else { + error(_t("chat.message-warning")); + } + } + if (isCurrentUser) { + window.messageService?.sendDirectMessage(receiverPubKey!, message); + } + setMessage(""); + setIsMessageText(false); + } + if ( + receiverPubKey && + !props.chat.directContacts.some((contact) => contact.name === currentUser) && + isCurrentUser + ) { + window.messageService?.publishContacts(currentUser, receiverPubKey); + } + }; + + const checkFile = (filename: string) => { + const filenameLow = filename.toLowerCase(); + return ["jpg", "jpeg", "gif", "png"].some((el) => filenameLow.endsWith(el)); + }; + + const fileInputChanged = (e: React.ChangeEvent): void => { + let files = [...(e.target.files as FileList)].filter((i) => checkFile(i.name)).filter((i) => i); + + const { + global: { isElectron } + } = props; + + if (files.length > 0) { + e.stopPropagation(); + e.preventDefault(); + } + + if (files.length > 1 && isElectron) { + let isWindows = process.platform === "win32"; + if (isWindows) { + files = files.reverse(); + } + } + + files.forEach((file) => upload(file)); + + // reset input + e.target.value = ""; + }; + const upload = async (file: File) => { + const { activeUser, global } = props; + + const username = activeUser?.username!; + + const tempImgTag = `![Uploading ${file.name} #${Math.floor(Math.random() * 99)}]()\n\n`; + + setMessage(tempImgTag); + + let imageUrl: string; + try { + let token = getAccessToken(username); + if (token) { + const resp = await uploadImage(file, token); + imageUrl = resp.url; + + if (global.usePrivate && imageUrl.length > 0) { + addImage(username, imageUrl).then(); + } + + const imgTag = imageUrl.length > 0 && `![](${imageUrl})\n\n`; + + imgTag && setMessage(imgTag); + setIsMessageText(true); + } else { + error(_t("editor-toolbar.image-error-cache")); + } + } catch (e) { + if (axios.isAxiosError(e) && e.response?.status === 413) { + error(_t("editor-toolbar.image-error-size")); + } else { + error(_t("editor-toolbar.image-error")); + } + return; + } + }; + + const handleMessage = (e: React.ChangeEvent) => { + setMessage(e.target.value); + setIsMessageText(e.target.value.length !== 0); + }; + + return ( +
+ showEmojiPicker && setShowEmojiPicker(false)}> +
+
+ +
setShowEmojiPicker(!showEmojiPicker)}> + {emoticonHappyOutlineSvg} +
+
+ {showEmojiPicker && ( + { + handleEmojiSelection(e); + }} + /> + )} +
+
+
+ + {message.length === 0 && ( + + shGif && setShGif(false)}> +
+
+ +
+ {" "} + {gifIcon} +
+
+ {shGif && ( + { + setShGif(gifState!); + }} + fallback={(e) => { + handleGifSelection(e); + }} + /> + )} +
+
+
+ + +
) => { + e.stopPropagation(); + const el = fileInput.current; + if (el) el.click(); + }} + > +
{chatBoxImageSvg}
+
+
+ + +
+ )} + +
{ + e.preventDefault(); + e.stopPropagation(); + sendMessage(); + }} + style={{ width: "100%" }} + > + + + + {messageSendSvg} + + +
+
+ ); +} diff --git a/src/common/components/chats-channel-messages/index.scss b/src/common/components/chats-channel-messages/index.scss new file mode 100644 index 00000000000..3f55b168d2c --- /dev/null +++ b/src/common/components/chats-channel-messages/index.scss @@ -0,0 +1,186 @@ +@import "../../../style/vars_mixins"; + +.channel-messages { + padding-bottom: 15px; + .date-and-month { + margin-bottom: 0; + color: $dark; + } + .profile-box { + padding: 15px; + .profile-box-content { + .user-avatar.large { + margin-bottom: 20px; + } + .profile-name { + margin-bottom: 10px; + font-size: 18px; + font-weight: 800; + } + .profile-box-buttons { + margin-bottom: 15px; + padding: 0 5px; + } + } + } + + .message { + display: flex; + .user-img { + padding: 18px 8px 8px 16px; + } + .community-user-img { + padding: 8px 8px 8px 16px; + .user-avatar.medium { + cursor: pointer; + } + } + .user-info { + padding-top: 8px; + width: 100%; + .user-msg-time { + margin: 0; + color: rgb(138, 141, 145); + font-weight: 400; + font-size: 12px; + margin-left: 4px; + margin-bottom: 2px; + .username-community { + margin-right: 8px; + } + } + .receiver-message-content { + @include themify(day) { + background: #e4e6eb; + } + @include themify(night) { + background: #cee2ff; + } + + &.gif { + background: none; + img { + max-width: 100%; + } + } + + &.chat-image { + background: none; + img { + max-width: 100%; + } + } + color: #050505; + // max-width: 290px; + display: inline-block; + max-width: 70% !important; + word-wrap: break-word; + padding: 10px; + border-radius: 0px 10px 10px; + } + } + } + + .sender { + margin-bottom: 0.17rem; + .sender-message-time { + color: rgb(138, 141, 145); + display: flex; + font-weight: 400; + justify-content: flex-end; + font-size: 12px; + margin: 0; + margin-right: 18px; + margin-bottom: 2px; + } + .sender-message { + // max-width: 320px; + margin-left: auto; + + display: flex; + justify-content: flex-end; + margin-right: 17px; + + .resend-svg { + margin: 5px 15px 0 0; + cursor: pointer; + svg { + @include themify(day) { + fill: $light-black; + } + @include themify(night) { + fill: $white; + } + } + } + + .failed-svg { + margin: 8px 0 0 5px; + svg { + width: 16px; + height: 16px; + fill: $red !important; + } + } + + &.sending { + margin-right: 5px; + } + &.failed { + margin-right: 7px; + } + + .sender-message-content { + border-radius: 10px 10px 0px; + max-width: 70%; + word-wrap: break-word; + margin-bottom: 0; + color: $charcoal-grey; + font-size: 16px; + font-weight: 400; + padding: 8px 12px 8px 12px; + a { + text-decoration: underline; + color: white; + } + @include themify(day) { + background-color: rgb(0, 132, 255); + color: $white; + } + @include themify(night) { + background-color: $charcoal-grey; + color: $white; + } + &.gif { + background: none; + img { + max-width: 100%; + } + } + + &.chat-image { + background: none; + img { + max-width: 100%; + } + } + } + } + } + .hide-msg { + margin-right: 15px; + margin-top: 5px; + svg { + height: 14px; + width: 14px; + } + .hide-msg-svg { + cursor: pointer; + margin-bottom: 0; + } + &.receiver { + margin-top: 33px; + margin-left: 10px; + } + } +} diff --git a/src/common/components/chats-channel-messages/index.tsx b/src/common/components/chats-channel-messages/index.tsx new file mode 100644 index 00000000000..b720be30e34 --- /dev/null +++ b/src/common/components/chats-channel-messages/index.tsx @@ -0,0 +1,355 @@ +import React, { useRef, useState, RefObject, useEffect } from "react"; +import { Channel, PublicMessage } from "../../../providers/message-provider-types"; +import { + formatMessageTime, + getFormattedDateAndDay, + getProfileName, + isMessageGif, + isMessageImage, + NostrKeysType +} from "../../helper/chat-utils"; +import { renderPostBody } from "@ecency/render-helper"; +import { useMappedStore } from "../../store/use-mapped-store"; +import { + Button, + Col, + Form, + FormControl, + InputGroup, + Modal, + OverlayTrigger, + Popover, + Row, + Spinner +} from "react-bootstrap"; +import UserAvatar from "../user-avatar"; +import FollowControls from "../follow-controls"; +import { Account } from "../../store/accounts/types"; +import { ToggleType, UI } from "../../store/ui/types"; +import { _t } from "../../i18n"; +import Tooltip from "../tooltip"; +import { failedMessageSvg, hideSvg, resendMessageSvg } from "../../img/svg"; + +import "./index.scss"; +import { User } from "../../store/users/types"; +import { ActiveUser } from "../../store/active-user/types"; +import ChatInput from "../chat-input"; + +interface Props { + publicMessages: PublicMessage[]; + currentChannel: Channel; + activeUserKeys: NostrKeysType; + username: string; + users: User[]; + activeUser: ActiveUser | null; + ui: UI; + setActiveUser: (username: string | null) => void; + updateActiveUser: (data?: Account) => void; + deleteUser: (username: string) => void; + toggleUIProp: (what: ToggleType) => void; + scrollToBottom: () => void; +} + +export default function ChatsChannelMessages(props: Props) { + const { publicMessages, currentChannel, activeUserKeys, activeUser, scrollToBottom } = props; + const { global, chat } = useMappedStore(); + + const popoverRef = useRef(null); + const channelMessagesRef = React.createRef(); + + const [communityAdmins, setCommunityAdmins] = useState([]); + const [hoveredMessageId, setHoveredMessageId] = useState(""); + const [dmMessage, setDmMessage] = useState(""); + const [clickedMessage, setClickedMessage] = useState(""); + const [removedUserId, setRemovedUserID] = useState(""); + const [privilegedUsers, setPrivilegedUsers] = useState([]); + const [hiddenMsgId, setHiddenMsgId] = useState(""); + const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); + const [resendMessage, setResendMessage] = useState(); + + // useEffect(() => { + // scrollToBottom(); + // }, [publicMessages]); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + const clickedElement = event.target as HTMLElement; + + if (!popoverRef.current) { + return; + } + + const isAvatarClicked = + clickedElement.classList.contains("user-avatar") && + clickedElement.classList.contains("medium"); + if ( + popoverRef.current && + !popoverRef.current?.contains(event.target as Node) && + !isAvatarClicked + ) { + setClickedMessage(""); + } + }; + + if (channelMessagesRef.current) { + channelMessagesRef.current.addEventListener("mousedown", handleClickOutside); + } + + return () => { + if (channelMessagesRef.current) { + channelMessagesRef.current.removeEventListener("mousedown", handleClickOutside); + } + }; + }, [clickedMessage]); + + const sendDM = (name: string, pubkey: string) => { + if (dmMessage) { + window.messageService?.sendDirectMessage(pubkey, dmMessage); + + // setIsCurrentUser(true); + // setCurrentUser(name); + // setIsCommunity(false); + // setCommunityName(""); + setClickedMessage(""); + setDmMessage(""); + } + }; + + const handleDMChange = (e: React.ChangeEvent) => { + setDmMessage(e.target.value); + }; + + const handleImageClick = (msgId: string, pubkey: string) => { + if (clickedMessage === msgId) { + popoverRef.current = null; + setClickedMessage(""); + } else { + popoverRef.current = null; + setClickedMessage(msgId); + setRemovedUserID(pubkey); + } + }; + + return ( +
+ {publicMessages.length !== 0 && + activeUserKeys && + publicMessages.map((pMsg, i) => { + const dayAndMonth = getFormattedDateAndDay(pMsg, i, publicMessages); + let renderedPreview = renderPostBody(pMsg.content, false, global.canUseWebp); + + renderedPreview = renderedPreview.replace(/]*>/g, ""); + renderedPreview = renderedPreview.replace(/<\/p>/g, ""); + + const isGif = isMessageGif(pMsg.content); + + const isImage = isMessageImage(pMsg.content); + + const name = getProfileName(pMsg.creator, chat.profiles); + + const popover = ( + + +
}> +
+
+ +
+ +

{`@${name!}`}

+
+ + + {communityAdmins.includes(activeUser?.username!) && ( + <> + {currentChannel?.removedUserIds?.includes(pMsg.creator) ? ( + <> + + + ) : ( + <> + + + )} + + )} +
+ +
{ + e.preventDefault(); + e.stopPropagation(); + sendDM(name!, pMsg.creator); + }} + > + + + +
+
+
+
+
+ ); + + return ( + + + {dayAndMonth} + + + {pMsg.creator !== activeUserKeys?.pub ? ( +
setHoveredMessageId(pMsg.id)} + onMouseLeave={() => setHoveredMessageId("")} + > +
+ handleImageClick(pMsg.id, pMsg.creator)} + > + + + + +
+ +
+

+ {name} + {formatMessageTime(pMsg.created)} +

+
+
+ {hoveredMessageId === pMsg.id && + privilegedUsers.includes(props.activeUser?.username!) && ( + +
+

{ + setClickedMessage(""); + // setKeyDialog(true); + // setStep(5); + setHiddenMsgId(pMsg.id); + }} + > + {hideSvg} +

+
+
+ )} +
+ ) : ( +
setHoveredMessageId(pMsg.id)} + onMouseLeave={() => setHoveredMessageId("")} + > +

{formatMessageTime(pMsg.created)}

+
+ {hoveredMessageId === pMsg.id && !isActveUserRemoved && ( + +
+

{ + setClickedMessage(""); + // setKeyDialog(true); + // setStep(5); + setHiddenMsgId(pMsg.id); + }} + > + {hideSvg} +

+
+
+ )} + {pMsg.sent === 2 && ( + + { + // setKeyDialog(true); + // setStep(11); + setResendMessage(pMsg); + }} + > + {resendMessageSvg} + + + )} +
+ {pMsg.sent === 0 && ( + + + + )} + {pMsg.sent === 2 && ( + + {failedMessageSvg} + + )} +
+
+ )} + + ); + })} +
+ ); +} diff --git a/src/common/components/chats-community-dropdown-menu/index.scss b/src/common/components/chats-community-dropdown-menu/index.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/common/components/chats-community-dropdown-menu/index.tsx b/src/common/components/chats-community-dropdown-menu/index.tsx new file mode 100644 index 00000000000..3b49d0acb46 --- /dev/null +++ b/src/common/components/chats-community-dropdown-menu/index.tsx @@ -0,0 +1,559 @@ +import React, { useEffect, useState } from "react"; +import { History } from "history"; +import DropDown, { MenuItem } from "../dropdown"; +import useDebounce from "react-use/lib/useDebounce"; + +import { KebabMenu, linkSvg, chatLeaveSvg, editSVG, removeUserSvg } from "../../img/svg"; +import { _t } from "../../i18n"; +import { + ADDROLE, + CHATPAGE, + DropDownStyle, + LEAVECOMMUNITY, + NOSTRKEY, + UNBLOCKUSER +} from "../chat-box/chat-constants"; +import { useMappedStore } from "../../store/use-mapped-store"; +import { + Channel, + ChannelUpdate, + communityModerator +} from "../../../providers/message-provider-types"; +import { copyToClipboard, formattedUserName, getProfileMetaData } from "../../helper/chat-utils"; +import { error, success } from "../feedback"; +import { Button, Form, Modal, Row, Col, InputGroup, FormControl } from "react-bootstrap"; +import LinearProgress from "../linear-progress"; +import { ROLES } from "../../store/communities"; +import UserAvatar from "../user-avatar"; + +interface Props { + history: History | null; + from?: string; + username: string; +} + +const roles = [ROLES.ADMIN, ROLES.MOD, ROLES.GUEST]; + +const ChatsCommunityDropdownMenu = (props: Props) => { + const { activeUser, chat } = useMappedStore(); + const { history, username, from } = props; + const [step, setStep] = useState(0); + const [keyDialog, setKeyDialog] = useState(false); + const [inProgress, setInProgress] = useState(false); + const [user, setUser] = useState(""); + const [addRoleError, setAddRoleError] = useState(""); + const [role, setRole] = useState("admin"); + const [currentChannel, setCurrentChannel] = useState(); + const [moderator, setModerator] = useState(); + const [communityAdmins, setCommunityAdmins] = useState([]); + const [blockedUsers, setBlockedUsers] = useState<{ name: string; pubkey: string }[]>([]); + const [removedUserId, setRemovedUserID] = useState(""); + + useEffect(() => { + fetchCurrentChannel(formattedUserName(username)); + }, [chat.updatedChannel, username, chat.channels]); + + useEffect(() => { + getCommunityAdmins(); + + if (currentChannel && currentChannel?.removedUserIds) { + getBlockedUsers(currentChannel?.removedUserIds!); + } + }, [currentChannel, removedUserId]); + + const fetchCurrentChannel = (communityName: string) => { + const channel = chat.channels.find((channel) => channel.communityName === communityName); + if (channel) { + const updated: ChannelUpdate = chat.updatedChannel + .filter((x) => x.channelId === channel.id) + .sort((a, b) => b.created - a.created)[0]; + if (updated) { + const channel = { + name: updated.name, + about: updated.about, + picture: updated.picture, + communityName: updated.communityName, + communityModerators: updated.communityModerators, + id: updated.channelId, + creator: updated.creator, + created: currentChannel?.created!, + hiddenMessageIds: updated.hiddenMessageIds, + removedUserIds: updated.removedUserIds + }; + setCurrentChannel(channel); + } else { + setCurrentChannel(channel); + } + } + }; + + const handleEditRoles = () => { + setKeyDialog(true); + setStep(2); + }; + + const toggleKeyDialog = () => { + setKeyDialog(!keyDialog); + }; + + const userChanged = (e: React.ChangeEvent) => { + const { value: user } = e.target; + setUser(user); + setInProgress(true); + }; + + const getCommunityAdmins = () => { + const communityAdminRoles = ["owner", "admin"]; + const communityAdmins = currentChannel?.communityModerators?.filter((user) => + communityAdminRoles.includes(user.role) + ); + const communityAdminNames = communityAdmins?.map((user) => user.name); + setCommunityAdmins(communityAdminNames!); + }; + + const getBlockedUsers = (blockedUser: string[]) => { + const blockedUsers = chat.profiles + .filter((item) => blockedUser.includes(item.creator)) + .map((item) => ({ name: item.name, pubkey: item.creator })); + setBlockedUsers(blockedUsers); + }; + + const roleChanged = (e: React.ChangeEvent) => { + const { value: role } = e.target; + setRole(role); + }; + + const handleBlockedUsers = () => { + setKeyDialog(true); + setStep(3); + }; + + const updateRole = ( + event: React.ChangeEvent, + moderator: communityModerator + ) => { + const selectedRole = event.target.value; + const moderatorIndex = currentChannel?.communityModerators?.findIndex( + (mod) => mod.name === moderator.name + ); + if (moderatorIndex !== -1 && currentChannel) { + const newUpdatedChannel: Channel = { ...currentChannel }; + const newUpdatedModerator = { ...newUpdatedChannel?.communityModerators![moderatorIndex!] }; + newUpdatedModerator.role = selectedRole; + newUpdatedChannel!.communityModerators![moderatorIndex!] = newUpdatedModerator; + setCurrentChannel(newUpdatedChannel); + window.messageService?.updateChannel(currentChannel, newUpdatedChannel); + success("Roles updated succesfully"); + } + }; + + const finish = () => { + setStep(0); + setKeyDialog(false); + }; + + useDebounce( + async () => { + if (user.length === 0) { + setAddRoleError(""); + setInProgress(false); + return; + } + + try { + const profileData = await getProfileMetaData(user); + if (profileData && profileData.hasOwnProperty(NOSTRKEY)) { + const alreadyExists = currentChannel?.communityModerators?.some( + (moderator) => moderator.name === profileData.name + ); + if (alreadyExists) { + setAddRoleError("You have already assigned some rule to this user."); + setInProgress(false); + return; + } + const moderator = { + name: user, + pubkey: profileData.nsKey, + role: role + }; + setModerator(moderator); + setAddRoleError(""); + } else { + setAddRoleError("You cannot set this user because this user hasn't joined the chat yet."); + } + } catch (err) { + error(err as string); + } + + setInProgress(false); + }, + 200, + [user, role] + ); + + const communityMenuItems: MenuItem[] = [ + { + label: _t("chat.invite"), + onClick: () => { + copyToClipboard( + `http://localhost:3000/created/${currentChannel?.communityName}?communityid=${currentChannel?.id}` + ); + success("Link copied into clipboard."); + }, + + icon: linkSvg + }, + { + label: _t("chat.leave"), + onClick: () => { + setStep(1); + setKeyDialog(true); + }, + icon: chatLeaveSvg + }, + ...(activeUser?.username === currentChannel?.communityName + ? [ + { + label: _t("chat.edit-roles"), + onClick: handleEditRoles, + icon: editSVG + } + ] + : []), + ...(communityAdmins && communityAdmins.includes(activeUser?.username!) + ? [ + { + label: _t("chat.blocked-users"), + onClick: handleBlockedUsers, + icon: removeUserSvg + } + ] + : []) + ]; + + const communityMenuConfig = { + history: props.history, + label: "", + icon: KebabMenu, + items: communityMenuItems + }; + + const confirmationModal = (actionType: string) => { + return ( + <> +
+
+

Confirmation

+
+
+
+ Are you sure? +
+

+ + +

+ + ); + }; + + const EditRolesModal = () => { + return ( + <> +
+
+

{_t("chat.edit-community-roles")}

+
+
+
+ {inProgress && } +
+ + + {_t("community-role-edit.username")} + + + + + @ + + + + {addRoleError && {addRoleError}} + + + + + {_t("community-role-edit.role")} + + + + {roles.map((r, i) => ( + + ))} + + + +
+ +
+
+ {currentChannel?.communityModerators?.length !== 0 ? ( + <> + + + + + + + + + {currentChannel?.communityModerators && + currentChannel?.communityModerators!.map((moderator, i) => { + return ( + + + + + ); + })} + +
{_t("community.roles-account")}{_t("community.roles-role")}
+ + {" "} + @{moderator.name} + + + {moderator.name === activeUser?.username ? ( +

{moderator.role}

+ ) : ( + ) => + updateRole(e, moderator) + } + > + {roles.map((r, i) => ( + + ))} + + )} +
+ + ) : ( +
+

{_t("chat.no-admin")}

+
+ )} +
+ + ); + }; + + const blockedUsersModal = () => { + return ( + <> +
+

{_t("chat.blocked-users")}

+
+ + {blockedUsers.length !== 0 ? ( + <> + + + + + + + + + {blockedUsers && + blockedUsers.map((user, i) => { + return ( + + + + + ); + })} + +
{_t("community.roles-account")}{_t("chat.action")}
+ + {" "} + + @{user.name} + + + + +
+ + ) : ( +
+

{_t("chat.no-locked-user")}

+
+ )} + + ); + }; + + const successModal = (message: string) => { + return ( + <> +
+
2
+
+
{_t("manage-authorities.success-title")}
+
+ {_t("manage-authorities.success-sub-title")} +
+
+
+
+
+ {message === UNBLOCKUSER ? "User unblock successfully" : ""} +
+
+ + +
+
+ + ); + }; + + const handleConfirmButton = (actionType: string) => { + switch (actionType) { + case LEAVECOMMUNITY: + window?.messageService + ?.updateLeftChannelList([...chat.leftChannelsList!, currentChannel?.id!]) + .then(() => {}) + .finally(() => { + setKeyDialog(false); + setStep(0); + if (from && from === CHATPAGE) { + history?.push("/chats"); + } + }); + break; + case UNBLOCKUSER: + handleChannelUpdate(UNBLOCKUSER); + break; + } + }; + + const handleChannelUpdate = (operationType: string) => { + let updatedMetaData = { + name: currentChannel?.name!, + about: currentChannel?.about!, + picture: "", + communityName: currentChannel?.communityName!, + communityModerators: currentChannel?.communityModerators, + hiddenMessageIds: currentChannel?.hiddenMessageIds, + removedUserIds: currentChannel?.removedUserIds + }; + switch (operationType) { + case ADDROLE: + const updatedRoles = [...(currentChannel?.communityModerators || []), moderator!]; + updatedMetaData.communityModerators = updatedRoles; + break; + case UNBLOCKUSER: + const NewUpdatedRemovedUsers = currentChannel?.removedUserIds?.filter( + (item) => item !== removedUserId + ); + updatedMetaData.removedUserIds = NewUpdatedRemovedUsers; + break; + default: + break; + } + try { + window.messageService?.updateChannel(currentChannel!, updatedMetaData); + setCurrentChannel({ ...currentChannel!, ...updatedMetaData }); + + if (operationType === UNBLOCKUSER) { + setStep(5); + setKeyDialog(true); + setRemovedUserID(""); + } + } catch (err) { + error(_t("chat.error-updating-community")); + } + }; + + return ( + <> + + {keyDialog && ( + + + + {step === 1 && confirmationModal(LEAVECOMMUNITY)} + {step === 2 && EditRolesModal()} + {step === 3 && blockedUsersModal()} + {step === 4 && confirmationModal(UNBLOCKUSER)} + {step === 5 && successModal(UNBLOCKUSER)} + + + )} + + ); +}; + +export default ChatsCommunityDropdownMenu; diff --git a/src/common/components/chats-direct-messages/index.scss b/src/common/components/chats-direct-messages/index.scss new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/common/components/chats-direct-messages/index.tsx b/src/common/components/chats-direct-messages/index.tsx new file mode 100644 index 00000000000..3a3c7837606 --- /dev/null +++ b/src/common/components/chats-direct-messages/index.tsx @@ -0,0 +1,5 @@ +import React from "react"; + +export default function ChatsDirectMessages() { + return
ChatsDirectMessages
; +} diff --git a/src/common/components/chats-dropdown-menu/index.tsx b/src/common/components/chats-dropdown-menu/index.tsx new file mode 100644 index 00000000000..3e82eaa4387 --- /dev/null +++ b/src/common/components/chats-dropdown-menu/index.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { History } from "history"; +import DropDown, { MenuItem } from "../dropdown"; + +import { KebabMenu, chatKeySvg } from "../../img/svg"; +import { _t } from "../../i18n"; +import { DropDownStyle } from "../chat-box/chat-constants"; + +interface Props { + history: History | null; + onManageChatKey?: () => void; +} + +const ChatsDropdownMenu = (props: Props) => { + const onManageChatKey = () => { + const { onManageChatKey } = props; + if (onManageChatKey) { + onManageChatKey(); + } + }; + + const menuItems: MenuItem[] = [ + { + label: "Manage Chat key", + onClick: onManageChatKey, + icon: chatKeySvg + } + ]; + + const menuConfig = { + history: props.history, + label: "", + icon: KebabMenu, + items: menuItems + }; + + return ( + + ); +}; + +export default ChatsDropdownMenu; diff --git a/src/common/components/chats-messages-box/index.scss b/src/common/components/chats-messages-box/index.scss new file mode 100644 index 00000000000..6603c8741ef --- /dev/null +++ b/src/common/components/chats-messages-box/index.scss @@ -0,0 +1,33 @@ +@import "../../../style/vars_mixins"; + +.chats-messages-box { + margin-top: 10px; + + display: grid; + grid-template-rows: repeat(18, 1fr); + grid-template-columns: repeat(1, 1fr); + + @include themify(day) { + box-shadow: 0px -5px 5px rgba(0, 0, 0, 0.1); + } + + @include themify(night) { + box-shadow: 0px -5px 5px $charcoal-grey; + } + width: 60vw; + + .no-chat-select { + height: calc(100vh - 70px); + font-size: 22px; + font-weight: 700; + + .start-chat { + @include themify(day) { + color: #65676b; + } + @include themify(night) { + color: $white; + } + } + } +} diff --git a/src/common/components/chats-messages-box/index.tsx b/src/common/components/chats-messages-box/index.tsx new file mode 100644 index 00000000000..232ecd36f46 --- /dev/null +++ b/src/common/components/chats-messages-box/index.tsx @@ -0,0 +1,65 @@ +import React, { useEffect, useState } from "react"; +import { match } from "react-router"; +import { History } from "history"; +import { Account } from "../../store/accounts/types"; +import { ActiveUser } from "../../store/active-user/types"; +import { Chat } from "../../store/chat/types"; +import { ToggleType, UI } from "../../store/ui/types"; +import { User } from "../../store/users/types"; +import ChatsMessagesHeader from "../chats-messages-header"; +import ChatsMessagesView from "../chats-messages-view"; +import { Global } from "../../store/global/types"; + +import "./index.scss"; +import { NostrKeysType } from "../../helper/chat-utils"; + +interface MatchParams { + filter: string; + name: string; + path: string; + url: string; + username: string; +} + +interface Props { + match: match; + chat: Chat; + global: Global; + users: User[]; + history: History | null; + activeUser: ActiveUser | null; + ui: UI; + activeUserKeys: NostrKeysType; + setActiveUser: (username: string | null) => void; + updateActiveUser: (data?: Account) => void; + deleteUser: (username: string) => void; + toggleUIProp: (what: ToggleType) => void; +} + +export default function ChatsMessagesBox(props: Props) { + const { match } = props; + const username = match.params.username; + + const [maxHeight, setMaxHeight] = useState(0); + + useEffect(() => { + setMaxHeight(window.innerHeight - 68); + }, [typeof window !== "undefined"]); + + return ( + <> +
+ {match.url == "/chats" ? ( +
+

Select a chat or start a new conversation

+
+ ) : ( + <> + + + + )} +
+ + ); +} diff --git a/src/common/components/chats-messages-header/index.scss b/src/common/components/chats-messages-header/index.scss new file mode 100644 index 00000000000..9dc3eb10914 --- /dev/null +++ b/src/common/components/chats-messages-header/index.scss @@ -0,0 +1,67 @@ +@import "../../../style/vars_mixins"; + +.chats-messages-header { + grid-row: span 1; + width: 60vw; + height: 67px; + @include themify(day) { + border-bottom: 1px solid #e7e7e7; + } + @include themify(night) { + border-bottom: 1px solid $charcoal-grey; + } + + .header-content { + .user-info { + display: inline-flex; + padding: 8px 10px; + margin: 5px 0 0 9px; + cursor: pointer; + + .username { + font-size: 21px; + font-family: Helvetica, Arial, sans-serif; + font-weight: 700; + margin: 7px 0 0 12px; + } + &:hover { + border-radius: 10px; + @include themify(day) { + background: #eeeeee; + } + + @include themify(night) { + background: $dusky-blue; + } + } + } + .community-menu { + border: none; + width: 40px; + height: 40px; + justify-content: center; + text-align: center; + display: flex; + margin-top: 14px; + cursor: pointer; + border-radius: 50%; + svg { + @include themify(day) { + color: $charcoal-grey; + } + + @include themify(night) { + color: $white-two; + } + } + &:hover { + @include themify(day) { + background: rgba(29, 155, 240, 0.1); + } + @include themify(night) { + background: $dark-grey-blue; + } + } + } + } +} diff --git a/src/common/components/chats-messages-header/index.tsx b/src/common/components/chats-messages-header/index.tsx new file mode 100644 index 00000000000..e52e92ca71c --- /dev/null +++ b/src/common/components/chats-messages-header/index.tsx @@ -0,0 +1,37 @@ +import React, { useEffect, useState } from "react"; +import { History } from "history"; +import { formattedName, formattedUserName, isChannel } from "../../helper/chat-utils"; +import { _t } from "../../i18n"; +import { useMappedStore } from "../../store/use-mapped-store"; +import ChatsCommunityDropdownMenu from "../chats-community-dropdown-menu"; +import UserAvatar from "../user-avatar"; + +import "./index.scss"; +import { Channel } from "../../../providers/message-provider-types"; +import { CHATPAGE } from "../chat-box/chat-constants"; + +interface Props { + username: string; + history: History | null; +} + +export default function ChatsMessagesHeader(props: Props) { + const { username } = props; + const { chat } = useMappedStore(); + + return ( +
+
+
+ +

{formattedName(username, chat)}

+
+ {isChannel(username) && ( +
+ +
+ )} +
+
+ ); +} diff --git a/src/common/components/chats-messages-view/index.scss b/src/common/components/chats-messages-view/index.scss new file mode 100644 index 00000000000..1a179c4a856 --- /dev/null +++ b/src/common/components/chats-messages-view/index.scss @@ -0,0 +1,20 @@ +@import "../../../style/vars_mixins"; + +.chats-messages-view { + @include themify(day) { + border-bottom: 1px solid $white-three; + } + @include themify(night) { + border-bottom: 1px solid $charcoal-grey; + } + + grid-row: span 16; + // height: 79.5vh; + overflow: scroll; + overflow-x: hidden; + // margin-bottom: 20px; + + a::after { + display: none; + } +} diff --git a/src/common/components/chats-messages-view/index.tsx b/src/common/components/chats-messages-view/index.tsx new file mode 100644 index 00000000000..8dfb28a291f --- /dev/null +++ b/src/common/components/chats-messages-view/index.tsx @@ -0,0 +1,223 @@ +import React, { RefObject, useEffect, useState } from "react"; +import { Link } from "react-router-dom"; +import { Channel, PublicMessage } from "../../../providers/message-provider-types"; +import { + fetchCommunityMessages, + fetchCurrentUserData, + getPrivateKey, + getProfileMetaData, + NostrKeysType +} from "../../helper/chat-utils"; +import { useMappedStore } from "../../store/use-mapped-store"; +import { Global } from "../../store/global/types"; +import ChatsProfileBox from "../chats-profile-box"; + +import "./index.scss"; +import { _t } from "../../i18n"; +import ChatsChannelMessages from "../chats-channel-messages"; +import ChatsDirectMessages from "../chats-direct-messages"; +import { Account } from "../../store/accounts/types"; +import { ToggleType, UI } from "../../store/ui/types"; +import { User } from "../../store/users/types"; +import { ActiveUser } from "../../store/active-user/types"; +import ChatInput from "../chat-input"; +import ChatsScroller from "../chats-scroller"; +import { EmojiPickerStyleProps } from "../chat-box"; + +const EmojiPickerStyle: EmojiPickerStyleProps = { + width: "56.5%", + bottom: "58px", + left: "305px", + marginLeft: "14px", + borderTopLeftRadius: "8px", + borderTopRightRadius: "8px", + borderBottomLeftRadius: "0px" +}; +interface Props { + username: string; + users: User[]; + activeUser: ActiveUser | null; + ui: UI; + global: Global; + setActiveUser: (username: string | null) => void; + updateActiveUser: (data?: Account) => void; + deleteUser: (username: string) => void; + toggleUIProp: (what: ToggleType) => void; +} + +export default function ChatsMessagesView(props: Props) { + const { username, activeUser, global } = props; + + const messagesBoxRef = React.createRef(); + + const { chat } = useMappedStore(); + const [directUser, setDirectUser] = useState(""); + const [publicMessages, setPublicMessages] = useState([]); + const [communityName, setCommunityName] = useState(""); + const [currentChannel, setCurrentChannel] = useState(); + const [activeUserKeys, setActiveUserKeys] = useState(); + const [receiverPubKey, setReceiverPubKey] = useState(""); + const [isScrollToTop, setIsScrollToTop] = useState(false); + const [isScrollToBottom, setIsScrollToBottom] = useState(false); + const [isTop, setIsTop] = useState(false); + + useEffect(() => { + getActiveUserKeys(); + setChannelData(); + }, []); + + useEffect(() => { + console.log("publicMessages", publicMessages); + if (publicMessages.length !== 0) { + scrollToBottom(); + } + }, [publicMessages]); + + useEffect(() => { + if (directUser) { + getReceiverPubKey(); + } + }, [directUser]); + + useEffect(() => { + setChannelData(); + }, [chat.channels]); + + useEffect(() => { + getChannelMessages(); + }, [chat.publicMessages]); + + useEffect(() => { + if (directUser) { + } else if (communityName && currentChannel) { + getChannelMessages(); + } + }, [directUser, communityName, currentChannel]); + + useEffect(() => { + setDirectUser(""); + setCommunityName(""); + setChannelData(); + }, [username]); + + // useEffect(() => { + // if (isTop) { + // fetchPrevMessages(); + // } + // }, [isTop]); + + const getActiveUserKeys = async () => { + const profileData = await getProfileMetaData(props.activeUser?.username!); + const noStrPrivKey = getPrivateKey(props.activeUser?.username!); + const activeUserKeys = { + pub: profileData?.nsKey, + priv: noStrPrivKey + }; + setActiveUserKeys(activeUserKeys); + }; + + const getReceiverPubKey = async () => { + const profileData = await getProfileMetaData(directUser); + + if (profileData?.nsKey) { + setReceiverPubKey(profileData?.nsKey); + } + }; + + const setChannelData = () => { + if (username) { + if (username && username.startsWith("@")) { + setDirectUser(username.replace("@", "")); + } else { + setCommunityName(username); + const channel = chat.channels.find((channel) => channel.communityName === username); + // console.log("channel", channel); + setCurrentChannel(channel); + } + } + }; + + const getChannelMessages = () => { + if (currentChannel) { + const publicMessages = fetchCommunityMessages( + chat.publicMessages, + currentChannel, + currentChannel.hiddenMessageIds + ); + const messages = publicMessages.sort((a, b) => a.created - b.created); + setPublicMessages(messages); + } + }; + + const scrollToBottom = () => { + console.log("Scroll to bottom scliced"); + messagesBoxRef && + messagesBoxRef?.current?.scroll({ + top: messagesBoxRef.current?.scrollHeight, + behavior: "auto" + }); + }; + + const handleScroll = (event: React.UIEvent) => { + var element = event.currentTarget; + // let srollHeight: number = (element.scrollHeight / 100) * 25; + // const isScrollToTop = !isCurrentUser && !isCommunity && element.scrollTop >= srollHeight; + const isScrollToBottom = + element.scrollTop + messagesBoxRef?.current?.clientHeight! < element.scrollHeight - 200; + setIsScrollToBottom(isScrollToBottom); + const scrollerTop = element.scrollTop <= 600 && publicMessages.length > 25; + if (communityName && scrollerTop) { + setIsTop(true); + } else { + setIsTop(false); + } + }; + + return ( + <> +
+ + + + {communityName.length !== 0 ? ( + <> + + + ) : ( + + )} + {isScrollToBottom && ( + + )} +
+ + + ); +} diff --git a/src/common/components/chats-profile-box/index.scss b/src/common/components/chats-profile-box/index.scss new file mode 100644 index 00000000000..32bb46d57a1 --- /dev/null +++ b/src/common/components/chats-profile-box/index.scss @@ -0,0 +1,65 @@ +@import "../../../style/vars_mixins"; + +.chats-profile-box { + .user-profile { + border-bottom-right-radius: 20px; + border-bottom-left-radius: 20px; + min-height: 266px; + + @include themify(day) { + background: $white-three; + } + @include themify(night) { + background: #172b44; + } + + padding: 0 16px 16px 16px; + margin: 0 16px 16px 16px; + + .user-logo { + display: flex; + justify-content: center; + align-items: center; + padding-top: 16px; + cursor: pointer; + } + .user-name { + font-size: 20px; + font-weight: 700; + @include themify(day) { + color: $charcoal-grey; + } + @include themify(night) { + color: $white; + } + } + .about { + text-align: center; + padding: 4px 0; + @include themify(day) { + color: $charcoal-grey; + } + @include themify(night) { + color: $white; + } + } + .joining-info { + justify-content: space-evenly; + } + .created-date { + padding-top: 8px; + font-size: 14px; + @include themify(day) { + color: $charcoal-grey; + } + @include themify(night) { + color: $white; + } + } + .followers { + font-size: 14px; + padding-top: 4px !important; + font-family: New Century Schoolbook; + } + } +} diff --git a/src/common/components/chats-profile-box/index.tsx b/src/common/components/chats-profile-box/index.tsx new file mode 100644 index 00000000000..88cd19ce536 --- /dev/null +++ b/src/common/components/chats-profile-box/index.tsx @@ -0,0 +1,119 @@ +import React, { useEffect, useState } from "react"; +import UserAvatar from "../user-avatar"; + +import "./index.scss"; +import { dateToFormatted } from "../../helper/parse-date"; +import { formatFollowers, formattedUserName } from "../../helper/chat-utils"; +import { _t } from "../../i18n"; +import { useMappedStore } from "../../store/use-mapped-store"; +import { getAccountFull } from "../../api/hive"; +import { getCommunity } from "../../api/bridge"; + +export interface profileData { + joiningData: string; + about: string | undefined; + followers: number | undefined; + name: string; + subscribers: string; + username: string; +} + +interface Props { + username?: string; + isCommunity?: boolean; + isCurrentUser?: boolean; + communityName?: string; + currentUser?: string; +} + +export default function ChatsProfileBox(props: Props) { + const { username, isCommunity, isCurrentUser, communityName, currentUser } = props; + const { chat, activeUser } = useMappedStore(); + + const [profileData, setProfileData] = useState(); + + useEffect(() => { + fetchProfileData(); + }, [username, isCommunity, isCurrentUser, communityName, currentUser]); + + const fetchProfileData = async () => { + if (username && username?.length !== 0) { + if (username?.startsWith("@")) { + const response = await getAccountFull(formattedUserName(username)); + console.log("1", response); + setProfileData({ + joiningData: response.created, + about: response.profile?.about, + followers: response.follow_stats?.follower_count, + name: response.name, + subscribers: _t("chat.followers"), + username: response.name + }); + } else { + const community = await getCommunity(username!, activeUser?.username); + console.log("2", community); + setProfileData({ + joiningData: community?.created_at!, + about: community?.about, + followers: community?.subscribers, + name: community?.title!, + subscribers: _t("chat.subscribers"), + username: community?.name! + }); + } + } else { + if (isCurrentUser && currentUser?.length !== 0) { + const response = await getAccountFull(currentUser!); + console.log("3", response); + setProfileData({ + joiningData: response.created, + about: response.profile?.about, + followers: response.follow_stats?.follower_count, + name: response.name, + subscribers: _t("chat.followers"), + username: response.name + }); + } else { + const community = await getCommunity(communityName!, activeUser?.username); + console.log("4", community); + setProfileData({ + joiningData: community?.created_at!, + about: community?.about, + followers: community?.subscribers, + name: community?.title!, + subscribers: _t("chat.subscribers"), + username: community?.name! + }); + } + } + }; + + return ( +
+
+ {profileData?.joiningData && ( +
+ + + +

{profileData.name}

+ {profileData.about?.length !== 0 && ( +

{profileData.about}

+ )} + +
+

+ {" "} + {_t("chat.joined")} {dateToFormatted(profileData!.joiningData, "LL")} +

+

+ {" "} + {formatFollowers(profileData!.followers)} {profileData.subscribers} +

+
+
+ )} +
+
+ ); +} diff --git a/src/common/components/chats-scroller/index.scss b/src/common/components/chats-scroller/index.scss new file mode 100644 index 00000000000..36f090571a1 --- /dev/null +++ b/src/common/components/chats-scroller/index.scss @@ -0,0 +1,33 @@ +@import "../../../style/vars_mixins"; + +.scroller { + position: sticky; + width: 33px; + height: 33px; + border-radius: 50%; + float: right; + z-index: 10; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + @include themify(day) { + background: #e4e6eb; + border: 1px solid $white-five; + } + @include themify(night) { + background: #0f223a; + } + + svg { + @include themify(day) { + color: $dark-sky-blue; + } + @include themify(night) { + color: $white; + } + + width: 20px; + height: 20px; + } +} \ No newline at end of file diff --git a/src/common/components/chats-scroller/index.tsx b/src/common/components/chats-scroller/index.tsx new file mode 100644 index 00000000000..c32b793f012 --- /dev/null +++ b/src/common/components/chats-scroller/index.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import Tooltip from "../tooltip"; + +import { _t } from "../../i18n"; +import { chevronDownSvgForSlider, chevronUpSvg } from "../../img/svg"; + +import "./index.scss"; + +interface Props { + bodyRef: React.RefObject; + isScrollToTop: boolean; + isScrollToBottom: boolean; + marginRight: string; +} +export default function ChatsScroller(props: Props) { + const { bodyRef, isScrollToTop, isScrollToBottom, marginRight } = props; + + const scrollerClicked = () => { + bodyRef?.current?.scroll({ + top: isScrollToBottom ? bodyRef.current?.scrollHeight : 0, + behavior: "auto" + }); + }; + + return ( + +
+ {isScrollToTop ? chevronUpSvg : chevronDownSvgForSlider} +
+
+ ); +} diff --git a/src/common/components/chats-sidebar/indes.tsx b/src/common/components/chats-sidebar/indes.tsx new file mode 100644 index 00000000000..ef7824875db --- /dev/null +++ b/src/common/components/chats-sidebar/indes.tsx @@ -0,0 +1,345 @@ +import React, { useEffect, useState } from "react"; +import { Form } from "react-bootstrap"; +import { Link } from "react-router-dom"; +import { History } from "history"; +import { match } from "react-router"; +import useDebounce from "react-use/lib/useDebounce"; +import { setNostrkeys } from "../../../providers/message-provider"; +import { Channel } from "../../../providers/message-provider-types"; +import { getAccountReputations } from "../../api/hive"; +import accountReputation from "../../helper/account-reputation"; +import { getCommunities, NostrKeysType } from "../../helper/chat-utils"; +import { _t } from "../../i18n"; +import { arrowBackSvg, chatKeySvg, KebabMenu, syncSvg } from "../../img/svg"; +import { Chat, DirectContactsType } from "../../store/chat/types"; +import { AccountWithReputation } from "../chat-box"; +import ChatsScroller from "../chats-scroller"; +import LinearProgress from "../linear-progress"; +import Tooltip from "../tooltip"; +import UserAvatar from "../user-avatar"; + +import "./index.scss"; +import DropDown, { MenuItem } from "../dropdown"; +import { CHAT, DropDownStyle } from "../chat-box/chat-constants"; +import ChatsDropdownMenu from "../chats-dropdown-menu"; + +interface MatchParams { + filter: string; + name: string; + path: string; + url: string; + username: string; +} +interface Props { + match: match; + channels: Channel[]; + directContacts: DirectContactsType[]; + activeUserKeys: NostrKeysType; + chatPrivKey: string; + chat: Chat; + history: History; + revelPrivateKey: boolean; + inProgressSetter: (d: boolean) => void; + revealPrivateKeySetter: (d: boolean) => void; + resetChat: () => void; +} +export default function ChatsSideBar(props: Props) { + const { + channels, + directContacts, + activeUserKeys, + match, + revelPrivateKey, + chatPrivKey, + resetChat, + inProgressSetter, + revealPrivateKeySetter + } = props; + + const chatsSideBarRef = React.createRef(); + const username = match.params.username; + + const [showDivider, setShowDivider] = useState(false); + const [searchText, setSearchText] = useState(""); + const [inProgress, setInProgress] = useState(false); + const [userList, setUserList] = useState([]); + const [isScrollToTop, setIsScrollToTop] = useState(false); + const [communities, setCommunities] = useState([]); + + // console.log("username in chats sidebar", username); + + useDebounce( + async () => { + if (searchText.length !== 0) { + const resp = await getAccountReputations(searchText, 30); + const sortedByReputation = resp.sort((a, b) => (a.reputation > b.reputation ? -1 : 1)); + setUserList(sortedByReputation); + setInProgress(false); + } + }, + 500, + [searchText] + ); + + // useEffect(() => { + // if (isScrollToTop) { + // // console.log("Hurrah"); + // } + // }, [isScrollToTop]); + + useEffect(() => { + const communities = getCommunities(props.chat.channels, props.chat.leftChannelsList); + setCommunities(communities); + // fetchProfileData(); + }, [props.chat.channels, props.chat.leftChannelsList]); + + // const communities = [ + // { + // id: "dewf3efq3", + // name: "Best Community" + // }, + // { + // id: "kefkjehdewjk", + // name: "Cars Forum" + // }, + // { + // id: "hj3fhkl3flk", + // name: "Hive BlockChaiin" + // }, + // { + // id: "dewddefq3", + // name: "Devsinc" + // }, + // { + // id: "kefksahdewjk", + // name: "Rolustech" + // }, + // { + // id: "hj3fdl3flk", + // name: "Stack Overflow" + // }, + // { + // id: "kefkjehdsewjk", + // name: "Cars Forum" + // }, + // { + // id: "hj3fhkls3flk", + // name: "Hive BlockChain" + // }, + // { + // id: "dewddesfq3", + // name: "Devsinc" + // }, + // { + // id: "kefksahsdewjk", + // name: "Rolustech" + // }, + // { + // id: "hj3fdls3flk", + // name: "Stack Overflow" + // } + // ]; + + const DMS = [ + { + id: "dewf3efq3", + name: "ahmed" + }, + { + id: "kefkjehdewjk", + name: "hive-189310" + }, + { + id: "hj3fhkl3flk", + name: "demo.com" + }, + { + id: "dewddefq3", + name: "good-karma" + }, + { + id: "kefksahdewjk", + name: "mtsaeed" + }, + { + id: "hj3fdl3flk", + name: "testers" + }, + { + id: "dewf3sefq3", + name: "ahmed" + }, + { + id: "kefkjeshdewjk", + name: "hive-189310" + }, + { + id: "hj3fhskl3flk", + name: "demo.com" + }, + { + id: "dewddesfq3", + name: "good-karma" + }, + { + id: "kefksashdewjk", + name: "mtsaeed" + }, + { + id: "hj3fdsl3flk", + name: "hive-198973" + } + ]; + + const handleScroll = (event: React.UIEvent) => { + var element = event.currentTarget; + console.log("element.scrollTop", element.scrollTop); + if (element.scrollTop > 2) { + setShowDivider(true); + } else { + if (showDivider) { + setShowDivider(false); + } + } + let srollHeight: number = (element.scrollHeight / 100) * 25; + const isScrollToTop = element.scrollTop >= srollHeight; + setIsScrollToTop(isScrollToTop); + }; + + const handleRefreshChat = () => { + resetChat(); + setNostrkeys(activeUserKeys); + inProgressSetter(true); + }; + + const handleRevealPrivKey = () => { + if (revelPrivateKey) { + revealPrivateKeySetter(false); + } + }; + + return ( +
+
+
+ {revelPrivateKey && ( + +
revealPrivateKeySetter(false)} + > + {arrowBackSvg} +
+
+ )} + +

Chats

+
+ +
+
+ +

+ {syncSvg} +

+
+
+ {chatPrivKey && ( +
+ { + revealPrivateKeySetter(!revelPrivateKey); + }} + {...props} + /> +
+ )} +
+
+
+ + { + setSearchText(e.target.value); + setInProgress(true); + if (e.target.value.length === 0) { + setInProgress(false); + setUserList([]); + } + }} + /> + +
+ {showDivider &&
} + {inProgress && } +
+ {searchText ? ( +
+ {userList.map((user) => ( + setSearchText("")} + key={user.account} + > +
+ + + + {user.account} + ({accountReputation(user.reputation)}) +
+ + ))} +
+ ) : ( + <> + {communities.length !== 0 &&

Communities

} + {communities.map((channel) => ( + +
+ +
+

{channel.name}

+

hello

+
+
+ + ))} + {directContacts.length !== 0 &&

DMs

} + {directContacts.map((contact) => ( + +
+ +
+

{contact.name}

+

hello

+
+
+ + ))} + + )} +
+ {isScrollToTop && ( + + )} +
+ ); +} diff --git a/src/common/components/chats-sidebar/index.scss b/src/common/components/chats-sidebar/index.scss new file mode 100644 index 00000000000..f717c951c59 --- /dev/null +++ b/src/common/components/chats-sidebar/index.scss @@ -0,0 +1,224 @@ +@import "../../../style/vars_mixins"; + +.chats-sidebar { + @include themify(day) { + background: $white-two; + box-shadow: 0px -5px 5px rgba(0, 0, 0, 0.1); + } + @include themify(night) { + background: $dark-two; + box-shadow: 0px -5px 5px $charcoal-grey; + } + width: 330px; + height: 100%; + margin-top: 10px; + + .chats-title { + margin: 30px 16px 12px 16px; + + .chats-content { + .back-arrow-image { + cursor: pointer; + width: 40px; + height: 40px; + border-radius: 50%; + margin-top: 1px; + + &:hover { + @include themify(day) { + background: rgba(29, 155, 240, 0.1); + } + @include themify(night) { + background: $dark-grey-blue; + } + } + } + } + + .chats { + @include themify(day) { + color: #050505; + } + @include themify(night) { + color: $white; + } + + color: #050505; + font-weight: 700; + font-size: 24px; + padding-top: 8px; + margin-left: 6px; + } + + .chat-actions { + .refresh-button { + margin-top: 2px; + .refresh-svg { + width: 40px; + height: 40px; + justify-content: center; + text-align: center; + border-radius: 50%; + padding-top: 10px; + display: flex; + cursor: pointer; + + svg { + height: 20px; + width: 20px; + } + &:hover { + @include themify(day) { + background: rgba(29, 155, 240, 0.1); + } + @include themify(night) { + background: $dark-grey-blue; + } + } + } + } + .chat-menu { + border: none; + width: 40px; + height: 40px; + justify-content: center; + text-align: center; + display: flex; + margin-top: 3px; + cursor: pointer; + border-radius: 50%; + svg { + @include themify(day) { + color: $charcoal-grey; + } + + @include themify(night) { + color: $white-two; + } + } + &:hover { + @include themify(day) { + background: rgba(29, 155, 240, 0.1); + } + @include themify(night) { + background: $dark-grey-blue; + } + } + } + } + } + + .chats-search { + padding: 0 8px 0 15px; + margin-bottom: 20px; + } + + .divider { + @include themify(day) { + border-bottom: 1px solid #ced0d4; + } + @include themify(night) { + border-bottom: 1px solid $charcoal-grey; + } + } + + .searched-users { + .user-info { + cursor: pointer; + padding: 15px 3px 15px 25px; + } + .user-name { + font-size: 20px; + font-weight: 700; + margin: 7px 5px 0 15px; + } + .user-reputation { + margin: 9px 5px 0 5px; + } + } + + .community-title, + .dm-title { + font-size: 1.3rem; + font-weight: 700; + font-family: Faktum, sans-serif; + margin-bottom: 0; + padding: 15px 0 10px 15px; + } + + .community:hover, + .dm:hover, + .user-info:hover { + @include themify(day) { + background: #eeeeee; + } + + @include themify(night) { + background: $dusky-blue; + } + + // rgba(29, 155, 240, 0.1); + } + + .community, + .dm { + display: flex; + padding: 12.5px 5px 12.5px 13px; + cursor: pointer; + border-radius: 8px; + margin: 0px 10px; + + // @include themify(day) { + // border: 1px solid #f5f5f5; + // } + + // @include themify(night) { + // border: 1px solid $charcoal-grey; + // } + + .community-info, + .dm-info { + .community-name, + .dm-name { + padding: 3px 0 0 25px; + margin-bottom: 0; + font-size: 18px; + font-weight: 700; + } + .community-last-message, + .dm-last-message { + margin-bottom: 0; + padding: 4px 0 0 25px; + font-size: 13px; + font-family: 400; + } + } + &.selected { + @include themify(day) { + background: #ecf3ff; + } + + @include themify(night) { + background: $charcoal-grey; + } + + &:hover { + @include themify(day) { + background: #ecf3ff; + } + + @include themify(night) { + background: $charcoal-grey; + } + } + } + } + + .chats-list { + height: calc(100vh - 220px); + overflow: auto; + a { + color: inherit; + } + } +} diff --git a/src/common/components/dropdown/index.tsx b/src/common/components/dropdown/index.tsx index ab6399aec24..7714cc4fe9e 100644 --- a/src/common/components/dropdown/index.tsx +++ b/src/common/components/dropdown/index.tsx @@ -18,11 +18,6 @@ export interface MenuItem { isStatic?: boolean; } -interface MenuDownStyle { - width: string; - height: string; -} - interface Props { history: History | null; float: "left" | "right" | "none"; diff --git a/src/common/components/emoji-picker/index.tsx b/src/common/components/emoji-picker/index.tsx index edc870fd271..70e55b5263e 100644 --- a/src/common/components/emoji-picker/index.tsx +++ b/src/common/components/emoji-picker/index.tsx @@ -4,6 +4,7 @@ import { FormControl } from "react-bootstrap"; import BaseComponent from "../base"; import SearchBox from "../search-box"; +import { EmojiPickerStyleProps } from "../../components/chat-box"; import { _t } from "../../i18n"; @@ -39,15 +40,7 @@ interface EmojiCacheItem { interface Props { fallback?: (e: string) => void; - style?: { - width: string; - bottom: string; - left: string | number; - marginLeft: string; - borderTopLeftRadius: string; - borderTopRightRadius: string; - borderBottomLeftRadius: string; - }; + style?: EmojiPickerStyleProps; } interface State { diff --git a/src/common/components/join-community-chat-btn/index.tsx b/src/common/components/join-community-chat-btn/index.tsx index 3e981a7903c..39140dbe999 100644 --- a/src/common/components/join-community-chat-btn/index.tsx +++ b/src/common/components/join-community-chat-btn/index.tsx @@ -185,6 +185,7 @@ export default function JoinCommunityChatBtn(props: Props) { chat.leftChannelsList.filter((x) => x !== currentChannel?.id) ); } + setIsCommunityChatJoined(true); window?.messageService?.loadChannel(currentChannel?.id!); setIsCommunityChatJoined(true); }; diff --git a/src/common/components/manage-chat-key/index.tsx b/src/common/components/manage-chat-key/index.tsx index 47919e8f915..d0720c122df 100644 --- a/src/common/components/manage-chat-key/index.tsx +++ b/src/common/components/manage-chat-key/index.tsx @@ -13,6 +13,7 @@ interface Props { } export default function ManageChatKey(props: Props) { + console.log("hello", props); return ( <>
@@ -30,6 +31,7 @@ export default function ManageChatKey(props: Props) {

{ + console.log("SVG clicked"); props.copyPrivateKey(props.noStrPrivKey); }} > diff --git a/src/common/components/navbar/index.tsx b/src/common/components/navbar/index.tsx index 54f04e9cbae..e778c821fe6 100644 --- a/src/common/components/navbar/index.tsx +++ b/src/common/components/navbar/index.tsx @@ -36,7 +36,8 @@ import { sunSvg, upArrowSvg, userOutlineSvg, - walletSvg + walletSvg, + messangerSvg } from "../../img/svg"; import UserAvatar from "../user-avatar"; import { downVotingPower, votingPower } from "../../api/hive"; @@ -518,6 +519,12 @@ export default ({ match, history, setStepOne, setStepTwo, step }: Props) => {

+ setSmVisible(false)}> +
+
{messangerSvg}
+
{_t("user-nav.chats")}
+
+ )} diff --git a/src/common/components/user-nav/_index.scss b/src/common/components/user-nav/_index.scss index 1016cac1afc..2ef2fed069c 100644 --- a/src/common/components/user-nav/_index.scss +++ b/src/common/components/user-nav/_index.scss @@ -39,6 +39,7 @@ .user-wallet, .user-points, .notifications, + .chats, .points { color: $white; display: flex; @@ -71,7 +72,8 @@ } } - .notifications { + .notifications, + .chats { cursor: pointer; position: relative; @@ -117,7 +119,6 @@ } .label { - } .power { @@ -128,10 +129,10 @@ margin-right: 2px; } - .voting, .downVoting { + .voting, + .downVoting { display: flex; align-items: center; - } .voting { @@ -151,7 +152,6 @@ // custom dropdown override .custom-dropdown { - .menu-inner { width: 220px; diff --git a/src/common/components/user-nav/index.tsx b/src/common/components/user-nav/index.tsx index 45cecfb0f4a..3b54616435d 100644 --- a/src/common/components/user-nav/index.tsx +++ b/src/common/components/user-nav/index.tsx @@ -30,7 +30,8 @@ import { bellSvg, bellOffSvg, chevronUpSvg, - rocketSvg + rocketSvg, + messangerSvg } from "../../img/svg"; import { votingPower, downVotingPower } from "../../api/hive"; @@ -282,6 +283,13 @@ class UserNav extends Component { )} + {global.usePrivate && ( + + + {messangerSvg} + + + )}
diff --git a/src/common/helper/chat-utils.ts b/src/common/helper/chat-utils.ts index ef33baad54b..33546643f9d 100644 --- a/src/common/helper/chat-utils.ts +++ b/src/common/helper/chat-utils.ts @@ -1,9 +1,16 @@ import moment from "moment"; import { generatePrivateKey, getPublicKey } from "../../lib/nostr-tools/keys"; -import { Channel } from "../../providers/message-provider-types"; +import { + Channel, + DirectMessage, + Profile, + PublicMessage +} from "../../providers/message-provider-types"; import { getAccountFull } from "../api/hive"; import { updateProfile } from "../api/operations"; +import { GIPHGY } from "../components/chat-box/chat-constants"; import { ActiveUser } from "../store/active-user/types"; +import { Chat, publicMessagesList } from "../store/chat/types"; import * as ls from "../util/local-storage"; export interface NostrKeysType { @@ -91,3 +98,102 @@ export const getCommunities = (channels: Channel[], leftChannels: string[]) => { export const getPrivateKey = (username: string) => { return ls.get(`${username}_nsPrivKey`); }; + +export const formatFollowers = (count: number | undefined) => { + if (count) { + return count >= 1e6 + ? (count / 1e6).toLocaleString() + "M" + : count >= 1e3 + ? (count / 1e3).toLocaleString() + "K" + : count.toLocaleString(); + } + return count; +}; + +export const formattedName = (username: string, chat: Chat) => { + if (username && !username.startsWith("@")) { + const community = chat.channels.find((channel) => channel.communityName === username); + if (community) { + return community.name; + } + } + const replacedUserName = username.replace("@", ""); + return replacedUserName; +}; + +export const formattedUserName = (username: string) => { + if (username && username.startsWith("@")) { + return username.replace("@", ""); + } + return username; +}; + +export const isChannel = (username: string) => { + if (username.startsWith("@")) { + return false; + } + return true; +}; + +export const fetchCommunityMessages = ( + publicMessages: publicMessagesList[], + currentChannel: Channel, + hiddenMessageIds?: string[] +) => { + const hideMessageIds = hiddenMessageIds || currentChannel?.hiddenMessageIds || []; + for (const item of publicMessages) { + if (item.channelId === currentChannel.id) { + const filteredPublicMessages = Object.values(item.PublicMessage).filter( + (message) => !hideMessageIds.includes(message.id) + ); + return filteredPublicMessages; + } + } + return []; +}; + +export const isMessageGif = (content: string) => { + return content.includes(GIPHGY); +}; + +export const isMessageImage = (content: string) => { + return content.includes("https://images.ecency.com"); +}; + +export const getProfileName = (creator: string, profiles: Profile[]) => { + const profile = profiles.find((x) => x.creator === creator); + return profile?.name; +}; + +export const getFormattedDateAndDay = ( + msg: DirectMessage | PublicMessage, + i: number, + messagesList: DirectMessage[] | PublicMessage[] +) => { + const prevMsg = messagesList[i - 1]; + const msgDate = formatMessageDate(msg.created); + const prevMsgDate = prevMsg ? formatMessageDate(prevMsg.created) : null; + if (msgDate !== prevMsgDate) { + return msgDate; + } + return null; +}; + +export const fetchCurrentUserData = async (username: string) => { + const response = await getAccountFull(username); + const { posting_json_metadata } = response; + const profile = JSON.parse(posting_json_metadata!).profile; + const { nsKey } = profile || {}; + console.log("nsKey", nsKey); + return nsKey; +}; + +export const copyToClipboard = (content: string) => { + console.log("copy to clipboard run"); + const textField = document.createElement("textarea"); + textField.innerText = content; + document.body.appendChild(textField); + textField.select(); + document.execCommand("copy"); + textField.remove(); +}; diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts index faaa7fe536f..ff85f7b6338 100644 --- a/src/common/helper/message-service.ts +++ b/src/common/helper/message-service.ts @@ -243,6 +243,7 @@ class MessageService extends TypedEventEmitter { } public async updateLeftChannelList(channelIds: string[]) { + console.log("Update channel list"); const tags = [["d", "left-channel-list"]]; return this.publish(NewKinds.Arbitrary, tags, JSON.stringify(channelIds)); } @@ -446,6 +447,7 @@ class MessageService extends TypedEventEmitter { } public async updateChannel(channel: Channel, meta: Metadata) { + console.log("Update channel Run"); return this.findHealthyRelay(this.pool.seenOn(channel.id) as string[]).then((relay) => { return this.publish(Kind.ChannelMetadata, [["e", channel.id, relay]], JSON.stringify(meta)); }); diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 55e11fb65ea..3e5e2f4a6dd 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -170,7 +170,8 @@ "dark": "dark", "switch-to": "Switch", "vote-power": "Vote Power:", - "boost": "Boost" + "boost": "Boost", + "chats": "Chats" }, "intro": { "title": "Aspire to greatness", diff --git a/src/common/img/svg.tsx b/src/common/img/svg.tsx index f58a105e94c..87163999531 100644 --- a/src/common/img/svg.tsx +++ b/src/common/img/svg.tsx @@ -2253,3 +2253,19 @@ export const chatSvg = ( ); + +export const messangerSvg = ( + + + +); diff --git a/src/common/pages/chats/index.scss b/src/common/pages/chats/index.scss new file mode 100644 index 00000000000..58b1e9b3d87 --- /dev/null +++ b/src/common/pages/chats/index.scss @@ -0,0 +1,16 @@ +@import "../../../style/vars_mixins"; + +.chats-page { + display: flex; + margin-top: 3.5rem; + + .full-page { + width: 100vw; + height: 100vh; + } + .chats-messages-box { + .private-key { + margin-top: 4.5rem; + } + } +} diff --git a/src/common/pages/chats/index.tsx b/src/common/pages/chats/index.tsx new file mode 100644 index 00000000000..91e8669b559 --- /dev/null +++ b/src/common/pages/chats/index.tsx @@ -0,0 +1,203 @@ +import React, { useEffect, useState } from "react"; +import { connect } from "react-redux"; +import { match } from "react-router"; + +import NavBar from "../../components/navbar"; +import NavBarElectron from "../../../desktop/app/components/navbar"; +import { pageMapDispatchToProps, pageMapStateToProps, PageProps } from "../common"; +import ChatsSideBar from "../../components/chats-sidebar/indes"; +import ChatsMessagesBox from "../../components/chats-messages-box"; + +import "./index.scss"; + +import * as ls from "../../util/local-storage"; +import { + copyToClipboard, + createNoStrAccount, + getPrivateKey, + getProfileMetaData, + NostrKeysType, + setProfileMetaData +} from "../../helper/chat-utils"; +import { Button, Spinner } from "react-bootstrap"; +import { setNostrkeys } from "../../../providers/message-provider"; +import ManageChatKey from "../../components/manage-chat-key"; +import Feedback, { success } from "../../components/feedback"; + +interface MatchParams { + filter: string; + name: string; + path: string; + url: string; + username: string; +} + +interface Props extends PageProps { + match: match; +} + +export const Chats = (props: Props) => { + const { channels, directContacts } = props.chat; + + const [marginTop, setMarginTop] = useState(0); + const [activeUserKeys, setActiveUserKeys] = useState(); + const [inProgrss, setInProgress] = useState(true); + const [showSpinner, setShowSpinner] = useState(false); + const [noStrPrivKey, setNoStrPrivKey] = useState(""); + const [revelPrivateKey, setRevealPrivateKey] = useState(false); + + useEffect(() => { + document.body.style.overflow = "hidden"; + + return () => { + document.body.style.overflow = "auto"; + }; + }, []); + + useEffect(() => { + console.log("noStrPrivKey", noStrPrivKey); + }, [noStrPrivKey]); + + useEffect(() => { + handleResize(); + getActiveUserChatKeys(); + const noStrPrivKey = getPrivateKey(props.activeUser?.username!); + setNoStrPrivKey(noStrPrivKey); + }, []); + + useEffect(() => { + setInProgress(false); + }, [activeUserKeys]); + + useEffect(() => { + window.addEventListener("resize", handleResize); + return () => { + window.removeEventListener("resize", handleResize); + }; + }, []); + + useEffect(() => { + if (inProgrss) { + setTimeout(() => { + setInProgress(false); + }, 7000); + } + }, [inProgrss]); + + const handleResize = () => { + const parentElemet = document.getElementById("sticky-container")?.getBoundingClientRect(); + if (parentElemet) { + setMarginTop(parentElemet?.y + parentElemet?.height - 30); + } + }; + + const getActiveUserChatKeys = async () => { + setInProgress(true); + const profileData = await getProfileMetaData(props.activeUser?.username!); + const noStrPrivKey = getPrivateKey(props.activeUser?.username!); + const activeUserKeys = { + pub: profileData?.nsKey, + priv: noStrPrivKey + }; + setActiveUserKeys(activeUserKeys); + }; + + const inProgressSetter = (d: boolean) => { + setInProgress(d); + }; + + const revealPrivateKeySetter = (d: boolean) => { + setRevealPrivateKey(d); + }; + + const handleJoinChat = async () => { + const { resetChat } = props; + setShowSpinner(true); + resetChat(); + const keys = createNoStrAccount(); + ls.set(`${props.activeUser?.username}_nsPrivKey`, keys.priv); + setNoStrPrivKey(keys.priv); + await setProfileMetaData(props.activeUser, keys.pub); + // setHasUserJoinedChat(true); + setNostrkeys(keys); + window.messageService?.updateProfile({ + name: props.activeUser?.username!, + about: "", + picture: "" + }); + setActiveUserKeys(keys); + setShowSpinner(false); + }; + + const copyPrivateKey = () => { + copyToClipboard(noStrPrivKey); + success("Key copied into clipboad"); + }; + + return ( + <> + + {props.global.isElectron ? : } +
+ {inProgrss ? ( +
+

Loading...

+
+ ) : ( + <> + {activeUserKeys?.pub ? ( + noStrPrivKey ? ( + <> + + {revelPrivateKey ? ( +
+ +
+ ) : ( + //if any person has not joined any community then how can he see its message. handle this thing here. + + )} + + ) : ( +

No private key

+ ) + ) : ( +
+
+

You haven't joined the chat yet. Please join the chat to start chatting.

+ +
+
+ )} + + )} +
+ + ); +}; +export default connect(pageMapStateToProps, pageMapDispatchToProps)(Chats); diff --git a/src/common/routes.ts b/src/common/routes.ts index 5e5213b3ca0..260fb9ff960 100644 --- a/src/common/routes.ts +++ b/src/common/routes.ts @@ -41,5 +41,6 @@ export default { PROPOSALS: `/proposals`, PROPOSAL_DETAIL: `/proposals/:id(\\d+)`, PURCHASE: "/purchase", - DECKS: "/decks" + DECKS: "/decks", + CHATS: `/chats/:username?` }; diff --git a/src/config.ts b/src/config.ts index a1392ba4265..a947bb3075d 100644 --- a/src/config.ts +++ b/src/config.ts @@ -1,7 +1,7 @@ /* !!! DO NOT IMPORT config.js TO FRONTEND CODE !!! */ export default { - usePrivate: process.env.USE_PRIVATE || "0", // "1" | "0" + usePrivate: process.env.USE_PRIVATE || "1", // "1" | "0" hsClientSecret: process.env.HIVESIGNER_SECRET || "", // when USE_PRIVATE=0 and HIVESIGNER client secret must be provided hsClientId: process.env.HIVESIGNER_ID || "ecency.app", // When USE_PRIVATE=0, this is used to override which user will do posting authority on behalf of the user via hive signer redisPass: process.env.REDIS_HOST_PASSWORD From 5868973cd983ccb5d1f89aad0fef9ef47df7a582 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Mon, 4 Sep 2023 19:04:40 +0500 Subject: [PATCH 052/179] Make a separate component for confirmation and to show direct messages --- src/common/components/chat-box/index.scss | 370 +++++----- src/common/components/chat-box/index.tsx | 678 ++++-------------- src/common/components/chat-input/index.tsx | 204 +++--- .../chats-channel-messages/index.scss | 60 +- .../chats-channel-messages/index.tsx | 625 ++++++++++------ .../chats-community-dropdown-menu/index.tsx | 40 +- .../chats-confirmation-modal/index.tsx | 64 ++ .../chats-direct-messages/index.scss | 160 +++++ .../chats-direct-messages/index.tsx | 171 ++++- .../components/chats-messages-box/index.tsx | 63 +- .../chats-messages-header/index.tsx | 11 +- .../components/chats-messages-view/index.scss | 4 + .../components/chats-messages-view/index.tsx | 122 +++- src/common/components/chats-sidebar/indes.tsx | 20 +- .../components/chats-sidebar/index.scss | 8 + src/common/helper/chat-utils.ts | 37 +- src/common/pages/chats/index.tsx | 13 +- 17 files changed, 1502 insertions(+), 1148 deletions(-) create mode 100644 src/common/components/chats-confirmation-modal/index.tsx diff --git a/src/common/components/chat-box/index.scss b/src/common/components/chat-box/index.scss index 3cdb488c182..cbef7e23d98 100644 --- a/src/common/components/chat-box/index.scss +++ b/src/common/components/chat-box/index.scss @@ -177,7 +177,6 @@ } &.community { - padding-bottom: 10px; height: 412px; margin-bottom: 8px; @include themify(day) { @@ -404,171 +403,171 @@ justify-content: center; margin-top: 14%; } - .custom-divider { - margin: 12px 24px 0px 24px; - font-size: 0.7em; - color: $dark; - .custom-divider-text { - display: flex; - justify-content: center; - padding: 0 8px; - } - } - .message { - display: flex; - .user-img { - padding: 18px 8px 8px 16px; - } - .community-user-img { - padding: 8px 8px 8px 16px; - .user-avatar.medium { - cursor: pointer; - } - } - .user-info { - padding-top: 8px; - .user-msg-time { - margin: 0; - color: rgb(138, 141, 145); - font-weight: 400; - font-size: 12px; - margin-left: 4px; - margin-bottom: 2px; - .username-community { - margin-right: 8px; - } - } - .receiver-message-content { - @include themify(day) { - background: #e4e6eb; - } - @include themify(night) { - background: #cee2ff; - } - - &.gif { - background: none; - img { - max-width: 100%; - } - } - - &.chat-image { - background: none; - img { - max-width: 100%; - } - } - color: #050505; - max-width: 290px; - word-wrap: break-word; - padding: 10px; - border-radius: 0px 10px 10px; - } - } - } - .sender { - margin-bottom: 0.17rem; - .sender-message-time { - color: rgb(138, 141, 145); - display: flex; - font-weight: 400; - justify-content: flex-end; - font-size: 12px; - margin: 0; - margin-right: 18px; - margin-bottom: 2px; - } - .sender-message { - // max-width: 320px; - margin-left: auto; - - display: flex; - justify-content: flex-end; - margin-right: 17px; - - .resend-svg { - margin: 5px 15px 0 0; - cursor: pointer; - svg { - @include themify(day) { - fill: $light-black; - } - @include themify(night) { - fill: $white; - } - } - } - - .failed-svg { - margin: 8px 0 0 5px; - svg { - width: 16px; - height: 16px; - fill: $red !important; - } - } - - &.sending { - margin-right: 5px; - } - &.failed { - margin-right: 7px; - } - - .sender-message-content { - border-radius: 10px 10px 0px; - max-width: 290px; - word-wrap: break-word; - margin-bottom: 0; - color: $charcoal-grey; - font-size: 16px; - font-weight: 400; - padding: 8px 12px 8px 12px; - a { - text-decoration: underline; - color: white; - } - @include themify(day) { - background-color: rgb(0, 132, 255); - color: $white; - } - @include themify(night) { - background: $charcoal-grey; - color: $white; - } - &.gif { - background: none; - img { - max-width: 100%; - } - } - - &.chat-image { - background: none; - img { - max-width: 100%; - } - } - } - } - } - .hide-msg { - margin-right: 15px; - margin-top: 5px; - svg { - height: 14px; - width: 14px; - } - .hide-msg-svg { - cursor: pointer; - margin-bottom: 0; - } - &.receiver { - margin-top: 33px; - margin-left: 10px; - } - } + // .custom-divider { + // margin: 12px 24px 0px 24px; + // font-size: 0.7em; + // color: $dark; + // .custom-divider-text { + // display: flex; + // justify-content: center; + // padding: 0 8px; + // } + // } + // .message { + // display: flex; + // .user-img { + // padding: 18px 8px 8px 16px; + // } + // .community-user-img { + // padding: 8px 8px 8px 16px; + // .user-avatar.medium { + // cursor: pointer; + // } + // } + // .user-info { + // padding-top: 8px; + // .user-msg-time { + // margin: 0; + // color: rgb(138, 141, 145); + // font-weight: 400; + // font-size: 12px; + // margin-left: 4px; + // margin-bottom: 2px; + // .username-community { + // margin-right: 8px; + // } + // } + // .receiver-message-content { + // @include themify(day) { + // background: #e4e6eb; + // } + // @include themify(night) { + // background: #cee2ff; + // } + + // &.gif { + // background: none; + // img { + // max-width: 100%; + // } + // } + + // &.chat-image { + // background: none; + // img { + // max-width: 100%; + // } + // } + // color: #050505; + // max-width: 290px; + // word-wrap: break-word; + // padding: 10px; + // border-radius: 0px 10px 10px; + // } + // } + // } + // .sender { + // margin-bottom: 0.17rem; + // .sender-message-time { + // color: rgb(138, 141, 145); + // display: flex; + // font-weight: 400; + // justify-content: flex-end; + // font-size: 12px; + // margin: 0; + // margin-right: 18px; + // margin-bottom: 2px; + // } + // .sender-message { + // // max-width: 320px; + // margin-left: auto; + + // display: flex; + // justify-content: flex-end; + // margin-right: 17px; + + // .resend-svg { + // margin: 5px 15px 0 0; + // cursor: pointer; + // svg { + // @include themify(day) { + // fill: $light-black; + // } + // @include themify(night) { + // fill: $white; + // } + // } + // } + + // .failed-svg { + // margin: 8px 0 0 5px; + // svg { + // width: 16px; + // height: 16px; + // fill: $red !important; + // } + // } + + // &.sending { + // margin-right: 5px; + // } + // &.failed { + // margin-right: 7px; + // } + + // .sender-message-content { + // border-radius: 10px 10px 0px; + // max-width: 290px; + // word-wrap: break-word; + // margin-bottom: 0; + // color: $charcoal-grey; + // font-size: 16px; + // font-weight: 400; + // padding: 8px 12px 8px 12px; + // a { + // text-decoration: underline; + // color: white; + // } + // @include themify(day) { + // background-color: rgb(0, 132, 255); + // color: $white; + // } + // @include themify(night) { + // background: $charcoal-grey; + // color: $white; + // } + // &.gif { + // background: none; + // img { + // max-width: 100%; + // } + // } + + // &.chat-image { + // background: none; + // img { + // max-width: 100%; + // } + // } + // } + // } + // } + // .hide-msg { + // margin-right: 15px; + // margin-top: 5px; + // svg { + // height: 14px; + // width: 14px; + // } + // .hide-msg-svg { + // cursor: pointer; + // margin-bottom: 0; + // } + // &.receiver { + // margin-top: 33px; + // margin-left: 10px; + // } + // } } } @@ -659,23 +658,23 @@ } } -.profile-box { - padding: 15px; - .profile-box-content { - .user-avatar.large { - margin-bottom: 20px; - } - .profile-name { - margin-bottom: 10px; - font-size: 18px; - font-weight: 800; - } - .profile-box-buttons { - margin-bottom: 15px; - padding: 0 5px; - } - } -} +// .profile-box { +// padding: 15px; +// .profile-box-content { +// .user-avatar.large { +// margin-bottom: 20px; +// } +// .profile-name { +// margin-bottom: 10px; +// font-size: 18px; +// font-weight: 800; +// } +// .profile-box-buttons { +// margin-bottom: 15px; +// padding: 0 5px; +// } +// } +// } .chat-button { position: fixed; @@ -690,8 +689,3 @@ .medium-zoom-image--opened { z-index: 999; } - -.medium-zoom--opened .medium-zoom-overlay { - // opacity: 0; - // background: white; -} diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index f5cd4939d42..4054f7ec7dd 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -1,33 +1,20 @@ -import React, { RefObject, useEffect, useRef, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import useDebounce from "react-use/lib/useDebounce"; import { useLocation } from "react-router"; import { History } from "history"; -import { - Button, - Col, - Form, - FormControl, - InputGroup, - Modal, - OverlayTrigger, - Popover, - Row, - Spinner -} from "react-bootstrap"; +import { Button, Form, InputGroup, Modal, Spinner } from "react-bootstrap"; import { Link } from "react-router-dom"; -import mediumZoom, { Zoom } from "medium-zoom"; import { ActiveUser } from "../../store/active-user/types"; import { Chat, DirectContactsType } from "../../store/chat/types"; -import { Community, ROLES } from "../../store/communities/types"; -import { Global, Theme } from "../../store/global/types"; +import { Community } from "../../store/communities/types"; +import { Global } from "../../store/global/types"; import { User } from "../../store/users/types"; import { ToggleType, UI } from "../../store/ui/types"; import { Account } from "../../store/accounts/types"; import { Channel, ChannelUpdate, - communityModerator, DirectMessage, PublicMessage } from "../../../providers/message-provider-types"; @@ -37,12 +24,13 @@ import UserAvatar from "../user-avatar"; import LinearProgress from "../linear-progress"; import { error, success } from "../feedback"; import { setNostrkeys } from "../../../providers/message-provider"; -import DropDown, { MenuItem } from "../dropdown"; -import FollowControls from "../follow-controls"; import OrDivider from "../or-divider"; import ManageChatKey from "../manage-chat-key"; import ChatInput from "../chat-input"; import ChatsProfileBox from "../chats-profile-box"; +import ChatsDropdownMenu from "../chats-dropdown-menu"; +import ChatsCommunityDropdownMenu from "../chats-community-dropdown-menu"; +import ChatsChannelMessages from "../chats-channel-messages"; import { addMessageSVG, @@ -51,40 +39,25 @@ import { arrowBackSvg, chevronUpSvg, chevronDownSvgForSlider, - linkSvg, - KebabMenu, - chatLeaveSvg, - editSVG, keySvg, - syncSvg, - hideSvg, - removeUserSvg, - resendMessageSvg, - failedMessageSvg, - chatKeySvg + syncSvg } from "../../img/svg"; import { - DropDownStyle, NOSTRKEY, - ADDROLE, HIDEMESSAGE, BLOCKUSER, - LEAVECOMMUNITY, UNBLOCKUSER, NEWCHATACCOUNT, CHATIMPORT, RESENDMESSAGE, EmojiPickerStyle, - GifPickerStyle, - CHAT + GifPickerStyle } from "./chat-constants"; import { getPublicKey } from "../../../lib/nostr-tools/keys"; import { createNoStrAccount, - formatMessageDate, - formatMessageTime, getProfileMetaData, NostrKeysType, setProfileMetaData, @@ -92,12 +65,10 @@ import { getCommunities, getPrivateKey, fetchCommunityMessages, - isMessageGif, - isMessageImage, - getProfileName + getCommunityLastMessage, + getDirectLastMessage } from "../../helper/chat-utils"; import * as ls from "../../util/local-storage"; -import { renderPostBody } from "@ecency/render-helper"; import accountReputation from "../../helper/account-reputation"; import { _t } from "../../i18n"; @@ -105,8 +76,7 @@ import { getAccountFull, getAccountReputations } from "../../api/hive"; import { getCommunity } from "../../api/bridge"; import "./index.scss"; -import ChatsDropdownMenu from "../chats-dropdown-menu"; -import ChatsCommunityDropdownMenu from "../chats-community-dropdown-menu"; +import ChatsDirectMessages from "../chats-direct-messages"; export interface profileData { joiningData: string; @@ -147,17 +117,13 @@ interface Props { deleteDirectMessage: (peer: string, msgId: string) => void; } -let zoom: Zoom | null = null; - export default function ChatBox(props: Props) { const routerLocation = useLocation(); const prevPropsRef = useRef(props); - const popoverRef = useRef(null); const chatBodyDivRef = React.createRef(); const [expanded, setExpanded] = useState(false); const [currentUser, setCurrentUser] = useState(""); const [isCurrentUser, setIsCurrentUser] = useState(false); - const [dmMessage, setDmMessage] = useState(""); const [isScrollToTop, setIsScrollToTop] = useState(false); const [isScrollToBottom, setIsScrollToBottom] = useState(false); const [showSearchUser, setShowSearchUser] = useState(false); @@ -183,13 +149,9 @@ export default function ChatBox(props: Props) { const [addRoleError, setAddRoleError] = useState(""); const [noStrPrivKey, setNoStrPrivKey] = useState(""); const [chatPrivKey, setChatPrivkey] = useState(""); - const [hoveredMessageId, setHoveredMessageId] = useState(""); - const [privilegedUsers, setPrivilegedUsers] = useState([]); const [hiddenMsgId, setHiddenMsgId] = useState(""); const [removedUserId, setRemovedUserID] = useState(""); - const [removedUsers, setRemovedUsers] = useState([]); const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); - const [communityAdmins, setCommunityAdmins] = useState([]); const [isTop, setIsTop] = useState(false); const [hasMore, setHasMore] = useState(true); const [resendMessage, setResendMessage] = useState(); @@ -197,6 +159,7 @@ export default function ChatBox(props: Props) { const [revelPrivateKey, setRevealPrivateKey] = useState(false); const [innerWidth, setInnerWidth] = useState(0); const [isChatPage, setIsChatPage] = useState(false); + const [refreshChat, setRefreshChat] = useState(false); useEffect(() => { console.log("Chat in store", props.chat); @@ -262,53 +225,6 @@ export default function ChatBox(props: Props) { } }, [isTop]); - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - const clickedElement = event.target as HTMLElement; - - if (!popoverRef.current) { - return; - } - - const isAvatarClicked = - clickedElement.classList.contains("user-avatar") && - clickedElement.classList.contains("medium"); - if ( - popoverRef.current && - !popoverRef.current?.contains(event.target as Node) && - !isAvatarClicked - ) { - setClickedMessage(""); - } - }; - - if (chatBodyDivRef.current) { - chatBodyDivRef.current.addEventListener("mousedown", handleClickOutside); - } - - return () => { - if (chatBodyDivRef.current) { - chatBodyDivRef.current.removeEventListener("mousedown", handleClickOutside); - } - }; - }, [clickedMessage]); - - useEffect(() => { - if (props.chat.channels.length !== 0 || props.chat.directContacts.length !== 0) { - setInProgress(false); - } - }, [props.chat]); - - useEffect(() => { - currentChannel?.communityModerators && getPrivilegedUsers(currentChannel?.communityModerators!); - currentChannel?.removedUserIds && setRemovedUsers(currentChannel.removedUserIds); - currentChannel && scrollerClicked(); - if (removedUsers) { - const removed = removedUsers.includes(activeUserKeys?.pub!); - setIsActiveUserRemoved(removed); - } - }, [currentChannel, removedUsers]); - useEffect(() => { if (window.messageService) { setHasUserJoinedChat(true); @@ -317,7 +233,7 @@ export default function ChatBox(props: Props) { } setTimeout(() => { if (props.chat.channels.length === 0 && props.chat.directContacts.length === 0) { - setInProgress(false); + setRefreshChat(false); } }, 5000); }, [typeof window !== "undefined" && window?.messageService]); @@ -336,9 +252,7 @@ export default function ChatBox(props: Props) { useEffect(() => { const prevProps = prevPropsRef.current; - if (prevProps.global.theme !== props.global.theme) { - setBackground(); - } + if (prevProps.activeUser?.username !== props.activeUser?.username) { setIsCommunity(false); setIsCurrentUser(false); @@ -350,7 +264,7 @@ export default function ChatBox(props: Props) { useEffect(() => { if (directMessagesList.length !== 0 || publicMessages.length !== 0) { - zoomInitializer(); + // zoomInitializer(); } if (directMessagesList.length !== 0) { scrollerClicked(); @@ -375,7 +289,7 @@ export default function ChatBox(props: Props) { useEffect(() => { if (isCurrentUser) { - zoomInitializer(); + // zoomInitializer(); scrollerClicked(); } else { scrollerClicked(); @@ -447,24 +361,6 @@ export default function ChatBox(props: Props) { [searchtext] ); - const getPrivilegedUsers = (communityModerators: communityModerator[]) => { - const privilegedRoles = ["owner", "admin", "mod"]; - const communityAdminRoles = ["owner", "admin"]; - - const privilegedUsers = communityModerators.filter((user) => - privilegedRoles.includes(user.role) - ); - const communityAdmins = communityModerators.filter((user) => - communityAdminRoles.includes(user.role) - ); - - const privilegedUserNames = privilegedUsers.map((user) => user.name); - const communityAdminNames = communityAdmins.map((user) => user.name); - - setPrivilegedUsers(privilegedUserNames); - setCommunityAdmins(communityAdminNames); - }; - const handleRouterChange = () => { if (routerLocation.pathname.match("/chats")) { setShow(false); @@ -509,22 +405,6 @@ export default function ChatBox(props: Props) { setInnerWidth(window.innerWidth); }; - const zoomInitializer = () => { - const elements: HTMLElement[] = [ - ...document.querySelectorAll(".chat-image img") - ].filter((x) => x.parentNode?.nodeName !== "A"); - zoom = mediumZoom(elements); - setBackground(); - }; - - const setBackground = () => { - if (props.global.theme === Theme.day) { - zoom?.update({ background: "#ffffff" }); - } else { - zoom?.update({ background: "#131111" }); - } - }; - const fetchDirectMessages = (peer: string) => { for (const item of props.chat.directMessages) { if (item.peer === peer) { @@ -611,7 +491,7 @@ export default function ChatBox(props: Props) { resetChat(); handleBackArrowSvg(); if (getPrivateKey(props.activeUser?.username!)) { - setInProgress(true); + setRefreshChat(true); const keys = { pub: activeUserKeys?.pub!, priv: getPrivateKey(props.activeUser?.username!) @@ -648,60 +528,11 @@ export default function ChatBox(props: Props) { ); - const getFormattedDateAndDay = (msg: DirectMessage | PublicMessage, i: number) => { - const prevMsg = isCurrentUser ? directMessagesList[i - 1] : publicMessages[i - 1]; - const msgDate = formatMessageDate(msg.created); - const prevMsgDate = prevMsg ? formatMessageDate(prevMsg.created) : null; - if (msgDate !== prevMsgDate) { - return ( -
- {msgDate} -
- ); - } - return <>; - }; - - const getLastMessage = (pubkey: string) => { - const msgsList = fetchDirectMessages(pubkey!); - const messages = msgsList.sort((a, b) => a.created - b.created); - const lastMessage = messages.slice(-1); - return lastMessage[0]?.content; - }; - const communityClicked = (community: string, name: string) => { setIsCommunity(true); setCommunityName(community); }; - const sendDM = (name: string, pubkey: string) => { - if (dmMessage) { - window.messageService?.sendDirectMessage(pubkey, dmMessage); - - setIsCurrentUser(true); - setCurrentUser(name); - setIsCommunity(false); - setCommunityName(""); - setClickedMessage(""); - setDmMessage(""); - } - }; - - const handleDMChange = (e: React.ChangeEvent) => { - setDmMessage(e.target.value); - }; - - const handleImageClick = (msgId: string, pubkey: string) => { - if (clickedMessage === msgId) { - popoverRef.current = null; - setClickedMessage(""); - } else { - popoverRef.current = null; - setClickedMessage(msgId); - setRemovedUserID(pubkey); - } - }; - const copyToClipboard = (content: string) => { const textField = document.createElement("textarea"); textField.innerText = content; @@ -741,20 +572,6 @@ export default function ChatBox(props: Props) { setKeyDialog(false); }; - const handleResendMessage = () => { - setKeyDialog(false); - setStep(0); - if (resendMessage) { - if (currentChannel) { - props.deletePublicMessage(currentChannel.id, resendMessage.id); - window?.messageService?.sendPublicMessage(currentChannel!, resendMessage.content, [], ""); - } else if (isCurrentUser && receiverPubKey) { - props.deleteDirectMessage(receiverPubKey, resendMessage.id); - window.messageService?.sendDirectMessage(receiverPubKey, resendMessage.content); - } - } - }; - const handleConfirmButton = (actionType: string) => { switch (actionType) { case HIDEMESSAGE: @@ -776,7 +593,7 @@ export default function ChatBox(props: Props) { }); break; case RESENDMESSAGE: - handleResendMessage(); + // handleResendMessage(); break; default: break; @@ -998,7 +815,12 @@ export default function ChatBox(props: Props) { )} {isCommunity && (
- +
)}{" "} {!isCommunity && !isCurrentUser && noStrPrivKey && ( @@ -1025,9 +847,8 @@ export default function ChatBox(props: Props) {
- + {inProgress && } {inProgress && !isCommunity && !isCurrentUser && } -
{_t("chat.not-joined")}

)} - {isCurrentUser - ? directMessagesList.map((msg, i) => { - const dayAndMonth = getFormattedDateAndDay(msg, i); - let renderedPreview = renderPostBody( - msg.content, - false, - props.global.canUseWebp - ); - - renderedPreview = renderedPreview.replace(/]*>/g, ""); - renderedPreview = renderedPreview.replace(/<\/p>/g, ""); - - const isGif = isMessageGif(msg.content); - - const isImage = isMessageImage(msg.content); - - return ( - - {dayAndMonth} - {msg.creator !== activeUserKeys?.pub ? ( -
-
- - - - - -
-
-

- {formatMessageTime(msg.created)} -

-
-
-
- ) : ( -
-

- {formatMessageTime(msg.created)} -

-
- {msg.sent === 2 && ( - - { - setKeyDialog(true); - setStep(11); - setResendMessage(msg); - }} - > - {resendMessageSvg} - - - )} -
- {msg.sent === 0 && ( - - - - )} - {msg.sent === 2 && ( - - {failedMessageSvg} - - )} -
-
- )} - - ); - }) - : publicMessages.map((pMsg, i) => { - const dayAndMonth = getFormattedDateAndDay(pMsg, i); - let renderedPreview = renderPostBody( - pMsg.content, - false, - props.global.canUseWebp - ); - - renderedPreview = renderedPreview.replace(/]*>/g, ""); - renderedPreview = renderedPreview.replace(/<\/p>/g, ""); - - const isGif = isMessageGif(pMsg.content); - - const isImage = isMessageImage(pMsg.content); - - const name = getProfileName(pMsg.creator, props.chat.profiles); - - const popover = ( - - -
} - > -
-
- -
- -

- {`@${name!}`} -

-
- - - {communityAdmins.includes(props.activeUser?.username!) && ( - <> - {currentChannel?.removedUserIds?.includes( - pMsg.creator - ) ? ( - <> - - - ) : ( - <> - - - )} - - )} -
- -
{ - e.preventDefault(); - e.stopPropagation(); - sendDM(name!, pMsg.creator); - }} - > - - - -
-
-
-
-
- ); - - return ( - - {dayAndMonth} - {pMsg.creator !== activeUserKeys?.pub ? ( -
setHoveredMessageId(pMsg.id)} - onMouseLeave={() => setHoveredMessageId("")} - > -
- handleImageClick(pMsg.id, pMsg.creator)} - > - - - - -
- -
-

- {name} - {formatMessageTime(pMsg.created)} -

-
-
- {hoveredMessageId === pMsg.id && - privilegedUsers.includes(props.activeUser?.username!) && ( - -
-

{ - setClickedMessage(""); - setKeyDialog(true); - setStep(5); - setHiddenMsgId(pMsg.id); - }} - > - {hideSvg} -

-
-
- )} -
- ) : ( -
setHoveredMessageId(pMsg.id)} - onMouseLeave={() => setHoveredMessageId("")} - > -

- {formatMessageTime(pMsg.created)} -

-
- {hoveredMessageId === pMsg.id && !isActveUserRemoved && ( - -
-

{ - setClickedMessage(""); - setKeyDialog(true); - setStep(5); - setHiddenMsgId(pMsg.id); - }} - > - {hideSvg} -

-
-
- )} - {pMsg.sent === 2 && ( - - { - setKeyDialog(true); - setStep(11); - setResendMessage(pMsg); - }} - > - {resendMessageSvg} - - - )} -
- {pMsg.sent === 0 && ( - - - - )} - {pMsg.sent === 2 && ( - - {failedMessageSvg} - - )} -
-
- )} - - ); - })} + {isCurrentUser ? ( + // directMessagesList.map((msg, i) => { + // const dayAndMonth = getFormattedDateAndDay(msg, i); + // let renderedPreview = renderPostBody( + // msg.content, + // false, + // props.global.canUseWebp + // ); + + // renderedPreview = renderedPreview.replace(/]*>/g, ""); + // renderedPreview = renderedPreview.replace(/<\/p>/g, ""); + + // const isGif = isMessageGif(msg.content); + + // const isImage = isMessageImage(msg.content); + + // return ( + // + // {dayAndMonth} + // {msg.creator !== activeUserKeys?.pub ? ( + //
+ //
+ // + // + // + // + // + //
+ //
+ //

+ // {formatMessageTime(msg.created)} + //

+ //
+ //
+ //
+ // ) : ( + //
+ //

+ // {formatMessageTime(msg.created)} + //

+ //
+ // {msg.sent === 2 && ( + // + // { + // setKeyDialog(true); + // setStep(11); + // setResendMessage(msg); + // }} + // > + // {resendMessageSvg} + // + // + // )} + //
+ // {msg.sent === 0 && ( + // + // + // + // )} + // {msg.sent === 2 && ( + // + // {failedMessageSvg} + // + // )} + //
+ //
+ // )} + // + // ); + // }) + + ) : ( + + )}
) : showSearchUser ? ( @@ -1440,6 +1042,7 @@ export default function ChatBox(props: Props) { <> {(props.chat.directContacts.length !== 0 || (props.chat.channels.length !== 0 && communities.length !== 0)) && + !refreshChat && noStrPrivKey ? ( {props.chat.channels.length !== 0 && communities.length !== 0 && ( @@ -1468,6 +1071,12 @@ export default function ChatBox(props: Props) {

{channel.name}

+

+ {getCommunityLastMessage( + channel.id, + props.chat.publicMessages + )} +

); @@ -1490,7 +1099,9 @@ export default function ChatBox(props: Props) {
userClicked(user.name)}>

{user.name}

-

{getLastMessage(user.pubkey)}

+

+ {getDirectLastMessage(user.pubkey, props.chat.directMessages)} +

); @@ -1553,8 +1164,10 @@ export default function ChatBox(props: Props) {
- ) : inProgress ? ( -

{_t("chat.loading")}

+ ) : refreshChat ? ( +
+ +
) : ( <>

{_t("chat.no-chat")}

@@ -1599,7 +1212,6 @@ export default function ChatBox(props: Props) {

)}
- {inProgress && } {(isCurrentUser || isCommunity) && ( contact.name === currentUser) && isCurrentUser ) { + console.log("Contact has been published"); window.messageService?.publishContacts(currentUser, receiverPubKey); } }; @@ -178,109 +180,111 @@ export default function ChatInput(props: Props) { }; return ( -
- showEmojiPicker && setShowEmojiPicker(false)}> -
-
- -
setShowEmojiPicker(!showEmojiPicker)}> - {emoticonHappyOutlineSvg} + <> +
+ showEmojiPicker && setShowEmojiPicker(false)}> +
+
+ +
setShowEmojiPicker(!showEmojiPicker)}> + {emoticonHappyOutlineSvg} +
+
+ {showEmojiPicker && ( + { + handleEmojiSelection(e); + }} + /> + )} +
+
+
+ + {message.length === 0 && ( + + shGif && setShGif(false)}> +
+
+ +
+ {" "} + {gifIcon} +
+
+ {shGif && ( + { + setShGif(gifState!); + }} + fallback={(e) => { + handleGifSelection(e); + }} + /> + )} +
- - {showEmojiPicker && ( - { - handleEmojiSelection(e); +
+ + +
) => { + e.stopPropagation(); + const el = fileInput.current; + if (el) el.click(); }} - /> - )} -
-
- - - {message.length === 0 && ( - - shGif && setShGif(false)}> -
-
- -
- {" "} - {gifIcon} -
-
- {shGif && ( - { - setShGif(gifState!); - }} - fallback={(e) => { - handleGifSelection(e); - }} - /> - )} + > +
{chatBoxImageSvg}
-
-
- - -
) => { - e.stopPropagation(); - const el = fileInput.current; - if (el) el.click(); - }} + + + + + )} + +
{ + e.preventDefault(); + e.stopPropagation(); + sendMessage(); + }} + style={{ width: "100%" }} + > + + + -
{chatBoxImageSvg}
-
-
- - -
- )} - - { - e.preventDefault(); - e.stopPropagation(); - sendMessage(); - }} - style={{ width: "100%" }} - > - - - - {messageSendSvg} - - - -
+ {messageSendSvg} + + + +
+ ); } diff --git a/src/common/components/chats-channel-messages/index.scss b/src/common/components/chats-channel-messages/index.scss index 3f55b168d2c..67da4d70435 100644 --- a/src/common/components/chats-channel-messages/index.scss +++ b/src/common/components/chats-channel-messages/index.scss @@ -2,9 +2,9 @@ .channel-messages { padding-bottom: 15px; - .date-and-month { - margin-bottom: 0; - color: $dark; + .day-and-month { + margin-bottom: 1rem; + font-size: 14px; } .profile-box { padding: 15px; @@ -18,13 +18,12 @@ font-weight: 800; } .profile-box-buttons { - margin-bottom: 15px; padding: 0 5px; } } } - .message { + .receiver { display: flex; .user-img { padding: 18px 8px 8px 16px; @@ -49,34 +48,37 @@ margin-right: 8px; } } - .receiver-message-content { - @include themify(day) { - background: #e4e6eb; - } - @include themify(night) { - background: #cee2ff; - } + .receiver-messag { + display: flex; + .receiver-message-content { + @include themify(day) { + background: #e4e6eb; + } + @include themify(night) { + background: #cee2ff; + } - &.gif { - background: none; - img { - max-width: 100%; + &.gif { + background: none; + img { + max-width: 100%; + } } - } - &.chat-image { - background: none; - img { - max-width: 100%; + &.chat-image { + background: none; + img { + max-width: 100%; + } } + color: #050505; + // max-width: 290px; + display: inline-block; + max-width: 70% !important; + word-wrap: break-word; + padding: 10px; + border-radius: 0px 10px 10px; } - color: #050505; - // max-width: 290px; - display: inline-block; - max-width: 70% !important; - word-wrap: break-word; - padding: 10px; - border-radius: 0px 10px 10px; } } } @@ -179,7 +181,7 @@ margin-bottom: 0; } &.receiver { - margin-top: 33px; + margin-top: 5px; margin-left: 10px; } } diff --git a/src/common/components/chats-channel-messages/index.tsx b/src/common/components/chats-channel-messages/index.tsx index b720be30e34..75a0eb5e3a6 100644 --- a/src/common/components/chats-channel-messages/index.tsx +++ b/src/common/components/chats-channel-messages/index.tsx @@ -1,5 +1,10 @@ import React, { useRef, useState, RefObject, useEffect } from "react"; -import { Channel, PublicMessage } from "../../../providers/message-provider-types"; +import mediumZoom, { Zoom } from "medium-zoom"; +import { + Channel, + communityModerator, + PublicMessage +} from "../../../providers/message-provider-types"; import { formatMessageTime, getFormattedDateAndDay, @@ -8,24 +13,23 @@ import { isMessageImage, NostrKeysType } from "../../helper/chat-utils"; +import { History } from "history"; import { renderPostBody } from "@ecency/render-helper"; import { useMappedStore } from "../../store/use-mapped-store"; import { Button, - Col, Form, FormControl, InputGroup, - Modal, OverlayTrigger, Popover, - Row, Spinner } from "react-bootstrap"; import UserAvatar from "../user-avatar"; import FollowControls from "../follow-controls"; import { Account } from "../../store/accounts/types"; import { ToggleType, UI } from "../../store/ui/types"; +import { Global } from "../../store/global/types"; import { _t } from "../../i18n"; import Tooltip from "../tooltip"; import { failedMessageSvg, hideSvg, resendMessageSvg } from "../../img/svg"; @@ -33,44 +37,111 @@ import { failedMessageSvg, hideSvg, resendMessageSvg } from "../../img/svg"; import "./index.scss"; import { User } from "../../store/users/types"; import { ActiveUser } from "../../store/active-user/types"; -import ChatInput from "../chat-input"; +import ChatsConfirmationModal from "../chats-confirmation-modal"; +import { error } from "../feedback"; +import { CHATPAGE } from "../chat-box/chat-constants"; +import { Theme } from "../../store/global/types"; interface Props { publicMessages: PublicMessage[]; currentChannel: Channel; activeUserKeys: NostrKeysType; username: string; + global: Global; + from?: string; users: User[]; activeUser: ActiveUser | null; ui: UI; + history: History | null; + isScrollToBottom: boolean; + isScrolled?: boolean; setActiveUser: (username: string | null) => void; updateActiveUser: (data?: Account) => void; deleteUser: (username: string) => void; toggleUIProp: (what: ToggleType) => void; - scrollToBottom: () => void; + scrollToBottom?: () => void; + currentChannelSetter: (channel: Channel) => void; + deletePublicMessage: (channelId: string, msgId: string) => void; } +let zoom: Zoom | null = null; export default function ChatsChannelMessages(props: Props) { - const { publicMessages, currentChannel, activeUserKeys, activeUser, scrollToBottom } = props; - const { global, chat } = useMappedStore(); + const { + publicMessages, + currentChannel, + activeUserKeys, + activeUser, + history, + from, + isScrollToBottom, + isScrolled, + global, + scrollToBottom, + currentChannelSetter, + deletePublicMessage + } = props; + const { chat } = useMappedStore(); + + const prevPropsRef = useRef(props); const popoverRef = useRef(null); const channelMessagesRef = React.createRef(); const [communityAdmins, setCommunityAdmins] = useState([]); const [hoveredMessageId, setHoveredMessageId] = useState(""); + const [step, setStep] = useState(0); const [dmMessage, setDmMessage] = useState(""); const [clickedMessage, setClickedMessage] = useState(""); const [removedUserId, setRemovedUserID] = useState(""); const [privilegedUsers, setPrivilegedUsers] = useState([]); const [hiddenMsgId, setHiddenMsgId] = useState(""); const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); + const [removedUsers, setRemovedUsers] = useState([]); const [resendMessage, setResendMessage] = useState(); // useEffect(() => { // scrollToBottom(); + // console.log("Heloooooooo"); // }, [publicMessages]); + useEffect(() => { + const prevProps = prevPropsRef.current; + if (prevProps.global.theme !== props.global.theme) { + setBackground(); + } + // if (prevProps.activeUser?.username !== props.activeUser?.username) { + // setIsCommunity(false); + // setIsCurrentUser(false); + // setCurrentUser(""); + // setCommunityName(""); + // } + prevPropsRef.current = props; + }, [props.global.theme, props.activeUser]); + + useEffect(() => { + if (publicMessages.length !== 0) { + zoomInitializer(); + } + if (!isScrollToBottom && publicMessages.length !== 0 && !isScrolled) { + scrollToBottom && scrollToBottom(); + } + }, [publicMessages, isScrollToBottom, channelMessagesRef]); + + useEffect(() => { + if (currentChannel) { + zoomInitializer(); + getPrivilegedUsers(currentChannel?.communityModerators!); + currentChannel?.removedUserIds && setRemovedUsers(currentChannel.removedUserIds); + } + }, [currentChannel]); + + useEffect(() => { + if (removedUsers) { + const removed = removedUsers.includes(activeUserKeys?.pub!); + setIsActiveUserRemoved(removed); + } + }, [removedUsers]); + useEffect(() => { const handleClickOutside = (event: MouseEvent) => { const clickedElement = event.target as HTMLElement; @@ -112,6 +183,18 @@ export default function ChatsChannelMessages(props: Props) { // setCommunityName(""); setClickedMessage(""); setDmMessage(""); + // console.log("Message is send to", name); + // if ( + // receiverPubKey && + // !props.chat.directContacts.some((contact) => contact.name === currentUser) && + // isCurrentUser + // ) { + // window.messageService?.publishContacts(currentUser, receiverPubKey); + // } + + if (from && from === CHATPAGE) { + history?.push(`/chats/@${name}`); + } } }; @@ -119,6 +202,21 @@ export default function ChatsChannelMessages(props: Props) { setDmMessage(e.target.value); }; + const zoomInitializer = () => { + const elements: HTMLElement[] = [...document.querySelectorAll(".chat-image img")]; + // .filter((x) => x.parentNode?.nodeName !== "A"); + zoom = mediumZoom(elements); + setBackground(); + }; + + const setBackground = () => { + if (global.theme === Theme.day) { + zoom?.update({ background: "#ffffff" }); + } else { + zoom?.update({ background: "#131111" }); + } + }; + const handleImageClick = (msgId: string, pubkey: string) => { if (clickedMessage === msgId) { popoverRef.current = null; @@ -130,226 +228,333 @@ export default function ChatsChannelMessages(props: Props) { } }; - return ( -
- {publicMessages.length !== 0 && - activeUserKeys && - publicMessages.map((pMsg, i) => { - const dayAndMonth = getFormattedDateAndDay(pMsg, i, publicMessages); - let renderedPreview = renderPostBody(pMsg.content, false, global.canUseWebp); + const getPrivilegedUsers = (communityModerators: communityModerator[]) => { + const privilegedRoles = ["owner", "admin", "mod"]; + const communityAdminRoles = ["owner", "admin"]; - renderedPreview = renderedPreview.replace(/]*>/g, ""); - renderedPreview = renderedPreview.replace(/<\/p>/g, ""); + const privilegedUsers = communityModerators.filter((user) => + privilegedRoles.includes(user.role) + ); + const communityAdmins = communityModerators.filter((user) => + communityAdminRoles.includes(user.role) + ); - const isGif = isMessageGif(pMsg.content); + const privilegedUserNames = privilegedUsers.map((user) => user.name); + const communityAdminNames = communityAdmins.map((user) => user.name); - const isImage = isMessageImage(pMsg.content); + setPrivilegedUsers(privilegedUserNames); + setCommunityAdmins(communityAdminNames); + }; - const name = getProfileName(pMsg.creator, chat.profiles); + const handleConfirm = () => { + let updatedMetaData = { + name: currentChannel?.name!, + about: currentChannel?.about!, + picture: "", + communityName: currentChannel?.communityName!, + communityModerators: currentChannel?.communityModerators, + hiddenMessageIds: currentChannel?.hiddenMessageIds, + removedUserIds: currentChannel?.removedUserIds + }; - const popover = ( - - -
}> -
-
- -
+ switch (step) { + case 1: + const updatedHiddenMessages = [...(currentChannel?.hiddenMessageIds || []), hiddenMsgId!]; + updatedMetaData.hiddenMessageIds = updatedHiddenMessages; + break; + case 2: + const updatedRemovedUsers = [...(currentChannel?.removedUserIds || []), removedUserId!]; + updatedMetaData.removedUserIds = updatedRemovedUsers; -

{`@${name!}`}

-
- - - {communityAdmins.includes(activeUser?.username!) && ( - <> - {currentChannel?.removedUserIds?.includes(pMsg.creator) ? ( - <> - - - ) : ( - <> - - - )} - - )} -
+ const isRemovedUserModerator = currentChannel?.communityModerators?.find( + (x) => x.pubkey === removedUserId + ); + const isModerator = !!isRemovedUserModerator; + if (isModerator) { + const NewUpdatedRoles = currentChannel?.communityModerators?.filter( + (item) => item.pubkey !== removedUserId + ); + updatedMetaData.communityModerators = NewUpdatedRoles; + } + break; + case 3: + console.log("Current removed users", currentChannel?.removedUserIds); + console.log("removedUserId", removedUserId); + const newUpdatedRemovedUsers = + currentChannel && + currentChannel?.removedUserIds!.filter((item) => item !== removedUserId); -
{ - e.preventDefault(); - e.stopPropagation(); - sendDM(name!, pMsg.creator); - }} - > - - - -
-
-
-
-
+ console.log("newUpdatedRemovedUsers", newUpdatedRemovedUsers); + updatedMetaData.removedUserIds = newUpdatedRemovedUsers; + break; + case 4: + if (resendMessage) { + deletePublicMessage(currentChannel.id, resendMessage?.id); + window?.messageService?.sendPublicMessage( + currentChannel!, + resendMessage?.content, + [], + "" ); + } + break; + default: + break; + } - return ( - - - {dayAndMonth} - - - {pMsg.creator !== activeUserKeys?.pub ? ( -
setHoveredMessageId(pMsg.id)} - onMouseLeave={() => setHoveredMessageId("")} - > -
- handleImageClick(pMsg.id, pMsg.creator)} - > - - - - + try { + window.messageService?.updateChannel(currentChannel!, updatedMetaData); + currentChannelSetter({ ...currentChannel!, ...updatedMetaData }); + setStep(0); + // if (operationType === HIDEMESSAGE) { + // setStep(0); + // setKeyDialog(false); + // fetchCommunityMessages( + // props.chat.publicMessages, + // currentChannel!, + // currentChannel?.hiddenMessageIds + // ); + // setHiddenMsgId(""); + // } + // if (operationType === BLOCKUSER || operationType === UNBLOCKUSER) { + // setStep(0); + // setKeyDialog(false); + // setRemovedUserID(""); + // } + } catch (err) { + error(_t("chat.error-updating-community")); + } + }; + + return ( + <> +
+ {publicMessages.length !== 0 && + activeUserKeys && + publicMessages.map((pMsg, i) => { + const dayAndMonth = getFormattedDateAndDay(pMsg, i, publicMessages); + let renderedPreview = renderPostBody(pMsg.content, false, global.canUseWebp); + + renderedPreview = renderedPreview.replace(/]*>/g, ""); + renderedPreview = renderedPreview.replace(/<\/p>/g, ""); + + const isGif = isMessageGif(pMsg.content); + + const isImage = isMessageImage(pMsg.content); + + const name = getProfileName(pMsg.creator, chat.profiles); + + const popover = ( + + +
}> +
+
+ +
+ +

{`@${name!}`}

+
+ + + {communityAdmins.includes(activeUser?.username!) && ( + <> + {currentChannel?.removedUserIds?.includes(pMsg.creator) ? ( + <> + + + ) : ( + <> + + + )} + + )} +
+ +
{ + e.preventDefault(); + e.stopPropagation(); + sendDM(name!, pMsg.creator); + }} + > + + + +
+
+
+
+ ); -
-

- {name} - {formatMessageTime(pMsg.created)} -

-
+ return ( + + {dayAndMonth !== null && ( + + {dayAndMonth} + + )} + + {pMsg.creator !== activeUserKeys?.pub ? ( +
setHoveredMessageId(pMsg.id)} + onMouseLeave={() => setHoveredMessageId("")} + > +
+ handleImageClick(pMsg.id, pMsg.creator)} + > + + + + +
+ +
+

+ {name} + {formatMessageTime(pMsg.created)} +

+
+
+ {hoveredMessageId === pMsg.id && + privilegedUsers.includes(props.activeUser?.username!) && ( + +
+

{ + setClickedMessage(""); + setStep(1); + setHiddenMsgId(pMsg.id); + }} + > + {hideSvg} +

+
+
+ )} +
+
- {hoveredMessageId === pMsg.id && - privilegedUsers.includes(props.activeUser?.username!) && ( - -
-

{ - setClickedMessage(""); - // setKeyDialog(true); - // setStep(5); - setHiddenMsgId(pMsg.id); - }} - > - {hideSvg} -

-
-
- )} -
- ) : ( -
setHoveredMessageId(pMsg.id)} - onMouseLeave={() => setHoveredMessageId("")} - > -

{formatMessageTime(pMsg.created)}

+ ) : (
setHoveredMessageId(pMsg.id)} + onMouseLeave={() => setHoveredMessageId("")} > - {hoveredMessageId === pMsg.id && !isActveUserRemoved && ( - -
-

{formatMessageTime(pMsg.created)}

+
+ {hoveredMessageId === pMsg.id && !isActveUserRemoved && ( + +
+

{ + setClickedMessage(""); + setStep(1); + setHiddenMsgId(pMsg.id); + }} + > + {hideSvg} +

+
+
+ )} + {pMsg.sent === 2 && ( + + { - setClickedMessage(""); - // setKeyDialog(true); - // setStep(5); - setHiddenMsgId(pMsg.id); + setStep(4); + setResendMessage(pMsg); }} > - {hideSvg} -

-
- - )} - {pMsg.sent === 2 && ( - - { - // setKeyDialog(true); - // setStep(11); - setResendMessage(pMsg); - }} - > - {resendMessageSvg} + {resendMessageSvg} + + + )} +
+ {pMsg.sent === 0 && ( + + - - )} -
- {pMsg.sent === 0 && ( - - - - )} - {pMsg.sent === 2 && ( - - {failedMessageSvg} - - )} + )} + {pMsg.sent === 2 && ( + + {failedMessageSvg} + + )} +
-
- )} - - ); - })} -
+ )} + + ); + })} +
+ {step !== 0 && ( + { + setStep(0); + }} + onConfirm={handleConfirm} + /> + )} + ); } diff --git a/src/common/components/chats-community-dropdown-menu/index.tsx b/src/common/components/chats-community-dropdown-menu/index.tsx index 3b49d0acb46..17ea29bda9d 100644 --- a/src/common/components/chats-community-dropdown-menu/index.tsx +++ b/src/common/components/chats-community-dropdown-menu/index.tsx @@ -30,29 +30,26 @@ interface Props { history: History | null; from?: string; username: string; + currentChannel: Channel; + currentChannelSetter: (channe: Channel) => void; } const roles = [ROLES.ADMIN, ROLES.MOD, ROLES.GUEST]; const ChatsCommunityDropdownMenu = (props: Props) => { const { activeUser, chat } = useMappedStore(); - const { history, username, from } = props; + const { history, currentChannelSetter, from, currentChannel } = props; const [step, setStep] = useState(0); const [keyDialog, setKeyDialog] = useState(false); const [inProgress, setInProgress] = useState(false); const [user, setUser] = useState(""); const [addRoleError, setAddRoleError] = useState(""); const [role, setRole] = useState("admin"); - const [currentChannel, setCurrentChannel] = useState(); const [moderator, setModerator] = useState(); const [communityAdmins, setCommunityAdmins] = useState([]); const [blockedUsers, setBlockedUsers] = useState<{ name: string; pubkey: string }[]>([]); const [removedUserId, setRemovedUserID] = useState(""); - useEffect(() => { - fetchCurrentChannel(formattedUserName(username)); - }, [chat.updatedChannel, username, chat.channels]); - useEffect(() => { getCommunityAdmins(); @@ -61,32 +58,6 @@ const ChatsCommunityDropdownMenu = (props: Props) => { } }, [currentChannel, removedUserId]); - const fetchCurrentChannel = (communityName: string) => { - const channel = chat.channels.find((channel) => channel.communityName === communityName); - if (channel) { - const updated: ChannelUpdate = chat.updatedChannel - .filter((x) => x.channelId === channel.id) - .sort((a, b) => b.created - a.created)[0]; - if (updated) { - const channel = { - name: updated.name, - about: updated.about, - picture: updated.picture, - communityName: updated.communityName, - communityModerators: updated.communityModerators, - id: updated.channelId, - creator: updated.creator, - created: currentChannel?.created!, - hiddenMessageIds: updated.hiddenMessageIds, - removedUserIds: updated.removedUserIds - }; - setCurrentChannel(channel); - } else { - setCurrentChannel(channel); - } - } - }; - const handleEditRoles = () => { setKeyDialog(true); setStep(2); @@ -141,7 +112,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => { const newUpdatedModerator = { ...newUpdatedChannel?.communityModerators![moderatorIndex!] }; newUpdatedModerator.role = selectedRole; newUpdatedChannel!.communityModerators![moderatorIndex!] = newUpdatedModerator; - setCurrentChannel(newUpdatedChannel); + currentChannelSetter(newUpdatedChannel); window.messageService?.updateChannel(currentChannel, newUpdatedChannel); success("Roles updated succesfully"); } @@ -511,7 +482,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => { } try { window.messageService?.updateChannel(currentChannel!, updatedMetaData); - setCurrentChannel({ ...currentChannel!, ...updatedMetaData }); + currentChannelSetter({ ...currentChannel!, ...updatedMetaData }); if (operationType === UNBLOCKUSER) { setStep(5); @@ -532,6 +503,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => { alignBottom={false} noMarginTop={true} /> + {keyDialog && ( void; + onConfirm: () => void; + // t: any; +} +const ChatsConfirmationModal = (props: Props) => { + const { onClose, onConfirm } = props; + const confirmationModalContent = ( + <> +
+
+

Confirmation

+
+
+
+ Are you sure? +
+

+ + +

+ + ); + + return ( + + + {confirmationModalContent} + + ); +}; + +export default ChatsConfirmationModal; diff --git a/src/common/components/chats-direct-messages/index.scss b/src/common/components/chats-direct-messages/index.scss index e69de29bb2d..2c4315048e2 100644 --- a/src/common/components/chats-direct-messages/index.scss +++ b/src/common/components/chats-direct-messages/index.scss @@ -0,0 +1,160 @@ +@import "../../../style/vars_mixins"; + +.direct-messages { + padding-bottom: 15px; + .day-and-month { + margin-bottom: 1rem; + font-size: 14px; + } + .receiver { + display: flex; + .user-img { + padding: 18px 8px 8px 16px; + } + .community-user-img { + padding: 8px 8px 8px 16px; + .user-avatar.medium { + cursor: pointer; + } + } + .user-info { + padding-top: 8px; + width: 100%; + .user-msg-time { + margin: 0; + color: rgb(138, 141, 145); + font-weight: 400; + font-size: 12px; + margin-left: 4px; + margin-bottom: 2px; + .username-community { + margin-right: 8px; + } + } + .receiver-messag { + display: flex; + .receiver-message-content { + @include themify(day) { + background: #e4e6eb; + } + @include themify(night) { + background: #cee2ff; + } + + &.gif { + background: none; + img { + max-width: 100%; + } + } + + &.chat-image { + background: none; + img { + max-width: 100%; + } + } + color: #050505; + // max-width: 290px; + display: inline-block; + max-width: 70% !important; + word-wrap: break-word; + padding: 10px; + border-radius: 0px 10px 10px; + } + } + } + } + + .sender { + margin-bottom: 0.17rem; + .sender-message-time { + color: rgb(138, 141, 145); + display: flex; + font-weight: 400; + justify-content: flex-end; + font-size: 12px; + margin: 0; + margin-right: 18px; + margin-bottom: 2px; + } + .sender-message { + // max-width: 320px; + margin-left: auto; + + display: flex; + justify-content: flex-end; + margin-right: 17px; + + .resend-svg { + margin: 5px 15px 0 0; + cursor: pointer; + svg { + @include themify(day) { + fill: $light-black; + } + @include themify(night) { + fill: $white; + } + } + } + + .failed-svg { + margin: 8px 0 0 5px; + svg { + width: 16px; + height: 16px; + fill: $red !important; + } + } + + &.sending { + margin-right: 5px; + } + &.failed { + margin-right: 7px; + } + + .sender-message-content { + border-radius: 10px 10px 0px; + max-width: 70%; + word-wrap: break-word; + margin-bottom: 0; + color: $charcoal-grey; + font-size: 16px; + font-weight: 400; + padding: 8px 12px 8px 12px; + a { + text-decoration: underline; + color: white; + } + @include themify(day) { + background-color: rgb(0, 132, 255); + color: $white; + } + @include themify(night) { + background-color: $charcoal-grey; + color: $white; + } + &.gif { + background: none; + img { + max-width: 100%; + } + } + + &.chat-image { + background: none; + img { + max-width: 100%; + } + } + } + } + } + + // &.receiver { + // margin-top: 5px; + // margin-left: 10px; + // } +} diff --git a/src/common/components/chats-direct-messages/index.tsx b/src/common/components/chats-direct-messages/index.tsx index 3a3c7837606..823324c54f4 100644 --- a/src/common/components/chats-direct-messages/index.tsx +++ b/src/common/components/chats-direct-messages/index.tsx @@ -1,5 +1,170 @@ -import React from "react"; +import React, { useEffect, useRef } from "react"; +import { DirectMessage } from "../../../providers/message-provider-types"; +import mediumZoom, { Zoom } from "medium-zoom"; +import { Global, Theme } from "../../store/global/types"; +import { + formatMessageTime, + getFormattedDateAndDay, + isMessageGif, + isMessageImage, + NostrKeysType +} from "../../helper/chat-utils"; +import { renderPostBody } from "@ecency/render-helper"; +import { useMappedStore } from "../../store/use-mapped-store"; +import { Link } from "react-router-dom"; +import UserAvatar from "../user-avatar"; +import Tooltip from "../tooltip"; +import { failedMessageSvg, resendMessageSvg } from "../../img/svg"; +import { Spinner } from "react-bootstrap"; -export default function ChatsDirectMessages() { - return
ChatsDirectMessages
; +import "./index.scss"; +import { ActiveUser } from "../../store/active-user/types"; + +interface Props { + directMessages: DirectMessage[]; + activeUserKeys: NostrKeysType; + currentUser: string; + global: Global; + activeUser: ActiveUser | null; + isScrollToBottom: boolean; + isScrolled?: boolean; + scrollToBottom?: () => void; +} + +let zoom: Zoom | null = null; +export default function ChatsDirectMessages(props: Props) { + const { + directMessages, + activeUserKeys, + currentUser, + isScrolled, + global, + isScrollToBottom, + activeUser, + scrollToBottom + } = props; + const { chat } = useMappedStore(); + + const prevPropsRef = useRef(props); + + useEffect(() => { + const prevProps = prevPropsRef.current; + if (prevProps.global.theme !== global.theme) { + setBackground(); + } + prevPropsRef.current = props; + }, [global.theme, activeUser]); + + useEffect(() => { + if (directMessages && directMessages.length !== 0) { + zoomInitializer(); + } + if (!isScrollToBottom && directMessages && directMessages.length !== 0 && !isScrolled) { + scrollToBottom && scrollToBottom(); + } + }, [directMessages, isScrollToBottom, scrollToBottom]); + + const zoomInitializer = () => { + const elements: HTMLElement[] = [...document.querySelectorAll(".chat-image img")]; + zoom = mediumZoom(elements); + setBackground(); + }; + + const setBackground = () => { + if (global.theme === Theme.day) { + zoom?.update({ background: "#ffffff" }); + } else { + zoom?.update({ background: "#131111" }); + } + }; + + return ( +
+ {directMessages && + directMessages.map((msg, i) => { + const dayAndMonth = getFormattedDateAndDay(msg, i, directMessages); + let renderedPreview = renderPostBody(msg.content, false, global.canUseWebp); + + renderedPreview = renderedPreview.replace(/]*>/g, ""); + renderedPreview = renderedPreview.replace(/<\/p>/g, ""); + + const isGif = isMessageGif(msg.content); + + const isImage = isMessageImage(msg.content); + + return ( + + {dayAndMonth !== null && ( + + {dayAndMonth} + + )} + {msg.creator !== activeUserKeys?.pub ? ( +
+
+ + + + + +
+
+

{formatMessageTime(msg.created)}

+ +
+
+
+
+
+ ) : ( +
+

{formatMessageTime(msg.created)}

+
+ {msg.sent === 2 && ( + + { + // setKeyDialog(true); + // setStep(11); + // setResendMessage(msg); + }} + > + {resendMessageSvg} + + + )} +
+ {msg.sent === 0 && ( + + + + )} + {msg.sent === 2 && ( + + {failedMessageSvg} + + )} +
+
+ )} + + ); + })} +
+ ); } diff --git a/src/common/components/chats-messages-box/index.tsx b/src/common/components/chats-messages-box/index.tsx index 232ecd36f46..41a644abce7 100644 --- a/src/common/components/chats-messages-box/index.tsx +++ b/src/common/components/chats-messages-box/index.tsx @@ -8,10 +8,12 @@ import { ToggleType, UI } from "../../store/ui/types"; import { User } from "../../store/users/types"; import ChatsMessagesHeader from "../chats-messages-header"; import ChatsMessagesView from "../chats-messages-view"; -import { Global } from "../../store/global/types"; +import { Global, Theme } from "../../store/global/types"; import "./index.scss"; -import { NostrKeysType } from "../../helper/chat-utils"; +import { formattedUserName, NostrKeysType } from "../../helper/chat-utils"; +import { Channel, ChannelUpdate } from "../../../providers/message-provider-types"; +import LinearProgress from "../linear-progress"; interface MatchParams { filter: string; @@ -34,18 +36,55 @@ interface Props { updateActiveUser: (data?: Account) => void; deleteUser: (username: string) => void; toggleUIProp: (what: ToggleType) => void; + deletePublicMessage: (channelId: string, msgId: string) => void; } export default function ChatsMessagesBox(props: Props) { - const { match } = props; + const { match, global, deletePublicMessage } = props; const username = match.params.username; const [maxHeight, setMaxHeight] = useState(0); + const [currentChannel, setCurrentChannel] = useState(); + const [inProgress, setInProgress] = useState(false); useEffect(() => { setMaxHeight(window.innerHeight - 68); }, [typeof window !== "undefined"]); + useEffect(() => { + console.log("state has been updated", currentChannel); + }, [currentChannel]); + + useEffect(() => { + fetchCurrentChannel(formattedUserName(username)); + }, [props.chat.updatedChannel, username, props.chat.channels]); + + const fetchCurrentChannel = (communityName: string) => { + const channel = props.chat.channels.find((channel) => channel.communityName === communityName); + if (channel) { + const updated: ChannelUpdate = props.chat.updatedChannel + .filter((x) => x.channelId === channel.id) + .sort((a, b) => b.created - a.created)[0]; + if (updated) { + const channel = { + name: updated.name, + about: updated.about, + picture: updated.picture, + communityName: updated.communityName, + communityModerators: updated.communityModerators, + id: updated.channelId, + creator: updated.creator, + created: currentChannel?.created!, + hiddenMessageIds: updated.hiddenMessageIds, + removedUserIds: updated.removedUserIds + }; + setCurrentChannel(channel); + } else { + setCurrentChannel(channel); + } + } + }; + return ( <>
@@ -55,8 +94,22 @@ export default function ChatsMessagesBox(props: Props) {
) : ( <> - - + + {inProgress && } + )}
diff --git a/src/common/components/chats-messages-header/index.tsx b/src/common/components/chats-messages-header/index.tsx index e52e92ca71c..43a08d06360 100644 --- a/src/common/components/chats-messages-header/index.tsx +++ b/src/common/components/chats-messages-header/index.tsx @@ -13,10 +13,12 @@ import { CHATPAGE } from "../chat-box/chat-constants"; interface Props { username: string; history: History | null; + currentChannel: Channel; + currentChannelSetter: (channe: Channel) => void; } export default function ChatsMessagesHeader(props: Props) { - const { username } = props; + const { username, currentChannel, currentChannelSetter } = props; const { chat } = useMappedStore(); return ( @@ -28,7 +30,12 @@ export default function ChatsMessagesHeader(props: Props) {
{isChannel(username) && (
- +
)}
diff --git a/src/common/components/chats-messages-view/index.scss b/src/common/components/chats-messages-view/index.scss index 1a179c4a856..55c51e27342 100644 --- a/src/common/components/chats-messages-view/index.scss +++ b/src/common/components/chats-messages-view/index.scss @@ -17,4 +17,8 @@ a::after { display: none; } + + &.no-scroll { + overflow: hidden; + } } diff --git a/src/common/components/chats-messages-view/index.tsx b/src/common/components/chats-messages-view/index.tsx index 8dfb28a291f..80d47bba252 100644 --- a/src/common/components/chats-messages-view/index.tsx +++ b/src/common/components/chats-messages-view/index.tsx @@ -1,15 +1,17 @@ -import React, { RefObject, useEffect, useState } from "react"; +import React, { RefObject, useEffect, useRef, useState } from "react"; import { Link } from "react-router-dom"; -import { Channel, PublicMessage } from "../../../providers/message-provider-types"; +import { Channel, DirectMessage, PublicMessage } from "../../../providers/message-provider-types"; import { fetchCommunityMessages, fetchCurrentUserData, + fetchDirectMessages, getPrivateKey, getProfileMetaData, NostrKeysType } from "../../helper/chat-utils"; +import { History } from "history"; import { useMappedStore } from "../../store/use-mapped-store"; -import { Global } from "../../store/global/types"; +import { Global, Theme } from "../../store/global/types"; import ChatsProfileBox from "../chats-profile-box"; import "./index.scss"; @@ -23,11 +25,12 @@ import { ActiveUser } from "../../store/active-user/types"; import ChatInput from "../chat-input"; import ChatsScroller from "../chats-scroller"; import { EmojiPickerStyleProps } from "../chat-box"; +import { CHATPAGE } from "../chat-box/chat-constants"; const EmojiPickerStyle: EmojiPickerStyleProps = { width: "56.5%", bottom: "58px", - left: "305px", + left: "340px", marginLeft: "14px", borderTopLeftRadius: "8px", borderTopRightRadius: "8px", @@ -36,43 +39,54 @@ const EmojiPickerStyle: EmojiPickerStyleProps = { interface Props { username: string; users: User[]; + history: History | null; activeUser: ActiveUser | null; ui: UI; global: Global; + currentChannel: Channel; + inProgress: boolean; setActiveUser: (username: string | null) => void; updateActiveUser: (data?: Account) => void; deleteUser: (username: string) => void; toggleUIProp: (what: ToggleType) => void; + currentChannelSetter: (channel: Channel) => void; + setInProgress: (d: boolean) => void; + deletePublicMessage: (channelId: string, msgId: string) => void; } export default function ChatsMessagesView(props: Props) { - const { username, activeUser, global } = props; + const { + username, + activeUser, + global, + currentChannel, + inProgress, + currentChannelSetter, + setInProgress, + deletePublicMessage + } = props; - const messagesBoxRef = React.createRef(); + const messagesBoxRef = useRef(null); const { chat } = useMappedStore(); const [directUser, setDirectUser] = useState(""); const [publicMessages, setPublicMessages] = useState([]); + const [directMessages, setDirectMessages] = useState([]); const [communityName, setCommunityName] = useState(""); - const [currentChannel, setCurrentChannel] = useState(); const [activeUserKeys, setActiveUserKeys] = useState(); const [receiverPubKey, setReceiverPubKey] = useState(""); const [isScrollToTop, setIsScrollToTop] = useState(false); const [isScrollToBottom, setIsScrollToBottom] = useState(false); const [isTop, setIsTop] = useState(false); + const [hasMore, setHasMore] = useState(true); + // const [inProgress, setInProgress] = useState(false); + const [isScrolled, setIsScrolled] = useState(false); useEffect(() => { getActiveUserKeys(); - setChannelData(); + isDirectUserOrCommunity(); }, []); - useEffect(() => { - console.log("publicMessages", publicMessages); - if (publicMessages.length !== 0) { - scrollToBottom(); - } - }, [publicMessages]); - useEffect(() => { if (directUser) { getReceiverPubKey(); @@ -80,7 +94,7 @@ export default function ChatsMessagesView(props: Props) { }, [directUser]); useEffect(() => { - setChannelData(); + isDirectUserOrCommunity(); }, [chat.channels]); useEffect(() => { @@ -89,22 +103,41 @@ export default function ChatsMessagesView(props: Props) { useEffect(() => { if (directUser) { + getDirectMessages(); } else if (communityName && currentChannel) { getChannelMessages(); } - }, [directUser, communityName, currentChannel]); + }, [directUser, communityName, currentChannel, chat.directMessages]); useEffect(() => { setDirectUser(""); setCommunityName(""); - setChannelData(); + isDirectUserOrCommunity(); }, [username]); - // useEffect(() => { - // if (isTop) { - // fetchPrevMessages(); - // } - // }, [isTop]); + useEffect(() => { + if (isTop) { + fetchPrevMessages(); + } + }, [isTop]); + + const fetchPrevMessages = () => { + if (!hasMore || inProgress) return; + + setInProgress(true); + window.messageService + ?.fetchPrevMessages(currentChannel!.id, publicMessages[0].created) + .then((num) => { + console.log("number", num); + if (num < 25) { + setHasMore(false); + } + }) + .finally(() => { + setInProgress(false); + setIsTop(false); + }); + }; const getActiveUserKeys = async () => { const profileData = await getProfileMetaData(props.activeUser?.username!); @@ -124,15 +157,12 @@ export default function ChatsMessagesView(props: Props) { } }; - const setChannelData = () => { + const isDirectUserOrCommunity = () => { if (username) { if (username && username.startsWith("@")) { setDirectUser(username.replace("@", "")); } else { setCommunityName(username); - const channel = chat.channels.find((channel) => channel.communityName === username); - // console.log("channel", channel); - setCurrentChannel(channel); } } }; @@ -149,8 +179,16 @@ export default function ChatsMessagesView(props: Props) { } }; + const getDirectMessages = () => { + const user = chat.directContacts.find((item) => item.name === directUser); + console.log("user", user?.pubkey); + const messages = user && fetchDirectMessages(user?.pubkey, chat.directMessages); + setDirectMessages(messages!); + console.log("Messages", messages); + }; + const scrollToBottom = () => { - console.log("Scroll to bottom scliced"); + console.log("Scroll to bottom clicked", messagesBoxRef.current?.scrollHeight); messagesBoxRef && messagesBoxRef?.current?.scroll({ top: messagesBoxRef.current?.scrollHeight, @@ -160,11 +198,16 @@ export default function ChatsMessagesView(props: Props) { const handleScroll = (event: React.UIEvent) => { var element = event.currentTarget; - // let srollHeight: number = (element.scrollHeight / 100) * 25; + let srollHeight: number = (element.scrollHeight / 100) * 25; // const isScrollToTop = !isCurrentUser && !isCommunity && element.scrollTop >= srollHeight; const isScrollToBottom = element.scrollTop + messagesBoxRef?.current?.clientHeight! < element.scrollHeight - 200; setIsScrollToBottom(isScrollToBottom); + // console.log(element.scrollTop, (element.scrollTop / 100) * 98); + const isScrolled = element.scrollTop + element.clientHeight <= element.scrollHeight - 20; + setIsScrolled(isScrolled); + // const isScrolled = element.scrollHeight/100 *3 - 50; + // console.log("isScrolled", isScrolled) const scrollerTop = element.scrollTop <= 600 && publicMessages.length > 25; if (communityName && scrollerTop) { setIsTop(true); @@ -175,7 +218,11 @@ export default function ChatsMessagesView(props: Props) { return ( <> -
+
) : ( - + )} {isScrollToBottom && (

{channel.name}

-

hello

+

+ {getCommunityLastMessage(channel.id, chat.publicMessages)} +

@@ -316,15 +324,15 @@ export default function ChatsSideBar(props: Props) { {directContacts.map((contact) => (

{contact.name}

-

hello

+

+ {getDirectLastMessage(contact.pubkey, chat.directMessages)} +

diff --git a/src/common/components/chats-sidebar/index.scss b/src/common/components/chats-sidebar/index.scss index f717c951c59..3e18bf38897 100644 --- a/src/common/components/chats-sidebar/index.scss +++ b/src/common/components/chats-sidebar/index.scss @@ -192,6 +192,14 @@ font-size: 13px; font-family: 400; } + .community-last-message { + max-width: 255px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + margin-bottom: 0; + font-size: 14px; + } } &.selected { @include themify(day) { diff --git a/src/common/helper/chat-utils.ts b/src/common/helper/chat-utils.ts index 33546643f9d..33234075bb3 100644 --- a/src/common/helper/chat-utils.ts +++ b/src/common/helper/chat-utils.ts @@ -10,7 +10,7 @@ import { getAccountFull } from "../api/hive"; import { updateProfile } from "../api/operations"; import { GIPHGY } from "../components/chat-box/chat-constants"; import { ActiveUser } from "../store/active-user/types"; -import { Chat, publicMessagesList } from "../store/chat/types"; +import { Chat, directMessagesList, publicMessagesList } from "../store/chat/types"; import * as ls from "../util/local-storage"; export interface NostrKeysType { @@ -197,3 +197,38 @@ export const copyToClipboard = (content: string) => { document.execCommand("copy"); textField.remove(); }; + +export const getCommunityLastMessage = ( + channelId: string, + publicMessages: publicMessagesList[] +) => { + const msgsList = fetchChannelMessages(channelId!, publicMessages); + const messages = msgsList.sort((a, b) => a.created - b.created); + const lastMessage = messages.slice(-1); + return lastMessage[0]?.content; +}; + +export const fetchChannelMessages = (channelId: string, publicMessages: publicMessagesList[]) => { + for (const item of publicMessages) { + if (item.channelId === channelId) { + return Object.values(item.PublicMessage); + } + } + return []; +}; + +export const fetchDirectMessages = (peer: string, directMessages: directMessagesList[]) => { + for (const item of directMessages) { + if (item.peer === peer) { + return Object.values(item.chat); + } + } + return []; +}; + +export const getDirectLastMessage = (pubkey: string, directMessages: directMessagesList[]) => { + const msgsList = fetchDirectMessages(pubkey, directMessages); + const messages = msgsList.sort((a, b) => a.created - b.created); + const lastMessage = messages.slice(-1); + return lastMessage[0]?.content; +}; diff --git a/src/common/pages/chats/index.tsx b/src/common/pages/chats/index.tsx index 91e8669b559..d13bc9674c6 100644 --- a/src/common/pages/chats/index.tsx +++ b/src/common/pages/chats/index.tsx @@ -143,11 +143,8 @@ export const Chats = (props: Props) => { className={props.global.isElectron ? "chats-page mb-lg-0 pt-6" : "chats-page mb-lg-0"} > {inProgrss ? ( -
-

Loading...

+
+
) : ( <> @@ -170,7 +167,11 @@ export const Chats = (props: Props) => {
) : ( //if any person has not joined any community then how can he see its message. handle this thing here. - + )} ) : ( From 15804137dcbf4cbbcdc38c755a79a753ccf302e2 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Tue, 5 Sep 2023 12:19:26 +0500 Subject: [PATCH 053/179] Remove commented code --- src/common/components/chat-box/index.tsx | 107 ++---------------- .../chats-channel-messages/index.tsx | 17 ++- .../components/chats-messages-view/index.tsx | 13 ++- 3 files changed, 29 insertions(+), 108 deletions(-) diff --git a/src/common/components/chat-box/index.tsx b/src/common/components/chat-box/index.tsx index 4054f7ec7dd..e48cdf42231 100644 --- a/src/common/components/chat-box/index.tsx +++ b/src/common/components/chat-box/index.tsx @@ -154,7 +154,7 @@ export default function ChatBox(props: Props) { const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); const [isTop, setIsTop] = useState(false); const [hasMore, setHasMore] = useState(true); - const [resendMessage, setResendMessage] = useState(); + const [removedUsers, setRemovedUsers] = useState([]); const [importPrivKey, setImportPrivKey] = useState(false); const [revelPrivateKey, setRevealPrivateKey] = useState(false); const [innerWidth, setInnerWidth] = useState(0); @@ -282,6 +282,7 @@ export default function ChatBox(props: Props) { currentChannel, currentChannel.hiddenMessageIds ); + currentChannel?.removedUserIds && setRemovedUsers(currentChannel.removedUserIds); const messages = publicMessages.sort((a, b) => a.created - b.created); setPublicMessages(messages); } @@ -296,6 +297,13 @@ export default function ChatBox(props: Props) { } }, [isCurrentUser]); + useEffect(() => { + if (removedUsers) { + const removed = removedUsers.includes(activeUserKeys?.pub!); + setIsActiveUserRemoved(removed); + } + }, [removedUsers]); + useEffect(() => { fetchProfileData(); const msgsList = fetchDirectMessages(receiverPubKey!); @@ -574,12 +582,6 @@ export default function ChatBox(props: Props) { const handleConfirmButton = (actionType: string) => { switch (actionType) { - case HIDEMESSAGE: - handleChannelUpdate(HIDEMESSAGE); - break; - case BLOCKUSER: - handleChannelUpdate(BLOCKUSER); - break; case NEWCHATACCOUNT: setInProgress(true); resetProfile(props.activeUser) @@ -592,9 +594,6 @@ export default function ChatBox(props: Props) { error(err); }); break; - case RESENDMESSAGE: - // handleResendMessage(); - break; default: break; } @@ -888,91 +887,6 @@ export default function ChatBox(props: Props) {

{_t("chat.not-joined")}

)} {isCurrentUser ? ( - // directMessagesList.map((msg, i) => { - // const dayAndMonth = getFormattedDateAndDay(msg, i); - // let renderedPreview = renderPostBody( - // msg.content, - // false, - // props.global.canUseWebp - // ); - - // renderedPreview = renderedPreview.replace(/]*>/g, ""); - // renderedPreview = renderedPreview.replace(/<\/p>/g, ""); - - // const isGif = isMessageGif(msg.content); - - // const isImage = isMessageImage(msg.content); - - // return ( - // - // {dayAndMonth} - // {msg.creator !== activeUserKeys?.pub ? ( - //
- //
- // - // - // - // - // - //
- //
- //

- // {formatMessageTime(msg.created)} - //

- //
- //
- //
- // ) : ( - //
- //

- // {formatMessageTime(msg.created)} - //

- //
- // {msg.sent === 2 && ( - // - // { - // setKeyDialog(true); - // setStep(11); - // setResendMessage(msg); - // }} - // > - // {resendMessageSvg} - // - // - // )} - //
- // {msg.sent === 0 && ( - // - // - // - // )} - // {msg.sent === 2 && ( - // - // {failedMessageSvg} - // - // )} - //
- //
- // )} - // - // ); - // }) )} @@ -1241,8 +1156,6 @@ export default function ChatBox(props: Props) { > - {step === 5 && confirmationModal(HIDEMESSAGE)} - {step === 6 && confirmationModal(BLOCKUSER)} {step === 9 && confirmationModal(NEWCHATACCOUNT)} {step === 11 && confirmationModal(RESENDMESSAGE)} {step === 10 && successModal(NEWCHATACCOUNT)} diff --git a/src/common/components/chats-channel-messages/index.tsx b/src/common/components/chats-channel-messages/index.tsx index 75a0eb5e3a6..ba6613c27b4 100644 --- a/src/common/components/chats-channel-messages/index.tsx +++ b/src/common/components/chats-channel-messages/index.tsx @@ -55,6 +55,7 @@ interface Props { history: History | null; isScrollToBottom: boolean; isScrolled?: boolean; + isActveUserRemoved: boolean; setActiveUser: (username: string | null) => void; updateActiveUser: (data?: Account) => void; deleteUser: (username: string) => void; @@ -76,6 +77,7 @@ export default function ChatsChannelMessages(props: Props) { isScrollToBottom, isScrolled, global, + isActveUserRemoved, scrollToBottom, currentChannelSetter, deletePublicMessage @@ -95,8 +97,6 @@ export default function ChatsChannelMessages(props: Props) { const [removedUserId, setRemovedUserID] = useState(""); const [privilegedUsers, setPrivilegedUsers] = useState([]); const [hiddenMsgId, setHiddenMsgId] = useState(""); - const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); - const [removedUsers, setRemovedUsers] = useState([]); const [resendMessage, setResendMessage] = useState(); // useEffect(() => { @@ -131,17 +131,9 @@ export default function ChatsChannelMessages(props: Props) { if (currentChannel) { zoomInitializer(); getPrivilegedUsers(currentChannel?.communityModerators!); - currentChannel?.removedUserIds && setRemovedUsers(currentChannel.removedUserIds); } }, [currentChannel]); - useEffect(() => { - if (removedUsers) { - const removed = removedUsers.includes(activeUserKeys?.pub!); - setIsActiveUserRemoved(removed); - } - }, [removedUsers]); - useEffect(() => { const handleClickOutside = (event: MouseEvent) => { const clickedElement = event.target as HTMLElement; @@ -546,6 +538,11 @@ export default function ChatsChannelMessages(props: Props) { ); })} + {isActveUserRemoved && ( + + You have been blocked from this community + + )}
{step !== 0 && ( ([]); + const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); useEffect(() => { getActiveUserKeys(); @@ -106,6 +108,7 @@ export default function ChatsMessagesView(props: Props) { getDirectMessages(); } else if (communityName && currentChannel) { getChannelMessages(); + currentChannel?.removedUserIds && setRemovedUsers(currentChannel.removedUserIds); } }, [directUser, communityName, currentChannel, chat.directMessages]); @@ -121,6 +124,13 @@ export default function ChatsMessagesView(props: Props) { } }, [isTop]); + useEffect(() => { + if (removedUsers) { + const removed = removedUsers.includes(activeUserKeys?.pub!); + setIsActiveUserRemoved(removed); + } + }, [removedUsers]); + const fetchPrevMessages = () => { if (!hasMore || inProgress) return; @@ -239,6 +249,7 @@ export default function ChatsMessagesView(props: Props) { isScrollToBottom={isScrollToBottom} from={CHATPAGE} isScrolled={isScrolled} + isActveUserRemoved={isActveUserRemoved} deletePublicMessage={deletePublicMessage} scrollToBottom={scrollToBottom} currentChannelSetter={currentChannelSetter} @@ -273,7 +284,7 @@ export default function ChatsMessagesView(props: Props) { isCurrentUser={directUser ? true : false} isCommunity={communityName ? true : false} receiverPubKey={receiverPubKey} - isActveUserRemoved={false} + isActveUserRemoved={isActveUserRemoved} currentUser={directUser} currentChannel={currentChannel!} isCurrentUserJoined={true} From 3a16f3a6b555b5f4489cfad96af38d1f25527f8c Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Wed, 6 Sep 2023 11:22:47 +0500 Subject: [PATCH 054/179] Move files in one parent component --- src/common/app.tsx | 11 +- .../{ => chats}/chat-input/index.scss | 0 .../{ => chats}/chat-input/index.tsx | 64 +++-- .../chat-popup}/chat-constants.ts | 0 .../{chat-box => chats/chat-popup}/index.scss | 0 .../{chat-box => chats/chat-popup}/index.tsx | 104 ++++---- .../chats-channel-messages/index.scss | 10 +- .../chats-channel-messages/index.tsx | 102 ++++---- .../chats-community-dropdown-menu/index.scss | 0 .../chats-community-dropdown-menu/index.tsx | 22 +- .../chats-confirmation-modal/index.tsx | 2 +- .../chats-direct-messages/index.scss | 10 +- .../chats-direct-messages/index.tsx | 61 ++--- .../{ => chats}/chats-dropdown-menu/index.tsx | 8 +- .../{ => chats}/chats-messages-box/index.scss | 2 +- .../{ => chats}/chats-messages-box/index.tsx | 30 ++- .../chats-messages-header/index.scss | 2 +- .../chats-messages-header/index.tsx | 31 ++- .../chats-messages-view/index.scss | 2 +- .../{ => chats}/chats-messages-view/index.tsx | 42 ++-- .../{ => chats}/chats-profile-box/index.scss | 2 +- .../{ => chats}/chats-profile-box/index.tsx | 25 +- .../{ => chats}/chats-scroller/index.scss | 2 +- .../{ => chats}/chats-scroller/index.tsx | 6 +- .../{ => chats}/chats-sidebar/indes.tsx | 51 ++-- .../{ => chats}/chats-sidebar/index.scss | 2 +- .../join-community-chat-btn/index.tsx | 20 +- .../components/chats/types/chat-types.ts | 25 ++ src/common/components/chats/types/index.ts | 1 + .../chats/utils/copy-to-clipboard.ts | 8 + .../chats/utils/create-nostr-account.ts | 7 + .../chats/utils/delete-chat-public-key.ts | 22 ++ .../chats/utils/fetch-profile-metadata.ts | 11 + .../chats/utils/format-message-data.ts | 0 .../chats/utils/format-message-time.ts | 5 + .../chats/utils/formatted-user-name.ts | 6 + .../chats/utils/get-chat-private-key.ts | 5 + .../chats/utils/get-joined-communities.ts | 5 + .../chats/utils/get-user-chat-public-key.ts | 10 + src/common/components/chats/utils/index.ts | 17 ++ .../components/chats/utils/is-message-gif.ts | 5 + .../chats/utils/is-message-image.ts | 3 + .../chats/utils/upload-channel-data.ts | 20 ++ .../chats/utils/upload-chat-public-key.ts | 16 ++ .../utils/use-fetch-community-messages.ts | 19 ++ .../chats/utils/use-fetch-direct-messages.ts | 10 + .../utils/use-get-community-last-message.ts | 20 ++ .../utils/use-get-direct-last-message.ts | 17 ++ .../components/community-cover/index.tsx | 2 +- src/common/components/emoji-picker/index.tsx | 2 +- src/common/components/login/index.tsx | 2 +- src/common/helper/chat-utils.ts | 234 ------------------ src/common/helper/message-service.ts | 31 ++- src/common/pages/chats/index.scss | 8 +- src/common/pages/chats/index.tsx | 39 ++- src/common/pages/community-functional.tsx | 16 +- src/providers/message-provider.tsx | 3 +- 57 files changed, 598 insertions(+), 582 deletions(-) rename src/common/components/{ => chats}/chat-input/index.scss (100%) rename src/common/components/{ => chats}/chat-input/index.tsx (84%) rename src/common/components/{chat-box => chats/chat-popup}/chat-constants.ts (100%) rename src/common/components/{chat-box => chats/chat-popup}/index.scss (100%) rename src/common/components/{chat-box => chats/chat-popup}/index.tsx (95%) rename src/common/components/{ => chats}/chats-channel-messages/index.scss (96%) rename src/common/components/{ => chats}/chats-channel-messages/index.tsx (88%) rename src/common/components/{ => chats}/chats-community-dropdown-menu/index.scss (100%) rename src/common/components/{ => chats}/chats-community-dropdown-menu/index.tsx (96%) rename src/common/components/{ => chats}/chats-confirmation-modal/index.tsx (97%) rename src/common/components/{ => chats}/chats-direct-messages/index.scss (95%) rename src/common/components/{ => chats}/chats-direct-messages/index.tsx (77%) rename src/common/components/{ => chats}/chats-dropdown-menu/index.tsx (78%) rename src/common/components/{ => chats}/chats-messages-box/index.scss (93%) rename src/common/components/{ => chats}/chats-messages-box/index.tsx (77%) rename src/common/components/{ => chats}/chats-messages-header/index.scss (97%) rename src/common/components/{ => chats}/chats-messages-header/index.tsx (57%) rename src/common/components/{ => chats}/chats-messages-view/index.scss (89%) rename src/common/components/{ => chats}/chats-messages-view/index.tsx (89%) rename src/common/components/{ => chats}/chats-profile-box/index.scss (96%) rename src/common/components/{ => chats}/chats-profile-box/index.tsx (84%) rename src/common/components/{ => chats}/chats-scroller/index.scss (92%) rename src/common/components/{ => chats}/chats-scroller/index.tsx (85%) rename src/common/components/{ => chats}/chats-sidebar/indes.tsx (88%) rename src/common/components/{ => chats}/chats-sidebar/index.scss (99%) rename src/common/components/{ => chats}/join-community-chat-btn/index.tsx (93%) create mode 100644 src/common/components/chats/types/chat-types.ts create mode 100644 src/common/components/chats/types/index.ts create mode 100644 src/common/components/chats/utils/copy-to-clipboard.ts create mode 100644 src/common/components/chats/utils/create-nostr-account.ts create mode 100644 src/common/components/chats/utils/delete-chat-public-key.ts create mode 100644 src/common/components/chats/utils/fetch-profile-metadata.ts create mode 100644 src/common/components/chats/utils/format-message-data.ts create mode 100644 src/common/components/chats/utils/format-message-time.ts create mode 100644 src/common/components/chats/utils/formatted-user-name.ts create mode 100644 src/common/components/chats/utils/get-chat-private-key.ts create mode 100644 src/common/components/chats/utils/get-joined-communities.ts create mode 100644 src/common/components/chats/utils/get-user-chat-public-key.ts create mode 100644 src/common/components/chats/utils/index.ts create mode 100644 src/common/components/chats/utils/is-message-gif.ts create mode 100644 src/common/components/chats/utils/is-message-image.ts create mode 100644 src/common/components/chats/utils/upload-channel-data.ts create mode 100644 src/common/components/chats/utils/upload-chat-public-key.ts create mode 100644 src/common/components/chats/utils/use-fetch-community-messages.ts create mode 100644 src/common/components/chats/utils/use-fetch-direct-messages.ts create mode 100644 src/common/components/chats/utils/use-get-community-last-message.ts create mode 100644 src/common/components/chats/utils/use-get-direct-last-message.ts delete mode 100644 src/common/helper/chat-utils.ts diff --git a/src/common/app.tsx b/src/common/app.tsx index 08014f1886b..03c7c378456 100644 --- a/src/common/app.tsx +++ b/src/common/app.tsx @@ -25,7 +25,7 @@ import { connect } from "react-redux"; import loadable from "@loadable/component"; import Announcement from "./components/announcement"; import FloatingFAQ from "./components/floating-faq"; -import ChatBox from "./components/chat-box"; +import ChatPopUp from "./components/chats/chat-popup"; import { useMappedStore } from "./store/use-mapped-store"; import { EntriesCacheManager } from "./core"; @@ -135,7 +135,12 @@ const App = (props: any) => { - + @@ -180,7 +185,7 @@ const App = (props: any) => { - + ); }; diff --git a/src/common/components/chat-input/index.scss b/src/common/components/chats/chat-input/index.scss similarity index 100% rename from src/common/components/chat-input/index.scss rename to src/common/components/chats/chat-input/index.scss diff --git a/src/common/components/chat-input/index.tsx b/src/common/components/chats/chat-input/index.tsx similarity index 84% rename from src/common/components/chat-input/index.tsx rename to src/common/components/chats/chat-input/index.tsx index 16d1e483e94..306716c072f 100644 --- a/src/common/components/chat-input/index.tsx +++ b/src/common/components/chats/chat-input/index.tsx @@ -1,34 +1,33 @@ -import React, { useEffect, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { Form, FormControl, InputGroup } from "react-bootstrap"; import axios from "axios"; -import { Channel } from "../../../providers/message-provider-types"; -import { Chat } from "../../store/chat/types"; -import { ActiveUser } from "../../store/active-user/types"; -import { Global } from "../../store/global/types"; - -import ClickAwayListener from "../clickaway-listener"; -import EmojiPicker from "../emoji-picker"; -import GifPicker from "../gif-picker"; -import { error } from "../feedback"; -import Tooltip from "../tooltip"; - -import { emoticonHappyOutlineSvg, gifIcon, chatBoxImageSvg, messageSendSvg } from "../../img/svg"; -import { GifImagesStyle, UPLOADING } from "../chat-box/chat-constants"; - -import { _t } from "../../i18n"; -import { getAccessToken } from "../../helper/user-token"; -import { uploadImage } from "../../api/misc"; -import { addImage } from "../../api/private-api"; +import { Channel } from "../../../../providers/message-provider-types"; +import { EmojiPickerStyleProps } from "../types"; + +import ClickAwayListener from "../../clickaway-listener"; +import EmojiPicker from "../../emoji-picker"; +import GifPicker from "../../gif-picker"; +import { error } from "../../feedback"; +import Tooltip from "../../tooltip"; + +import { + emoticonHappyOutlineSvg, + gifIcon, + chatBoxImageSvg, + messageSendSvg +} from "../../../img/svg"; +import { GifImagesStyle, UPLOADING } from "../chat-popup/chat-constants"; + +import { useMappedStore } from "../../../store/use-mapped-store"; +import { _t } from "../../../i18n"; +import { getAccessToken } from "../../../helper/user-token"; +import { uploadImage } from "../../../api/misc"; +import { addImage } from "../../../api/private-api"; import "./index.scss"; -import { EmojiPickerStyleProps } from "../chat-box"; -import LinearProgress from "../linear-progress"; interface Props { - activeUser: ActiveUser | null; - global: Global; - chat: Chat; isCurrentUser: boolean; isCommunity: boolean; receiverPubKey: string | null; @@ -41,7 +40,9 @@ interface Props { } export default function ChatInput(props: Props) { - const fileInput = React.createRef(); + const fileInputRef = useRef(null); + const { global, activeUser, chat } = useMappedStore(); + const [showEmojiPicker, setShowEmojiPicker] = useState(false); const [message, setMessage] = useState(""); const [shGif, setShGif] = useState(false); @@ -58,7 +59,6 @@ export default function ChatInput(props: Props) { emojiPickerStyles, gifPickerStyle } = props; - // console.log("currentUser in chat input", currentUser); useEffect(() => { if (!isCurrentUser && !isCommunity) { @@ -100,7 +100,7 @@ export default function ChatInput(props: Props) { } if ( receiverPubKey && - !props.chat.directContacts.some((contact) => contact.name === currentUser) && + !chat.directContacts.some((contact) => contact.name === currentUser) && isCurrentUser ) { console.log("Contact has been published"); @@ -116,9 +116,7 @@ export default function ChatInput(props: Props) { const fileInputChanged = (e: React.ChangeEvent): void => { let files = [...(e.target.files as FileList)].filter((i) => checkFile(i.name)).filter((i) => i); - const { - global: { isElectron } - } = props; + const { isElectron } = global; if (files.length > 0) { e.stopPropagation(); @@ -138,8 +136,6 @@ export default function ChatInput(props: Props) { e.target.value = ""; }; const upload = async (file: File) => { - const { activeUser, global } = props; - const username = activeUser?.username!; const tempImgTag = `![Uploading ${file.name} #${Math.floor(Math.random() * 99)}]()\n\n`; @@ -235,7 +231,7 @@ export default function ChatInput(props: Props) { className="chatbox-image" onClick={(e: React.MouseEvent) => { e.stopPropagation(); - const el = fileInput.current; + const el = fileInputRef.current; if (el) el.click(); }} > @@ -246,7 +242,7 @@ export default function ChatInput(props: Props) { void; } -export default function ChatBox(props: Props) { +export default function ChatPopUp(props: Props) { const routerLocation = useLocation(); const prevPropsRef = useRef(props); - const chatBodyDivRef = React.createRef(); + const chatBodyDivRef = useRef(null); + + const { activeUser } = useMappedStore(); + const [expanded, setExpanded] = useState(false); const [currentUser, setCurrentUser] = useState(""); const [isCurrentUser, setIsCurrentUser] = useState(false); @@ -239,7 +222,7 @@ export default function ChatBox(props: Props) { }, [typeof window !== "undefined" && window?.messageService]); useEffect(() => { - const communities = getCommunities(props.chat.channels, props.chat.leftChannelsList); + const communities = getJoinedCommunities(props.chat.channels, props.chat.leftChannelsList); setCommunities(communities); fetchProfileData(); }, [props.chat.channels, props.chat.leftChannelsList]); @@ -584,7 +567,7 @@ export default function ChatBox(props: Props) { switch (actionType) { case NEWCHATACCOUNT: setInProgress(true); - resetProfile(props.activeUser) + deleteChatPublicKey(props.activeUser) .then((updatedProfile) => { handleJoinChat(); setStep(10); @@ -1129,7 +1112,6 @@ export default function ChatBox(props: Props) {
{(isCurrentUser || isCommunity) && ( (null); const channelMessagesRef = React.createRef(); @@ -105,18 +98,11 @@ export default function ChatsChannelMessages(props: Props) { // }, [publicMessages]); useEffect(() => { - const prevProps = prevPropsRef.current; - if (prevProps.global.theme !== props.global.theme) { + if (prevGlobal?.theme !== global.theme) { setBackground(); } - // if (prevProps.activeUser?.username !== props.activeUser?.username) { - // setIsCommunity(false); - // setIsCurrentUser(false); - // setCurrentUser(""); - // setCommunityName(""); - // } - prevPropsRef.current = props; - }, [props.global.theme, props.activeUser]); + prevGlobal = global; + }, [global.theme, activeUser]); useEffect(() => { if (publicMessages.length !== 0) { @@ -238,6 +224,11 @@ export default function ChatsChannelMessages(props: Props) { setCommunityAdmins(communityAdminNames); }; + const getProfileName = (creator: string, profiles: Profile[]) => { + const profile = profiles.find((x) => x.creator === creator); + return profile?.name; + }; + const handleConfirm = () => { let updatedMetaData = { name: currentChannel?.name!, @@ -318,13 +309,29 @@ export default function ChatsChannelMessages(props: Props) { } }; + const getFormattedDateAndDay = (msg: PublicMessage, i: number) => { + const prevMsg = publicMessages[i - 1]; + const msgDate = formatMessageDate(msg.created); + const prevMsgDate = prevMsg ? formatMessageDate(prevMsg.created) : null; + if (msgDate !== prevMsgDate) { + return ( +
+ + {msgDate} + +
+ ); + } + return <>; + }; + return ( <>
{publicMessages.length !== 0 && activeUserKeys && publicMessages.map((pMsg, i) => { - const dayAndMonth = getFormattedDateAndDay(pMsg, i, publicMessages); + const dayAndMonth = getFormattedDateAndDay(pMsg, i); let renderedPreview = renderPostBody(pMsg.content, false, global.canUseWebp); renderedPreview = renderedPreview.replace(/]*>/g, ""); @@ -353,7 +360,12 @@ export default function ChatsChannelMessages(props: Props) { : "justify-content-center" } profile-box-buttons`} > - + {communityAdmins.includes(activeUser?.username!) && ( <> @@ -414,11 +426,7 @@ export default function ChatsChannelMessages(props: Props) { return ( - {dayAndMonth !== null && ( - - {dayAndMonth} - - )} + {dayAndMonth} {pMsg.creator !== activeUserKeys?.pub ? (
{hoveredMessageId === pMsg.id && - privilegedUsers.includes(props.activeUser?.username!) && ( + privilegedUsers.includes(activeUser?.username!) && (

void; @@ -38,21 +32,18 @@ export default function ChatsDirectMessages(props: Props) { activeUserKeys, currentUser, isScrolled, - global, isScrollToBottom, - activeUser, scrollToBottom } = props; - const { chat } = useMappedStore(); + const { chat, global, activeUser } = useMappedStore(); - const prevPropsRef = useRef(props); + let prevGlobal = usePrevious(global); useEffect(() => { - const prevProps = prevPropsRef.current; - if (prevProps.global.theme !== global.theme) { + if (prevGlobal?.theme !== global.theme) { setBackground(); } - prevPropsRef.current = props; + prevGlobal = global; }, [global.theme, activeUser]); useEffect(() => { @@ -78,11 +69,27 @@ export default function ChatsDirectMessages(props: Props) { } }; + const getFormattedDateAndDay = (msg: DirectMessage, i: number) => { + const prevMsg = directMessages[i - 1]; + const msgDate = formatMessageDate(msg.created); + const prevMsgDate = prevMsg ? formatMessageDate(prevMsg.created) : null; + if (msgDate !== prevMsgDate) { + return ( +

+ + {msgDate} + +
+ ); + } + return <>; + }; + return (
{directMessages && directMessages.map((msg, i) => { - const dayAndMonth = getFormattedDateAndDay(msg, i, directMessages); + const dayAndMonth = getFormattedDateAndDay(msg, i); let renderedPreview = renderPostBody(msg.content, false, global.canUseWebp); renderedPreview = renderedPreview.replace(/]*>/g, ""); @@ -94,11 +101,7 @@ export default function ChatsDirectMessages(props: Props) { return ( - {dayAndMonth !== null && ( - - {dayAndMonth} - - )} + {dayAndMonth} {msg.creator !== activeUserKeys?.pub ? (
diff --git a/src/common/components/chats-dropdown-menu/index.tsx b/src/common/components/chats/chats-dropdown-menu/index.tsx similarity index 78% rename from src/common/components/chats-dropdown-menu/index.tsx rename to src/common/components/chats/chats-dropdown-menu/index.tsx index 3e82eaa4387..ae335b04534 100644 --- a/src/common/components/chats-dropdown-menu/index.tsx +++ b/src/common/components/chats/chats-dropdown-menu/index.tsx @@ -1,10 +1,10 @@ import React from "react"; import { History } from "history"; -import DropDown, { MenuItem } from "../dropdown"; +import DropDown, { MenuItem } from "../../dropdown"; -import { KebabMenu, chatKeySvg } from "../../img/svg"; -import { _t } from "../../i18n"; -import { DropDownStyle } from "../chat-box/chat-constants"; +import { KebabMenu, chatKeySvg } from "../../../img/svg"; +import { _t } from "../../../i18n"; +import { DropDownStyle } from "../chat-popup/chat-constants"; interface Props { history: History | null; diff --git a/src/common/components/chats-messages-box/index.scss b/src/common/components/chats/chats-messages-box/index.scss similarity index 93% rename from src/common/components/chats-messages-box/index.scss rename to src/common/components/chats/chats-messages-box/index.scss index 6603c8741ef..fcf93fe9ca6 100644 --- a/src/common/components/chats-messages-box/index.scss +++ b/src/common/components/chats/chats-messages-box/index.scss @@ -1,4 +1,4 @@ -@import "../../../style/vars_mixins"; +@import "../../../../style/vars_mixins"; .chats-messages-box { margin-top: 10px; diff --git a/src/common/components/chats-messages-box/index.tsx b/src/common/components/chats/chats-messages-box/index.tsx similarity index 77% rename from src/common/components/chats-messages-box/index.tsx rename to src/common/components/chats/chats-messages-box/index.tsx index 41a644abce7..1f320527604 100644 --- a/src/common/components/chats-messages-box/index.tsx +++ b/src/common/components/chats/chats-messages-box/index.tsx @@ -1,19 +1,18 @@ import React, { useEffect, useState } from "react"; import { match } from "react-router"; import { History } from "history"; -import { Account } from "../../store/accounts/types"; -import { ActiveUser } from "../../store/active-user/types"; -import { Chat } from "../../store/chat/types"; -import { ToggleType, UI } from "../../store/ui/types"; -import { User } from "../../store/users/types"; +import { Account } from "../../../store/accounts/types"; +import { ToggleType, UI } from "../../../store/ui/types"; +import { User } from "../../../store/users/types"; import ChatsMessagesHeader from "../chats-messages-header"; import ChatsMessagesView from "../chats-messages-view"; -import { Global, Theme } from "../../store/global/types"; import "./index.scss"; -import { formattedUserName, NostrKeysType } from "../../helper/chat-utils"; -import { Channel, ChannelUpdate } from "../../../providers/message-provider-types"; -import LinearProgress from "../linear-progress"; +import { Channel, ChannelUpdate } from "../../../../providers/message-provider-types"; +import LinearProgress from "../../linear-progress"; +import { NostrKeysType } from "../types"; +import { formattedUserName } from "../utils"; +import { useMappedStore } from "../../../store/use-mapped-store"; interface MatchParams { filter: string; @@ -25,11 +24,8 @@ interface MatchParams { interface Props { match: match; - chat: Chat; - global: Global; users: User[]; history: History | null; - activeUser: ActiveUser | null; ui: UI; activeUserKeys: NostrKeysType; setActiveUser: (username: string | null) => void; @@ -40,7 +36,9 @@ interface Props { } export default function ChatsMessagesBox(props: Props) { - const { match, global, deletePublicMessage } = props; + const { activeUser, chat } = useMappedStore(); + const { channels, updatedChannel } = chat; + const { match, deletePublicMessage } = props; const username = match.params.username; const [maxHeight, setMaxHeight] = useState(0); @@ -57,12 +55,12 @@ export default function ChatsMessagesBox(props: Props) { useEffect(() => { fetchCurrentChannel(formattedUserName(username)); - }, [props.chat.updatedChannel, username, props.chat.channels]); + }, [updatedChannel, username, channels]); const fetchCurrentChannel = (communityName: string) => { - const channel = props.chat.channels.find((channel) => channel.communityName === communityName); + const channel = channels.find((channel) => channel.communityName === communityName); if (channel) { - const updated: ChannelUpdate = props.chat.updatedChannel + const updated: ChannelUpdate = updatedChannel .filter((x) => x.channelId === channel.id) .sort((a, b) => b.created - a.created)[0]; if (updated) { diff --git a/src/common/components/chats-messages-header/index.scss b/src/common/components/chats/chats-messages-header/index.scss similarity index 97% rename from src/common/components/chats-messages-header/index.scss rename to src/common/components/chats/chats-messages-header/index.scss index 9dc3eb10914..3bc216a772e 100644 --- a/src/common/components/chats-messages-header/index.scss +++ b/src/common/components/chats/chats-messages-header/index.scss @@ -1,4 +1,4 @@ -@import "../../../style/vars_mixins"; +@import "../../../../style/vars_mixins"; .chats-messages-header { grid-row: span 1; diff --git a/src/common/components/chats-messages-header/index.tsx b/src/common/components/chats/chats-messages-header/index.tsx similarity index 57% rename from src/common/components/chats-messages-header/index.tsx rename to src/common/components/chats/chats-messages-header/index.tsx index 43a08d06360..9c46c04e357 100644 --- a/src/common/components/chats-messages-header/index.tsx +++ b/src/common/components/chats/chats-messages-header/index.tsx @@ -1,14 +1,15 @@ import React, { useEffect, useState } from "react"; import { History } from "history"; -import { formattedName, formattedUserName, isChannel } from "../../helper/chat-utils"; -import { _t } from "../../i18n"; -import { useMappedStore } from "../../store/use-mapped-store"; +import { _t } from "../../../i18n"; +import { useMappedStore } from "../../../store/use-mapped-store"; import ChatsCommunityDropdownMenu from "../chats-community-dropdown-menu"; -import UserAvatar from "../user-avatar"; +import UserAvatar from "../../user-avatar"; import "./index.scss"; -import { Channel } from "../../../providers/message-provider-types"; -import { CHATPAGE } from "../chat-box/chat-constants"; +import { Channel } from "../../../../providers/message-provider-types"; +import { CHATPAGE } from "../chat-popup/chat-constants"; +import { Chat } from "../../../store/chat/types"; +import { formattedUserName } from "../utils"; interface Props { username: string; @@ -21,6 +22,24 @@ export default function ChatsMessagesHeader(props: Props) { const { username, currentChannel, currentChannelSetter } = props; const { chat } = useMappedStore(); + const isChannel = (username: string) => { + if (username.startsWith("@")) { + return false; + } + return true; + }; + + const formattedName = (username: string, chat: Chat) => { + if (username && !username.startsWith("@")) { + const community = chat.channels.find((channel) => channel.communityName === username); + if (community) { + return community.name; + } + } + const replacedUserName = username.replace("@", ""); + return replacedUserName; + }; + return (
diff --git a/src/common/components/chats-messages-view/index.scss b/src/common/components/chats/chats-messages-view/index.scss similarity index 89% rename from src/common/components/chats-messages-view/index.scss rename to src/common/components/chats/chats-messages-view/index.scss index 55c51e27342..5b0b1467b8b 100644 --- a/src/common/components/chats-messages-view/index.scss +++ b/src/common/components/chats/chats-messages-view/index.scss @@ -1,4 +1,4 @@ -@import "../../../style/vars_mixins"; +@import "../../../../style/vars_mixins"; .chats-messages-view { @include themify(day) { diff --git a/src/common/components/chats-messages-view/index.tsx b/src/common/components/chats/chats-messages-view/index.tsx similarity index 89% rename from src/common/components/chats-messages-view/index.tsx rename to src/common/components/chats/chats-messages-view/index.tsx index 2e2f1cf49a2..d8bba46f97f 100644 --- a/src/common/components/chats-messages-view/index.tsx +++ b/src/common/components/chats/chats-messages-view/index.tsx @@ -1,31 +1,32 @@ import React, { RefObject, useEffect, useRef, useState } from "react"; import { Link } from "react-router-dom"; -import { Channel, DirectMessage, PublicMessage } from "../../../providers/message-provider-types"; +import { + Channel, + DirectMessage, + PublicMessage +} from "../../../../providers/message-provider-types"; import { fetchCommunityMessages, - fetchCurrentUserData, fetchDirectMessages, getPrivateKey, - getProfileMetaData, - NostrKeysType -} from "../../helper/chat-utils"; + getProfileMetaData +} from "../utils"; import { History } from "history"; -import { useMappedStore } from "../../store/use-mapped-store"; -import { Global, Theme } from "../../store/global/types"; +import { useMappedStore } from "../../../store/use-mapped-store"; +import { Global } from "../../../store/global/types"; import ChatsProfileBox from "../chats-profile-box"; import "./index.scss"; -import { _t } from "../../i18n"; +import { _t } from "../../../i18n"; import ChatsChannelMessages from "../chats-channel-messages"; import ChatsDirectMessages from "../chats-direct-messages"; -import { Account } from "../../store/accounts/types"; -import { ToggleType, UI } from "../../store/ui/types"; -import { User } from "../../store/users/types"; -import { ActiveUser } from "../../store/active-user/types"; +import { Account } from "../../../store/accounts/types"; +import { ToggleType, UI } from "../../../store/ui/types"; +import { User } from "../../../store/users/types"; import ChatInput from "../chat-input"; import ChatsScroller from "../chats-scroller"; -import { EmojiPickerStyleProps } from "../chat-box"; -import { CHATPAGE } from "../chat-box/chat-constants"; +import { CHATPAGE } from "../chat-popup/chat-constants"; +import { EmojiPickerStyleProps, NostrKeysType } from "../types"; const EmojiPickerStyle: EmojiPickerStyleProps = { width: "56.5%", @@ -40,9 +41,7 @@ interface Props { username: string; users: User[]; history: History | null; - activeUser: ActiveUser | null; ui: UI; - global: Global; currentChannel: Channel; inProgress: boolean; setActiveUser: (username: string | null) => void; @@ -57,8 +56,6 @@ interface Props { export default function ChatsMessagesView(props: Props) { const { username, - activeUser, - global, currentChannel, inProgress, currentChannelSetter, @@ -68,7 +65,7 @@ export default function ChatsMessagesView(props: Props) { const messagesBoxRef = useRef(null); - const { chat } = useMappedStore(); + const { chat, activeUser } = useMappedStore(); const [directUser, setDirectUser] = useState(""); const [publicMessages, setPublicMessages] = useState([]); const [directMessages, setDirectMessages] = useState([]); @@ -150,8 +147,8 @@ export default function ChatsMessagesView(props: Props) { }; const getActiveUserKeys = async () => { - const profileData = await getProfileMetaData(props.activeUser?.username!); - const noStrPrivKey = getPrivateKey(props.activeUser?.username!); + const profileData = await getProfileMetaData(activeUser?.username!); + const noStrPrivKey = getPrivateKey(activeUser?.username!); const activeUserKeys = { pub: profileData?.nsKey, priv: noStrPrivKey @@ -278,9 +275,6 @@ export default function ChatsMessagesView(props: Props) { { + if (count) { + return count >= 1e6 + ? (count / 1e6).toLocaleString() + "M" + : count >= 1e3 + ? (count / 1e3).toLocaleString() + "K" + : count.toLocaleString(); + } + return count; + }; + const fetchProfileData = async () => { if (username && username?.length !== 0) { if (username?.startsWith("@")) { diff --git a/src/common/components/chats-scroller/index.scss b/src/common/components/chats/chats-scroller/index.scss similarity index 92% rename from src/common/components/chats-scroller/index.scss rename to src/common/components/chats/chats-scroller/index.scss index 36f090571a1..aefcae9b48f 100644 --- a/src/common/components/chats-scroller/index.scss +++ b/src/common/components/chats/chats-scroller/index.scss @@ -1,4 +1,4 @@ -@import "../../../style/vars_mixins"; +@import "../../../../style/vars_mixins"; .scroller { position: sticky; diff --git a/src/common/components/chats-scroller/index.tsx b/src/common/components/chats/chats-scroller/index.tsx similarity index 85% rename from src/common/components/chats-scroller/index.tsx rename to src/common/components/chats/chats-scroller/index.tsx index c32b793f012..8f77b368621 100644 --- a/src/common/components/chats-scroller/index.tsx +++ b/src/common/components/chats/chats-scroller/index.tsx @@ -1,8 +1,8 @@ import React from "react"; -import Tooltip from "../tooltip"; +import Tooltip from "../../tooltip"; -import { _t } from "../../i18n"; -import { chevronDownSvgForSlider, chevronUpSvg } from "../../img/svg"; +import { _t } from "../../../i18n"; +import { chevronDownSvgForSlider, chevronUpSvg } from "../../../img/svg"; import "./index.scss"; diff --git a/src/common/components/chats-sidebar/indes.tsx b/src/common/components/chats/chats-sidebar/indes.tsx similarity index 88% rename from src/common/components/chats-sidebar/indes.tsx rename to src/common/components/chats/chats-sidebar/indes.tsx index f601420deb5..ffa213f8dcc 100644 --- a/src/common/components/chats-sidebar/indes.tsx +++ b/src/common/components/chats/chats-sidebar/indes.tsx @@ -4,29 +4,25 @@ import { Link } from "react-router-dom"; import { History } from "history"; import { match } from "react-router"; import useDebounce from "react-use/lib/useDebounce"; -import { setNostrkeys } from "../../../providers/message-provider"; -import { Channel } from "../../../providers/message-provider-types"; -import { getAccountReputations } from "../../api/hive"; -import accountReputation from "../../helper/account-reputation"; -import { - getCommunities, - getCommunityLastMessage, - getDirectLastMessage, - NostrKeysType -} from "../../helper/chat-utils"; -import { _t } from "../../i18n"; -import { arrowBackSvg, chatKeySvg, KebabMenu, syncSvg } from "../../img/svg"; -import { Chat, DirectContactsType } from "../../store/chat/types"; -import { AccountWithReputation } from "../chat-box"; +import { setNostrkeys } from "../../../../providers/message-provider"; +import { Channel } from "../../../../providers/message-provider-types"; +import { getAccountReputations } from "../../../api/hive"; +import accountReputation from "../../../helper/account-reputation"; +import { getCommunityLastMessage, getDirectLastMessage, getJoinedCommunities } from "../utils"; +import { _t } from "../../../i18n"; +import { arrowBackSvg, syncSvg } from "../../../img/svg"; +import { Chat, DirectContactsType } from "../../../store/chat/types"; import ChatsScroller from "../chats-scroller"; -import LinearProgress from "../linear-progress"; -import Tooltip from "../tooltip"; -import UserAvatar from "../user-avatar"; +import LinearProgress from "../../linear-progress"; +import Tooltip from "../../tooltip"; +import UserAvatar from "../../user-avatar"; import "./index.scss"; -import DropDown, { MenuItem } from "../dropdown"; -import { CHAT, DropDownStyle } from "../chat-box/chat-constants"; +import DropDown, { MenuItem } from "../../dropdown"; +import { CHAT, DropDownStyle } from "../chat-popup/chat-constants"; import ChatsDropdownMenu from "../chats-dropdown-menu"; +import { AccountWithReputation, NostrKeysType } from "../types"; +import { useMappedStore } from "../../../store/use-mapped-store"; interface MatchParams { filter: string; @@ -37,11 +33,11 @@ interface MatchParams { } interface Props { match: match; - channels: Channel[]; - directContacts: DirectContactsType[]; + // channels: Channel[]; + // directContacts: DirectContactsType[]; activeUserKeys: NostrKeysType; chatPrivKey: string; - chat: Chat; + // chat: Chat; history: History; revelPrivateKey: boolean; inProgressSetter: (d: boolean) => void; @@ -49,10 +45,9 @@ interface Props { resetChat: () => void; } export default function ChatsSideBar(props: Props) { + const { chat } = useMappedStore(); + const { channels, directContacts, leftChannelsList } = chat; const { - channels, - chat, - directContacts, activeUserKeys, match, revelPrivateKey, @@ -72,8 +67,6 @@ export default function ChatsSideBar(props: Props) { const [isScrollToTop, setIsScrollToTop] = useState(false); const [communities, setCommunities] = useState([]); - // console.log("username in chats sidebar", username); - useDebounce( async () => { if (searchText.length !== 0) { @@ -94,10 +87,10 @@ export default function ChatsSideBar(props: Props) { // }, [isScrollToTop]); useEffect(() => { - const communities = getCommunities(props.chat.channels, props.chat.leftChannelsList); + const communities = getJoinedCommunities(channels, leftChannelsList); setCommunities(communities); // fetchProfileData(); - }, [props.chat.channels, props.chat.leftChannelsList]); + }, [channels, leftChannelsList]); // const communities = [ // { diff --git a/src/common/components/chats-sidebar/index.scss b/src/common/components/chats/chats-sidebar/index.scss similarity index 99% rename from src/common/components/chats-sidebar/index.scss rename to src/common/components/chats/chats-sidebar/index.scss index 3e18bf38897..bbdf3e28add 100644 --- a/src/common/components/chats-sidebar/index.scss +++ b/src/common/components/chats/chats-sidebar/index.scss @@ -1,4 +1,4 @@ -@import "../../../style/vars_mixins"; +@import "../../../../style/vars_mixins"; .chats-sidebar { @include themify(day) { diff --git a/src/common/components/join-community-chat-btn/index.tsx b/src/common/components/chats/join-community-chat-btn/index.tsx similarity index 93% rename from src/common/components/join-community-chat-btn/index.tsx rename to src/common/components/chats/join-community-chat-btn/index.tsx index 39140dbe999..10c80455467 100644 --- a/src/common/components/join-community-chat-btn/index.tsx +++ b/src/common/components/chats/join-community-chat-btn/index.tsx @@ -2,23 +2,23 @@ import React, { useEffect, useState } from "react"; import { Button, Spinner } from "react-bootstrap"; import { History } from "history"; -import { Community, ROLES } from "../../store/communities/types"; -import { ActiveUser } from "../../store/active-user/types"; +import { Community, ROLES } from "../../../store/communities/types"; +import { ActiveUser } from "../../../store/active-user/types"; -import { _t } from "../../i18n"; +import { _t } from "../../../i18n"; -import { useMappedStore } from "../../store/use-mapped-store"; -import { Channel, communityModerator } from "../../../providers/message-provider-types"; +import { useMappedStore } from "../../../store/use-mapped-store"; +import { Channel, communityModerator } from "../../../../providers/message-provider-types"; +import * as ls from "../../../util/local-storage"; +import { setNostrkeys } from "../../../../providers/message-provider"; +import { NOSTRKEY } from "../chat-popup/chat-constants"; +import { NostrKeysType } from "../types"; import { createNoStrAccount, getProfileMetaData, - NostrKeysType, setChannelMetaData, setProfileMetaData -} from "../../helper/chat-utils"; -import * as ls from "../../util/local-storage"; -import { setNostrkeys } from "../../../providers/message-provider"; -import { NOSTRKEY } from "../chat-box/chat-constants"; +} from "../utils"; interface Props { history: History; diff --git a/src/common/components/chats/types/chat-types.ts b/src/common/components/chats/types/chat-types.ts new file mode 100644 index 00000000000..1ccd0e3e5b5 --- /dev/null +++ b/src/common/components/chats/types/chat-types.ts @@ -0,0 +1,25 @@ +export interface NostrKeysType { + pub: string; + priv: string; +} + +export interface profileData { + joiningData: string; + about: string | undefined; + followers: number | undefined; +} + +export interface AccountWithReputation { + account: string; + reputation: number; +} + +export interface EmojiPickerStyleProps { + width: string; + bottom: string; + left: string | number; + marginLeft: string; + borderTopLeftRadius: string; + borderTopRightRadius: string; + borderBottomLeftRadius: string; +} diff --git a/src/common/components/chats/types/index.ts b/src/common/components/chats/types/index.ts new file mode 100644 index 00000000000..44983652000 --- /dev/null +++ b/src/common/components/chats/types/index.ts @@ -0,0 +1 @@ +export * from "./chat-types"; diff --git a/src/common/components/chats/utils/copy-to-clipboard.ts b/src/common/components/chats/utils/copy-to-clipboard.ts new file mode 100644 index 00000000000..2683d6f2725 --- /dev/null +++ b/src/common/components/chats/utils/copy-to-clipboard.ts @@ -0,0 +1,8 @@ +export const copyToClipboard = (content: string) => { + const textField = document.createElement("textarea"); + textField.innerText = content; + document.body.appendChild(textField); + textField.select(); + document.execCommand("copy"); + textField.remove(); +}; diff --git a/src/common/components/chats/utils/create-nostr-account.ts b/src/common/components/chats/utils/create-nostr-account.ts new file mode 100644 index 00000000000..6d2ba017c7b --- /dev/null +++ b/src/common/components/chats/utils/create-nostr-account.ts @@ -0,0 +1,7 @@ +import { generatePrivateKey, getPublicKey } from "../../../../lib/nostr-tools/keys"; + +export const createNoStrAccount = () => { + const priv = generatePrivateKey(); + const pub = getPublicKey(priv); + return { pub, priv }; +}; diff --git a/src/common/components/chats/utils/delete-chat-public-key.ts b/src/common/components/chats/utils/delete-chat-public-key.ts new file mode 100644 index 00000000000..66ad862a979 --- /dev/null +++ b/src/common/components/chats/utils/delete-chat-public-key.ts @@ -0,0 +1,22 @@ +import { getAccountFull } from "../../../api/hive"; +import { updateProfile } from "../../../api/operations"; +import { ActiveUser } from "../../../store/active-user/types"; +import { getProfileMetaData } from "./fetch-profile-metadata"; +import * as ls from "../../../util/local-storage"; + +export const deleteChatPublicKey = (activeUser: ActiveUser | null) => { + return new Promise(async (resolve, reject) => { + try { + const profile = await getProfileMetaData(activeUser?.username!); + delete profile.nsKey; + delete profile.channel; + ls.remove(`${activeUser?.username}_nsPrivKey`); + const response = await getAccountFull(activeUser?.username!); + const updatedProfile = await updateProfile(response, { ...profile }); + console.log("Updated profile", updatedProfile); + resolve(updatedProfile); + } catch (error) { + reject(error); + } + }); +}; diff --git a/src/common/components/chats/utils/fetch-profile-metadata.ts b/src/common/components/chats/utils/fetch-profile-metadata.ts new file mode 100644 index 00000000000..56988efb3cf --- /dev/null +++ b/src/common/components/chats/utils/fetch-profile-metadata.ts @@ -0,0 +1,11 @@ +import { getAccountFull } from "../../../api/hive"; + +export const getProfileMetaData = async (username: string) => { + const response = await getAccountFull(username); + const { posting_json_metadata } = response; + if (posting_json_metadata) { + const profile = JSON.parse(posting_json_metadata!).profile; + + return profile; + } +}; diff --git a/src/common/components/chats/utils/format-message-data.ts b/src/common/components/chats/utils/format-message-data.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/common/components/chats/utils/format-message-time.ts b/src/common/components/chats/utils/format-message-time.ts new file mode 100644 index 00000000000..41d5f69fe87 --- /dev/null +++ b/src/common/components/chats/utils/format-message-time.ts @@ -0,0 +1,5 @@ +import moment from "moment"; + +export const formatMessageTime = (unixTs: number) => moment.unix(unixTs).format("h:mm a"); + +export const formatMessageDate = (unixTs: number) => moment.unix(unixTs).format("dddd, MMMM Do"); diff --git a/src/common/components/chats/utils/formatted-user-name.ts b/src/common/components/chats/utils/formatted-user-name.ts new file mode 100644 index 00000000000..820af9bf5e8 --- /dev/null +++ b/src/common/components/chats/utils/formatted-user-name.ts @@ -0,0 +1,6 @@ +export const formattedUserName = (username: string) => { + if (username && username.startsWith("@")) { + return username.replace("@", ""); + } + return username; +}; diff --git a/src/common/components/chats/utils/get-chat-private-key.ts b/src/common/components/chats/utils/get-chat-private-key.ts new file mode 100644 index 00000000000..c5b04fa691c --- /dev/null +++ b/src/common/components/chats/utils/get-chat-private-key.ts @@ -0,0 +1,5 @@ +import * as ls from "../../../util/local-storage"; + +export const getPrivateKey = (username: string) => { + return ls.get(`${username}_nsPrivKey`); +}; diff --git a/src/common/components/chats/utils/get-joined-communities.ts b/src/common/components/chats/utils/get-joined-communities.ts new file mode 100644 index 00000000000..ff486aa1026 --- /dev/null +++ b/src/common/components/chats/utils/get-joined-communities.ts @@ -0,0 +1,5 @@ +import { Channel } from "../../../../providers/message-provider-types"; + +export const getJoinedCommunities = (channels: Channel[], leftChannels: string[]) => { + return channels.filter((item) => !leftChannels.includes(item.id)); +}; diff --git a/src/common/components/chats/utils/get-user-chat-public-key.ts b/src/common/components/chats/utils/get-user-chat-public-key.ts new file mode 100644 index 00000000000..df9c056a671 --- /dev/null +++ b/src/common/components/chats/utils/get-user-chat-public-key.ts @@ -0,0 +1,10 @@ +import { getAccountFull } from "../../../api/hive"; + +export const getUserChatPublicKey = async (username: string) => { + const response = await getAccountFull(username); + const { posting_json_metadata } = response; + const profile = JSON.parse(posting_json_metadata!).profile; + const { nsKey } = profile || {}; + console.log("nsKey", nsKey); + return nsKey; +}; diff --git a/src/common/components/chats/utils/index.ts b/src/common/components/chats/utils/index.ts new file mode 100644 index 00000000000..d1b52969366 --- /dev/null +++ b/src/common/components/chats/utils/index.ts @@ -0,0 +1,17 @@ +export * from "./fetch-profile-metadata"; +export * from "./delete-chat-public-key"; +export * from "./upload-chat-public-key"; +export * from "./upload-channel-data"; +export * from "./create-nostr-account"; +export * from "./format-message-time"; +export * from "./use-fetch-community-messages"; +export * from "./is-message-gif"; +export * from "./get-user-chat-public-key"; +export * from "./use-get-community-last-message"; +export * from "./use-get-direct-last-message"; +export * from "./formatted-user-name"; +export * from "./is-message-image"; +export * from "./get-chat-private-key"; +export * from "./get-joined-communities"; +export * from "./copy-to-clipboard"; +export * from "./use-fetch-direct-messages"; diff --git a/src/common/components/chats/utils/is-message-gif.ts b/src/common/components/chats/utils/is-message-gif.ts new file mode 100644 index 00000000000..e3e2ee5b823 --- /dev/null +++ b/src/common/components/chats/utils/is-message-gif.ts @@ -0,0 +1,5 @@ +import { GIPHGY } from "../chat-popup/chat-constants"; + +export const isMessageGif = (content: string) => { + return content.includes(GIPHGY); +}; diff --git a/src/common/components/chats/utils/is-message-image.ts b/src/common/components/chats/utils/is-message-image.ts new file mode 100644 index 00000000000..7fbd5b8025c --- /dev/null +++ b/src/common/components/chats/utils/is-message-image.ts @@ -0,0 +1,3 @@ +export const isMessageImage = (content: string) => { + return content.includes("https://images.ecency.com"); +}; diff --git a/src/common/components/chats/utils/upload-channel-data.ts b/src/common/components/chats/utils/upload-channel-data.ts new file mode 100644 index 00000000000..faf34365ff5 --- /dev/null +++ b/src/common/components/chats/utils/upload-channel-data.ts @@ -0,0 +1,20 @@ +import { Channel } from "../../../../providers/message-provider-types"; +import { getAccountFull } from "../../../api/hive"; +import { updateProfile } from "../../../api/operations"; + +export const setChannelMetaData = (username: string, channel: Channel) => { + return new Promise(async (resolve, reject) => { + try { + const response = await getAccountFull(username!); + const { posting_json_metadata } = response; + const profile = JSON.parse(posting_json_metadata!).profile; + const newProfile = { + channel: channel + }; + const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); + resolve(updatedProfile); + } catch (error) { + reject(error); + } + }); +}; diff --git a/src/common/components/chats/utils/upload-chat-public-key.ts b/src/common/components/chats/utils/upload-chat-public-key.ts new file mode 100644 index 00000000000..c407bb527fb --- /dev/null +++ b/src/common/components/chats/utils/upload-chat-public-key.ts @@ -0,0 +1,16 @@ +import { getAccountFull } from "../../../api/hive"; +import { updateProfile } from "../../../api/operations"; +import { ActiveUser } from "../../../store/active-user/types"; + +export const setProfileMetaData = async (activeUser: ActiveUser | null, noStrPubKey: string) => { + const response = await getAccountFull(activeUser?.username!); + + const { posting_json_metadata } = response; + const profile = JSON.parse(posting_json_metadata!).profile; + const newProfile = { + nsKey: noStrPubKey + }; + + const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); + return updatedProfile; +}; diff --git a/src/common/components/chats/utils/use-fetch-community-messages.ts b/src/common/components/chats/utils/use-fetch-community-messages.ts new file mode 100644 index 00000000000..e490e5d828d --- /dev/null +++ b/src/common/components/chats/utils/use-fetch-community-messages.ts @@ -0,0 +1,19 @@ +import { Channel } from "../../../../providers/message-provider-types"; +import { publicMessagesList } from "../../../store/chat/types"; + +export const fetchCommunityMessages = ( + publicMessages: publicMessagesList[], + currentChannel: Channel, + hiddenMessageIds?: string[] +) => { + const hideMessageIds = hiddenMessageIds || currentChannel?.hiddenMessageIds || []; + for (const item of publicMessages) { + if (item.channelId === currentChannel.id) { + const filteredPublicMessages = Object.values(item.PublicMessage).filter( + (message) => !hideMessageIds.includes(message.id) + ); + return filteredPublicMessages; + } + } + return []; +}; diff --git a/src/common/components/chats/utils/use-fetch-direct-messages.ts b/src/common/components/chats/utils/use-fetch-direct-messages.ts new file mode 100644 index 00000000000..189d930d98b --- /dev/null +++ b/src/common/components/chats/utils/use-fetch-direct-messages.ts @@ -0,0 +1,10 @@ +import { directMessagesList } from "../../../store/chat/types"; + +export const fetchDirectMessages = (peer: string, directMessages: directMessagesList[]) => { + for (const item of directMessages) { + if (item.peer === peer) { + return Object.values(item.chat); + } + } + return []; +}; diff --git a/src/common/components/chats/utils/use-get-community-last-message.ts b/src/common/components/chats/utils/use-get-community-last-message.ts new file mode 100644 index 00000000000..24fea4b2ba9 --- /dev/null +++ b/src/common/components/chats/utils/use-get-community-last-message.ts @@ -0,0 +1,20 @@ +import { publicMessagesList } from "../../../store/chat/types"; + +export const getCommunityLastMessage = ( + channelId: string, + publicMessages: publicMessagesList[] +) => { + const msgsList = fetchChannelMessages(channelId!, publicMessages); + const messages = msgsList.sort((a, b) => a.created - b.created); + const lastMessage = messages.slice(-1); + return lastMessage[0]?.content; +}; + +const fetchChannelMessages = (channelId: string, publicMessages: publicMessagesList[]) => { + for (const item of publicMessages) { + if (item.channelId === channelId) { + return Object.values(item.PublicMessage); + } + } + return []; +}; diff --git a/src/common/components/chats/utils/use-get-direct-last-message.ts b/src/common/components/chats/utils/use-get-direct-last-message.ts new file mode 100644 index 00000000000..0dc34b20514 --- /dev/null +++ b/src/common/components/chats/utils/use-get-direct-last-message.ts @@ -0,0 +1,17 @@ +import { directMessagesList } from "../../../store/chat/types"; + +export const getDirectLastMessage = (pubkey: string, directMessages: directMessagesList[]) => { + const msgsList = fetchDirectMessages(pubkey, directMessages); + const messages = msgsList.sort((a, b) => a.created - b.created); + const lastMessage = messages.slice(-1); + return lastMessage[0]?.content; +}; + +const fetchDirectMessages = (peer: string, directMessages: directMessagesList[]) => { + for (const item of directMessages) { + if (item.peer === peer) { + return Object.values(item.chat); + } + } + return []; +}; diff --git a/src/common/components/community-cover/index.tsx b/src/common/components/community-cover/index.tsx index 4b5d92dbe9d..cd8d4bcb893 100644 --- a/src/common/components/community-cover/index.tsx +++ b/src/common/components/community-cover/index.tsx @@ -21,7 +21,7 @@ setProxyBase(defaults.imageServer); import BaseComponent from "../base"; import SubscriptionBtn from "../subscription-btn"; import CommunityPostBtn from "../community-post-btn"; -import JoinCommunityChatBtn from "../join-community-chat-btn"; +import JoinCommunityChatBtn from "../chats/join-community-chat-btn"; import Tooltip from "../tooltip"; import ImageUploadDialog from "../image-upload"; diff --git a/src/common/components/emoji-picker/index.tsx b/src/common/components/emoji-picker/index.tsx index 70e55b5263e..0a0c478a5bc 100644 --- a/src/common/components/emoji-picker/index.tsx +++ b/src/common/components/emoji-picker/index.tsx @@ -4,7 +4,7 @@ import { FormControl } from "react-bootstrap"; import BaseComponent from "../base"; import SearchBox from "../search-box"; -import { EmojiPickerStyleProps } from "../../components/chat-box"; +import { EmojiPickerStyleProps } from "../chats/chat-popup"; import { _t } from "../../i18n"; diff --git a/src/common/components/login/index.tsx b/src/common/components/login/index.tsx index 6abd54bf919..0c770316cea 100644 --- a/src/common/components/login/index.tsx +++ b/src/common/components/login/index.tsx @@ -36,7 +36,7 @@ import { hsTokenRenew } from "../../api/auth-api"; import { formatError, grantPostingPermission, revokePostingPermission } from "../../api/operations"; import { getRefreshToken } from "../../helper/user-token"; -import { getPrivateKey, getProfileMetaData } from "../../helper/chat-utils"; +import { getPrivateKey, getProfileMetaData } from "../../components/chats/utils"; import ReCAPTCHA from "react-google-recaptcha"; diff --git a/src/common/helper/chat-utils.ts b/src/common/helper/chat-utils.ts deleted file mode 100644 index 33234075bb3..00000000000 --- a/src/common/helper/chat-utils.ts +++ /dev/null @@ -1,234 +0,0 @@ -import moment from "moment"; -import { generatePrivateKey, getPublicKey } from "../../lib/nostr-tools/keys"; -import { - Channel, - DirectMessage, - Profile, - PublicMessage -} from "../../providers/message-provider-types"; -import { getAccountFull } from "../api/hive"; -import { updateProfile } from "../api/operations"; -import { GIPHGY } from "../components/chat-box/chat-constants"; -import { ActiveUser } from "../store/active-user/types"; -import { Chat, directMessagesList, publicMessagesList } from "../store/chat/types"; -import * as ls from "../util/local-storage"; - -export interface NostrKeysType { - pub: string; - priv: string; -} - -export const getProfileMetaData = async (username: string) => { - const response = await getAccountFull(username); - const { posting_json_metadata } = response; - if (posting_json_metadata) { - const profile = JSON.parse(posting_json_metadata!).profile; - - return profile; - } -}; - -export const resetProfile = (activeUser: ActiveUser | null) => { - return new Promise(async (resolve, reject) => { - try { - const profile = await getProfileMetaData(activeUser?.username!); - delete profile.nsKey; - delete profile.channel; - ls.remove(`${activeUser?.username}_nsPrivKey`); - const response = await getAccountFull(activeUser?.username!); - const updatedProfile = await updateProfile(response, { ...profile }); - console.log("Updated profile", updatedProfile); - resolve(updatedProfile); - } catch (error) { - reject(error); - } - }); -}; - -export const setProfileMetaData = async (activeUser: ActiveUser | null, noStrPubKey: string) => { - const response = await getAccountFull(activeUser?.username!); - - const { posting_json_metadata } = response; - const profile = JSON.parse(posting_json_metadata!).profile; - const newProfile = { - nsKey: noStrPubKey - }; - - const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); - return updatedProfile; -}; - -export const setChannelMetaData = (username: string, channel: Channel) => { - return new Promise(async (resolve, reject) => { - try { - const response = await getAccountFull(username!); - const { posting_json_metadata } = response; - const profile = JSON.parse(posting_json_metadata!).profile; - const newProfile = { - channel: channel - }; - const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); - resolve(updatedProfile); - } catch (error) { - reject(error); - } - }); -}; - -export const createNoStrAccount = () => { - const priv = generatePrivateKey(); - const pub = getPublicKey(priv); - return { pub, priv }; -}; - -export function notEmpty(value: TValue | null | undefined): value is TValue { - return value !== null && value !== undefined; -} - -export const formatMessageTime = (unixTs: number) => moment.unix(unixTs).format("h:mm a"); - -export const formatMessageDate = (unixTs: number) => moment.unix(unixTs).format("dddd, MMMM Do"); - -export const isSha256 = (s: string) => /^[a-f0-9]{64}$/gi.test(s); - -export const getCommunities = (channels: Channel[], leftChannels: string[]) => { - return channels.filter((item) => !leftChannels.includes(item.id)); -}; - -export const getPrivateKey = (username: string) => { - return ls.get(`${username}_nsPrivKey`); -}; - -export const formatFollowers = (count: number | undefined) => { - if (count) { - return count >= 1e6 - ? (count / 1e6).toLocaleString() + "M" - : count >= 1e3 - ? (count / 1e3).toLocaleString() + "K" - : count.toLocaleString(); - } - return count; -}; - -export const formattedName = (username: string, chat: Chat) => { - if (username && !username.startsWith("@")) { - const community = chat.channels.find((channel) => channel.communityName === username); - if (community) { - return community.name; - } - } - const replacedUserName = username.replace("@", ""); - return replacedUserName; -}; - -export const formattedUserName = (username: string) => { - if (username && username.startsWith("@")) { - return username.replace("@", ""); - } - return username; -}; - -export const isChannel = (username: string) => { - if (username.startsWith("@")) { - return false; - } - return true; -}; - -export const fetchCommunityMessages = ( - publicMessages: publicMessagesList[], - currentChannel: Channel, - hiddenMessageIds?: string[] -) => { - const hideMessageIds = hiddenMessageIds || currentChannel?.hiddenMessageIds || []; - for (const item of publicMessages) { - if (item.channelId === currentChannel.id) { - const filteredPublicMessages = Object.values(item.PublicMessage).filter( - (message) => !hideMessageIds.includes(message.id) - ); - return filteredPublicMessages; - } - } - return []; -}; - -export const isMessageGif = (content: string) => { - return content.includes(GIPHGY); -}; - -export const isMessageImage = (content: string) => { - return content.includes("https://images.ecency.com"); -}; - -export const getProfileName = (creator: string, profiles: Profile[]) => { - const profile = profiles.find((x) => x.creator === creator); - return profile?.name; -}; - -export const getFormattedDateAndDay = ( - msg: DirectMessage | PublicMessage, - i: number, - messagesList: DirectMessage[] | PublicMessage[] -) => { - const prevMsg = messagesList[i - 1]; - const msgDate = formatMessageDate(msg.created); - const prevMsgDate = prevMsg ? formatMessageDate(prevMsg.created) : null; - if (msgDate !== prevMsgDate) { - return msgDate; - } - return null; -}; - -export const fetchCurrentUserData = async (username: string) => { - const response = await getAccountFull(username); - const { posting_json_metadata } = response; - const profile = JSON.parse(posting_json_metadata!).profile; - const { nsKey } = profile || {}; - console.log("nsKey", nsKey); - return nsKey; -}; - -export const copyToClipboard = (content: string) => { - console.log("copy to clipboard run"); - const textField = document.createElement("textarea"); - textField.innerText = content; - document.body.appendChild(textField); - textField.select(); - document.execCommand("copy"); - textField.remove(); -}; - -export const getCommunityLastMessage = ( - channelId: string, - publicMessages: publicMessagesList[] -) => { - const msgsList = fetchChannelMessages(channelId!, publicMessages); - const messages = msgsList.sort((a, b) => a.created - b.created); - const lastMessage = messages.slice(-1); - return lastMessage[0]?.content; -}; - -export const fetchChannelMessages = (channelId: string, publicMessages: publicMessagesList[]) => { - for (const item of publicMessages) { - if (item.channelId === channelId) { - return Object.values(item.PublicMessage); - } - } - return []; -}; - -export const fetchDirectMessages = (peer: string, directMessages: directMessagesList[]) => { - for (const item of directMessages) { - if (item.peer === peer) { - return Object.values(item.chat); - } - } - return []; -}; - -export const getDirectLastMessage = (pubkey: string, directMessages: directMessagesList[]) => { - const msgsList = fetchDirectMessages(pubkey, directMessages); - const messages = msgsList.sort((a, b) => a.created - b.created); - const lastMessage = messages.slice(-1); - return lastMessage[0]?.content; -}; diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts index ff85f7b6338..db9015710ad 100644 --- a/src/common/helper/message-service.ts +++ b/src/common/helper/message-service.ts @@ -15,7 +15,6 @@ import { import { encrypt, decrypt } from "../../lib/nostr-tools/nip04"; import SimplePool from "../../lib/nostr-tools/pool"; import { signEvent, getEventHash, Event } from "../../lib/nostr-tools/event"; -import { isSha256, notEmpty } from "./chat-utils"; const relays = { "wss://relay1.nostrchat.io": { read: true, write: true }, @@ -152,7 +151,7 @@ class MessageService extends TypedEventEmitter { return undefined; }) .filter((x) => !deletions.includes(x)) - .filter(notEmpty) + .filter(MessageService.notEmpty) ) ); if (channels.length !== 0) { @@ -183,7 +182,7 @@ class MessageService extends TypedEventEmitter { const root = eTags.find((x) => x[3] === "root")?.[1]; const mentions = MessageService.filterTagValue(ev, "p") .map((x) => x?.[1]) - .filter(notEmpty); + .filter(MessageService.notEmpty); if (!root) return null; return ev.content ? { @@ -197,7 +196,7 @@ class MessageService extends TypedEventEmitter { } : null; }) - .filter(notEmpty); + .filter(MessageService.notEmpty); this.emit(MessageEvents.PreviousPublicMessages, formattedEvents); } @@ -460,7 +459,7 @@ class MessageService extends TypedEventEmitter { const root = eTags.find((x) => x[3] === "root")?.[1]; const mentions = MessageService.filterTagValue(event, "p") .map((x) => x?.[1]) - .filter(notEmpty); + .filter(MessageService.notEmpty); const formattedEvent = event.content ? { id: event.id, @@ -641,7 +640,7 @@ class MessageService extends TypedEventEmitter { const profiles: Array<[string, string]> = e.tags; return profiles.map(([pubkey, name]) => ({ pubkey, name })); }) - .filter(notEmpty); + .filter(MessageService.notEmpty); if (directContacts.length > 0) { const directContactsProfile: Array<{ pubkey: string; name: string }> = directContacts[0]; @@ -664,7 +663,7 @@ class MessageService extends TypedEventEmitter { } : null; }) - .filter(notEmpty); + .filter(MessageService.notEmpty); if (profileUpdates.length > 0) { this.emit(MessageEvents.ProfileUpdate, profileUpdates); } @@ -679,7 +678,7 @@ class MessageService extends TypedEventEmitter { if (leftChannelListEv) { const content = MessageService.parseJson(leftChannelListEv.content); - if (Array.isArray(content) && content.every((x) => isSha256(x))) { + if (Array.isArray(content) && content.every((x) => MessageService.isSha256(x))) { this.emit(MessageEvents.LeftChannelList, content); } } @@ -701,7 +700,7 @@ class MessageService extends TypedEventEmitter { } : null; }) - .filter(notEmpty); + .filter(MessageService.notEmpty); if (channelCreations.length > 0) { this.emit(MessageEvents.ChannelCreation, channelCreations); } @@ -726,7 +725,7 @@ class MessageService extends TypedEventEmitter { } : null; }) - .filter(notEmpty); + .filter(MessageService.notEmpty); if (channelUpdates.length > 0) { this.emit(MessageEvents.ChannelUpdate, channelUpdates); } @@ -738,7 +737,7 @@ class MessageService extends TypedEventEmitter { const root = eTags.find((x) => x[3] === "root")?.[1]; const mentions = MessageService.filterTagValue(ev, "p") .map((x) => x?.[1]) - .filter(notEmpty); + .filter(MessageService.notEmpty); if (!root) return null; return ev.content ? { @@ -752,7 +751,7 @@ class MessageService extends TypedEventEmitter { } : null; }) - .filter(notEmpty); + .filter(MessageService.notEmpty); if (publicMessages.length > 0) { this.emit(MessageEvents.PublicMessageAfterSent, publicMessages); } @@ -790,7 +789,7 @@ class MessageService extends TypedEventEmitter { }; }); }) - .filter(notEmpty) + .filter(MessageService.notEmpty) ).then((directMessages: DirectMessage[]) => { if (directMessages.length > 0) { this.emit(MessageEvents.DirectMessageAfterSent, directMessages); @@ -822,6 +821,12 @@ class MessageService extends TypedEventEmitter { } } + static isSha256 = (s: string) => /^[a-f0-9]{64}$/gi.test(s); + + static notEmpty(value: TValue | null | undefined): value is TValue { + return value !== null && value !== undefined; + } + static findTagValue(ev: Event, tag: "e" | "p" | "d") { return ev.tags.find(([t]) => t === tag)?.[1]; } diff --git a/src/common/pages/chats/index.scss b/src/common/pages/chats/index.scss index 58b1e9b3d87..43c87a713fa 100644 --- a/src/common/pages/chats/index.scss +++ b/src/common/pages/chats/index.scss @@ -8,9 +8,9 @@ width: 100vw; height: 100vh; } - .chats-messages-box { - .private-key { - margin-top: 4.5rem; - } + .chats-messages-box { + .private-key { + margin-top: 4.5rem; } + } } diff --git a/src/common/pages/chats/index.tsx b/src/common/pages/chats/index.tsx index d13bc9674c6..6fa1fb3de06 100644 --- a/src/common/pages/chats/index.tsx +++ b/src/common/pages/chats/index.tsx @@ -1,28 +1,28 @@ import React, { useEffect, useState } from "react"; import { connect } from "react-redux"; import { match } from "react-router"; - import NavBar from "../../components/navbar"; import NavBarElectron from "../../../desktop/app/components/navbar"; import { pageMapDispatchToProps, pageMapStateToProps, PageProps } from "../common"; -import ChatsSideBar from "../../components/chats-sidebar/indes"; -import ChatsMessagesBox from "../../components/chats-messages-box"; +import ChatsSideBar from "../../components/chats/chats-sidebar/indes"; +import ChatsMessagesBox from "../../components/chats/chats-messages-box"; import "./index.scss"; import * as ls from "../../util/local-storage"; +import { Button, Spinner } from "react-bootstrap"; +import { setNostrkeys } from "../../../providers/message-provider"; +import ManageChatKey from "../../components/manage-chat-key"; +import Feedback, { success } from "../../components/feedback"; +import { NostrKeysType } from "../../components/chats/types"; import { copyToClipboard, createNoStrAccount, getPrivateKey, getProfileMetaData, - NostrKeysType, setProfileMetaData -} from "../../helper/chat-utils"; -import { Button, Spinner } from "react-bootstrap"; -import { setNostrkeys } from "../../../providers/message-provider"; -import ManageChatKey from "../../components/manage-chat-key"; -import Feedback, { success } from "../../components/feedback"; +} from "../../components/chats/utils"; +import { useMappedStore } from "../../store/use-mapped-store"; interface MatchParams { filter: string; @@ -37,7 +37,8 @@ interface Props extends PageProps { } export const Chats = (props: Props) => { - const { channels, directContacts } = props.chat; + const { chat, activeUser, global } = useMappedStore(); + const { channels, directContacts } = chat; const [marginTop, setMarginTop] = useState(0); const [activeUserKeys, setActiveUserKeys] = useState(); @@ -61,7 +62,7 @@ export const Chats = (props: Props) => { useEffect(() => { handleResize(); getActiveUserChatKeys(); - const noStrPrivKey = getPrivateKey(props.activeUser?.username!); + const noStrPrivKey = getPrivateKey(activeUser?.username!); setNoStrPrivKey(noStrPrivKey); }, []); @@ -93,8 +94,8 @@ export const Chats = (props: Props) => { const getActiveUserChatKeys = async () => { setInProgress(true); - const profileData = await getProfileMetaData(props.activeUser?.username!); - const noStrPrivKey = getPrivateKey(props.activeUser?.username!); + const profileData = await getProfileMetaData(activeUser?.username!); + const noStrPrivKey = getPrivateKey(activeUser?.username!); const activeUserKeys = { pub: profileData?.nsKey, priv: noStrPrivKey @@ -115,13 +116,13 @@ export const Chats = (props: Props) => { setShowSpinner(true); resetChat(); const keys = createNoStrAccount(); - ls.set(`${props.activeUser?.username}_nsPrivKey`, keys.priv); + ls.set(`${activeUser?.username}_nsPrivKey`, keys.priv); setNoStrPrivKey(keys.priv); - await setProfileMetaData(props.activeUser, keys.pub); + await setProfileMetaData(activeUser, keys.pub); // setHasUserJoinedChat(true); setNostrkeys(keys); window.messageService?.updateProfile({ - name: props.activeUser?.username!, + name: activeUser?.username!, about: "", picture: "" }); @@ -136,8 +137,8 @@ export const Chats = (props: Props) => { return ( <> - - {props.global.isElectron ? : } + + {global.isElectron ? : }
{ <> { }, [props.activeUser]); useEffect(() => { - const communities = getCommunities(chat.channels, chat.leftChannelsList); + const communities = getJoinedCommunities(chat.channels, chat.leftChannelsList); setCommunities(communities); }, [chat.channels, chat.leftChannelsList]); diff --git a/src/providers/message-provider.tsx b/src/providers/message-provider.tsx index ae00c1f05fb..e48f53ad964 100644 --- a/src/providers/message-provider.tsx +++ b/src/providers/message-provider.tsx @@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react"; import { ActiveUser } from "../common/store/active-user/types"; import { DirectContactsType } from "../common/store/chat/types"; +import { NostrKeysType } from "../common/components/chats/types"; import { DirectMessage, Profile, @@ -14,7 +15,7 @@ import { } from "./message-provider-types"; import { initMessageService, MessageEvents } from "../common/helper/message-service"; -import { getProfileMetaData, NostrKeysType, getPrivateKey } from "../common/helper/chat-utils"; +import { getProfileMetaData, getPrivateKey } from "../common/components/chats/utils"; import { useMappedStore } from "../common/store/use-mapped-store"; From 9cf6ffd133b8d78c6d74f105d83bcb28d33bac9f Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Thu, 7 Sep 2023 14:53:41 +0500 Subject: [PATCH 055/179] Context implemented for extended chat page --- src/common/components/chats/chat-provider.tsx | 89 +++++++ .../chats/chats-messages-box/index.tsx | 7 +- .../components/chats/chats-sidebar/indes.tsx | 146 ++--------- .../components/manage-chat-key/index.tsx | 24 +- src/common/pages/chats/index.tsx | 241 ++++++++++-------- 5 files changed, 257 insertions(+), 250 deletions(-) create mode 100644 src/common/components/chats/chat-provider.tsx diff --git a/src/common/components/chats/chat-provider.tsx b/src/common/components/chats/chat-provider.tsx new file mode 100644 index 00000000000..c3e96ca4c66 --- /dev/null +++ b/src/common/components/chats/chat-provider.tsx @@ -0,0 +1,89 @@ +import React, { useEffect, useState } from "react"; +import { useMappedStore } from "../../store/use-mapped-store"; +import { NostrKeysType } from "./types"; +import { getPrivateKey, getUserChatPublicKey } from "./utils"; + +interface Context { + activeUserKeys?: NostrKeysType; + inProgress: boolean; + revealPrivKey: boolean; + chatPrivKey: string; + setRevealPrivKey: (d: boolean) => void; + setInProgress: (d: boolean) => void; + setChatPrivKey: (key: string) => void; + setActiveUserKeys: (keys: NostrKeysType) => void; +} + +interface ChatProviderProps { + children: (context: Context) => React.ReactNode; +} + +export const ChatContext = React.createContext({ + activeUserKeys: { pub: " ", priv: "" }, + inProgress: false, + revealPrivKey: false, + chatPrivKey: "", + setRevealPrivKey: () => {}, + setInProgress: () => {}, + setChatPrivKey: () => {}, + setActiveUserKeys: () => {} +}); + +export default function ChatProvider({ children }: ChatProviderProps) { + const { activeUser } = useMappedStore(); + + const [activeUserKeys, setActiveUserKeys] = useState(); + const [inProgress, setInProgress] = useState(true); + const [chatPrivKey, setChatPrivKey] = useState(""); + const [revealPrivKey, setRevealPrivKey] = useState(false); + + useEffect(() => { + getActiveUserKeys(); + }, []); + + useEffect(() => { + if (inProgress) { + setTimeout(() => { + setInProgress(false); + }, 7000); + } + }, [inProgress]); + + const getActiveUserKeys = async () => { + const pubKey = await getUserChatPublicKey(activeUser?.username!); + const privKey = getPrivateKey(activeUser?.username!); + setChatPrivKey(privKey); + const activeUserKeys = { + pub: pubKey, + priv: privKey + }; + setInProgress(false); + setActiveUserKeys(activeUserKeys); + }; + + return ( + + {children({ + activeUserKeys, + inProgress, + revealPrivKey, + chatPrivKey, + setRevealPrivKey, + setInProgress, + setChatPrivKey, + setActiveUserKeys + })} + + ); +} diff --git a/src/common/components/chats/chats-messages-box/index.tsx b/src/common/components/chats/chats-messages-box/index.tsx index 1f320527604..7b0bf280dfd 100644 --- a/src/common/components/chats/chats-messages-box/index.tsx +++ b/src/common/components/chats/chats-messages-box/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { match } from "react-router"; import { History } from "history"; import { Account } from "../../../store/accounts/types"; @@ -13,6 +13,7 @@ import LinearProgress from "../../linear-progress"; import { NostrKeysType } from "../types"; import { formattedUserName } from "../utils"; import { useMappedStore } from "../../../store/use-mapped-store"; +import { ChatContext } from "../chat-provider"; interface MatchParams { filter: string; @@ -27,7 +28,6 @@ interface Props { users: User[]; history: History | null; ui: UI; - activeUserKeys: NostrKeysType; setActiveUser: (username: string | null) => void; updateActiveUser: (data?: Account) => void; deleteUser: (username: string) => void; @@ -36,7 +36,10 @@ interface Props { } export default function ChatsMessagesBox(props: Props) { + const context = useContext(ChatContext); + const { activeUserKeys } = context; const { activeUser, chat } = useMappedStore(); + const { channels, updatedChannel } = chat; const { match, deletePublicMessage } = props; const username = match.params.username; diff --git a/src/common/components/chats/chats-sidebar/indes.tsx b/src/common/components/chats/chats-sidebar/indes.tsx index ffa213f8dcc..516f9d481e2 100644 --- a/src/common/components/chats/chats-sidebar/indes.tsx +++ b/src/common/components/chats/chats-sidebar/indes.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { Form } from "react-bootstrap"; import { Link } from "react-router-dom"; import { History } from "history"; @@ -23,6 +23,7 @@ import { CHAT, DropDownStyle } from "../chat-popup/chat-constants"; import ChatsDropdownMenu from "../chats-dropdown-menu"; import { AccountWithReputation, NostrKeysType } from "../types"; import { useMappedStore } from "../../../store/use-mapped-store"; +import { ChatContext } from "../chat-provider"; interface MatchParams { filter: string; @@ -33,36 +34,23 @@ interface MatchParams { } interface Props { match: match; - // channels: Channel[]; - // directContacts: DirectContactsType[]; - activeUserKeys: NostrKeysType; - chatPrivKey: string; - // chat: Chat; history: History; - revelPrivateKey: boolean; - inProgressSetter: (d: boolean) => void; - revealPrivateKeySetter: (d: boolean) => void; resetChat: () => void; } export default function ChatsSideBar(props: Props) { const { chat } = useMappedStore(); + const context = useContext(ChatContext); const { channels, directContacts, leftChannelsList } = chat; - const { - activeUserKeys, - match, - revelPrivateKey, - chatPrivKey, - resetChat, - inProgressSetter, - revealPrivateKeySetter - } = props; + const { match, resetChat } = props; + + const { activeUserKeys, revealPrivKey, chatPrivKey, setInProgress, setRevealPrivKey } = context; const chatsSideBarRef = React.createRef(); const username = match.params.username; const [showDivider, setShowDivider] = useState(false); const [searchText, setSearchText] = useState(""); - const [inProgress, setInProgress] = useState(false); + const [searchInProgress, setSearchInProgress] = useState(false); const [userList, setUserList] = useState([]); const [isScrollToTop, setIsScrollToTop] = useState(false); const [communities, setCommunities] = useState([]); @@ -73,7 +61,7 @@ export default function ChatsSideBar(props: Props) { const resp = await getAccountReputations(searchText, 30); const sortedByReputation = resp.sort((a, b) => (a.reputation > b.reputation ? -1 : 1)); setUserList(sortedByReputation); - setInProgress(false); + setSearchInProgress(false); } }, 500, @@ -92,104 +80,6 @@ export default function ChatsSideBar(props: Props) { // fetchProfileData(); }, [channels, leftChannelsList]); - // const communities = [ - // { - // id: "dewf3efq3", - // name: "Best Community" - // }, - // { - // id: "kefkjehdewjk", - // name: "Cars Forum" - // }, - // { - // id: "hj3fhkl3flk", - // name: "Hive BlockChaiin" - // }, - // { - // id: "dewddefq3", - // name: "Devsinc" - // }, - // { - // id: "kefksahdewjk", - // name: "Rolustech" - // }, - // { - // id: "hj3fdl3flk", - // name: "Stack Overflow" - // }, - // { - // id: "kefkjehdsewjk", - // name: "Cars Forum" - // }, - // { - // id: "hj3fhkls3flk", - // name: "Hive BlockChain" - // }, - // { - // id: "dewddesfq3", - // name: "Devsinc" - // }, - // { - // id: "kefksahsdewjk", - // name: "Rolustech" - // }, - // { - // id: "hj3fdls3flk", - // name: "Stack Overflow" - // } - // ]; - - const DMS = [ - { - id: "dewf3efq3", - name: "ahmed" - }, - { - id: "kefkjehdewjk", - name: "hive-189310" - }, - { - id: "hj3fhkl3flk", - name: "demo.com" - }, - { - id: "dewddefq3", - name: "good-karma" - }, - { - id: "kefksahdewjk", - name: "mtsaeed" - }, - { - id: "hj3fdl3flk", - name: "testers" - }, - { - id: "dewf3sefq3", - name: "ahmed" - }, - { - id: "kefkjeshdewjk", - name: "hive-189310" - }, - { - id: "hj3fhskl3flk", - name: "demo.com" - }, - { - id: "dewddesfq3", - name: "good-karma" - }, - { - id: "kefksashdewjk", - name: "mtsaeed" - }, - { - id: "hj3fdsl3flk", - name: "hive-198973" - } - ]; - const handleScroll = (event: React.UIEvent) => { var element = event.currentTarget; console.log("element.scrollTop", element.scrollTop); @@ -207,13 +97,13 @@ export default function ChatsSideBar(props: Props) { const handleRefreshChat = () => { resetChat(); - setNostrkeys(activeUserKeys); - inProgressSetter(true); + setNostrkeys(activeUserKeys!); + setInProgress(true); }; const handleRevealPrivKey = () => { - if (revelPrivateKey) { - revealPrivateKeySetter(false); + if (revealPrivKey) { + setRevealPrivKey(false); } }; @@ -221,11 +111,11 @@ export default function ChatsSideBar(props: Props) {
- {revelPrivateKey && ( + {revealPrivKey && (
revealPrivateKeySetter(false)} + onClick={() => setRevealPrivKey(false)} > {arrowBackSvg}
@@ -247,7 +137,7 @@ export default function ChatsSideBar(props: Props) {
{ - revealPrivateKeySetter(!revelPrivateKey); + setRevealPrivKey(!revealPrivKey); }} {...props} /> @@ -263,9 +153,9 @@ export default function ChatsSideBar(props: Props) { value={searchText} onChange={(e) => { setSearchText(e.target.value); - setInProgress(true); + setSearchInProgress(true); if (e.target.value.length === 0) { - setInProgress(false); + setSearchInProgress(false); setUserList([]); } }} @@ -273,7 +163,7 @@ export default function ChatsSideBar(props: Props) {
{showDivider &&
} - {inProgress && } + {searchInProgress && }
{searchText ? (
diff --git a/src/common/components/manage-chat-key/index.tsx b/src/common/components/manage-chat-key/index.tsx index d0720c122df..9b68d64ec26 100644 --- a/src/common/components/manage-chat-key/index.tsx +++ b/src/common/components/manage-chat-key/index.tsx @@ -1,19 +1,25 @@ -import React from "react"; +import React, { useContext } from "react"; import { Form } from "react-bootstrap"; import { _t } from "../../i18n"; import { copyContent } from "../../img/svg"; +import { ChatContext } from "../chats/chat-provider"; +import { copyToClipboard } from "../chats/utils"; +import { success } from "../feedback"; import OrDivider from "../or-divider"; import Tooltip from "../tooltip"; import "./index.scss"; -interface Props { - noStrPrivKey: string; - copyPrivateKey: (privKey: string) => void; -} +export default function ManageChatKey() { + const context = useContext(ChatContext); + + const { chatPrivKey } = context; + + const copyPrivateKey = () => { + copyToClipboard(chatPrivKey); + success("Key copied into clipboad"); + }; -export default function ManageChatKey(props: Props) { - console.log("hello", props); return ( <>
@@ -25,14 +31,14 @@ export default function ManageChatKey(props: Props) { className="chat-priv-key" type="text" readOnly={true} - value={props.noStrPrivKey} + value={chatPrivKey} />

{ console.log("SVG clicked"); - props.copyPrivateKey(props.noStrPrivKey); + copyPrivateKey(); }} > {copyContent} diff --git a/src/common/pages/chats/index.tsx b/src/common/pages/chats/index.tsx index 6fa1fb3de06..7e84c8e0d3a 100644 --- a/src/common/pages/chats/index.tsx +++ b/src/common/pages/chats/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { connect } from "react-redux"; import { match } from "react-router"; import NavBar from "../../components/navbar"; @@ -23,6 +23,7 @@ import { setProfileMetaData } from "../../components/chats/utils"; import { useMappedStore } from "../../store/use-mapped-store"; +import ChatProvider, { ChatContext } from "../../components/chats/chat-provider"; interface MatchParams { filter: string; @@ -37,15 +38,13 @@ interface Props extends PageProps { } export const Chats = (props: Props) => { - const { chat, activeUser, global } = useMappedStore(); - const { channels, directContacts } = chat; + const { activeUser, global } = useMappedStore(); + + const context = useContext(ChatContext); + const { setChatPrivKey, setActiveUserKeys } = context; const [marginTop, setMarginTop] = useState(0); - const [activeUserKeys, setActiveUserKeys] = useState(); - const [inProgrss, setInProgress] = useState(true); const [showSpinner, setShowSpinner] = useState(false); - const [noStrPrivKey, setNoStrPrivKey] = useState(""); - const [revelPrivateKey, setRevealPrivateKey] = useState(false); useEffect(() => { document.body.style.overflow = "hidden"; @@ -55,21 +54,10 @@ export const Chats = (props: Props) => { }; }, []); - useEffect(() => { - console.log("noStrPrivKey", noStrPrivKey); - }, [noStrPrivKey]); - useEffect(() => { handleResize(); - getActiveUserChatKeys(); - const noStrPrivKey = getPrivateKey(activeUser?.username!); - setNoStrPrivKey(noStrPrivKey); }, []); - useEffect(() => { - setInProgress(false); - }, [activeUserKeys]); - useEffect(() => { window.addEventListener("resize", handleResize); return () => { @@ -77,14 +65,6 @@ export const Chats = (props: Props) => { }; }, []); - useEffect(() => { - if (inProgrss) { - setTimeout(() => { - setInProgress(false); - }, 7000); - } - }, [inProgrss]); - const handleResize = () => { const parentElemet = document.getElementById("sticky-container")?.getBoundingClientRect(); if (parentElemet) { @@ -92,34 +72,14 @@ export const Chats = (props: Props) => { } }; - const getActiveUserChatKeys = async () => { - setInProgress(true); - const profileData = await getProfileMetaData(activeUser?.username!); - const noStrPrivKey = getPrivateKey(activeUser?.username!); - const activeUserKeys = { - pub: profileData?.nsKey, - priv: noStrPrivKey - }; - setActiveUserKeys(activeUserKeys); - }; - - const inProgressSetter = (d: boolean) => { - setInProgress(d); - }; - - const revealPrivateKeySetter = (d: boolean) => { - setRevealPrivateKey(d); - }; - const handleJoinChat = async () => { const { resetChat } = props; setShowSpinner(true); resetChat(); const keys = createNoStrAccount(); ls.set(`${activeUser?.username}_nsPrivKey`, keys.priv); - setNoStrPrivKey(keys.priv); + setChatPrivKey(keys.priv); await setProfileMetaData(activeUser, keys.pub); - // setHasUserJoinedChat(true); setNostrkeys(keys); window.messageService?.updateProfile({ name: activeUser?.username!, @@ -130,74 +90,133 @@ export const Chats = (props: Props) => { setShowSpinner(false); }; - const copyPrivateKey = () => { - copyToClipboard(noStrPrivKey); - success("Key copied into clipboad"); - }; - + // return ( + // <> + // + // + // {global.isElectron ? : } + //

+ // {inProgrss ? ( + //
+ // + //
+ // ) : ( + // <> + // {activeUserKeys?.pub ? ( + // chatPrivKey ? ( + // <> + // + // {revelPrivateKey ? ( + //
+ // + //
+ // ) : ( + // //if any person has not joined any community then how can he see its message. handle this thing here. + // + // )} + // + // ) : ( + //

No private key

+ // ) + // ) : ( + //
+ //
+ //

You haven't joined the chat yet. Please join the chat to start chatting.

+ // + //
+ //
+ // )} + // + // )} + //
+ // + // + // ); return ( - <> - - {global.isElectron ? : } -
- {inProgrss ? ( -
- -
- ) : ( - <> - {activeUserKeys?.pub ? ( - noStrPrivKey ? ( - <> - - {revelPrivateKey ? ( -
- -
- ) : ( - //if any person has not joined any community then how can he see its message. handle this thing here. - - )} - - ) : ( -

No private key

- ) - ) : ( + + {(context) => ( + <> + + {global.isElectron ? : } +
+ {context.inProgress ? (
-
-

You haven't joined the chat yet. Please join the chat to start chatting.

- -
+
+ ) : ( + <> + {context.activeUserKeys?.pub ? ( + context.chatPrivKey ? ( + <> + + {context.revealPrivKey ? ( +
+ +
+ ) : ( + // Handle the case when the user hasn't joined any community here + + )} + + ) : ( +

No private key

+ ) + ) : ( +
+
+

+ You haven't joined the chat yet. Please join the chat to start chatting. +

+ +
+
+ )} + )} - - )} -
- +
+ + )} + ); }; export default connect(pageMapStateToProps, pageMapDispatchToProps)(Chats); From 70ab1b73dcbfd00470a48265ca34ac24f7149028 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Thu, 7 Sep 2023 15:08:19 +0500 Subject: [PATCH 056/179] Fix error in chat-popup --- src/common/components/chats/chat-popup/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/common/components/chats/chat-popup/index.tsx b/src/common/components/chats/chat-popup/index.tsx index e9093c6991f..e36a380ea0b 100644 --- a/src/common/components/chats/chat-popup/index.tsx +++ b/src/common/components/chats/chat-popup/index.tsx @@ -884,7 +884,6 @@ export default function ChatPopUp(props: Props) { publicMessages={publicMessages} currentChannel={currentChannel!} activeUserKeys={activeUserKeys!} - activeUser={props.activeUser} isScrollToBottom={false} isActveUserRemoved={isActveUserRemoved} currentChannelSetter={setCurrentChannel} @@ -1080,7 +1079,7 @@ export default function ChatPopUp(props: Props) { )} ) : revelPrivateKey ? ( - + ) : ( +
+ {importPrivKey && ( +
+
{ + e.preventDefault(); + }} + > + + + {keySvg} + + setChatPrivkey(e.target.value)} + /> + + + + + {addRoleError && ( + {addRoleError} + )} +
+
)} + {} +
+ +
*/} + - )} - {props.chat.directContacts.map((user: DirectContactsType) => { - return ( -
- -
- - - -
- - -
userClicked(user.name)}> -

{user.name}

-

- {getDirectLastMessage(user.pubkey, props.chat.directMessages)} -

-
-
- ); - })} - - ) : !noStrPrivKey || noStrPrivKey.length === 0 || noStrPrivKey === null ? ( - <> -
- -
- {importPrivKey && ( -
-
{ - e.preventDefault(); - }} - > - - - {keySvg} - - setChatPrivkey(e.target.value)} - /> - - - - - {addRoleError && ( - {addRoleError} - )} -
+ ) : refreshChat ? ( +
+
+ ) : ( + <> +

{_t("chat.no-chat")}

+
+ +
+ )} - {} -
- -
- - ) : refreshChat ? ( -
- -
- ) : ( - <> -

{_t("chat.no-chat")}

-
- -
)} + ) : revelPrivateKey ? ( + + ) : ( + )} - - ) : revelPrivateKey ? ( - - ) : ( - - )} - - {((isScrollToTop && !isCurrentUser) || - ((isCurrentUser || isCommunity) && isScrollToBottom)) && ( - -
- {isCurrentUser || isCommunity ? chevronDownSvgForSlider : chevronUpSvg} -
-
- )} - {isActveUserRemoved && isCommunity && ( -

- {_t("chat.blocked-user-message")} -

- )} -
- {(isCurrentUser || isCommunity) && ( - + + {((isScrollToTop && !isCurrentUser) || + ((isCurrentUser || isCommunity) && isScrollToBottom)) && ( + +
+ {isCurrentUser || isCommunity ? chevronDownSvgForSlider : chevronUpSvg} +
+
+ )} + {isActveUserRemoved && isCommunity && ( +

+ {_t("chat.blocked-user-message")} +

+ )} +
+ {(isCurrentUser || isCommunity) && ( + + )} +
)} -
- )} - {keyDialog && ( - - - - {step === 9 && confirmationModal(NEWCHATACCOUNT)} - {step === 11 && confirmationModal(RESENDMESSAGE)} - {step === 10 && successModal(NEWCHATACCOUNT)} - - + {keyDialog && ( + + + + {step === 9 && confirmationModal(NEWCHATACCOUNT)} + {step === 11 && confirmationModal(RESENDMESSAGE)} + {step === 10 && successModal(NEWCHATACCOUNT)} + + + )} + )} - + ); } diff --git a/src/common/components/chats/chat-provider.tsx b/src/common/components/chats/chat-provider.tsx index c3e96ca4c66..7aae07d1d91 100644 --- a/src/common/components/chats/chat-provider.tsx +++ b/src/common/components/chats/chat-provider.tsx @@ -8,10 +8,12 @@ interface Context { inProgress: boolean; revealPrivKey: boolean; chatPrivKey: string; + receiverPubKey: string; setRevealPrivKey: (d: boolean) => void; setInProgress: (d: boolean) => void; setChatPrivKey: (key: string) => void; setActiveUserKeys: (keys: NostrKeysType) => void; + setReceiverPubKey: (key: string) => void; } interface ChatProviderProps { @@ -23,10 +25,12 @@ export const ChatContext = React.createContext({ inProgress: false, revealPrivKey: false, chatPrivKey: "", + receiverPubKey: "", setRevealPrivKey: () => {}, setInProgress: () => {}, setChatPrivKey: () => {}, - setActiveUserKeys: () => {} + setActiveUserKeys: () => {}, + setReceiverPubKey: () => {} }); export default function ChatProvider({ children }: ChatProviderProps) { @@ -35,6 +39,7 @@ export default function ChatProvider({ children }: ChatProviderProps) { const [activeUserKeys, setActiveUserKeys] = useState(); const [inProgress, setInProgress] = useState(true); const [chatPrivKey, setChatPrivKey] = useState(""); + const [receiverPubKey, setReceiverPubKey] = useState(""); const [revealPrivKey, setRevealPrivKey] = useState(false); useEffect(() => { @@ -68,10 +73,12 @@ export default function ChatProvider({ children }: ChatProviderProps) { inProgress, revealPrivKey, chatPrivKey, + receiverPubKey, setRevealPrivKey, setInProgress, setChatPrivKey, - setActiveUserKeys + setActiveUserKeys, + setReceiverPubKey }} > {children({ @@ -79,10 +86,12 @@ export default function ChatProvider({ children }: ChatProviderProps) { inProgress, revealPrivKey, chatPrivKey, + receiverPubKey, setRevealPrivKey, setInProgress, setChatPrivKey, - setActiveUserKeys + setActiveUserKeys, + setReceiverPubKey })} ); diff --git a/src/common/components/chats/chats-channel-messages/index.tsx b/src/common/components/chats/chats-channel-messages/index.tsx index 779ad4f7959..00a5888b0c5 100644 --- a/src/common/components/chats/chats-channel-messages/index.tsx +++ b/src/common/components/chats/chats-channel-messages/index.tsx @@ -554,6 +554,8 @@ export default function ChatsChannelMessages(props: Props) {
{step !== 0 && ( { setStep(0); }} diff --git a/src/common/components/chats/chats-confirmation-modal/index.tsx b/src/common/components/chats/chats-confirmation-modal/index.tsx index 105d9d2a0c4..1049e60aa12 100644 --- a/src/common/components/chats/chats-confirmation-modal/index.tsx +++ b/src/common/components/chats/chats-confirmation-modal/index.tsx @@ -3,23 +3,22 @@ import { Button, Modal } from "react-bootstrap"; import { _t } from "../../../i18n"; interface Props { - // actionType: any; - // step: any; + actionType: string; + content: string; onClose: () => void; onConfirm: () => void; - // t: any; } const ChatsConfirmationModal = (props: Props) => { - const { onClose, onConfirm } = props; + const { onClose, onConfirm, content, actionType } = props; const confirmationModalContent = ( <>
-

Confirmation

+

{actionType}

- Are you sure? + {content}

+

+ {importPrivKey && ( +
+
{ + e.preventDefault(); + }} + > + + + {keySvg} + + setPrivKey(e.target.value)} + /> + + + + + {inProgress && } + {error && {error}} + +
+ )} + {} +
+ +
+
+
+ {step !== 0 && ( + { + setStep(0); + }} + onConfirm={handleCreateAccount} + /> + )} + + ); +} diff --git a/src/common/components/chats/join-chat/index.tsx b/src/common/components/chats/join-chat/index.tsx new file mode 100644 index 00000000000..8baab224958 --- /dev/null +++ b/src/common/components/chats/join-chat/index.tsx @@ -0,0 +1,51 @@ +import React, { useContext, useState } from "react"; +import { Button, Spinner } from "react-bootstrap"; +import { useMappedStore } from "../../../store/use-mapped-store"; +import { ChatContext } from "../chat-provider"; +import { createNoStrAccount, setProfileMetaData } from "../utils"; + +import * as ls from "../../../util/local-storage"; +import { setNostrkeys } from "../../../../providers/message-provider"; + +interface Props { + resetChat: () => void; +} + +export default function JoinChat(props: Props) { + const context = useContext(ChatContext); + const { setChatPrivKey, setActiveUserKeys } = context; + + const { activeUser } = useMappedStore(); + + const [showSpinner, setShowSpinner] = useState(false); + + const handleJoinChat = async () => { + const { resetChat } = props; + setShowSpinner(true); + resetChat(); + const keys = createNoStrAccount(); + ls.set(`${activeUser?.username}_nsPrivKey`, keys.priv); + setChatPrivKey(keys.priv); + await setProfileMetaData(activeUser, keys.pub); + setNostrkeys(keys); + window.messageService?.updateProfile({ + name: activeUser?.username!, + about: "", + picture: "" + }); + setActiveUserKeys(keys); + setShowSpinner(false); + }; + + return ( +
+

You haven't joined the chat yet. Please join the chat to start chatting.

+ +
+ ); +} diff --git a/src/common/components/chats/set-chat-keys/index.tsx b/src/common/components/chats/set-chat-keys/index.tsx new file mode 100644 index 00000000000..93abebe740e --- /dev/null +++ b/src/common/components/chats/set-chat-keys/index.tsx @@ -0,0 +1,28 @@ +import React, { useContext, useEffect } from "react"; +import { useMappedStore } from "../../../store/use-mapped-store"; + +import { ChatContext } from "../chat-provider"; +import { getPrivateKey, getUserChatPublicKey } from "../utils"; + +export default function SetActiveUserChatKeys() { + const context = useContext(ChatContext); + const { setActiveUserKeys, setChatPrivKey } = context; + const { activeUser } = useMappedStore(); + + useEffect(() => { + getActiveUserKeys(); + }, []); + + const getActiveUserKeys = async () => { + const pubKey = await getUserChatPublicKey(activeUser?.username!); + const privKey = getPrivateKey(activeUser?.username!); + const keys = { + pub: pubKey, + priv: privKey + }; + setChatPrivKey(privKey); + setActiveUserKeys(keys); + }; + + return <>; +} diff --git a/src/common/components/emoji-picker/index.tsx b/src/common/components/emoji-picker/index.tsx index 0a0c478a5bc..021649830b8 100644 --- a/src/common/components/emoji-picker/index.tsx +++ b/src/common/components/emoji-picker/index.tsx @@ -4,7 +4,6 @@ import { FormControl } from "react-bootstrap"; import BaseComponent from "../base"; import SearchBox from "../search-box"; -import { EmojiPickerStyleProps } from "../chats/chat-popup"; import { _t } from "../../i18n"; @@ -14,6 +13,7 @@ import * as ls from "../../util/local-storage"; import { insertOrReplace } from "../../util/input-util"; import "./_index.scss"; +import { EmojiPickerStyleProps } from "../chats/types/chat-types"; interface Emoji { a: string; diff --git a/src/common/components/manage-chat-key/index.tsx b/src/common/components/manage-chat-key/index.tsx index 9b68d64ec26..973bb5ba708 100644 --- a/src/common/components/manage-chat-key/index.tsx +++ b/src/common/components/manage-chat-key/index.tsx @@ -15,6 +15,8 @@ export default function ManageChatKey() { const { chatPrivKey } = context; + console.log("chatPrivKey in manage chat key", chatPrivKey); + const copyPrivateKey = () => { copyToClipboard(chatPrivKey); success("Key copied into clipboad"); diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts index db9015710ad..c3c8c09cac8 100644 --- a/src/common/helper/message-service.ts +++ b/src/common/helper/message-service.ts @@ -847,6 +847,7 @@ export const initMessageService = (keys: Keys): MessageService | undefined => { if (keys) { window.messageService = new MessageService(keys.priv, keys.pub); } + console.log("raven instance created"); return window.messageService; }; diff --git a/src/common/pages/chats/index.scss b/src/common/pages/chats/index.scss index 43c87a713fa..905f03fd7bf 100644 --- a/src/common/pages/chats/index.scss +++ b/src/common/pages/chats/index.scss @@ -13,4 +13,9 @@ margin-top: 4.5rem; } } + .import-chat { + width: 100%; + height: 100%; + margin-top: 10%; + } } diff --git a/src/common/pages/chats/index.tsx b/src/common/pages/chats/index.tsx index 7e84c8e0d3a..f9b72c1c33a 100644 --- a/src/common/pages/chats/index.tsx +++ b/src/common/pages/chats/index.tsx @@ -24,6 +24,9 @@ import { } from "../../components/chats/utils"; import { useMappedStore } from "../../store/use-mapped-store"; import ChatProvider, { ChatContext } from "../../components/chats/chat-provider"; +import ImportChats from "../../components/chats/import-chats"; +import JoinChat from "../../components/chats/join-chat"; +import SetActiveUserChatKeys from "../../components/chats/set-chat-keys"; interface MatchParams { filter: string; @@ -40,11 +43,7 @@ interface Props extends PageProps { export const Chats = (props: Props) => { const { activeUser, global } = useMappedStore(); - const context = useContext(ChatContext); - const { setChatPrivKey, setActiveUserKeys } = context; - const [marginTop, setMarginTop] = useState(0); - const [showSpinner, setShowSpinner] = useState(false); useEffect(() => { document.body.style.overflow = "hidden"; @@ -72,95 +71,12 @@ export const Chats = (props: Props) => { } }; - const handleJoinChat = async () => { - const { resetChat } = props; - setShowSpinner(true); - resetChat(); - const keys = createNoStrAccount(); - ls.set(`${activeUser?.username}_nsPrivKey`, keys.priv); - setChatPrivKey(keys.priv); - await setProfileMetaData(activeUser, keys.pub); - setNostrkeys(keys); - window.messageService?.updateProfile({ - name: activeUser?.username!, - about: "", - picture: "" - }); - setActiveUserKeys(keys); - setShowSpinner(false); - }; - - // return ( - // <> - // - // - // {global.isElectron ? : } - //
- // {inProgrss ? ( - //
- // - //
- // ) : ( - // <> - // {activeUserKeys?.pub ? ( - // chatPrivKey ? ( - // <> - // - // {revelPrivateKey ? ( - //
- // - //
- // ) : ( - // //if any person has not joined any community then how can he see its message. handle this thing here. - // - // )} - // - // ) : ( - //

No private key

- // ) - // ) : ( - //
- //
- //

You haven't joined the chat yet. Please join the chat to start chatting.

- // - //
- //
- // )} - // - // )} - //
- //
- // - // ); return ( {(context) => ( <> + {global.isElectron ? : }
{ )} ) : ( -

No private key

+ <> +
+ +
+ ) ) : (
-
-

- You haven't joined the chat yet. Please join the chat to start chatting. -

- -
+
)} From 49adf6fa6f3f9d86179d92beca55514f53d8ad30 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Thu, 7 Sep 2023 20:37:22 +0500 Subject: [PATCH 058/179] Small issue fix --- src/common/components/chats/chat-popup/index.tsx | 7 ++++--- .../components/chats/chats-direct-messages/index.tsx | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/common/components/chats/chat-popup/index.tsx b/src/common/components/chats/chat-popup/index.tsx index babfd0921f8..18dfdb3c1b7 100644 --- a/src/common/components/chats/chat-popup/index.tsx +++ b/src/common/components/chats/chat-popup/index.tsx @@ -108,7 +108,6 @@ export default function ChatPopUp(props: Props) { const { activeUser } = useMappedStore(); const context = useContext(ChatContext); - const { receiverPubKey, setReceiverPubKey } = context; const [expanded, setExpanded] = useState(false); const [currentUser, setCurrentUser] = useState(""); @@ -120,6 +119,7 @@ export default function ChatPopUp(props: Props) { const [inProgress, setInProgress] = useState(false); const [show, setShow] = useState(false); const [activeUserKeys, setActiveUserKeys] = useState(); + const [receiverPubKey, setReceiverPubKey] = useState(); const [showSpinner, setShowSpinner] = useState(false); const [directMessagesList, setDirectMessagesList] = useState([]); const [isCurrentUserJoined, setIsCurrentUserJoined] = useState(true); @@ -150,8 +150,8 @@ export default function ChatPopUp(props: Props) { const [refreshChat, setRefreshChat] = useState(false); useEffect(() => { - console.log("Chat in store", props.chat); - }, [props.chat]); + console.log("directMessagesList in chat popup", directMessagesList); + }, [directMessagesList]); useEffect(() => { if (currentChannel && props.chat.leftChannelsList.includes(currentChannel.id)) { @@ -416,6 +416,7 @@ export default function ChatPopUp(props: Props) { const { posting_json_metadata } = response; const profile = JSON.parse(posting_json_metadata!).profile; const { nsKey } = profile || {}; + setReceiverPubKey(nsKey); setIsCurrentUserJoined(!!nsKey); setInProgress(false); }; diff --git a/src/common/components/chats/chats-direct-messages/index.tsx b/src/common/components/chats/chats-direct-messages/index.tsx index c13b0c68df6..d0bd91c9b7e 100644 --- a/src/common/components/chats/chats-direct-messages/index.tsx +++ b/src/common/components/chats/chats-direct-messages/index.tsx @@ -48,11 +48,14 @@ export default function ChatsDirectMessages(props: Props) { const { receiverPubKey, setReceiverPubKey } = context; const { chat, global, activeUser } = useMappedStore(); + console.log("directMessages", directMessages); + let prevGlobal = usePrevious(global); useEffect(() => { if (currentUser) { getReceiverPubKey(); + console.log("use effect in currentUser"); } }, [currentUser]); From bf0585d18c7105c2d3d3935008cd2caae089b08f Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Fri, 8 Sep 2023 15:19:53 +0500 Subject: [PATCH 059/179] Add receiver public key in context --- .../components/chats/chat-input/index.tsx | 16 ++- .../components/chats/chat-popup/index.tsx | 102 +++--------------- src/common/components/chats/chat-provider.tsx | 6 +- .../chats/chats-direct-messages/index.tsx | 24 +---- .../chats/chats-messages-view/index.tsx | 24 +---- .../chats/chats-profile-box/index.tsx | 4 - .../components/chats/chats-sidebar/indes.tsx | 53 +++++++-- .../components/chats/chats-sidebar/index.scss | 11 +- 8 files changed, 83 insertions(+), 157 deletions(-) diff --git a/src/common/components/chats/chat-input/index.tsx b/src/common/components/chats/chat-input/index.tsx index c91831e7d97..8f8f21673b7 100644 --- a/src/common/components/chats/chat-input/index.tsx +++ b/src/common/components/chats/chat-input/index.tsx @@ -35,6 +35,7 @@ interface Props { currentChannel: Channel; currentUser: string; isCurrentUserJoined: boolean; + receiverPubKey: string; emojiPickerStyles: EmojiPickerStyleProps; gifPickerStyle: EmojiPickerStyleProps; } @@ -43,11 +44,6 @@ export default function ChatInput(props: Props) { const fileInputRef = useRef(null); const { global, activeUser, chat } = useMappedStore(); - const context = useContext(ChatContext); - const { receiverPubKey } = context; - - console.log("receiverPubKey", receiverPubKey); - const [showEmojiPicker, setShowEmojiPicker] = useState(false); const [message, setMessage] = useState(""); const [shGif, setShGif] = useState(false); @@ -61,7 +57,8 @@ export default function ChatInput(props: Props) { currentUser, isCurrentUserJoined, emojiPickerStyles, - gifPickerStyle + gifPickerStyle, + receiverPubKey } = props; useEffect(() => { @@ -107,7 +104,6 @@ export default function ChatInput(props: Props) { !chat.directContacts.some((contact) => contact.name === currentUser) && isCurrentUser ) { - console.log("Contact has been published"); window.messageService?.publishContacts(currentUser, receiverPubKey); } }; @@ -183,7 +179,9 @@ export default function ChatInput(props: Props) { <>
(); - const [receiverPubKey, setReceiverPubKey] = useState(); + const [receiverPubKey, setReceiverPubKey] = useState(""); const [showSpinner, setShowSpinner] = useState(false); const [directMessagesList, setDirectMessagesList] = useState([]); const [isCurrentUserJoined, setIsCurrentUserJoined] = useState(true); @@ -279,7 +279,6 @@ export default function ChatPopUp(props: Props) { useEffect(() => { if (isCurrentUser) { - // zoomInitializer(); scrollerClicked(); } else { scrollerClicked(); @@ -319,11 +318,12 @@ export default function ChatPopUp(props: Props) { useEffect(() => { if (currentUser) { - const isCurrentUserFound = props.chat.directContacts.some( + const isCurrentUserFound = props.chat.directContacts.find( (contact) => contact.name === currentUser ); if (isCurrentUserFound) { - fetchCurrentUserData(); + setReceiverPubKey(isCurrentUserFound.pubkey); + setIsCurrentUserJoined(true); } else { setInProgress(true); fetchCurrentUserData(); @@ -416,7 +416,12 @@ export default function ChatPopUp(props: Props) { const { posting_json_metadata } = response; const profile = JSON.parse(posting_json_metadata!).profile; const { nsKey } = profile || {}; - setReceiverPubKey(nsKey); + if (nsKey) { + setReceiverPubKey(nsKey); + } else { + setReceiverPubKey(""); + } + setIsCurrentUserJoined(!!nsKey); setInProgress(false); }; @@ -530,30 +535,6 @@ export default function ChatPopUp(props: Props) { setCommunityName(community); }; - const handleImportChatSubmit = () => { - try { - const pubKey = getPublicKey(chatPrivKey); - if (pubKey === activeUserKeys?.pub) { - setNoStrPrivKey(chatPrivKey); - ls.set(`${props.activeUser?.username}_nsPrivKey`, chatPrivKey); - const keys = { - pub: activeUserKeys?.pub!, - priv: chatPrivKey - }; - setNostrkeys(keys); - setStep(4); - setChatPrivkey(""); - setImportPrivKey(false); - } else { - setImportPrivKey(true); - setAddRoleError("Invalid Private key"); - } - } catch (error) { - setImportPrivKey(true); - setAddRoleError("Invalid Private key"); - } - }; - const finish = () => { setStep(0); setKeyDialog(false); @@ -677,7 +658,7 @@ export default function ChatPopUp(props: Props) { innerWidth <= 666 ? "small-screen" : "" }`} > - + {/* */}
{(currentUser || communityName || showSearchUser || revelPrivateKey) && expanded && ( @@ -812,6 +793,7 @@ export default function ChatPopUp(props: Props) { {isCurrentUser ? ( userClicked(user.name)} + onClick={() => { + userClicked(user.name); + setReceiverPubKey(user.pubkey); + }} >

{user.name}

@@ -949,60 +934,6 @@ export default function ChatPopUp(props: Props) { ) : !noStrPrivKey || noStrPrivKey.length === 0 || noStrPrivKey === null ? ( <> - {/*

- -
- {importPrivKey && ( -
-
{ - e.preventDefault(); - }} - > - - - {keySvg} - - setChatPrivkey(e.target.value)} - /> - - - - - {addRoleError && ( - {addRoleError} - )} -
-
- )} - {} -
- -
*/} ) : refreshChat ? ( @@ -1061,6 +992,7 @@ export default function ChatPopUp(props: Props) { isCommunity={isCommunity} isActveUserRemoved={isActveUserRemoved} currentUser={currentUser} + receiverPubKey={receiverPubKey} currentChannel={currentChannel!} isCurrentUserJoined={isCurrentUserJoined} emojiPickerStyles={EmojiPickerStyle} diff --git a/src/common/components/chats/chat-provider.tsx b/src/common/components/chats/chat-provider.tsx index 7aae07d1d91..7ea46627e0a 100644 --- a/src/common/components/chats/chat-provider.tsx +++ b/src/common/components/chats/chat-provider.tsx @@ -39,8 +39,8 @@ export default function ChatProvider({ children }: ChatProviderProps) { const [activeUserKeys, setActiveUserKeys] = useState(); const [inProgress, setInProgress] = useState(true); const [chatPrivKey, setChatPrivKey] = useState(""); - const [receiverPubKey, setReceiverPubKey] = useState(""); const [revealPrivKey, setRevealPrivKey] = useState(false); + const [receiverPubKey, setReceiverPubKey] = useState(""); useEffect(() => { getActiveUserKeys(); @@ -72,8 +72,8 @@ export default function ChatProvider({ children }: ChatProviderProps) { activeUserKeys, inProgress, revealPrivKey, - chatPrivKey, receiverPubKey, + chatPrivKey, setRevealPrivKey, setInProgress, setChatPrivKey, @@ -85,8 +85,8 @@ export default function ChatProvider({ children }: ChatProviderProps) { activeUserKeys, inProgress, revealPrivKey, - chatPrivKey, receiverPubKey, + chatPrivKey, setRevealPrivKey, setInProgress, setChatPrivKey, diff --git a/src/common/components/chats/chats-direct-messages/index.tsx b/src/common/components/chats/chats-direct-messages/index.tsx index d0bd91c9b7e..5b741bb9612 100644 --- a/src/common/components/chats/chats-direct-messages/index.tsx +++ b/src/common/components/chats/chats-direct-messages/index.tsx @@ -22,7 +22,6 @@ import { ActiveUser } from "../../../store/active-user/types"; import usePrevious from "react-use/lib/usePrevious"; import { NostrKeysType } from "../types"; import { _t } from "../../../i18n"; -import { ChatContext } from "../chat-provider"; interface Props { directMessages: DirectMessage[]; @@ -30,6 +29,7 @@ interface Props { currentUser: string; isScrollToBottom: boolean; isScrolled?: boolean; + receiverPubKey: string; scrollToBottom?: () => void; } @@ -40,35 +40,17 @@ export default function ChatsDirectMessages(props: Props) { activeUserKeys, currentUser, isScrolled, + receiverPubKey, isScrollToBottom, scrollToBottom } = props; - const context = useContext(ChatContext); - const { receiverPubKey, setReceiverPubKey } = context; const { chat, global, activeUser } = useMappedStore(); console.log("directMessages", directMessages); let prevGlobal = usePrevious(global); - useEffect(() => { - if (currentUser) { - getReceiverPubKey(); - console.log("use effect in currentUser"); - } - }, [currentUser]); - - const getReceiverPubKey = async () => { - const pubKey = await getUserChatPublicKey(currentUser); - - if (pubKey) { - setReceiverPubKey(pubKey); - } else { - setReceiverPubKey(""); - } - }; - useEffect(() => { if (prevGlobal?.theme !== global.theme) { setBackground(); @@ -83,7 +65,7 @@ export default function ChatsDirectMessages(props: Props) { if (!isScrollToBottom && directMessages && directMessages.length !== 0 && !isScrolled) { scrollToBottom && scrollToBottom(); } - }, [directMessages, isScrollToBottom, scrollToBottom]); + }, [directMessages, isScrollToBottom, scrollToBottom, receiverPubKey]); const zoomInitializer = () => { const elements: HTMLElement[] = [...document.querySelectorAll(".chat-image img")]; diff --git a/src/common/components/chats/chats-messages-view/index.tsx b/src/common/components/chats/chats-messages-view/index.tsx index de7d244ed04..6cb285e96ef 100644 --- a/src/common/components/chats/chats-messages-view/index.tsx +++ b/src/common/components/chats/chats-messages-view/index.tsx @@ -65,7 +65,9 @@ export default function ChatsMessagesView(props: Props) { } = props; const context = useContext(ChatContext); - const { setReceiverPubKey } = context; + const { receiverPubKey } = context; + + // console.log("Receiver pubkey in Message view component", receiverPubKey, setReceiverPubKey); const messagesBoxRef = useRef(null); @@ -82,19 +84,11 @@ export default function ChatsMessagesView(props: Props) { const [removedUsers, setRemovedUsers] = useState([]); const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); - // console.log("yaha kiya a rha ha", setReceiverPubKey); - useEffect(() => { getActiveUserKeys(); isDirectUserOrCommunity(); }, []); - // useEffect(() => { - // if (directUser) { - // getReceiverPubKey(); - // } - // }, [directUser]); - useEffect(() => { isDirectUserOrCommunity(); }, [chat.channels]); @@ -159,16 +153,6 @@ export default function ChatsMessagesView(props: Props) { setActiveUserKeys(activeUserKeys); }; - // const getReceiverPubKey = async () => { - // const profileData = await getProfileMetaData(directUser); - - // if (profileData?.nsKey) { - // setReceiverPubKey(profileData?.nsKey); - // } else { - // setReceiverPubKey(""); - // } - // }; - const isDirectUserOrCommunity = () => { if (username) { if (username && username.startsWith("@")) { @@ -264,6 +248,7 @@ export default function ChatsMessagesView(props: Props) { activeUserKeys={activeUserKeys!} currentUser={directUser!} isScrolled={isScrolled} + receiverPubKey={receiverPubKey} isScrollToBottom={isScrollToBottom} scrollToBottom={scrollToBottom} /> @@ -278,6 +263,7 @@ export default function ChatsMessagesView(props: Props) { )}
(); const username = match.params.username; @@ -55,6 +63,8 @@ export default function ChatsSideBar(props: Props) { const [isScrollToTop, setIsScrollToTop] = useState(false); const [communities, setCommunities] = useState([]); + console.log("username in chats sidebar", username); + useDebounce( async () => { if (searchText.length !== 0) { @@ -68,11 +78,11 @@ export default function ChatsSideBar(props: Props) { [searchText] ); - // useEffect(() => { - // if (isScrollToTop) { - // // console.log("Hurrah"); - // } - // }, [isScrollToTop]); + useEffect(() => { + if (username.startsWith("@")) { + getReceiverPubKey(formattedUserName(username)); + } + }, [username]); useEffect(() => { const communities = getJoinedCommunities(channels, leftChannelsList); @@ -107,6 +117,22 @@ export default function ChatsSideBar(props: Props) { } }; + const getReceiverPubKey = async (username: string) => { + console.log("Username ha yar", username); + const peer = directContacts.find((x) => x.name === username)?.pubkey ?? ""; + if (peer) { + setReceiverPubKey(peer); + } else { + const pubkey = await getUserChatPublicKey(username); + console.log("Pubkey", pubkey); + if (pubkey === undefined) { + setReceiverPubKey(""); + } else { + setReceiverPubKey(pubkey); + } + } + }; + return (
@@ -173,7 +199,14 @@ export default function ChatsSideBar(props: Props) { onClick={() => setSearchText("")} key={user.account} > -
+
{ + handleRevealPrivKey(); + getReceiverPubKey(user.account); + }} + > @@ -205,7 +238,11 @@ export default function ChatsSideBar(props: Props) { ))} {directContacts.length !== 0 &&

DMs

} {directContacts.map((contact) => ( - + setReceiverPubKey(contact.pubkey)} + >
Date: Fri, 8 Sep 2023 19:52:28 +0500 Subject: [PATCH 060/179] handle username in chats sidebar component --- src/common/components/chats/chats-sidebar/indes.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/common/components/chats/chats-sidebar/indes.tsx b/src/common/components/chats/chats-sidebar/indes.tsx index 81f300e6075..9831e8cf95a 100644 --- a/src/common/components/chats/chats-sidebar/indes.tsx +++ b/src/common/components/chats/chats-sidebar/indes.tsx @@ -79,8 +79,10 @@ export default function ChatsSideBar(props: Props) { ); useEffect(() => { - if (username.startsWith("@")) { - getReceiverPubKey(formattedUserName(username)); + if (username !== undefined) { + if (username.startsWith("@")) { + getReceiverPubKey(formattedUserName(username)); + } } }, [username]); From c8d4506ab1e2c24ea18e3a6c72303677065038f3 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Tue, 12 Sep 2023 19:44:22 +0500 Subject: [PATCH 061/179] WIP --- src/common/app.tsx | 11 +- ...provider.tsx => chat-context-provider.tsx} | 60 +- .../components/chats/chat-input/index.scss | 8 +- .../components/chats/chat-input/index.tsx | 34 +- .../chats/chat-popup/chat-constants.ts | 2 + .../components/chats/chat-popup/index.scss | 197 +--- .../components/chats/chat-popup/index.tsx | 846 +++++++++--------- .../chats/chats-channel-messages/index.tsx | 21 +- .../chats-community-dropdown-menu/index.scss | 0 .../chats-community-dropdown-menu/index.tsx | 13 +- .../chats/chats-direct-messages/index.tsx | 2 +- .../chats/chats-messages-box/index.tsx | 8 +- .../chats/chats-messages-header/index.tsx | 2 +- .../chats/chats-messages-view/index.tsx | 13 +- .../chats/chats-profile-box/index.tsx | 11 +- .../components/chats/chats-sidebar/indes.tsx | 10 +- .../components/chats/import-chats/index.scss | 2 + .../components/chats/import-chats/index.tsx | 10 +- .../components/chats/join-chat/index.tsx | 9 +- .../chats/join-community-chat-btn/index.tsx | 16 +- .../components/chats/set-chat-keys/index.tsx | 28 - src/common/components/chats/types/index.ts | 1 + .../chats/utils/get-joined-communities.ts | 2 +- .../chats/utils/get-user-chat-public-key.ts | 2 +- .../chats/utils/upload-channel-data.ts | 2 +- .../utils/use-fetch-community-messages.ts | 2 +- .../components/community-cover/index.tsx | 6 +- src/common/components/login/index.tsx | 2 +- .../components/manage-chat-key/index.tsx | 2 +- src/common/helper/message-service.ts | 33 +- src/common/pages/chats/index.tsx | 106 +-- src/common/pages/community-functional.tsx | 22 +- src/common/store/chat/index.ts | 2 +- src/common/store/chat/types.ts | 2 +- .../message-manager-types.ts} | 0 .../message-manager.tsx} | 54 +- 36 files changed, 698 insertions(+), 843 deletions(-) rename src/common/components/chats/{chat-provider.tsx => chat-context-provider.tsx} (57%) delete mode 100644 src/common/components/chats/chats-community-dropdown-menu/index.scss delete mode 100644 src/common/components/chats/set-chat-keys/index.tsx rename src/{providers/message-provider-types.ts => managers/message-manager-types.ts} (100%) rename src/{providers/message-provider.tsx => managers/message-manager.tsx} (89%) diff --git a/src/common/app.tsx b/src/common/app.tsx index 03c7c378456..ced6574a230 100644 --- a/src/common/app.tsx +++ b/src/common/app.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import { Route, Switch } from "react-router-dom"; import EntryIndexContainer from "./pages/index"; -import MessageProvider from "../providers/message-provider"; +import MessageManager from "../managers/message-manager"; import { EntryScreen } from "./pages/entry"; import { SearchMorePageContainer, SearchPageContainer } from "./pages/search"; import { ProposalDetailContainer, ProposalsIndexContainer } from "./pages/proposals"; @@ -25,11 +25,12 @@ import { connect } from "react-redux"; import loadable from "@loadable/component"; import Announcement from "./components/announcement"; import FloatingFAQ from "./components/floating-faq"; -import ChatPopUp from "./components/chats/chat-popup"; import { useMappedStore } from "./store/use-mapped-store"; import { EntriesCacheManager } from "./core"; import { UserActivityRecorder } from "./components/user-activity-recorder"; +import ChatContextProvider from "./components/chats/chat-context-provider"; +import ChatPopUp from "./components/chats/chat-popup"; // Define lazy pages const ProfileContainer = loadable(() => import("./pages/profile-functional")); @@ -184,8 +185,10 @@ const App = (props: any) => { - - + + + + ); }; diff --git a/src/common/components/chats/chat-provider.tsx b/src/common/components/chats/chat-context-provider.tsx similarity index 57% rename from src/common/components/chats/chat-provider.tsx rename to src/common/components/chats/chat-context-provider.tsx index 7ea46627e0a..6c1d8b765af 100644 --- a/src/common/components/chats/chat-provider.tsx +++ b/src/common/components/chats/chat-context-provider.tsx @@ -1,4 +1,6 @@ import React, { useEffect, useState } from "react"; +import { Keys } from "../../../managers/message-manager-types"; +import MessageService from "../../helper/message-service"; import { useMappedStore } from "../../store/use-mapped-store"; import { NostrKeysType } from "./types"; import { getPrivateKey, getUserChatPublicKey } from "./utils"; @@ -9,15 +11,19 @@ interface Context { revealPrivKey: boolean; chatPrivKey: string; receiverPubKey: string; + messageServiceInstance: MessageService | undefined; setRevealPrivKey: (d: boolean) => void; setInProgress: (d: boolean) => void; setChatPrivKey: (key: string) => void; setActiveUserKeys: (keys: NostrKeysType) => void; setReceiverPubKey: (key: string) => void; + + setMessageServiceInstance: (instance: MessageService | undefined) => void; + initMessageServiceInstance: (keys: Keys) => void; } -interface ChatProviderProps { - children: (context: Context) => React.ReactNode; +interface Props { + children: JSX.Element | JSX.Element[]; } export const ChatContext = React.createContext({ @@ -26,14 +32,17 @@ export const ChatContext = React.createContext({ revealPrivKey: false, chatPrivKey: "", receiverPubKey: "", + messageServiceInstance: undefined, setRevealPrivKey: () => {}, setInProgress: () => {}, setChatPrivKey: () => {}, setActiveUserKeys: () => {}, - setReceiverPubKey: () => {} + setReceiverPubKey: () => {}, + setMessageServiceInstance: () => {}, + initMessageServiceInstance: () => {} }); -export default function ChatProvider({ children }: ChatProviderProps) { +export default function ChatContextProvider({ children }: Props) { const { activeUser } = useMappedStore(); const [activeUserKeys, setActiveUserKeys] = useState(); @@ -41,11 +50,20 @@ export default function ChatProvider({ children }: ChatProviderProps) { const [chatPrivKey, setChatPrivKey] = useState(""); const [revealPrivKey, setRevealPrivKey] = useState(false); const [receiverPubKey, setReceiverPubKey] = useState(""); + const [messageServiceInstance, setMessageServiceInstance] = useState( + undefined + ); useEffect(() => { getActiveUserKeys(); }, []); + useEffect(() => { + if (messageServiceInstance) { + console.log("messageServiceInstance in context", messageServiceInstance); + } + }, [messageServiceInstance]); + useEffect(() => { if (inProgress) { setTimeout(() => { @@ -66,6 +84,22 @@ export default function ChatProvider({ children }: ChatProviderProps) { setActiveUserKeys(activeUserKeys); }; + const initMessageServiceInstance = (keys: Keys) => { + console.log("Keys are in context", keys, messageServiceInstance); + if (messageServiceInstance) { + messageServiceInstance.close(); + setMessageServiceInstance(undefined); + } + + let newMessageService: MessageService | undefined = undefined; + if (keys) { + newMessageService = new MessageService(keys.priv, keys.pub); + console.log("raven instance created", newMessageService); + setMessageServiceInstance(newMessageService); + } + return newMessageService; + }; + return ( - {children({ - activeUserKeys, - inProgress, - revealPrivKey, - receiverPubKey, - chatPrivKey, - setRevealPrivKey, - setInProgress, - setChatPrivKey, - setActiveUserKeys, - setReceiverPubKey - })} + {children} ); } diff --git a/src/common/components/chats/chat-input/index.scss b/src/common/components/chats/chat-input/index.scss index b5af1d1fa20..a93f6b97fd1 100644 --- a/src/common/components/chats/chat-input/index.scss +++ b/src/common/components/chats/chat-input/index.scss @@ -1,7 +1,4 @@ -@import "src/style/colors"; -@import "src/style/variables"; -@import "src/style/bootstrap_vars"; -@import "src/style/mixins"; +@import "../../../../style/vars_mixins"; .chat { grid-row: span 1; @@ -44,6 +41,9 @@ .chat-input { padding-top: 10px; border: none; + max-width: 100%; + overflow-wrap: break-word; + @include themify(day) { background: #e4e6eb; } diff --git a/src/common/components/chats/chat-input/index.tsx b/src/common/components/chats/chat-input/index.tsx index 8f8f21673b7..7ac59dae052 100644 --- a/src/common/components/chats/chat-input/index.tsx +++ b/src/common/components/chats/chat-input/index.tsx @@ -2,7 +2,7 @@ import React, { useContext, useEffect, useRef, useState } from "react"; import { Form, FormControl, InputGroup } from "react-bootstrap"; import axios from "axios"; -import { Channel } from "../../../../providers/message-provider-types"; +import { Channel } from "../../../../managers/message-manager-types"; import { EmojiPickerStyleProps } from "../types"; import ClickAwayListener from "../../clickaway-listener"; @@ -17,7 +17,8 @@ import { chatBoxImageSvg, messageSendSvg } from "../../../img/svg"; -import { GifImagesStyle, UPLOADING } from "../chat-popup/chat-constants"; +import { CHAT_FILE_CONTENT_TYPES, GifImagesStyle, UPLOADING } from "../chat-popup/chat-constants"; +import { classNameObject } from "../../../helper/class-name-object"; import { useMappedStore } from "../../../store/use-mapped-store"; import { _t } from "../../../i18n"; @@ -26,7 +27,7 @@ import { uploadImage } from "../../../api/misc"; import { addImage } from "../../../api/private-api"; import "./index.scss"; -import { ChatContext } from "../chat-provider"; +import { ChatContext } from "../chat-context-provider"; interface Props { isCurrentUser: boolean; @@ -49,6 +50,9 @@ export default function ChatInput(props: Props) { const [shGif, setShGif] = useState(false); const [isMessageText, setIsMessageText] = useState(false); + const { messageServiceInstance, chatPrivKey } = useContext(ChatContext); + // console.log("yaha check kar ka dekho", messageServiceInstance, chatPrivKey); + const { isCommunity, isCurrentUser, @@ -80,21 +84,21 @@ export default function ChatInput(props: Props) { const handleGifSelection = (gif: string) => { isCurrentUser - ? window.messageService?.sendDirectMessage(receiverPubKey!, gif) - : window?.messageService?.sendPublicMessage(currentChannel, gif, [], ""); + ? messageServiceInstance?.sendDirectMessage(receiverPubKey!, gif) + : messageServiceInstance?.sendPublicMessage(currentChannel, gif, [], ""); }; const sendMessage = () => { if (message.length !== 0 && !message.includes(UPLOADING)) { if (isCommunity) { if (!isActveUserRemoved) { - window?.messageService?.sendPublicMessage(currentChannel, message, [], ""); + messageServiceInstance?.sendPublicMessage(currentChannel, message, [], ""); } else { error(_t("chat.message-warning")); } } if (isCurrentUser) { - window.messageService?.sendDirectMessage(receiverPubKey!, message); + messageServiceInstance?.sendDirectMessage(receiverPubKey!, message); } setMessage(""); setIsMessageText(false); @@ -104,13 +108,13 @@ export default function ChatInput(props: Props) { !chat.directContacts.some((contact) => contact.name === currentUser) && isCurrentUser ) { - window.messageService?.publishContacts(currentUser, receiverPubKey); + messageServiceInstance?.publishContacts(currentUser, receiverPubKey); } }; const checkFile = (filename: string) => { const filenameLow = filename.toLowerCase(); - return ["jpg", "jpeg", "gif", "png"].some((el) => filenameLow.endsWith(el)); + return CHAT_FILE_CONTENT_TYPES.some((el) => filenameLow.endsWith(el)); }; const fileInputChanged = (e: React.ChangeEvent): void => { @@ -249,12 +253,11 @@ export default function ChatInput(props: Props) { )} @@ -265,7 +268,7 @@ export default function ChatInput(props: Props) { e.stopPropagation(); sendMessage(); }} - style={{ width: "100%" }} + className="w-100" > {messageSendSvg} diff --git a/src/common/components/chats/chat-popup/chat-constants.ts b/src/common/components/chats/chat-popup/chat-constants.ts index dab6f664135..5401fcde0b6 100644 --- a/src/common/components/chats/chat-popup/chat-constants.ts +++ b/src/common/components/chats/chat-popup/chat-constants.ts @@ -27,6 +27,8 @@ export const GifImagesStyle = { width: "170px" }; +export const CHAT_FILE_CONTENT_TYPES = ["jpg", "jpeg", "gif", "png"]; + export const NOSTRKEY = "nsKey"; export const UPLOADING = "Uploading"; export const GIPHGY = "giphy"; diff --git a/src/common/components/chats/chat-popup/index.scss b/src/common/components/chats/chat-popup/index.scss index e6065a34a2f..d7a117ad0f3 100644 --- a/src/common/components/chats/chat-popup/index.scss +++ b/src/common/components/chats/chat-popup/index.scss @@ -1,7 +1,4 @@ -@import "src/style/colors"; -@import "src/style/variables"; -@import "src/style/bootstrap_vars"; -@import "src/style/mixins"; +@import "../../../../style/vars_mixins"; .chatbox-container { position: fixed; @@ -161,7 +158,6 @@ padding-left: 14px; font-family: Faktum, sans-serif; font-weight: 700; - // background: #e4e6eb; } &.current-user { @@ -258,7 +254,7 @@ padding: 13px 16px; } .user-title { - padding-top: 13px; + padding-top: 6px; width: -webkit-fill-available; .username { @@ -403,171 +399,6 @@ justify-content: center; margin-top: 14%; } - // .custom-divider { - // margin: 12px 24px 0px 24px; - // font-size: 0.7em; - // color: $dark; - // .custom-divider-text { - // display: flex; - // justify-content: center; - // padding: 0 8px; - // } - // } - // .message { - // display: flex; - // .user-img { - // padding: 18px 8px 8px 16px; - // } - // .community-user-img { - // padding: 8px 8px 8px 16px; - // .user-avatar.medium { - // cursor: pointer; - // } - // } - // .user-info { - // padding-top: 8px; - // .user-msg-time { - // margin: 0; - // color: rgb(138, 141, 145); - // font-weight: 400; - // font-size: 12px; - // margin-left: 4px; - // margin-bottom: 2px; - // .username-community { - // margin-right: 8px; - // } - // } - // .receiver-message-content { - // @include themify(day) { - // background: #e4e6eb; - // } - // @include themify(night) { - // background: #cee2ff; - // } - - // &.gif { - // background: none; - // img { - // max-width: 100%; - // } - // } - - // &.chat-image { - // background: none; - // img { - // max-width: 100%; - // } - // } - // color: #050505; - // max-width: 290px; - // word-wrap: break-word; - // padding: 10px; - // border-radius: 0px 10px 10px; - // } - // } - // } - // .sender { - // margin-bottom: 0.17rem; - // .sender-message-time { - // color: rgb(138, 141, 145); - // display: flex; - // font-weight: 400; - // justify-content: flex-end; - // font-size: 12px; - // margin: 0; - // margin-right: 18px; - // margin-bottom: 2px; - // } - // .sender-message { - // // max-width: 320px; - // margin-left: auto; - - // display: flex; - // justify-content: flex-end; - // margin-right: 17px; - - // .resend-svg { - // margin: 5px 15px 0 0; - // cursor: pointer; - // svg { - // @include themify(day) { - // fill: $light-black; - // } - // @include themify(night) { - // fill: $white; - // } - // } - // } - - // .failed-svg { - // margin: 8px 0 0 5px; - // svg { - // width: 16px; - // height: 16px; - // fill: $red !important; - // } - // } - - // &.sending { - // margin-right: 5px; - // } - // &.failed { - // margin-right: 7px; - // } - - // .sender-message-content { - // border-radius: 10px 10px 0px; - // max-width: 290px; - // word-wrap: break-word; - // margin-bottom: 0; - // color: $charcoal-grey; - // font-size: 16px; - // font-weight: 400; - // padding: 8px 12px 8px 12px; - // a { - // text-decoration: underline; - // color: white; - // } - // @include themify(day) { - // background-color: rgb(0, 132, 255); - // color: $white; - // } - // @include themify(night) { - // background: $charcoal-grey; - // color: $white; - // } - // &.gif { - // background: none; - // img { - // max-width: 100%; - // } - // } - - // &.chat-image { - // background: none; - // img { - // max-width: 100%; - // } - // } - // } - // } - // } - // .hide-msg { - // margin-right: 15px; - // margin-top: 5px; - // svg { - // height: 14px; - // width: 14px; - // } - // .hide-msg-svg { - // cursor: pointer; - // margin-bottom: 0; - // } - // &.receiver { - // margin-top: 33px; - // margin-left: 10px; - // } - // } } .import-chats { @@ -606,6 +437,12 @@ .chat-body { grid-row: span 16; height: 100%; + + .chat-content { + .last-message { + max-width: 75vw; + } + } } .chat { grid-row: span 1; @@ -662,24 +499,6 @@ } } -// .profile-box { -// padding: 15px; -// .profile-box-content { -// .user-avatar.large { -// margin-bottom: 20px; -// } -// .profile-name { -// margin-bottom: 10px; -// font-size: 18px; -// font-weight: 800; -// } -// .profile-box-buttons { -// margin-bottom: 15px; -// padding: 0 5px; -// } -// } -// } - .chat-button { position: fixed; z-index: 99; diff --git a/src/common/components/chats/chat-popup/index.tsx b/src/common/components/chats/chat-popup/index.tsx index 36c5f053ab2..025ffff88eb 100644 --- a/src/common/components/chats/chat-popup/index.tsx +++ b/src/common/components/chats/chat-popup/index.tsx @@ -2,13 +2,10 @@ import React, { useContext, useEffect, useRef, useState } from "react"; import useDebounce from "react-use/lib/useDebounce"; import { useLocation } from "react-router"; import { History } from "history"; -import { Button, Form, InputGroup, Modal, Spinner } from "react-bootstrap"; +import { Button, Form, Modal, Spinner } from "react-bootstrap"; import { Link } from "react-router-dom"; -import { ActiveUser } from "../../../store/active-user/types"; -import { Chat, DirectContactsType } from "../../../store/chat/types"; import { Community } from "../../../store/communities/types"; -import { Global } from "../../../store/global/types"; import { User } from "../../../store/users/types"; import { ToggleType, UI } from "../../../store/ui/types"; import { Account } from "../../../store/accounts/types"; @@ -17,14 +14,13 @@ import { ChannelUpdate, DirectMessage, PublicMessage -} from "../../../../providers/message-provider-types"; +} from "../../../../managers/message-manager-types"; import Tooltip from "../../tooltip"; import UserAvatar from "../../user-avatar"; import LinearProgress from "../../linear-progress"; -import { error, success } from "../../feedback"; -import { setNostrkeys } from "../../../../providers/message-provider"; -import OrDivider from "../../or-divider"; +import { error } from "../../feedback"; +import { setNostrkeys } from "../../../../managers/message-manager"; import ManageChatKey from "../../manage-chat-key"; import ChatInput from "../chat-input"; import ChatsProfileBox from "../chats-profile-box"; @@ -39,15 +35,11 @@ import { arrowBackSvg, chevronUpSvg, chevronDownSvgForSlider, - keySvg, syncSvg } from "../../../img/svg"; import { NOSTRKEY, - HIDEMESSAGE, - BLOCKUSER, - UNBLOCKUSER, NEWCHATACCOUNT, CHATIMPORT, RESENDMESSAGE, @@ -55,10 +47,10 @@ import { GifPickerStyle } from "./chat-constants"; -import { getPublicKey } from "../../../../lib/nostr-tools/keys"; import * as ls from "../../../util/local-storage"; import accountReputation from "../../../helper/account-reputation"; import { _t } from "../../../i18n"; +import { usePrevious } from "../../../util/use-previous"; import { getAccountFull, getAccountReputations } from "../../../api/hive"; import { getCommunity } from "../../../api/bridge"; @@ -78,19 +70,11 @@ import { setProfileMetaData } from "../utils"; import { useMappedStore } from "../../../store/use-mapped-store"; -import ChatProvider, { ChatContext } from "../chat-provider"; +import { ChatContext } from "../chat-context-provider"; import ImportChats from "../import-chats"; -import SetActiveUserChatKeys from "../set-chat-keys"; interface Props { - users: User[]; - activeUser: ActiveUser | null; - ui: UI; - targetUsername: string; - where?: string; history: History; - global: Global; - chat: Chat; resetChat: () => void; setActiveUser: (username: string | null) => void; updateActiveUser: (data?: Account) => void; @@ -100,14 +84,33 @@ interface Props { deleteDirectMessage: (peer: string, msgId: string) => void; } +export const profileUpdater = () => { + const ev = new CustomEvent("profileUpdater"); + window.dispatchEvent(ev); +}; + export default function ChatPopUp(props: Props) { - const routerLocation = useLocation(); - const prevPropsRef = useRef(props); - const chatBodyDivRef = useRef(null); + const { activeUser, global, chat } = useMappedStore(); + + const chatContext = useContext(ChatContext); + // const messageServiceContext = useContext(MessageServiceContext); + + const { + // activeUserKeys, + setRevealPrivKey, + // setReceiverPubKey, + messageServiceInstance, + chatPrivKey, + revealPrivKey + } = chatContext; - const { activeUser } = useMappedStore(); + // const { messageServiceInstance } = messageServiceContext; - const context = useContext(ChatContext); + console.log("messageServiceInstance chatpop up", chatPrivKey, messageServiceInstance); + + const routerLocation = useLocation(); + const prevActiveUser = usePrevious(activeUser); + const chatBodyDivRef = useRef(null); const [expanded, setExpanded] = useState(false); const [currentUser, setCurrentUser] = useState(""); @@ -134,31 +137,41 @@ export default function ChatPopUp(props: Props) { const [communities, setCommunities] = useState([]); const [searchtext, setSearchText] = useState(""); const [userList, setUserList] = useState([]); - const [addRoleError, setAddRoleError] = useState(""); const [noStrPrivKey, setNoStrPrivKey] = useState(""); - const [chatPrivKey, setChatPrivkey] = useState(""); - const [hiddenMsgId, setHiddenMsgId] = useState(""); - const [removedUserId, setRemovedUserID] = useState(""); const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); const [isTop, setIsTop] = useState(false); const [hasMore, setHasMore] = useState(true); const [removedUsers, setRemovedUsers] = useState([]); - const [importPrivKey, setImportPrivKey] = useState(false); const [revelPrivateKey, setRevealPrivateKey] = useState(false); const [innerWidth, setInnerWidth] = useState(0); const [isChatPage, setIsChatPage] = useState(false); const [refreshChat, setRefreshChat] = useState(false); + // useEffect(() => { + // console.log("directMessagesList in chat popup", directMessagesList); + // }, [directMessagesList]); + useEffect(() => { - console.log("directMessagesList in chat popup", directMessagesList); - }, [directMessagesList]); + window.addEventListener("profileUpdater", noStrProfileUpdater); + + return () => { + window.removeEventListener("profileUpdater", noStrProfileUpdater); + }; + }, []); useEffect(() => { - if (currentChannel && props.chat.leftChannelsList.includes(currentChannel.id)) { + console.log( + "-----------------------------------------------------------------", + messageServiceInstance + ); + }, [messageServiceInstance]); + + useEffect(() => { + if (currentChannel && chat.leftChannelsList.includes(currentChannel.id)) { setIsCommunity(false); setCommunityName(""); } - }, [props.chat.leftChannelsList]); + }, [chat.leftChannelsList]); useEffect(() => { handleRouterChange(); @@ -171,21 +184,21 @@ export default function ChatPopUp(props: Props) { }, [isChatPage]); useEffect(() => { - // deleteChatPublicKey(props.activeUser); + // deleteChatPublicKey(activeUser); fetchProfileData(); - !isChatPage && setShow(!!props.activeUser?.username); - const noStrPrivKey = getPrivateKey(props.activeUser?.username!); + setShow(!!activeUser?.username && !isChatPage); + const noStrPrivKey = getPrivateKey(activeUser?.username!); setNoStrPrivKey(noStrPrivKey); setInnerWidth(window.innerWidth); }, []); useEffect(() => { - const updated: ChannelUpdate = props.chat.updatedChannel + const updated: ChannelUpdate = chat.updatedChannel .filter((x) => x.channelId === currentChannel?.id!) .sort((a, b) => b.created - a.created)[0]; if (currentChannel && updated) { const publicMessages: PublicMessage[] = fetchCommunityMessages( - props.chat.publicMessages, + chat.publicMessages, currentChannel, updated?.hiddenMessageIds ); @@ -205,7 +218,7 @@ export default function ChatPopUp(props: Props) { }; setCurrentChannel(channel); } - }, [props.chat.updatedChannel]); + }, [chat.updatedChannel]); useEffect(() => { if (isTop) { @@ -214,47 +227,40 @@ export default function ChatPopUp(props: Props) { }, [isTop]); useEffect(() => { - if (window.messageService) { - console.log("Use effect run"); + if (messageServiceInstance) { setHasUserJoinedChat(true); - const noStrPrivKey = getPrivateKey(props.activeUser?.username!); + const noStrPrivKey = getPrivateKey(activeUser?.username!); setNoStrPrivKey(noStrPrivKey); } setTimeout(() => { - if (props.chat.channels.length === 0 && props.chat.directContacts.length === 0) { + if (chat.channels.length === 0 && chat.directContacts.length === 0) { setRefreshChat(false); } }, 5000); - }, [typeof window !== "undefined" && window?.messageService]); + }, [typeof window !== "undefined" && messageServiceInstance]); useEffect(() => { - const communities = getJoinedCommunities(props.chat.channels, props.chat.leftChannelsList); + const communities = getJoinedCommunities(chat.channels, chat.leftChannelsList); setCommunities(communities); fetchProfileData(); - }, [props.chat.channels, props.chat.leftChannelsList]); + }, [chat.channels, chat.leftChannelsList]); useEffect(() => { const msgsList = fetchDirectMessages(receiverPubKey!); const messages = msgsList.sort((a, b) => a.created - b.created); setDirectMessagesList(messages); - }, [props.chat.directMessages]); + }, [chat.directMessages]); useEffect(() => { - const prevProps = prevPropsRef.current; - - if (prevProps.activeUser?.username !== props.activeUser?.username) { + if (prevActiveUser?.username !== activeUser?.username) { setIsCommunity(false); setIsCurrentUser(false); setCurrentUser(""); setCommunityName(""); } - prevPropsRef.current = props; - }, [props.global.theme, props.activeUser]); + }, [global.theme, activeUser]); useEffect(() => { - if (directMessagesList.length !== 0 || publicMessages.length !== 0) { - // zoomInitializer(); - } if (directMessagesList.length !== 0) { scrollerClicked(); } @@ -267,7 +273,7 @@ export default function ChatPopUp(props: Props) { if (currentChannel && isCommunity) { window?.messageService?.fetchChannel(currentChannel.id); const publicMessages: PublicMessage[] = fetchCommunityMessages( - props.chat.publicMessages, + chat.publicMessages, currentChannel, currentChannel.hiddenMessageIds ); @@ -275,7 +281,7 @@ export default function ChatPopUp(props: Props) { const messages = publicMessages.sort((a, b) => a.created - b.created); setPublicMessages(messages); } - }, [currentChannel, isCommunity, props.chat.publicMessages]); + }, [currentChannel, isCommunity, chat.publicMessages]); useEffect(() => { if (isCurrentUser) { @@ -297,9 +303,9 @@ export default function ChatPopUp(props: Props) { const msgsList = fetchDirectMessages(receiverPubKey!); const messages = msgsList.sort((a, b) => a.created - b.created); setDirectMessagesList(messages); - const noStrPrivKey = getPrivateKey(props.activeUser?.username!); + const noStrPrivKey = getPrivateKey(activeUser?.username!); setNoStrPrivKey(noStrPrivKey); - }, [props.activeUser]); + }, [activeUser]); useEffect(() => { if (isCommunity) { @@ -318,7 +324,7 @@ export default function ChatPopUp(props: Props) { useEffect(() => { if (currentUser) { - const isCurrentUserFound = props.chat.directContacts.find( + const isCurrentUserFound = chat.directContacts.find( (contact) => contact.name === currentUser ); if (isCurrentUserFound) { @@ -329,11 +335,11 @@ export default function ChatPopUp(props: Props) { fetchCurrentUserData(); } - const peer = props.chat.directContacts.find((x) => x.name === currentUser)?.pubkey ?? ""; + const peer = chat.directContacts.find((x) => x.name === currentUser)?.pubkey ?? ""; const msgsList = fetchDirectMessages(peer!); const messages = msgsList.sort((a, b) => a.created - b.created); setDirectMessagesList(messages); - if (!window.messageService) { + if (!messageServiceInstance) { setNostrkeys(activeUserKeys!); } } else { @@ -367,15 +373,24 @@ export default function ChatPopUp(props: Props) { } }; + const noStrProfileUpdater = () => { + console.log("Function run", messageServiceInstance); + messageServiceInstance?.updateProfile({ + name: activeUser?.username!, + about: "", + picture: "" + }); + }; + const fetchCommunity = async () => { - const community = await getCommunity(communityName, props.activeUser?.username); + const community = await getCommunity(communityName, activeUser?.username); setCurrentCommunity(community!); }; const fetchCurrentChannel = (communityName: string) => { - const channel = props.chat.channels.find((channel) => channel.communityName === communityName); + const channel = chat.channels.find((channel) => channel.communityName === communityName); if (channel) { - const updated: ChannelUpdate = props.chat.updatedChannel + const updated: ChannelUpdate = chat.updatedChannel .filter((x) => x.channelId === channel.id) .sort((a, b) => b.created - a.created)[0]; if (updated) { @@ -403,7 +418,7 @@ export default function ChatPopUp(props: Props) { }; const fetchDirectMessages = (peer: string) => { - for (const item of props.chat.directMessages) { + for (const item of chat.directMessages) { if (item.peer === peer) { return Object.values(item.chat); } @@ -427,8 +442,8 @@ export default function ChatPopUp(props: Props) { }; const fetchProfileData = async () => { - const profileData = await getProfileMetaData(props.activeUser?.username!); - const noStrPrivKey = getPrivateKey(props.activeUser?.username!); + const profileData = await getProfileMetaData(activeUser?.username!); + const noStrPrivKey = getPrivateKey(activeUser?.username!); const activeUserKeys = { pub: profileData?.nsKey, priv: noStrPrivKey @@ -447,7 +462,7 @@ export default function ChatPopUp(props: Props) { if (!hasMore || inProgress) return; setInProgress(true); - window.messageService + messageServiceInstance ?.fetchPrevMessages(currentChannel!.id, publicMessages[0].created) .then((num) => { if (num < 25) { @@ -492,11 +507,11 @@ export default function ChatPopUp(props: Props) { const { resetChat } = props; resetChat(); handleBackArrowSvg(); - if (getPrivateKey(props.activeUser?.username!)) { - setRefreshChat(true); + if (getPrivateKey(activeUser?.username!)) { + // setRefreshChat(true); const keys = { pub: activeUserKeys?.pub!, - priv: getPrivateKey(props.activeUser?.username!) + priv: getPrivateKey(activeUser?.username!) }; setNostrkeys(keys); if (isCommunity && communityName) { @@ -512,16 +527,12 @@ export default function ChatPopUp(props: Props) { setShowSpinner(true); resetChat(); const keys = createNoStrAccount(); - ls.set(`${props.activeUser?.username}_nsPrivKey`, keys.priv); + ls.set(`${activeUser?.username}_nsPrivKey`, keys.priv); setNoStrPrivKey(keys.priv); - await setProfileMetaData(props.activeUser, keys.pub); + await setProfileMetaData(activeUser, keys.pub); setHasUserJoinedChat(true); setNostrkeys(keys); - window.messageService?.updateProfile({ - name: props.activeUser?.username!, - about: "", - picture: "" - }); + noStrProfileUpdater(); setActiveUserKeys(keys); setShowSpinner(false); }; @@ -544,7 +555,7 @@ export default function ChatPopUp(props: Props) { switch (actionType) { case NEWCHATACCOUNT: setInProgress(true); - deleteChatPublicKey(props.activeUser) + deleteChatPublicKey(activeUser) .then((updatedProfile) => { handleJoinChat(); setStep(10); @@ -632,7 +643,6 @@ export default function ChatPopUp(props: Props) { const toggleKeyDialog = () => { setKeyDialog(!keyDialog); - setAddRoleError(""); }; const handleBackArrowSvg = () => { @@ -645,383 +655,369 @@ export default function ChatPopUp(props: Props) { setSearchText(""); setHasMore(true); setRevealPrivateKey(false); - setAddRoleError(""); }; return ( - - {(context) => ( - <> - {show && ( -
- {/* */} -
- {(currentUser || communityName || showSearchUser || revelPrivateKey) && expanded && ( - -
- - {" "} - {arrowBackSvg} - + <> + {show && ( +
+
+ {(currentUser || communityName || showSearchUser || revelPrivateKey) && expanded && ( + +
+ + {" "} + {arrowBackSvg} + +
+
+ )} +
setExpanded(!expanded)}> + {(currentUser || isCommunity) && ( +

+ +

+ )} + +

+ {currentUser + ? currentUser + : isCommunity + ? currentCommunity?.title + : showSearchUser + ? "New Message" + : revelPrivateKey + ? "Manage chat key" + : "Messages"} +

+
+
+ {!currentUser && + hasUserJoinedChat && + noStrPrivKey && + !isCommunity && + !revelPrivateKey && ( + <> +
+ +

{addMessageSVG}

+
- + )} -
setExpanded(!expanded)}> - {(currentUser || isCommunity) && ( -

- + {hasUserJoinedChat && noStrPrivKey && !revelPrivateKey && ( +

+ +

+ {syncSvg}

- )} - -

- {currentUser - ? currentUser - : isCommunity - ? currentCommunity?.title - : showSearchUser - ? "New Message" - : revelPrivateKey - ? "Manage chat key" - : "Messages"} -

+
-
- {!currentUser && - hasUserJoinedChat && - noStrPrivKey && - !isCommunity && - !revelPrivateKey && ( - <> -
- -

{addMessageSVG}

-
-
- - )} - {hasUserJoinedChat && noStrPrivKey && !revelPrivateKey && ( -
- -

- {syncSvg} -

-
-
- )} - {isCommunity && ( -
- -
- )}{" "} - {!isCommunity && !isCurrentUser && noStrPrivKey && ( -
- { - setRevealPrivateKey(!revelPrivateKey); - }} - /> -
- )} -
- -

{ - setExpanded(!expanded); - }} - > - {expanded ? expandArrow : collapseArrow} -

-
-
+ )} + {isCommunity && ( +
+ +
+ )}{" "} + {!isCommunity && !isCurrentUser && noStrPrivKey && ( +
+ { + setRevealPrivateKey(!revelPrivateKey); + }} + />
+ )} +
+ +

{ + setExpanded(!expanded); + }} + > + {expanded ? expandArrow : collapseArrow} +

+
- {inProgress && } - {inProgress && !isCommunity && !isCurrentUser && } -
- {hasUserJoinedChat && !revelPrivateKey ? ( +
+
+ {inProgress && } + {inProgress && !isCommunity && !isCurrentUser && } +
+ {hasUserJoinedChat && !revelPrivateKey ? ( + <> + {currentUser.length !== 0 || communityName.length !== 0 ? ( +
+ <> + {" "} + + + + {isCurrentUser ? ( + + ) : ( + + )} + +
+ ) : showSearchUser ? ( <> - {currentUser.length !== 0 || communityName.length !== 0 ? ( -
- <> - {" "} - + + { + setSearchText(e.target.value); + setInProgress(true); + }} + /> + +
+
+ {userList.map((user, index) => { + return ( +
{ + setCurrentUser(user.account); + setIsCurrentUser(true); + }} > - - - {isCurrentUser ? ( - - ) : ( - - )} - -
- ) : showSearchUser ? ( - <> -
- - { - setSearchText(e.target.value); - setInProgress(true); - }} - /> - -
-
- {userList.map((user, index) => { - return ( -
{ - setCurrentUser(user.account); - setIsCurrentUser(true); - }} - > -
- - - -
+
+ + + +
-
-

{user.account}

-

- ({accountReputation(user.reputation)}) -

-
-
- ); - })} -
- - ) : ( - <> - {(props.chat.directContacts.length !== 0 || - (props.chat.channels.length !== 0 && communities.length !== 0)) && - !refreshChat && - noStrPrivKey ? ( - - {props.chat.channels.length !== 0 && communities.length !== 0 && ( - <> -
{_t("chat.communities")}
- {communities.map((channel) => { - return ( -
- -
- - - -
- - -
- communityClicked(channel.communityName!, channel.name) - } - > -

- {channel.name} -

-

- {getCommunityLastMessage( - channel.id, - props.chat.publicMessages - )} -

-
-
- ); - })} - {props.chat.directContacts.length !== 0 && ( -
{_t("chat.dms")}
- )} - - )} - {props.chat.directContacts.map((user: DirectContactsType) => { +
+

{user.account}

+

+ ({accountReputation(user.reputation)}) +

+
+
+ ); + })} +
+ + ) : ( + <> + {(chat.directContacts.length !== 0 || + (chat.channels.length !== 0 && communities.length !== 0)) && + !refreshChat && + noStrPrivKey ? ( + + {chat.channels.length !== 0 && communities.length !== 0 && ( + <> +
{_t("chat.communities")}
+ {communities.map((channel) => { return ( -
- +
+
- +
{ - userClicked(user.name); - setReceiverPubKey(user.pubkey); - }} + onClick={() => + communityClicked(channel.communityName!, channel.name) + } > -

{user.name}

+

+ {channel.name} +

- {getDirectLastMessage(user.pubkey, props.chat.directMessages)} + {getCommunityLastMessage(channel.id, chat.publicMessages)}

); })} - - ) : !noStrPrivKey || noStrPrivKey.length === 0 || noStrPrivKey === null ? ( - <> - - - ) : refreshChat ? ( -
- -
- ) : ( - <> -

{_t("chat.no-chat")}

-
- -
+ {chat.directContacts.length !== 0 && ( +
{_t("chat.dms")}
+ )} )} + {chat.directContacts.map((user) => { + return ( +
+ +
+ + + +
+ + +
{ + userClicked(user.name); + setReceiverPubKey(user.pubkey); + }} + > +

{user.name}

+

+ {getDirectLastMessage(user.pubkey, chat.directMessages)} +

+
+
+ ); + })} + + ) : !noStrPrivKey || noStrPrivKey.length === 0 || noStrPrivKey === null ? ( + <> + + + ) : refreshChat ? ( +
+ +
+ ) : ( + <> +

{_t("chat.no-chat")}

+
+ +
)} - ) : revelPrivateKey ? ( - - ) : ( - - )} - - {((isScrollToTop && !isCurrentUser) || - ((isCurrentUser || isCommunity) && isScrollToBottom)) && ( - -
- {isCurrentUser || isCommunity ? chevronDownSvgForSlider : chevronUpSvg} -
-
- )} - {isActveUserRemoved && isCommunity && ( -

- {_t("chat.blocked-user-message")} -

)} -
- {(isCurrentUser || isCommunity) && ( - - )} -
+ + ) : revelPrivateKey ? ( + + ) : ( + + )} + + {((isScrollToTop && !isCurrentUser) || + ((isCurrentUser || isCommunity) && isScrollToBottom)) && ( + +
+ {isCurrentUser || isCommunity ? chevronDownSvgForSlider : chevronUpSvg} +
+
+ )} + {isActveUserRemoved && isCommunity && ( +

+ {_t("chat.blocked-user-message")} +

+ )} +
+ {(isCurrentUser || isCommunity) && ( + )} +
+ )} - {keyDialog && ( - - - - {step === 9 && confirmationModal(NEWCHATACCOUNT)} - {step === 11 && confirmationModal(RESENDMESSAGE)} - {step === 10 && successModal(NEWCHATACCOUNT)} - - - )} - + {keyDialog && ( + + + + {step === 9 && confirmationModal(NEWCHATACCOUNT)} + {step === 11 && confirmationModal(RESENDMESSAGE)} + {step === 10 && successModal(NEWCHATACCOUNT)} + + )} - + ); } diff --git a/src/common/components/chats/chats-channel-messages/index.tsx b/src/common/components/chats/chats-channel-messages/index.tsx index 00a5888b0c5..0c5029d6203 100644 --- a/src/common/components/chats/chats-channel-messages/index.tsx +++ b/src/common/components/chats/chats-channel-messages/index.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState, RefObject, useEffect } from "react"; +import React, { useRef, useState, RefObject, useEffect, useContext } from "react"; import mediumZoom, { Zoom } from "medium-zoom"; import { Channel, @@ -6,7 +6,7 @@ import { DirectMessage, Profile, PublicMessage -} from "../../../../providers/message-provider-types"; +} from "../../../../managers/message-manager-types"; import { History } from "history"; import { renderPostBody } from "@ecency/render-helper"; import { useMappedStore } from "../../../store/use-mapped-store"; @@ -38,6 +38,7 @@ import { CHATPAGE } from "../chat-popup/chat-constants"; import { Theme } from "../../../store/global/types"; import { NostrKeysType } from "../types"; import { formatMessageTime, isMessageGif, isMessageImage, formatMessageDate } from "../utils"; +import { ChatContext } from "../chat-context-provider"; interface Props { publicMessages: PublicMessage[]; @@ -45,9 +46,7 @@ interface Props { activeUserKeys: NostrKeysType; username: string; from?: string; - users: User[]; - ui: UI; - history: History | null; + history: History; isScrollToBottom: boolean; isScrolled?: boolean; isActveUserRemoved: boolean; @@ -75,7 +74,7 @@ export default function ChatsChannelMessages(props: Props) { currentChannelSetter, deletePublicMessage } = props; - const { chat, global, activeUser } = useMappedStore(); + const { chat, global, activeUser, ui, users } = useMappedStore(); let prevGlobal = usePrevious(global); @@ -92,6 +91,8 @@ export default function ChatsChannelMessages(props: Props) { const [hiddenMsgId, setHiddenMsgId] = useState(""); const [resendMessage, setResendMessage] = useState(); + const { messageServiceInstance } = useContext(ChatContext); + // useEffect(() => { // scrollToBottom(); // console.log("Heloooooooo"); @@ -153,7 +154,7 @@ export default function ChatsChannelMessages(props: Props) { const sendDM = (name: string, pubkey: string) => { if (dmMessage) { - window.messageService?.sendDirectMessage(pubkey, dmMessage); + messageServiceInstance?.sendDirectMessage(pubkey, dmMessage); // setIsCurrentUser(true); // setCurrentUser(name); @@ -167,7 +168,7 @@ export default function ChatsChannelMessages(props: Props) { // !props.chat.directContacts.some((contact) => contact.name === currentUser) && // isCurrentUser // ) { - // window.messageService?.publishContacts(currentUser, receiverPubKey); + // messageServiceInstance?.publishContacts(currentUser, receiverPubKey); // } if (from && from === CHATPAGE) { @@ -286,7 +287,7 @@ export default function ChatsChannelMessages(props: Props) { } try { - window.messageService?.updateChannel(currentChannel!, updatedMetaData); + messageServiceInstance?.updateChannel(currentChannel!, updatedMetaData); currentChannelSetter({ ...currentChannel!, ...updatedMetaData }); setStep(0); // if (operationType === HIDEMESSAGE) { @@ -365,6 +366,8 @@ export default function ChatsChannelMessages(props: Props) { activeUser={activeUser} targetUsername={name!} where={"chat-box"} + ui={ui} + users={users} /> {communityAdmins.includes(activeUser?.username!) && ( diff --git a/src/common/components/chats/chats-community-dropdown-menu/index.scss b/src/common/components/chats/chats-community-dropdown-menu/index.scss deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/src/common/components/chats/chats-community-dropdown-menu/index.tsx b/src/common/components/chats/chats-community-dropdown-menu/index.tsx index 19dc308d4b5..5d8d1508fca 100644 --- a/src/common/components/chats/chats-community-dropdown-menu/index.tsx +++ b/src/common/components/chats/chats-community-dropdown-menu/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { History } from "history"; import DropDown, { MenuItem } from "../../dropdown"; import useDebounce from "react-use/lib/useDebounce"; @@ -18,16 +18,17 @@ import { Channel, ChannelUpdate, communityModerator -} from "../../../../providers/message-provider-types"; +} from "../../../../managers/message-manager-types"; import { error, success } from "../../feedback"; import { Button, Form, Modal, Row, Col, InputGroup, FormControl } from "react-bootstrap"; import LinearProgress from "../../linear-progress"; import { ROLES } from "../../../store/communities"; import UserAvatar from "../../user-avatar"; import { copyToClipboard, getProfileMetaData } from "../utils"; +import { ChatContext } from "../chat-context-provider"; interface Props { - history: History | null; + history: History; from?: string; username: string; currentChannel: Channel; @@ -50,6 +51,8 @@ const ChatsCommunityDropdownMenu = (props: Props) => { const [blockedUsers, setBlockedUsers] = useState<{ name: string; pubkey: string }[]>([]); const [removedUserId, setRemovedUserID] = useState(""); + const { messageServiceInstance } = useContext(ChatContext); + useEffect(() => { getCommunityAdmins(); @@ -113,7 +116,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => { newUpdatedModerator.role = selectedRole; newUpdatedChannel!.communityModerators![moderatorIndex!] = newUpdatedModerator; currentChannelSetter(newUpdatedChannel); - window.messageService?.updateChannel(currentChannel, newUpdatedChannel); + messageServiceInstance?.updateChannel(currentChannel, newUpdatedChannel); success("Roles updated succesfully"); } }; @@ -481,7 +484,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => { break; } try { - window.messageService?.updateChannel(currentChannel!, updatedMetaData); + messageServiceInstance?.updateChannel(currentChannel!, updatedMetaData); currentChannelSetter({ ...currentChannel!, ...updatedMetaData }); if (operationType === UNBLOCKUSER) { diff --git a/src/common/components/chats/chats-direct-messages/index.tsx b/src/common/components/chats/chats-direct-messages/index.tsx index 5b741bb9612..c54d76d0b90 100644 --- a/src/common/components/chats/chats-direct-messages/index.tsx +++ b/src/common/components/chats/chats-direct-messages/index.tsx @@ -1,5 +1,5 @@ import React, { useContext, useEffect, useRef } from "react"; -import { DirectMessage } from "../../../../providers/message-provider-types"; +import { DirectMessage } from "../../../../managers/message-manager-types"; import mediumZoom, { Zoom } from "medium-zoom"; import { Global, Theme } from "../../../store/global/types"; import { diff --git a/src/common/components/chats/chats-messages-box/index.tsx b/src/common/components/chats/chats-messages-box/index.tsx index 7b0bf280dfd..071f732c2ae 100644 --- a/src/common/components/chats/chats-messages-box/index.tsx +++ b/src/common/components/chats/chats-messages-box/index.tsx @@ -8,12 +8,12 @@ import ChatsMessagesHeader from "../chats-messages-header"; import ChatsMessagesView from "../chats-messages-view"; import "./index.scss"; -import { Channel, ChannelUpdate } from "../../../../providers/message-provider-types"; +import { Channel, ChannelUpdate } from "../../../../managers/message-manager-types"; import LinearProgress from "../../linear-progress"; import { NostrKeysType } from "../types"; import { formattedUserName } from "../utils"; import { useMappedStore } from "../../../store/use-mapped-store"; -import { ChatContext } from "../chat-provider"; +import { ChatContext } from "../chat-context-provider"; interface MatchParams { filter: string; @@ -36,8 +36,8 @@ interface Props { } export default function ChatsMessagesBox(props: Props) { - const context = useContext(ChatContext); - const { activeUserKeys } = context; + const chatContext = useContext(ChatContext); + const { activeUserKeys } = chatContext; const { activeUser, chat } = useMappedStore(); const { channels, updatedChannel } = chat; diff --git a/src/common/components/chats/chats-messages-header/index.tsx b/src/common/components/chats/chats-messages-header/index.tsx index 9c46c04e357..8a6340aabcc 100644 --- a/src/common/components/chats/chats-messages-header/index.tsx +++ b/src/common/components/chats/chats-messages-header/index.tsx @@ -6,7 +6,7 @@ import ChatsCommunityDropdownMenu from "../chats-community-dropdown-menu"; import UserAvatar from "../../user-avatar"; import "./index.scss"; -import { Channel } from "../../../../providers/message-provider-types"; +import { Channel } from "../../../../managers/message-manager-types"; import { CHATPAGE } from "../chat-popup/chat-constants"; import { Chat } from "../../../store/chat/types"; import { formattedUserName } from "../utils"; diff --git a/src/common/components/chats/chats-messages-view/index.tsx b/src/common/components/chats/chats-messages-view/index.tsx index 6cb285e96ef..06d3c4cd816 100644 --- a/src/common/components/chats/chats-messages-view/index.tsx +++ b/src/common/components/chats/chats-messages-view/index.tsx @@ -1,10 +1,6 @@ import React, { RefObject, useContext, useEffect, useRef, useState } from "react"; import { Link } from "react-router-dom"; -import { - Channel, - DirectMessage, - PublicMessage -} from "../../../../providers/message-provider-types"; +import { Channel, DirectMessage, PublicMessage } from "../../../../managers/message-manager-types"; import { fetchCommunityMessages, fetchDirectMessages, @@ -27,7 +23,7 @@ import ChatInput from "../chat-input"; import ChatsScroller from "../chats-scroller"; import { CHATPAGE } from "../chat-popup/chat-constants"; import { EmojiPickerStyleProps, NostrKeysType } from "../types"; -import { ChatContext } from "../chat-provider"; +import { ChatContext } from "../chat-context-provider"; const EmojiPickerStyle: EmojiPickerStyleProps = { width: "56.5%", @@ -64,8 +60,7 @@ export default function ChatsMessagesView(props: Props) { deletePublicMessage } = props; - const context = useContext(ChatContext); - const { receiverPubKey } = context; + const { messageServiceInstance, receiverPubKey } = useContext(ChatContext); // console.log("Receiver pubkey in Message view component", receiverPubKey, setReceiverPubKey); @@ -129,7 +124,7 @@ export default function ChatsMessagesView(props: Props) { if (!hasMore || inProgress) return; setInProgress(true); - window.messageService + messageServiceInstance ?.fetchPrevMessages(currentChannel!.id, publicMessages[0].created) .then((num) => { console.log("number", num); diff --git a/src/common/components/chats/chats-profile-box/index.tsx b/src/common/components/chats/chats-profile-box/index.tsx index 173f71c2c9d..35f7e4e03d2 100644 --- a/src/common/components/chats/chats-profile-box/index.tsx +++ b/src/common/components/chats/chats-profile-box/index.tsx @@ -8,6 +8,7 @@ import { useMappedStore } from "../../../store/use-mapped-store"; import { getAccountFull } from "../../../api/hive"; import { getCommunity } from "../../../api/bridge"; import { formattedUserName } from "../utils"; +import { useCommunityCache } from "../../../core/caches/communities-cache"; export interface profileData { joiningData: string; @@ -32,6 +33,14 @@ export default function ChatsProfileBox(props: Props) { const [profileData, setProfileData] = useState(); + const { data: community } = useCommunityCache(username ? username! : communityName!); + + // useEffect(() => { + // if (community) { + // console.log("Community 1st", community); + // } + // }, [community, communityName]); + useEffect(() => { fetchProfileData(); }, [username, isCommunity, isCurrentUser, communityName, currentUser]); @@ -60,7 +69,6 @@ export default function ChatsProfileBox(props: Props) { username: response.name }); } else { - const community = await getCommunity(username!, activeUser?.username); setProfileData({ joiningData: community?.created_at!, about: community?.about, @@ -82,7 +90,6 @@ export default function ChatsProfileBox(props: Props) { username: response.name }); } else { - const community = await getCommunity(communityName!, activeUser?.username); setProfileData({ joiningData: community?.created_at!, about: community?.about, diff --git a/src/common/components/chats/chats-sidebar/indes.tsx b/src/common/components/chats/chats-sidebar/indes.tsx index 9831e8cf95a..41e112e259f 100644 --- a/src/common/components/chats/chats-sidebar/indes.tsx +++ b/src/common/components/chats/chats-sidebar/indes.tsx @@ -4,8 +4,8 @@ import { Link } from "react-router-dom"; import { History } from "history"; import { match } from "react-router"; import useDebounce from "react-use/lib/useDebounce"; -import { setNostrkeys } from "../../../../providers/message-provider"; -import { Channel } from "../../../../providers/message-provider-types"; +import { setNostrkeys } from "../../../../managers/message-manager"; +import { Channel } from "../../../../managers/message-manager-types"; import { getAccountReputations } from "../../../api/hive"; import accountReputation from "../../../helper/account-reputation"; import { getCommunityLastMessage, getDirectLastMessage, getJoinedCommunities } from "../utils"; @@ -23,7 +23,7 @@ import { CHAT, DropDownStyle } from "../chat-popup/chat-constants"; import ChatsDropdownMenu from "../chats-dropdown-menu"; import { AccountWithReputation, NostrKeysType } from "../types"; import { useMappedStore } from "../../../store/use-mapped-store"; -import { ChatContext } from "../chat-provider"; +import { ChatContext } from "../chat-context-provider"; import { getUserChatPublicKey, formattedUserName } from "../../../components/chats/utils"; interface MatchParams { @@ -40,7 +40,7 @@ interface Props { } export default function ChatsSideBar(props: Props) { const { chat } = useMappedStore(); - const context = useContext(ChatContext); + const chatContext = useContext(ChatContext); const { channels, directContacts, leftChannelsList } = chat; const { match, resetChat } = props; @@ -51,7 +51,7 @@ export default function ChatsSideBar(props: Props) { setInProgress, setRevealPrivKey, setReceiverPubKey - } = context; + } = chatContext; const chatsSideBarRef = React.createRef(); const username = match.params.username; diff --git a/src/common/components/chats/import-chats/index.scss b/src/common/components/chats/import-chats/index.scss index 64758473829..08c32779aa7 100644 --- a/src/common/components/chats/import-chats/index.scss +++ b/src/common/components/chats/import-chats/index.scss @@ -1,3 +1,5 @@ +@import "../../../../style/vars_mixins"; + .import-chats { display: flex; justify-content: center; diff --git a/src/common/components/chats/import-chats/index.tsx b/src/common/components/chats/import-chats/index.tsx index c42a23569c9..23a5d0d7327 100644 --- a/src/common/components/chats/import-chats/index.tsx +++ b/src/common/components/chats/import-chats/index.tsx @@ -6,8 +6,8 @@ import { keySvg } from "../../../img/svg"; import { useMappedStore } from "../../../store/use-mapped-store"; import OrDivider from "../../or-divider"; import * as ls from "../../../util/local-storage"; -import { ChatContext } from "../chat-provider"; -import { setNostrkeys } from "../../../../providers/message-provider"; +import { ChatContext } from "../chat-context-provider"; +import { setNostrkeys } from "../../../../managers/message-manager"; import "./index.scss"; import LinearProgress from "../../linear-progress"; @@ -21,10 +21,10 @@ export default function ImportChats() { const [error, setError] = useState(""); const [inProgress, setInProgress] = useState(false); const [privKey, setPrivKey] = useState(""); - const context = useContext(ChatContext); const [step, setStep] = useState(0); - const { activeUserKeys, setChatPrivKey, setActiveUserKeys } = context; + const { activeUserKeys, messageServiceInstance, setActiveUserKeys, setChatPrivKey } = + useContext(ChatContext); const handleImportChatSubmit = () => { try { @@ -61,7 +61,7 @@ export default function ImportChats() { setChatPrivKey(keys.priv); await setProfileMetaData(activeUser, keys.pub); setNostrkeys(keys); - window.messageService?.updateProfile({ + messageServiceInstance?.updateProfile({ name: activeUser?.username!, about: "", picture: "" diff --git a/src/common/components/chats/join-chat/index.tsx b/src/common/components/chats/join-chat/index.tsx index 8baab224958..84e8e047436 100644 --- a/src/common/components/chats/join-chat/index.tsx +++ b/src/common/components/chats/join-chat/index.tsx @@ -1,19 +1,18 @@ import React, { useContext, useState } from "react"; import { Button, Spinner } from "react-bootstrap"; import { useMappedStore } from "../../../store/use-mapped-store"; -import { ChatContext } from "../chat-provider"; +import { ChatContext } from "../chat-context-provider"; import { createNoStrAccount, setProfileMetaData } from "../utils"; import * as ls from "../../../util/local-storage"; -import { setNostrkeys } from "../../../../providers/message-provider"; +import { setNostrkeys } from "../../../../managers/message-manager"; interface Props { resetChat: () => void; } export default function JoinChat(props: Props) { - const context = useContext(ChatContext); - const { setChatPrivKey, setActiveUserKeys } = context; + const { messageServiceInstance, setChatPrivKey, setActiveUserKeys } = useContext(ChatContext); const { activeUser } = useMappedStore(); @@ -28,7 +27,7 @@ export default function JoinChat(props: Props) { setChatPrivKey(keys.priv); await setProfileMetaData(activeUser, keys.pub); setNostrkeys(keys); - window.messageService?.updateProfile({ + messageServiceInstance?.updateProfile({ name: activeUser?.username!, about: "", picture: "" diff --git a/src/common/components/chats/join-community-chat-btn/index.tsx b/src/common/components/chats/join-community-chat-btn/index.tsx index 10c80455467..14abd8e1c8c 100644 --- a/src/common/components/chats/join-community-chat-btn/index.tsx +++ b/src/common/components/chats/join-community-chat-btn/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { Button, Spinner } from "react-bootstrap"; import { History } from "history"; @@ -8,9 +8,9 @@ import { ActiveUser } from "../../../store/active-user/types"; import { _t } from "../../../i18n"; import { useMappedStore } from "../../../store/use-mapped-store"; -import { Channel, communityModerator } from "../../../../providers/message-provider-types"; +import { Channel, communityModerator } from "../../../../managers/message-manager-types"; import * as ls from "../../../util/local-storage"; -import { setNostrkeys } from "../../../../providers/message-provider"; +import { setNostrkeys } from "../../../../managers/message-manager"; import { NOSTRKEY } from "../chat-popup/chat-constants"; import { NostrKeysType } from "../types"; import { @@ -19,6 +19,7 @@ import { setChannelMetaData, setProfileMetaData } from "../utils"; +import { ChatContext } from "../chat-context-provider"; interface Props { history: History; @@ -28,6 +29,8 @@ interface Props { } export default function JoinCommunityChatBtn(props: Props) { + const { messageServiceInstance } = useContext(ChatContext); + console.log("Bhai kiya masla ha apko", messageServiceInstance); const { chat } = useMappedStore(); const [inProgress, setInProgress] = useState(false); const [isCommunityChatJoined, setIsCommunityChatJoined] = useState(false); @@ -55,7 +58,7 @@ export default function JoinCommunityChatBtn(props: Props) { }, [activeUserKeys]); useEffect(() => { - if (window.messageService) { + if (messageServiceInstance) { fetchUserProfileData(); if (loadCommunity) { window?.messageService?.loadChannel(currentChannel?.id!); @@ -65,7 +68,7 @@ export default function JoinCommunityChatBtn(props: Props) { } } }, [ - typeof window !== "undefined" && window?.messageService, + typeof window !== "undefined" && messageServiceInstance, loadCommunity, communityRoles, initiateCommunityChat @@ -174,6 +177,7 @@ export default function JoinCommunityChatBtn(props: Props) { }; const joinCommunityChat = () => { + console.log("Join community chat", messageServiceInstance); if (!hasUserJoinedChat) { handleJoinChat(); setLoadCommunity(true); @@ -220,7 +224,7 @@ export default function JoinCommunityChatBtn(props: Props) { await setProfileMetaData(props.activeUser, keys.pub); setHasUserJoinedChat(true); setNostrkeys(keys); - window.messageService?.updateProfile({ + messageServiceInstance?.updateProfile({ name: props.activeUser?.username!, about: "", picture: "" diff --git a/src/common/components/chats/set-chat-keys/index.tsx b/src/common/components/chats/set-chat-keys/index.tsx deleted file mode 100644 index 93abebe740e..00000000000 --- a/src/common/components/chats/set-chat-keys/index.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React, { useContext, useEffect } from "react"; -import { useMappedStore } from "../../../store/use-mapped-store"; - -import { ChatContext } from "../chat-provider"; -import { getPrivateKey, getUserChatPublicKey } from "../utils"; - -export default function SetActiveUserChatKeys() { - const context = useContext(ChatContext); - const { setActiveUserKeys, setChatPrivKey } = context; - const { activeUser } = useMappedStore(); - - useEffect(() => { - getActiveUserKeys(); - }, []); - - const getActiveUserKeys = async () => { - const pubKey = await getUserChatPublicKey(activeUser?.username!); - const privKey = getPrivateKey(activeUser?.username!); - const keys = { - pub: pubKey, - priv: privKey - }; - setChatPrivKey(privKey); - setActiveUserKeys(keys); - }; - - return <>; -} diff --git a/src/common/components/chats/types/index.ts b/src/common/components/chats/types/index.ts index 44983652000..61514842ced 100644 --- a/src/common/components/chats/types/index.ts +++ b/src/common/components/chats/types/index.ts @@ -1 +1,2 @@ export * from "./chat-types"; +export * from "./message-service-types"; diff --git a/src/common/components/chats/utils/get-joined-communities.ts b/src/common/components/chats/utils/get-joined-communities.ts index ff486aa1026..c04b2b20567 100644 --- a/src/common/components/chats/utils/get-joined-communities.ts +++ b/src/common/components/chats/utils/get-joined-communities.ts @@ -1,4 +1,4 @@ -import { Channel } from "../../../../providers/message-provider-types"; +import { Channel } from "../../../../managers/message-manager-types"; export const getJoinedCommunities = (channels: Channel[], leftChannels: string[]) => { return channels.filter((item) => !leftChannels.includes(item.id)); diff --git a/src/common/components/chats/utils/get-user-chat-public-key.ts b/src/common/components/chats/utils/get-user-chat-public-key.ts index df9c056a671..8352b301047 100644 --- a/src/common/components/chats/utils/get-user-chat-public-key.ts +++ b/src/common/components/chats/utils/get-user-chat-public-key.ts @@ -5,6 +5,6 @@ export const getUserChatPublicKey = async (username: string) => { const { posting_json_metadata } = response; const profile = JSON.parse(posting_json_metadata!).profile; const { nsKey } = profile || {}; - console.log("nsKey", nsKey); + // console.log("nsKey", nsKey); return nsKey; }; diff --git a/src/common/components/chats/utils/upload-channel-data.ts b/src/common/components/chats/utils/upload-channel-data.ts index faf34365ff5..0a0f13477a6 100644 --- a/src/common/components/chats/utils/upload-channel-data.ts +++ b/src/common/components/chats/utils/upload-channel-data.ts @@ -1,4 +1,4 @@ -import { Channel } from "../../../../providers/message-provider-types"; +import { Channel } from "../../../../managers/message-manager-types"; import { getAccountFull } from "../../../api/hive"; import { updateProfile } from "../../../api/operations"; diff --git a/src/common/components/chats/utils/use-fetch-community-messages.ts b/src/common/components/chats/utils/use-fetch-community-messages.ts index e490e5d828d..8c0bc6f0c63 100644 --- a/src/common/components/chats/utils/use-fetch-community-messages.ts +++ b/src/common/components/chats/utils/use-fetch-community-messages.ts @@ -1,4 +1,4 @@ -import { Channel } from "../../../../providers/message-provider-types"; +import { Channel } from "../../../../managers/message-manager-types"; import { publicMessagesList } from "../../../store/chat/types"; export const fetchCommunityMessages = ( diff --git a/src/common/components/community-cover/index.tsx b/src/common/components/community-cover/index.tsx index cd8d4bcb893..fc60ee3ef70 100644 --- a/src/common/components/community-cover/index.tsx +++ b/src/common/components/community-cover/index.tsx @@ -35,6 +35,7 @@ import { _t } from "../../i18n"; import { pencilOutlineSvg } from "../../img/svg"; import "./_index.scss"; +import ChatContextProvider from "../chats/chat-context-provider"; const coverFallbackDay = require("../../img/cover-fallback-day.png"); const coverFallbackNight = require("../../img/cover-fallback-night.png"); @@ -205,7 +206,10 @@ export class CommunityCover extends Component {
{CommunityPostBtn({ ...this.props })} - + + + +
{canUpdateCoverImage && ( { } private async init() { + console.log("Init run of message service"); this.eventQueue = []; this.eventQueueFlag = true; this.eventQueueBuffer = []; @@ -530,6 +531,7 @@ class MessageService extends TypedEventEmitter { } public async updateProfile(profile: Metadata) { + console.log("Profile update run"); return this.publish(Kind.Metadata, [], JSON.stringify(profile)); } @@ -701,6 +703,7 @@ class MessageService extends TypedEventEmitter { : null; }) .filter(MessageService.notEmpty); + console.log("Channel creation event is emitted", channelCreations); if (channelCreations.length > 0) { this.emit(MessageEvents.ChannelCreation, channelCreations); } @@ -800,7 +803,8 @@ class MessageService extends TypedEventEmitter { this.eventQueueFlag = true; } - close = () => { + public close = () => { + console.log("Close run"); this.pool.close(this.readRelays); this.removeAllListeners(); }; @@ -838,16 +842,19 @@ class MessageService extends TypedEventEmitter { export default MessageService; -export const initMessageService = (keys: Keys): MessageService | undefined => { - if (window.messageService) { - window.messageService.close(); - window.messageService = undefined; - } +// export const initMessageService = (keys: Keys): MessageService | undefined => { +// if (window.messageService) { +// window.messageService.close(); +// window.messageService = undefined; +// } - if (keys) { - window.messageService = new MessageService(keys.priv, keys.pub); - } - console.log("raven instance created"); +// // const { messageServiceInstance, setMessageServiceInstance } = useMessageServiceContext(); +// // console.log("I have to check this", messageServiceInstance, setMessageServiceInstance); - return window.messageService; -}; +// if (keys) { +// window.messageService = new MessageService(keys.priv, keys.pub); +// } +// console.log("raven instance created", window.messageService); + +// return window.messageService; +// }; diff --git a/src/common/pages/chats/index.tsx b/src/common/pages/chats/index.tsx index f9b72c1c33a..f44b9259946 100644 --- a/src/common/pages/chats/index.tsx +++ b/src/common/pages/chats/index.tsx @@ -11,22 +11,13 @@ import "./index.scss"; import * as ls from "../../util/local-storage"; import { Button, Spinner } from "react-bootstrap"; -import { setNostrkeys } from "../../../providers/message-provider"; +import { setNostrkeys } from "../../../managers/message-manager"; import ManageChatKey from "../../components/manage-chat-key"; import Feedback, { success } from "../../components/feedback"; -import { NostrKeysType } from "../../components/chats/types"; -import { - copyToClipboard, - createNoStrAccount, - getPrivateKey, - getProfileMetaData, - setProfileMetaData -} from "../../components/chats/utils"; import { useMappedStore } from "../../store/use-mapped-store"; -import ChatProvider, { ChatContext } from "../../components/chats/chat-provider"; +import ChatContextProvider, { ChatContext } from "../../components/chats/chat-context-provider"; import ImportChats from "../../components/chats/import-chats"; import JoinChat from "../../components/chats/join-chat"; -import SetActiveUserChatKeys from "../../components/chats/set-chat-keys"; interface MatchParams { filter: string; @@ -72,56 +63,53 @@ export const Chats = (props: Props) => { }; return ( - - {(context) => ( - <> - - - {global.isElectron ? : } -
- {context.inProgress ? ( -
- -
- ) : ( - <> - {context.activeUserKeys?.pub ? ( - context.chatPrivKey ? ( - <> - - {context.revealPrivKey ? ( -
- -
- ) : ( - // Handle the case when the user hasn't joined any community here - - )} - - ) : ( - <> -
- + + <> + + {global.isElectron ? : } +
+ {context.inProgress ? ( +
+ +
+ ) : ( + <> + {context.activeUserKeys?.pub ? ( + context.chatPrivKey ? ( + <> + + {context.revealPrivKey ? ( +
+
- - ) + ) : ( + // Handle the case when the user hasn't joined any community here + + )} + ) : ( -
- -
- )} - - )} -
- - )} - + <> +
+ +
+ + ) + ) : ( +
+ +
+ )} + + )} +
+ + ); }; export default connect(pageMapStateToProps, pageMapDispatchToProps)(Chats); diff --git a/src/common/pages/community-functional.tsx b/src/common/pages/community-functional.tsx index 95b52b6cdb9..0ff211a78c5 100644 --- a/src/common/pages/community-functional.tsx +++ b/src/common/pages/community-functional.tsx @@ -4,7 +4,7 @@ import React, { Fragment, useEffect, useState } from "react"; import { search as searchApi, SearchResult } from "../api/search-api"; import { getSubscriptions } from "../api/bridge"; import { EntryFilter, ListStyle } from "../store/global/types"; -import { Channel } from "../../providers/message-provider-types"; +import { Channel } from "../../managers/message-manager-types"; import { usePrevious } from "../util/use-previous"; import * as ls from "../util/local-storage"; import { @@ -46,9 +46,10 @@ import "./community.scss"; import { Button, Modal } from "react-bootstrap"; import LoginRequired from "../components/login-required"; import { useMappedStore } from "../store/use-mapped-store"; -import { setNostrkeys } from "../../providers/message-provider"; +import { setNostrkeys } from "../../managers/message-manager"; import { QueryIdentifiers, useCommunityCache } from "../core"; import { useQueryClient } from "@tanstack/react-query"; +import { profileUpdater } from "../components/chats/chat-popup"; interface MatchParams { filter: string; @@ -83,6 +84,7 @@ export const CommunityPage = (props: Props) => { const [communities, setCommunities] = useState([]); const [isCommunityAlreadyJoined, setIsCommunityAlreadyJoined] = useState(false); const [isLoading, setIsLoading] = useState(false); + const [shouldUpdateProfile, setShouldUpdateProfile] = useState(false); const prevMatch = usePrevious(props.match); const prevActiveUser = usePrevious(props.activeUser); @@ -91,6 +93,13 @@ export const CommunityPage = (props: Props) => { fetchUserProfileData(); }, [props.activeUser]); + useEffect(() => { + if (shouldUpdateProfile) { + console.log("state changed in useeffect"); + profileUpdater(); + } + }, [shouldUpdateProfile]); + useEffect(() => { const communities = getJoinedCommunities(chat.channels, chat.leftChannelsList); setCommunities(communities); @@ -243,15 +252,12 @@ export const CommunityPage = (props: Props) => { setInProgress(true); const keys = createNoStrAccount(); ls.set(`${props.activeUser?.username}_nsPrivKey`, keys.priv); - await setProfileMetaData(props.activeUser, keys.pub); setNostrkeys(keys); + await setProfileMetaData(props.activeUser, keys.pub); setHasUserJoinedChat(true); - window.messageService?.updateProfile({ - name: props.activeUser?.username!, - about: "", - picture: "" - }); setInProgress(false); + console.log("State has been changed"); + setShouldUpdateProfile(true); }; const joinCommunityChat = () => { diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts index 80e4c8088e4..6bbcb79a15c 100644 --- a/src/common/store/chat/index.ts +++ b/src/common/store/chat/index.ts @@ -5,7 +5,7 @@ import { Profile, ChannelUpdate, MessagesObject -} from "./../../../providers/message-provider-types"; +} from "../../../managers/message-manager-types"; import { Dispatch } from "redux"; import { diff --git a/src/common/store/chat/types.ts b/src/common/store/chat/types.ts index f2cda78d603..2d9629331ae 100644 --- a/src/common/store/chat/types.ts +++ b/src/common/store/chat/types.ts @@ -5,7 +5,7 @@ import { MessagesObject, Profile, PublicMessage -} from "./../../../providers/message-provider-types"; +} from "../../../managers/message-manager-types"; export interface DirectContactsType { name: string; diff --git a/src/providers/message-provider-types.ts b/src/managers/message-manager-types.ts similarity index 100% rename from src/providers/message-provider-types.ts rename to src/managers/message-manager-types.ts diff --git a/src/providers/message-provider.tsx b/src/managers/message-manager.tsx similarity index 89% rename from src/providers/message-provider.tsx rename to src/managers/message-manager.tsx index e48f53ad964..820cfdc7b48 100644 --- a/src/providers/message-provider.tsx +++ b/src/managers/message-manager.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { ActiveUser } from "../common/store/active-user/types"; import { DirectContactsType } from "../common/store/chat/types"; @@ -12,12 +12,13 @@ import { PublicMessage, ChannelUpdate, MessagesObject -} from "./message-provider-types"; +} from "./message-manager-types"; -import { initMessageService, MessageEvents } from "../common/helper/message-service"; +import MessageService, { MessageEvents } from "../common/helper/message-service"; import { getProfileMetaData, getPrivateKey } from "../common/components/chats/utils"; import { useMappedStore } from "../common/store/use-mapped-store"; +import { ChatContext } from "../common/components/chats/chat-context-provider"; export const setNostrkeys = (keys: NostrKeysType) => { const detail: NostrKeysType = { @@ -43,12 +44,18 @@ interface Props { addPreviousPublicMessages: (channelId: string, data: MessagesObject) => void; } -const MessageProvider = (props: Props) => { +const MessageManager = (props: Props) => { const { activeUser, chat } = useMappedStore(); + + // const messageServiceContext = useContext(MessageServiceContext); + // const { setMessageServiceInstance } = messageServiceContext; + + // console.log("messageServiceContext", messageServiceContext); + const [messageServiceReady, setMessageServiceReady] = useState(false); - const [since, setSince] = useState(0); + const [since, setSince] = useState(0); const [keys, setKeys] = useState(); - const [messageService, setMessageService] = useState(); + const [messageService, setMessageService] = useState(undefined); const [directMessageBuffer, setDirectMessageBuffer] = useState([]); const [publicMessageBuffer, setPublicMessageBuffer] = useState([]); const [isCommunityCreated, setIsCommunityCreated] = useState(false); @@ -56,6 +63,9 @@ const MessageProvider = (props: Props) => { const [isDirectChatCreated, setIsDirectChatCreated] = useState(false); const [replacedDirectMessagesBuffer, setReplacedDirectMessagesBuffer] = useState([]); + const { messageServiceInstance, initMessageServiceInstance, setMessageServiceInstance } = + useContext(ChatContext); + useEffect(() => { if (chat.channels.length !== 0) { setIsCommunityCreated(true); @@ -74,9 +84,11 @@ const MessageProvider = (props: Props) => { }, []); useEffect(() => { - if (!window.messageService && keys?.priv) { - const messageServiceInstance = initMessageService(keys); - setMessageService(messageServiceInstance); + if (!messageServiceInstance && keys?.priv) { + const messageService = initMessageServiceInstance(keys); + console.log("I run from that", messageService); + setMessageServiceInstance(messageService!); + setMessageService(messageService!); } }, [keys]); @@ -88,8 +100,10 @@ const MessageProvider = (props: Props) => { const createMSInstance = (e: Event) => { const detail = (e as CustomEvent).detail as NostrKeysType; - const messageServiceInstance = initMessageService(detail); - setMessageService(messageServiceInstance); + const messageService = initMessageServiceInstance(detail); + console.log("I am here messageServiceInstance", messageService); + setMessageServiceInstance(messageService!); + setMessageService(messageService!); }; const getNostrKeys = async (activeUser: ActiveUser) => { @@ -264,8 +278,8 @@ const MessageProvider = (props: Props) => { // Channel creation handler const handleChannelCreation = (data: Channel[]) => { - const append = data.filter((x) => chat.channels.find((y) => y.id === x.id) === undefined); - props.addChannels(append); + console.log("handle channel creation run"); + props.addChannels(data.filter((x) => !chat.channels.find((y) => y.id === x.id))); }; useEffect(() => { @@ -343,20 +357,18 @@ const MessageProvider = (props: Props) => { } else { setPublicMessageBuffer((publicMessageBuffer) => [...publicMessageBuffer, ...data]); } - let uniqueUsers: string[] = []; + for (const item of data) { - const isCreatorMatch = chat.profiles.some((profile) => profile.creator === item.creator); + const isCreatorMatch = chat.profiles.find((profile) => profile.creator === item.creator); if (!isCreatorMatch) { - for (const item of data) { - if (!uniqueUsers.includes(item.creator)) { - uniqueUsers.push(item.creator); - } + if (!uniqueUsers.includes(item.creator)) { + uniqueUsers.push(item.creator); } } } - window?.messageService?.loadProfiles(uniqueUsers); + messageServiceInstance?.loadProfiles(uniqueUsers); }; useEffect(() => { @@ -469,4 +481,4 @@ const MessageProvider = (props: Props) => { return <>{}; }; -export default MessageProvider; +export default MessageManager; From dfc864fc90e45c53e60e9b646c4b4c7457044b77 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Wed, 13 Sep 2023 15:48:55 +0500 Subject: [PATCH 062/179] Implement Chat context --- src/common/app.tsx | 175 ++++++++++-------- .../chats/chat-context-provider.tsx | 35 ++-- .../components/chats/chat-popup/index.tsx | 125 ++++++------- .../chats/chats-channel-messages/index.tsx | 8 +- .../chats-community-dropdown-menu/index.tsx | 2 +- .../chats/chats-direct-messages/index.tsx | 4 +- .../components/chats/chats-sidebar/indes.tsx | 6 +- .../chats/join-community-chat-btn/index.tsx | 29 ++- .../components/community-cover/index.tsx | 5 +- src/common/helper/message-service.ts | 3 +- src/common/pages/chats/index.tsx | 103 +++++------ src/common/pages/community-functional.tsx | 21 ++- 12 files changed, 262 insertions(+), 254 deletions(-) diff --git a/src/common/app.tsx b/src/common/app.tsx index ced6574a230..94aaa913874 100644 --- a/src/common/app.tsx +++ b/src/common/app.tsx @@ -103,89 +103,100 @@ const App = (props: any) => { {/**/} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/common/components/chats/chat-context-provider.tsx b/src/common/components/chats/chat-context-provider.tsx index 6c1d8b765af..87d3e91c869 100644 --- a/src/common/components/chats/chat-context-provider.tsx +++ b/src/common/components/chats/chat-context-provider.tsx @@ -6,18 +6,17 @@ import { NostrKeysType } from "./types"; import { getPrivateKey, getUserChatPublicKey } from "./utils"; interface Context { - activeUserKeys?: NostrKeysType; - inProgress: boolean; + activeUserKeys: NostrKeysType; + showSpinner: boolean; revealPrivKey: boolean; chatPrivKey: string; receiverPubKey: string; messageServiceInstance: MessageService | undefined; setRevealPrivKey: (d: boolean) => void; - setInProgress: (d: boolean) => void; + setShowSpinner: (d: boolean) => void; setChatPrivKey: (key: string) => void; setActiveUserKeys: (keys: NostrKeysType) => void; setReceiverPubKey: (key: string) => void; - setMessageServiceInstance: (instance: MessageService | undefined) => void; initMessageServiceInstance: (keys: Keys) => void; } @@ -28,13 +27,13 @@ interface Props { export const ChatContext = React.createContext({ activeUserKeys: { pub: " ", priv: "" }, - inProgress: false, + showSpinner: false, revealPrivKey: false, chatPrivKey: "", receiverPubKey: "", messageServiceInstance: undefined, setRevealPrivKey: () => {}, - setInProgress: () => {}, + setShowSpinner: () => {}, setChatPrivKey: () => {}, setActiveUserKeys: () => {}, setReceiverPubKey: () => {}, @@ -45,8 +44,8 @@ export const ChatContext = React.createContext({ export default function ChatContextProvider({ children }: Props) { const { activeUser } = useMappedStore(); - const [activeUserKeys, setActiveUserKeys] = useState(); - const [inProgress, setInProgress] = useState(true); + const [activeUserKeys, setActiveUserKeys] = useState({ pub: " ", priv: "" }); + const [showSpinner, setShowSpinner] = useState(true); const [chatPrivKey, setChatPrivKey] = useState(""); const [revealPrivKey, setRevealPrivKey] = useState(false); const [receiverPubKey, setReceiverPubKey] = useState(""); @@ -59,18 +58,12 @@ export default function ChatContextProvider({ children }: Props) { }, []); useEffect(() => { - if (messageServiceInstance) { - console.log("messageServiceInstance in context", messageServiceInstance); - } - }, [messageServiceInstance]); - - useEffect(() => { - if (inProgress) { + if (showSpinner) { setTimeout(() => { - setInProgress(false); + setShowSpinner(false); }, 7000); } - }, [inProgress]); + }, [showSpinner]); const getActiveUserKeys = async () => { const pubKey = await getUserChatPublicKey(activeUser?.username!); @@ -80,12 +73,11 @@ export default function ChatContextProvider({ children }: Props) { pub: pubKey, priv: privKey }; - setInProgress(false); + setShowSpinner(false); setActiveUserKeys(activeUserKeys); }; const initMessageServiceInstance = (keys: Keys) => { - console.log("Keys are in context", keys, messageServiceInstance); if (messageServiceInstance) { messageServiceInstance.close(); setMessageServiceInstance(undefined); @@ -94,7 +86,6 @@ export default function ChatContextProvider({ children }: Props) { let newMessageService: MessageService | undefined = undefined; if (keys) { newMessageService = new MessageService(keys.priv, keys.pub); - console.log("raven instance created", newMessageService); setMessageServiceInstance(newMessageService); } return newMessageService; @@ -104,13 +95,13 @@ export default function ChatContextProvider({ children }: Props) { void; } -export const profileUpdater = () => { - const ev = new CustomEvent("profileUpdater"); +export const profileUpdater = (messageServiceInstance: MessageService) => { + const detail = { + messageServiceInstance + }; + const ev = new CustomEvent("profileUpdater", { detail }); window.dispatchEvent(ev); }; export default function ChatPopUp(props: Props) { const { activeUser, global, chat } = useMappedStore(); - const chatContext = useContext(ChatContext); - // const messageServiceContext = useContext(MessageServiceContext); - const { - // activeUserKeys, - setRevealPrivKey, - // setReceiverPubKey, messageServiceInstance, - chatPrivKey, - revealPrivKey - } = chatContext; + revealPrivKey, + activeUserKeys, + showSpinner, + setRevealPrivKey, + setShowSpinner, + setActiveUserKeys + } = useContext(ChatContext); + // const messageServiceContext = useContext(MessageServiceContext); // const { messageServiceInstance } = messageServiceContext; - console.log("messageServiceInstance chatpop up", chatPrivKey, messageServiceInstance); + // console.log("messageServiceInstance chatpop up", messageServiceInstance); const routerLocation = useLocation(); const prevActiveUser = usePrevious(activeUser); @@ -121,9 +123,8 @@ export default function ChatPopUp(props: Props) { const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); const [inProgress, setInProgress] = useState(false); const [show, setShow] = useState(false); - const [activeUserKeys, setActiveUserKeys] = useState(); const [receiverPubKey, setReceiverPubKey] = useState(""); - const [showSpinner, setShowSpinner] = useState(false); + const [isSpinner, setIsSpinner] = useState(false); const [directMessagesList, setDirectMessagesList] = useState([]); const [isCurrentUserJoined, setIsCurrentUserJoined] = useState(true); const [isCommunity, setIsCommunity] = useState(false); @@ -140,16 +141,15 @@ export default function ChatPopUp(props: Props) { const [noStrPrivKey, setNoStrPrivKey] = useState(""); const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); const [isTop, setIsTop] = useState(false); + const [shouldUpdateProfile, setShouldUpdateProfile] = useState(false); const [hasMore, setHasMore] = useState(true); const [removedUsers, setRemovedUsers] = useState([]); - const [revelPrivateKey, setRevealPrivateKey] = useState(false); const [innerWidth, setInnerWidth] = useState(0); const [isChatPage, setIsChatPage] = useState(false); - const [refreshChat, setRefreshChat] = useState(false); - // useEffect(() => { - // console.log("directMessagesList in chat popup", directMessagesList); - // }, [directMessagesList]); + useEffect(() => { + console.log("chat in store", chat); + }, [chat]); useEffect(() => { window.addEventListener("profileUpdater", noStrProfileUpdater); @@ -159,13 +159,6 @@ export default function ChatPopUp(props: Props) { }; }, []); - useEffect(() => { - console.log( - "-----------------------------------------------------------------", - messageServiceInstance - ); - }, [messageServiceInstance]); - useEffect(() => { if (currentChannel && chat.leftChannelsList.includes(currentChannel.id)) { setIsCommunity(false); @@ -192,6 +185,12 @@ export default function ChatPopUp(props: Props) { setInnerWidth(window.innerWidth); }, []); + useEffect(() => { + if (shouldUpdateProfile && messageServiceInstance) { + profileUpdater(messageServiceInstance); + } + }, [shouldUpdateProfile, messageServiceInstance]); + useEffect(() => { const updated: ChannelUpdate = chat.updatedChannel .filter((x) => x.channelId === currentChannel?.id!) @@ -229,12 +228,13 @@ export default function ChatPopUp(props: Props) { useEffect(() => { if (messageServiceInstance) { setHasUserJoinedChat(true); + setShouldUpdateProfile(false); const noStrPrivKey = getPrivateKey(activeUser?.username!); setNoStrPrivKey(noStrPrivKey); } setTimeout(() => { if (chat.channels.length === 0 && chat.directContacts.length === 0) { - setRefreshChat(false); + setShowSpinner(false); } }, 5000); }, [typeof window !== "undefined" && messageServiceInstance]); @@ -271,7 +271,7 @@ export default function ChatPopUp(props: Props) { useEffect(() => { if (currentChannel && isCommunity) { - window?.messageService?.fetchChannel(currentChannel.id); + messageServiceInstance?.fetchChannel(currentChannel.id); const publicMessages: PublicMessage[] = fetchCommunityMessages( chat.publicMessages, currentChannel, @@ -373,9 +373,10 @@ export default function ChatPopUp(props: Props) { } }; - const noStrProfileUpdater = () => { - console.log("Function run", messageServiceInstance); - messageServiceInstance?.updateProfile({ + const noStrProfileUpdater = (e: Event) => { + const detail = (e as CustomEvent).detail; + console.log("detail", detail.messageServiceInstance); + detail.messageServiceInstance.updateProfile({ name: activeUser?.username!, about: "", picture: "" @@ -443,12 +444,6 @@ export default function ChatPopUp(props: Props) { const fetchProfileData = async () => { const profileData = await getProfileMetaData(activeUser?.username!); - const noStrPrivKey = getPrivateKey(activeUser?.username!); - const activeUserKeys = { - pub: profileData?.nsKey, - priv: noStrPrivKey - }; - setActiveUserKeys(activeUserKeys); const hasNoStrKey = profileData && profileData.hasOwnProperty(NOSTRKEY); setHasUserJoinedChat(hasNoStrKey); }; @@ -508,7 +503,7 @@ export default function ChatPopUp(props: Props) { resetChat(); handleBackArrowSvg(); if (getPrivateKey(activeUser?.username!)) { - // setRefreshChat(true); + setShowSpinner(true); const keys = { pub: activeUserKeys?.pub!, priv: getPrivateKey(activeUser?.username!) @@ -524,7 +519,7 @@ export default function ChatPopUp(props: Props) { const handleJoinChat = async () => { const { resetChat } = props; - setShowSpinner(true); + setIsSpinner(true); resetChat(); const keys = createNoStrAccount(); ls.set(`${activeUser?.username}_nsPrivKey`, keys.priv); @@ -532,9 +527,9 @@ export default function ChatPopUp(props: Props) { await setProfileMetaData(activeUser, keys.pub); setHasUserJoinedChat(true); setNostrkeys(keys); - noStrProfileUpdater(); + setShouldUpdateProfile(true); setActiveUserKeys(keys); - setShowSpinner(false); + setIsSpinner(false); }; const chatButtonSpinner = ( @@ -654,7 +649,7 @@ export default function ChatPopUp(props: Props) { setShowSearchUser(false); setSearchText(""); setHasMore(true); - setRevealPrivateKey(false); + setRevealPrivKey(false); }; return ( @@ -666,7 +661,7 @@ export default function ChatPopUp(props: Props) { }`} >
- {(currentUser || communityName || showSearchUser || revelPrivateKey) && expanded && ( + {(currentUser || communityName || showSearchUser || revealPrivKey) && expanded && (
@@ -695,26 +690,22 @@ export default function ChatPopUp(props: Props) { ? currentCommunity?.title : showSearchUser ? "New Message" - : revelPrivateKey + : revealPrivKey ? "Manage chat key" : "Messages"}

- {!currentUser && - hasUserJoinedChat && - noStrPrivKey && - !isCommunity && - !revelPrivateKey && ( - <> -
- -

{addMessageSVG}

-
-
- - )} - {hasUserJoinedChat && noStrPrivKey && !revelPrivateKey && ( + {!currentUser && hasUserJoinedChat && noStrPrivKey && !isCommunity && !revealPrivKey && ( + <> +
+ +

{addMessageSVG}

+
+
+ + )} + {hasUserJoinedChat && noStrPrivKey && !revealPrivKey && (

@@ -738,7 +729,7 @@ export default function ChatPopUp(props: Props) { { - setRevealPrivateKey(!revelPrivateKey); + setRevealPrivKey(!revealPrivKey); }} />

@@ -772,7 +763,7 @@ export default function ChatPopUp(props: Props) { ref={chatBodyDivRef} onScroll={handleScroll} > - {hasUserJoinedChat && !revelPrivateKey ? ( + {hasUserJoinedChat && !revealPrivKey ? ( <> {currentUser.length !== 0 || communityName.length !== 0 ? (
@@ -798,7 +789,6 @@ export default function ChatPopUp(props: Props) { @@ -808,7 +798,6 @@ export default function ChatPopUp(props: Props) { username={communityName} publicMessages={publicMessages} currentChannel={currentChannel!} - activeUserKeys={activeUserKeys!} isScrollToBottom={false} isActveUserRemoved={isActveUserRemoved} currentChannelSetter={setCurrentChannel} @@ -864,7 +853,7 @@ export default function ChatPopUp(props: Props) { <> {(chat.directContacts.length !== 0 || (chat.channels.length !== 0 && communities.length !== 0)) && - !refreshChat && + !showSpinner && noStrPrivKey ? ( {chat.channels.length !== 0 && communities.length !== 0 && ( @@ -936,7 +925,7 @@ export default function ChatPopUp(props: Props) { <> - ) : refreshChat ? ( + ) : showSpinner ? (
@@ -953,11 +942,11 @@ export default function ChatPopUp(props: Props) { )} - ) : revelPrivateKey ? ( + ) : revealPrivKey ? ( ) : ( )} diff --git a/src/common/components/chats/chats-channel-messages/index.tsx b/src/common/components/chats/chats-channel-messages/index.tsx index 0c5029d6203..cc8a1070dee 100644 --- a/src/common/components/chats/chats-channel-messages/index.tsx +++ b/src/common/components/chats/chats-channel-messages/index.tsx @@ -43,7 +43,6 @@ import { ChatContext } from "../chat-context-provider"; interface Props { publicMessages: PublicMessage[]; currentChannel: Channel; - activeUserKeys: NostrKeysType; username: string; from?: string; history: History; @@ -64,7 +63,6 @@ export default function ChatsChannelMessages(props: Props) { const { publicMessages, currentChannel, - activeUserKeys, history, from, isScrollToBottom, @@ -76,6 +74,8 @@ export default function ChatsChannelMessages(props: Props) { } = props; const { chat, global, activeUser, ui, users } = useMappedStore(); + const { messageServiceInstance, activeUserKeys } = useContext(ChatContext); + let prevGlobal = usePrevious(global); const popoverRef = useRef(null); @@ -91,8 +91,6 @@ export default function ChatsChannelMessages(props: Props) { const [hiddenMsgId, setHiddenMsgId] = useState(""); const [resendMessage, setResendMessage] = useState(); - const { messageServiceInstance } = useContext(ChatContext); - // useEffect(() => { // scrollToBottom(); // console.log("Heloooooooo"); @@ -274,7 +272,7 @@ export default function ChatsChannelMessages(props: Props) { case 4: if (resendMessage) { deletePublicMessage(currentChannel.id, resendMessage?.id); - window?.messageService?.sendPublicMessage( + messageServiceInstance?.sendPublicMessage( currentChannel!, resendMessage?.content, [], diff --git a/src/common/components/chats/chats-community-dropdown-menu/index.tsx b/src/common/components/chats/chats-community-dropdown-menu/index.tsx index 5d8d1508fca..ee9be8fb671 100644 --- a/src/common/components/chats/chats-community-dropdown-menu/index.tsx +++ b/src/common/components/chats/chats-community-dropdown-menu/index.tsx @@ -442,7 +442,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => { const handleConfirmButton = (actionType: string) => { switch (actionType) { case LEAVECOMMUNITY: - window?.messageService + messageServiceInstance ?.updateLeftChannelList([...chat.leftChannelsList!, currentChannel?.id!]) .then(() => {}) .finally(() => { diff --git a/src/common/components/chats/chats-direct-messages/index.tsx b/src/common/components/chats/chats-direct-messages/index.tsx index c54d76d0b90..1337e1c47c6 100644 --- a/src/common/components/chats/chats-direct-messages/index.tsx +++ b/src/common/components/chats/chats-direct-messages/index.tsx @@ -22,10 +22,10 @@ import { ActiveUser } from "../../../store/active-user/types"; import usePrevious from "react-use/lib/usePrevious"; import { NostrKeysType } from "../types"; import { _t } from "../../../i18n"; +import { ChatContext } from "../chat-context-provider"; interface Props { directMessages: DirectMessage[]; - activeUserKeys: NostrKeysType; currentUser: string; isScrollToBottom: boolean; isScrolled?: boolean; @@ -37,7 +37,6 @@ let zoom: Zoom | null = null; export default function ChatsDirectMessages(props: Props) { const { directMessages, - activeUserKeys, currentUser, isScrolled, receiverPubKey, @@ -46,6 +45,7 @@ export default function ChatsDirectMessages(props: Props) { } = props; const { chat, global, activeUser } = useMappedStore(); + const { activeUserKeys } = useContext(ChatContext); console.log("directMessages", directMessages); diff --git a/src/common/components/chats/chats-sidebar/indes.tsx b/src/common/components/chats/chats-sidebar/indes.tsx index 41e112e259f..11ea4684aea 100644 --- a/src/common/components/chats/chats-sidebar/indes.tsx +++ b/src/common/components/chats/chats-sidebar/indes.tsx @@ -48,7 +48,7 @@ export default function ChatsSideBar(props: Props) { activeUserKeys, revealPrivKey, chatPrivKey, - setInProgress, + setShowSpinner, setRevealPrivKey, setReceiverPubKey } = chatContext; @@ -63,7 +63,7 @@ export default function ChatsSideBar(props: Props) { const [isScrollToTop, setIsScrollToTop] = useState(false); const [communities, setCommunities] = useState([]); - console.log("username in chats sidebar", username); + console.log("setInProgress in chats sidebar", setShowSpinner); useDebounce( async () => { @@ -110,7 +110,7 @@ export default function ChatsSideBar(props: Props) { const handleRefreshChat = () => { resetChat(); setNostrkeys(activeUserKeys!); - setInProgress(true); + setShowSpinner(true); }; const handleRevealPrivKey = () => { diff --git a/src/common/components/chats/join-community-chat-btn/index.tsx b/src/common/components/chats/join-community-chat-btn/index.tsx index 14abd8e1c8c..a9c1bc17a2e 100644 --- a/src/common/components/chats/join-community-chat-btn/index.tsx +++ b/src/common/components/chats/join-community-chat-btn/index.tsx @@ -11,6 +11,8 @@ import { useMappedStore } from "../../../store/use-mapped-store"; import { Channel, communityModerator } from "../../../../managers/message-manager-types"; import * as ls from "../../../util/local-storage"; import { setNostrkeys } from "../../../../managers/message-manager"; +import { ChatContext } from "../chat-context-provider"; +import { profileUpdater } from "../chat-popup"; import { NOSTRKEY } from "../chat-popup/chat-constants"; import { NostrKeysType } from "../types"; import { @@ -19,7 +21,6 @@ import { setChannelMetaData, setProfileMetaData } from "../utils"; -import { ChatContext } from "../chat-context-provider"; interface Props { history: History; @@ -29,8 +30,7 @@ interface Props { } export default function JoinCommunityChatBtn(props: Props) { - const { messageServiceInstance } = useContext(ChatContext); - console.log("Bhai kiya masla ha apko", messageServiceInstance); + const { messageServiceInstance, setShowSpinner } = useContext(ChatContext); const { chat } = useMappedStore(); const [inProgress, setInProgress] = useState(false); const [isCommunityChatJoined, setIsCommunityChatJoined] = useState(false); @@ -41,6 +41,7 @@ export default function JoinCommunityChatBtn(props: Props) { const [activeUserKeys, setActiveUserKeys] = useState(); const [loadCommunity, setLoadCommunity] = useState(false); const [initiateCommunityChat, setInitiateCommunityChat] = useState(false); + const [shouldUpdateProfile, setShouldUpdateProfile] = useState(false); useEffect(() => { fetchCommunityProfile(); @@ -57,11 +58,23 @@ export default function JoinCommunityChatBtn(props: Props) { } }, [activeUserKeys]); + useEffect(() => { + if (shouldUpdateProfile && messageServiceInstance) { + profileUpdater(messageServiceInstance); + } + }, [shouldUpdateProfile, messageServiceInstance]); + + useEffect(() => { + if (messageServiceInstance && shouldUpdateProfile) { + setShouldUpdateProfile(false); + } + }, [messageServiceInstance]); + useEffect(() => { if (messageServiceInstance) { fetchUserProfileData(); if (loadCommunity) { - window?.messageService?.loadChannel(currentChannel?.id!); + messageServiceInstance?.loadChannel(currentChannel?.id!); } if (initiateCommunityChat && communityRoles.length !== 0) { createCommunityChat(); @@ -146,7 +159,7 @@ export default function JoinCommunityChatBtn(props: Props) { const { community } = props; try { setInProgress(true); - const data = await window?.messageService?.createChannel({ + const data = await messageServiceInstance?.createChannel({ name: community.title, about: community.description, communityName: community.name, @@ -179,18 +192,19 @@ export default function JoinCommunityChatBtn(props: Props) { const joinCommunityChat = () => { console.log("Join community chat", messageServiceInstance); if (!hasUserJoinedChat) { + setShowSpinner(true); handleJoinChat(); setLoadCommunity(true); setIsCommunityChatJoined(true); return; } if (chat.leftChannelsList.includes(currentChannel?.id!)) { - window?.messageService?.updateLeftChannelList( + messageServiceInstance?.updateLeftChannelList( chat.leftChannelsList.filter((x) => x !== currentChannel?.id) ); } setIsCommunityChatJoined(true); - window?.messageService?.loadChannel(currentChannel?.id!); + messageServiceInstance?.loadChannel(currentChannel?.id!); setIsCommunityChatJoined(true); }; @@ -232,6 +246,7 @@ export default function JoinCommunityChatBtn(props: Props) { setInProgress(false); fetchCommunityProfile(); resetChat(); + setShouldUpdateProfile(true); }; const chatButtonSpinner = ( diff --git a/src/common/components/community-cover/index.tsx b/src/common/components/community-cover/index.tsx index fc60ee3ef70..7d973086661 100644 --- a/src/common/components/community-cover/index.tsx +++ b/src/common/components/community-cover/index.tsx @@ -35,7 +35,6 @@ import { _t } from "../../i18n"; import { pencilOutlineSvg } from "../../img/svg"; import "./_index.scss"; -import ChatContextProvider from "../chats/chat-context-provider"; const coverFallbackDay = require("../../img/cover-fallback-day.png"); const coverFallbackNight = require("../../img/cover-fallback-night.png"); @@ -207,9 +206,7 @@ export class CommunityCover extends Component { {CommunityPostBtn({ ...this.props })} - - - +
{canUpdateCoverImage && ( { } public async updateProfile(profile: Metadata) { - console.log("Profile update run"); + console.log("Profile update run", profile); return this.publish(Kind.Metadata, [], JSON.stringify(profile)); } @@ -574,6 +574,7 @@ class MessageService extends TypedEventEmitter { sig: "" }) .then((event) => { + console.log("event failed", event); if (!event) { reject("Couldn't sign event!"); return; diff --git a/src/common/pages/chats/index.tsx b/src/common/pages/chats/index.tsx index f44b9259946..85b684fc1d6 100644 --- a/src/common/pages/chats/index.tsx +++ b/src/common/pages/chats/index.tsx @@ -1,24 +1,22 @@ import React, { useContext, useEffect, useState } from "react"; import { connect } from "react-redux"; import { match } from "react-router"; +import { Spinner } from "react-bootstrap"; import NavBar from "../../components/navbar"; import NavBarElectron from "../../../desktop/app/components/navbar"; import { pageMapDispatchToProps, pageMapStateToProps, PageProps } from "../common"; import ChatsSideBar from "../../components/chats/chats-sidebar/indes"; import ChatsMessagesBox from "../../components/chats/chats-messages-box"; -import "./index.scss"; - -import * as ls from "../../util/local-storage"; -import { Button, Spinner } from "react-bootstrap"; -import { setNostrkeys } from "../../../managers/message-manager"; import ManageChatKey from "../../components/manage-chat-key"; -import Feedback, { success } from "../../components/feedback"; +import Feedback from "../../components/feedback"; import { useMappedStore } from "../../store/use-mapped-store"; -import ChatContextProvider, { ChatContext } from "../../components/chats/chat-context-provider"; +import { ChatContext } from "../../components/chats/chat-context-provider"; import ImportChats from "../../components/chats/import-chats"; import JoinChat from "../../components/chats/join-chat"; +import "./index.scss"; + interface MatchParams { filter: string; name: string; @@ -33,9 +31,13 @@ interface Props extends PageProps { export const Chats = (props: Props) => { const { activeUser, global } = useMappedStore(); - const [marginTop, setMarginTop] = useState(0); + const chatContext = useContext(ChatContext); + const { showSpinner, activeUserKeys, revealPrivKey, chatPrivKey } = chatContext; + + console.log("inProgress in chats page", showSpinner); + useEffect(() => { document.body.style.overflow = "hidden"; @@ -63,53 +65,48 @@ export const Chats = (props: Props) => { }; return ( - - <> - - {global.isElectron ? : } -
- {context.inProgress ? ( -
- -
- ) : ( - <> - {context.activeUserKeys?.pub ? ( - context.chatPrivKey ? ( - <> - - {context.revealPrivKey ? ( -
- -
- ) : ( - // Handle the case when the user hasn't joined any community here - - )} - - ) : ( - <> -
- + <> + + {global.isElectron ? : } +
+ {showSpinner ? ( +
+ +
+ ) : ( + <> + {activeUserKeys?.pub ? ( + chatPrivKey ? ( + <> + + {revealPrivKey ? ( +
+
- - ) + ) : ( + // Handle the case when the user hasn't joined any community here + + )} + ) : ( -
- -
- )} - - )} -
- - + <> +
+ +
+ + ) + ) : ( +
+ +
+ )} + + )} +
+ ); }; export default connect(pageMapStateToProps, pageMapDispatchToProps)(Chats); diff --git a/src/common/pages/community-functional.tsx b/src/common/pages/community-functional.tsx index 0ff211a78c5..4e5fa2bc9d2 100644 --- a/src/common/pages/community-functional.tsx +++ b/src/common/pages/community-functional.tsx @@ -1,6 +1,6 @@ import { pageMapDispatchToProps, pageMapStateToProps, PageProps } from "./common"; import { match } from "react-router"; -import React, { Fragment, useEffect, useState } from "react"; +import React, { Fragment, useContext, useEffect, useState } from "react"; import { search as searchApi, SearchResult } from "../api/search-api"; import { getSubscriptions } from "../api/bridge"; import { EntryFilter, ListStyle } from "../store/global/types"; @@ -50,6 +50,7 @@ import { setNostrkeys } from "../../managers/message-manager"; import { QueryIdentifiers, useCommunityCache } from "../core"; import { useQueryClient } from "@tanstack/react-query"; import { profileUpdater } from "../components/chats/chat-popup"; +import { ChatContext } from "../components/chats/chat-context-provider"; interface MatchParams { filter: string; @@ -89,16 +90,24 @@ export const CommunityPage = (props: Props) => { const prevMatch = usePrevious(props.match); const prevActiveUser = usePrevious(props.activeUser); + const chatContext = useContext(ChatContext); + const { messageServiceInstance } = chatContext; + useEffect(() => { fetchUserProfileData(); }, [props.activeUser]); useEffect(() => { - if (shouldUpdateProfile) { - console.log("state changed in useeffect"); - profileUpdater(); + if (shouldUpdateProfile && messageServiceInstance) { + profileUpdater(messageServiceInstance); + } + }, [shouldUpdateProfile, messageServiceInstance]); + + useEffect(() => { + if (messageServiceInstance && shouldUpdateProfile) { + setShouldUpdateProfile(false); } - }, [shouldUpdateProfile]); + }, [messageServiceInstance]); useEffect(() => { const communities = getJoinedCommunities(chat.channels, chat.leftChannelsList); @@ -263,7 +272,7 @@ export const CommunityPage = (props: Props) => { const joinCommunityChat = () => { const communityIds = new Set(communities.map((community) => community.id)); if (!communityIds.has(channelId)) { - const messageService = window?.messageService; + const messageService = messageServiceInstance; if (messageService) { const updatedLeftChannelsList = chat.leftChannelsList.filter((x) => x !== channelId); messageService.updateLeftChannelList(updatedLeftChannelsList); From 30668037332ee58853042a128afd538fd0804219 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Wed, 13 Sep 2023 17:23:51 +0500 Subject: [PATCH 063/179] Minor fixes --- src/common/components/chats/chat-popup/index.scss | 1 + src/common/components/chats/chat-popup/index.tsx | 13 ++++--------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/common/components/chats/chat-popup/index.scss b/src/common/components/chats/chat-popup/index.scss index d7a117ad0f3..1c56a18b60a 100644 --- a/src/common/components/chats/chat-popup/index.scss +++ b/src/common/components/chats/chat-popup/index.scss @@ -258,6 +258,7 @@ width: -webkit-fill-available; .username { + padding-top: 8px; @include themify(day) { color: $charcoal-grey; } diff --git a/src/common/components/chats/chat-popup/index.tsx b/src/common/components/chats/chat-popup/index.tsx index af33ade39b1..0f83e3e06d1 100644 --- a/src/common/components/chats/chat-popup/index.tsx +++ b/src/common/components/chats/chat-popup/index.tsx @@ -284,12 +284,8 @@ export default function ChatPopUp(props: Props) { }, [currentChannel, isCommunity, chat.publicMessages]); useEffect(() => { - if (isCurrentUser) { - scrollerClicked(); - } else { - scrollerClicked(); - } - }, [isCurrentUser]); + scrollerClicked(); + }, [isCurrentUser, isCommunity]); useEffect(() => { if (removedUsers) { @@ -488,6 +484,7 @@ export default function ChatPopUp(props: Props) { }; const scrollerClicked = () => { + console.log("Scroller clicked"); chatBodyDivRef?.current?.scroll({ top: isCurrentUser || isCommunity ? chatBodyDivRef?.current?.scrollHeight : 0, behavior: "auto" @@ -879,9 +876,7 @@ export default function ChatPopUp(props: Props) { communityClicked(channel.communityName!, channel.name) } > -

- {channel.name} -

+

{channel.name}

{getCommunityLastMessage(channel.id, chat.publicMessages)}

From 72fd2ba9933c03d2c251def1e99e29083bb2d872 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Wed, 13 Sep 2023 18:18:06 +0500 Subject: [PATCH 064/179] Resolve conflicts with development --- .github/workflows/master.yml | 45 +- .github/workflows/staging.yml | 14 +- docker-compose.production.yml | 17 + docker-compose.yml | 17 + package.json | 7 +- src/common/api/operations.ts | 1 + src/common/api/threespeak/queries.ts | 4 + .../components/chats/chat-input/index.tsx | 2 +- .../comment/__snapshots__/index.spec.tsx.snap | 2 + .../columns/deck-items/deck-thread-item.tsx | 7 +- .../decks/deck-settings/decks-settings.tsx | 2 +- .../deck-threads-form-emoji-picker.tsx | 25 +- .../decks/deck-threads-form/index.tsx | 24 +- .../deck-toolbar-base-actions.tsx | 4 +- .../__snapshots__/test1.spec.tsx.snap | 4 +- src/common/components/discussion/index.tsx | 21 +- .../components/editor-toolbar/index.tsx | 18 +- .../components/emoji-picker/_index-old.scss | 74 +++ .../components/emoji-picker/_index.scss | 86 +-- .../components/emoji-picker/index-old.tsx | 207 ++++++ .../components/emoji-picker/index.spec.tsx | 2 +- src/common/components/emoji-picker/index.tsx | 294 +++------ .../entry-menu/menu-items-generator.ts | 4 +- src/common/components/entry-votes/index.tsx | 17 +- src/common/components/navbar/index.tsx | 2 +- src/common/components/user-nav/index.spec.tsx | 11 +- src/common/components/user-nav/index.tsx | 424 ++++-------- .../components/user-nav/wallet-badge.tsx | 33 + .../components/video-gallery/index.scss | 18 +- src/common/components/video-gallery/index.tsx | 11 +- .../video-gallery/video-gallery-item.tsx | 10 +- .../video-upload-threespeak/index.scss | 82 ++- .../video-upload-threespeak/index.tsx | 52 +- .../video-upload-threespeak/utils/index.ts | 1 + .../utils/use-get-camera-list.ts | 18 + .../video-upload-item.tsx | 2 +- .../video-upload-recorder-actions.tsx | 66 ++ .../video-upload-recorder-no-permission.tsx | 10 + .../video-upload-recorder.tsx | 188 ++++++ src/common/constants/dmca.json | 601 +++++++++--------- src/common/constants/exchanges.ts | 3 +- src/common/core/caches/entries-cache.tsx | 4 +- src/common/core/react-query.ts | 4 +- src/common/helper/posting.ts | 5 +- src/common/i18n/locales/ac-ace.json | 3 +- src/common/i18n/locales/ar-SA.json | 147 ++--- src/common/i18n/locales/az-AZ.json | 3 +- src/common/i18n/locales/bg-BG.json | 3 +- src/common/i18n/locales/bn-BD.json | 3 +- src/common/i18n/locales/da-DK.json | 3 +- src/common/i18n/locales/de-DE.json | 3 +- src/common/i18n/locales/el-GR.json | 3 +- src/common/i18n/locales/en-US.json | 11 +- src/common/i18n/locales/es-ES.json | 3 +- src/common/i18n/locales/et-EE.json | 3 +- src/common/i18n/locales/fa-IR.json | 3 +- src/common/i18n/locales/fi-FI.json | 153 ++--- src/common/i18n/locales/fil-PH.json | 3 +- src/common/i18n/locales/fr-FR.json | 3 +- src/common/i18n/locales/he-IL.json | 3 +- src/common/i18n/locales/hi-IN.json | 3 +- src/common/i18n/locales/hr-HR.json | 3 +- src/common/i18n/locales/hu-HU.json | 3 +- src/common/i18n/locales/id-ID.json | 3 +- src/common/i18n/locales/it-IT.json | 3 +- src/common/i18n/locales/ja-JP.json | 3 +- src/common/i18n/locales/ka-GE.json | 3 +- src/common/i18n/locales/kk-KZ.json | 3 +- src/common/i18n/locales/ko-KR.json | 3 +- src/common/i18n/locales/ku-TR.json | 3 +- src/common/i18n/locales/ky-KG.json | 3 +- src/common/i18n/locales/lt-LT.json | 3 +- src/common/i18n/locales/lv-LV.json | 3 +- src/common/i18n/locales/ms-MY.json | 3 +- src/common/i18n/locales/ne-NP.json | 3 +- src/common/i18n/locales/nl-NL.json | 3 +- src/common/i18n/locales/no-NO.json | 3 +- src/common/i18n/locales/pa-IN.json | 3 +- src/common/i18n/locales/pcm-NG.json | 3 +- src/common/i18n/locales/pl-PL.json | 3 +- src/common/i18n/locales/pt-PT.json | 3 +- src/common/i18n/locales/ro-RO.json | 3 +- src/common/i18n/locales/ru-RU.json | 7 +- src/common/i18n/locales/sk-SK.json | 3 +- src/common/i18n/locales/sl-SI.json | 3 +- src/common/i18n/locales/sr-CS.json | 3 +- src/common/i18n/locales/sv-SE.json | 3 +- src/common/i18n/locales/ta-IN.json | 3 +- src/common/i18n/locales/th-TH.json | 3 +- src/common/i18n/locales/tr-TR.json | 3 +- src/common/i18n/locales/uk-UA.json | 3 +- src/common/i18n/locales/ur-IN.json | 3 +- src/common/i18n/locales/ur-PK.json | 3 +- src/common/i18n/locales/uz-UZ.json | 3 +- src/common/i18n/locales/vi-VN.json | 3 +- src/common/i18n/locales/yo-NG.json | 3 +- src/common/i18n/locales/zh-CN.json | 3 +- src/common/i18n/locales/zh-TW.json | 3 +- src/common/img/svg.tsx | 54 ++ src/common/pages/entry/index-amp.tsx | 2 +- src/common/pages/entry/index.tsx | 2 +- src/common/pages/submit/api/publish.ts | 6 +- src/common/pages/submit/api/update.ts | 9 +- .../submit/hooks/body-versioning-manager.tsx | 115 ++++ src/common/pages/submit/hooks/index.ts | 1 + .../submit/hooks/three-speak-manager.tsx | 125 +++- src/common/pages/submit/index.tsx | 33 +- src/desktop/app/components/navbar/index.tsx | 23 +- src/server/handlers/entry.tsx | 2 +- src/server/services/amp-pages.ts | 2 + yarn.lock | 23 +- 111 files changed, 2066 insertions(+), 1242 deletions(-) create mode 100644 src/common/components/emoji-picker/_index-old.scss create mode 100644 src/common/components/emoji-picker/index-old.tsx create mode 100644 src/common/components/user-nav/wallet-badge.tsx create mode 100644 src/common/components/video-upload-threespeak/utils/index.ts create mode 100644 src/common/components/video-upload-threespeak/utils/use-get-camera-list.ts create mode 100644 src/common/components/video-upload-threespeak/video-upload-recorder-actions.tsx create mode 100644 src/common/components/video-upload-threespeak/video-upload-recorder-no-permission.tsx create mode 100644 src/common/components/video-upload-threespeak/video-upload-recorder.tsx create mode 100644 src/common/pages/submit/hooks/body-versioning-manager.tsx diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index bac3987ea0d..7833b2e43bf 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -58,18 +58,31 @@ jobs: env: USE_PRIVATE: ${{secrets.USE_PRIVATE}} REDIS_HOST_PASSWORD: ${{secrets.REDIS_HOST_PASSWORD}} + PRIVATE_API_ADDR: ${{secrets.PRIVATE_API_ADDR}} + PRIVATE_API_AUTH: ${{secrets.PRIVATE_API_AUTH}} + HIVESIGNER_CLIENT_SECRET: ${{secrets.HIVESIGNER_CLIENT_SECRET}} + SEARCH_API_ADDR: ${{secrets.SEARCH_API_ADDR}} + SEARCH_API_SECRET: ${{secrets.SEARCH_API_SECRET}} + API_PORT: ${{secrets.API_PORT}} with: host: ${{ secrets.SSH_HOST_EU }} username: ${{ secrets.SSH_USERNAME }} key: ${{ secrets.SSH_KEY }} port: ${{ secrets.SSH_PORT }} - envs: USE_PRIVATE, REDIS_HOST_PASSWORD + envs: USE_PRIVATE,REDIS_HOST_PASSWORD,PRIVATE_API_ADDR,PRIVATE_API_AUTH,HIVESIGNER_CLIENT_SECRET,SEARCH_API_ADDR,SEARCH_API_SECRET,API_PORT script: | export USE_PRIVATE=$USE_PRIVATE export REDIS_HOST_PASSWORD=$REDIS_HOST_PASSWORD + export PRIVATE_API_ADDR=$PRIVATE_API_ADDR + export PRIVATE_API_AUTH=$PRIVATE_API_AUTH + export HIVESIGNER_CLIENT_SECRET=$HIVESIGNER_CLIENT_SECRET + export SEARCH_API_ADDR=$SEARCH_API_ADDR + export SEARCH_API_SECRET=$SEARCH_API_SECRET + export API_PORT=$API_PORT cd ~/vision-production git pull origin master docker pull ecency/vision:latest + docker pull ecency/api:latest docker-compose -f docker-compose.production.yml up -d deploy-US: @@ -81,18 +94,31 @@ jobs: env: USE_PRIVATE: ${{secrets.USE_PRIVATE}} REDIS_HOST_PASSWORD: ${{secrets.REDIS_HOST_PASSWORD}} + PRIVATE_API_ADDR: ${{secrets.PRIVATE_API_ADDR}} + PRIVATE_API_AUTH: ${{secrets.PRIVATE_API_AUTH}} + HIVESIGNER_CLIENT_SECRET: ${{secrets.HIVESIGNER_CLIENT_SECRET}} + SEARCH_API_ADDR: ${{secrets.SEARCH_API_ADDR}} + SEARCH_API_SECRET: ${{secrets.SEARCH_API_SECRET}} + API_PORT: ${{secrets.API_PORT}} with: host: ${{ secrets.SSH_HOST_US }} username: ${{ secrets.SSH_USERNAME }} key: ${{ secrets.SSH_KEY }} port: ${{ secrets.SSH_PORT }} - envs: USE_PRIVATE, REDIS_HOST_PASSWORD + envs: USE_PRIVATE,REDIS_HOST_PASSWORD,PRIVATE_API_ADDR,PRIVATE_API_AUTH,HIVESIGNER_CLIENT_SECRET,SEARCH_API_ADDR,SEARCH_API_SECRET,API_PORT script: | export USE_PRIVATE=$USE_PRIVATE export REDIS_HOST_PASSWORD=$REDIS_HOST_PASSWORD + export PRIVATE_API_ADDR=$PRIVATE_API_ADDR + export PRIVATE_API_AUTH=$PRIVATE_API_AUTH + export HIVESIGNER_CLIENT_SECRET=$HIVESIGNER_CLIENT_SECRET + export SEARCH_API_ADDR=$SEARCH_API_ADDR + export SEARCH_API_SECRET=$SEARCH_API_SECRET + export API_PORT=$API_PORT cd ~/vision-production git pull origin master docker pull ecency/vision:latest + docker pull ecency/api:latest docker-compose -f docker-compose.production.yml up -d deploy-SG: @@ -104,16 +130,29 @@ jobs: env: USE_PRIVATE: ${{secrets.USE_PRIVATE}} REDIS_HOST_PASSWORD: ${{secrets.REDIS_HOST_PASSWORD}} + PRIVATE_API_ADDR: ${{secrets.PRIVATE_API_ADDR}} + PRIVATE_API_AUTH: ${{secrets.PRIVATE_API_AUTH}} + HIVESIGNER_CLIENT_SECRET: ${{secrets.HIVESIGNER_CLIENT_SECRET}} + SEARCH_API_ADDR: ${{secrets.SEARCH_API_ADDR}} + SEARCH_API_SECRET: ${{secrets.SEARCH_API_SECRET}} + API_PORT: ${{secrets.API_PORT}} with: host: ${{ secrets.SSH_HOST_SG }} username: ${{ secrets.SSH_USERNAME }} key: ${{ secrets.SSH_KEY }} port: ${{ secrets.SSH_PORT }} - envs: USE_PRIVATE, REDIS_HOST_PASSWORD + envs: USE_PRIVATE,REDIS_HOST_PASSWORD,PRIVATE_API_ADDR,PRIVATE_API_AUTH,HIVESIGNER_CLIENT_SECRET,SEARCH_API_ADDR,SEARCH_API_SECRET,API_PORT script: | export USE_PRIVATE=$USE_PRIVATE export REDIS_HOST_PASSWORD=$REDIS_HOST_PASSWORD + export PRIVATE_API_ADDR=$PRIVATE_API_ADDR + export PRIVATE_API_AUTH=$PRIVATE_API_AUTH + export HIVESIGNER_CLIENT_SECRET=$HIVESIGNER_CLIENT_SECRET + export SEARCH_API_ADDR=$SEARCH_API_ADDR + export SEARCH_API_SECRET=$SEARCH_API_SECRET + export API_PORT=$API_PORT cd ~/vision-production git pull origin master docker pull ecency/vision:latest + docker pull ecency/api:latest docker-compose -f docker-compose.production.yml up -d diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index fb398adfbc5..24c3d56e00f 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -58,15 +58,27 @@ jobs: env: USE_PRIVATE: ${{secrets.USE_PRIVATE}} REDIS_HOST_PASSWORD: ${{secrets.REDIS_HOST_PASSWORD}} + PRIVATE_API_ADDR: ${{secrets.PRIVATE_API_ADDR}} + PRIVATE_API_AUTH: ${{secrets.PRIVATE_API_AUTH}} + HIVESIGNER_CLIENT_SECRET: ${{secrets.HIVESIGNER_CLIENT_SECRET}} + SEARCH_API_ADDR: ${{secrets.SEARCH_API_ADDR}} + SEARCH_API_SECRET: ${{secrets.SEARCH_API_SECRET}} + API_PORT: ${{secrets.API_PORT}} with: host: ${{ secrets.SSH_STAGING_HOST }} username: ${{ secrets.SSH_USERNAME }} key: ${{ secrets.SSH_KEY }} port: ${{ secrets.SSH_PORT }} - envs: USE_PRIVATE, REDIS_HOST_PASSWORD + envs: USE_PRIVATE,REDIS_HOST_PASSWORD,PRIVATE_API_ADDR,PRIVATE_API_AUTH,HIVESIGNER_CLIENT_SECRET,SEARCH_API_ADDR,SEARCH_API_SECRET,API_PORT script: | export USE_PRIVATE=$USE_PRIVATE export REDIS_HOST_PASSWORD=$REDIS_HOST_PASSWORD + export PRIVATE_API_ADDR=$PRIVATE_API_ADDR + export PRIVATE_API_AUTH=$PRIVATE_API_AUTH + export HIVESIGNER_CLIENT_SECRET=$HIVESIGNER_CLIENT_SECRET + export SEARCH_API_ADDR=$SEARCH_API_ADDR + export SEARCH_API_SECRET=$SEARCH_API_SECRET + export API_PORT=$API_PORT cd ~/vision-staging git pull origin development docker pull ecency/vision:development diff --git a/docker-compose.production.yml b/docker-compose.production.yml index 124c3423f6d..0108d1b86b5 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -18,6 +18,23 @@ services: networks: - vision + vapi: + image: ecency/api:latest + environment: + - PRIVATE_API_ADDR + - PRIVATE_API_AUTH + - HIVESIGNER_CLIENT_SECRET + - SEARCH_API_ADDR + - SEARCH_API_SECRET + restart: always + ports: + - "4000:4000" + hostname: vapi + depends_on: + - redis + networks: + - vision + web1: image: ecency/vision:latest environment: diff --git a/docker-compose.yml b/docker-compose.yml index 5ab37e61a24..9ef5262f80b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,6 +18,23 @@ services: networks: - vision + vapi: + image: ecency/api:latest + environment: + - PRIVATE_API_ADDR + - PRIVATE_API_AUTH + - HIVESIGNER_CLIENT_SECRET + - SEARCH_API_ADDR + - SEARCH_API_SECRET + restart: always + ports: + - "4000:4000" + hostname: vapi + depends_on: + - redis + networks: + - vision + web1: image: ecency/vision:development environment: diff --git a/package.json b/package.json index 831b58bcc1b..bcf0bd2561a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ecency-vision", - "version": "3.0.35", + "version": "3.0.36", "private": true, "license": "MIT", "scripts": { @@ -13,8 +13,9 @@ "start:prod": "NODE_ENV=production node build/server.js" }, "dependencies": { - "@ecency/render-helper": "^2.2.25", + "@ecency/render-helper": "^2.2.26", "@ecency/render-helper-amp": "^1.1.0", + "@emoji-mart/data": "^1.1.2", "@firebase/analytics": "^0.8.0", "@firebase/app": "^0.7.28", "@firebase/messaging": "^0.9.16", @@ -35,6 +36,7 @@ "currency-symbol-map": "^4.0.4", "debounce": "^1.2.1", "diff-match-patch": "^1.0.5", + "emoji-mart": "^5.5.2", "express": "^4.17.3", "history": "^4.7.2", "hive-uri": "^0.2.3", @@ -100,6 +102,7 @@ "@types/bytebuffer": "^5.0.41", "@types/cookie-parser": "^1.4.2", "@types/diff-match-patch": "^1.0.32", + "@types/dom-mediacapture-record": "^1.0.16", "@types/express": "^4.17.0", "@types/jest": "^26.0.24", "@types/js-cookie": "^2.2.6", diff --git a/src/common/api/operations.ts b/src/common/api/operations.ts index 58b0c319307..702328164ff 100644 --- a/src/common/api/operations.ts +++ b/src/common/api/operations.ts @@ -40,6 +40,7 @@ export interface MetaData { community?: string; description?: string; video?: any; + type?: string; } export interface BeneficiaryRoute { diff --git a/src/common/api/threespeak/queries.ts b/src/common/api/threespeak/queries.ts index 3aaf4239f80..7cbc0bb993d 100644 --- a/src/common/api/threespeak/queries.ts +++ b/src/common/api/threespeak/queries.ts @@ -18,6 +18,10 @@ export function useThreeSpeakVideo( const apiQuery = useQuery( [QueryIdentifiers.THREE_SPEAK_VIDEO_LIST, activeUser?.username ?? ""], async () => { + if (!activeUser) { + return []; + } + try { return await getAllVideoStatuses(activeUser!.username); } catch (e) { diff --git a/src/common/components/chats/chat-input/index.tsx b/src/common/components/chats/chat-input/index.tsx index 7ac59dae052..eca1dd740e9 100644 --- a/src/common/components/chats/chat-input/index.tsx +++ b/src/common/components/chats/chat-input/index.tsx @@ -6,7 +6,7 @@ import { Channel } from "../../../../managers/message-manager-types"; import { EmojiPickerStyleProps } from "../types"; import ClickAwayListener from "../../clickaway-listener"; -import EmojiPicker from "../../emoji-picker"; +import EmojiPicker from "../../emoji-picker/index-old"; import GifPicker from "../../gif-picker"; import { error } from "../../feedback"; import Tooltip from "../../tooltip"; diff --git a/src/common/components/comment/__snapshots__/index.spec.tsx.snap b/src/common/components/comment/__snapshots__/index.spec.tsx.snap index 77680626640..1a2628f958d 100644 --- a/src/common/components/comment/__snapshots__/index.spec.tsx.snap +++ b/src/common/components/comment/__snapshots__/index.spec.tsx.snap @@ -203,6 +203,7 @@ exports[`(1) Default render 1`] = `
@@ -545,6 +546,7 @@ exports[`(2) Cancellable, in progress 1`] = `
diff --git a/src/common/components/decks/columns/deck-items/deck-thread-item.tsx b/src/common/components/decks/columns/deck-items/deck-thread-item.tsx index 00bff1f3d84..a04ab85597c 100644 --- a/src/common/components/decks/columns/deck-items/deck-thread-item.tsx +++ b/src/common/components/decks/columns/deck-items/deck-thread-item.tsx @@ -16,7 +16,6 @@ import { EntriesCacheContext, useEntryCache } from "../../../../core"; import { useEntryChecking } from "../../utils"; import { Entry } from "../../../../store/entries/types"; import { DeckThreadItemHeader } from "./deck-thread-item-header"; -import moment from "moment"; import { dateToRelative } from "../../../../helper/parse-date"; import EntryMenu from "../../../entry-menu"; @@ -102,6 +101,12 @@ export const ThreadItem = ({ // } }, [entry]); + useEffect(() => { + if (entry.updated !== entry.created) { + setStatus("default"); + } + }, [entry]); + return (
void; } export const DeckThreadsFormEmojiPicker = ({ onPick }: Props) => { + const anchorRef = useRef(null); + return ( -
- - -
- { - onPick(value); - }} - /> -
-
-
+
+ {emojiIconSvg} + onPick(value)} />
); }; diff --git a/src/common/components/decks/deck-threads-form/index.tsx b/src/common/components/decks/deck-threads-form/index.tsx index c1c9386a270..8aa7302c793 100644 --- a/src/common/components/decks/deck-threads-form/index.tsx +++ b/src/common/components/decks/deck-threads-form/index.tsx @@ -89,11 +89,25 @@ export const DeckThreadsForm = ({ useEffect(() => { if (persistable) { - setThreadHost(threadHost ? threadHost : persistedForm?.threadHost); - setText(text ? text : persistedForm?.text ?? ""); - setImage(image ? image : persistedForm?.image); - setImageName(imageName ? imageName : persistedForm?.imageName); - setVideo(video ? video : persistedForm?.video); + if (!threadHost && persistedForm?.threadHost) { + setThreadHost(persistedForm.threadHost); + } + + if (!text && persistedForm?.text) { + setText(persistedForm.text); + } + + if (!image && persistedForm?.image) { + setImage(persistedForm.image); + } + + if (!imageName && persistedForm?.imageName) { + setImageName(persistedForm.imageName); + } + + if (!video && persistedForm?.video) { + setVideo(persistedForm.video); + } } }, [persistedForm]); diff --git a/src/common/components/decks/deck-toolbar/deck-toolbar-base-actions.tsx b/src/common/components/decks/deck-toolbar/deck-toolbar-base-actions.tsx index 3c554450642..4fbfb3a1090 100644 --- a/src/common/components/decks/deck-toolbar/deck-toolbar-base-actions.tsx +++ b/src/common/components/decks/deck-toolbar/deck-toolbar-base-actions.tsx @@ -4,7 +4,7 @@ import { useMappedStore } from "../../../store/use-mapped-store"; import { WalletBadge } from "../../user-nav"; import { Dropdown } from "react-bootstrap"; import DropdownToggle from "react-bootstrap/DropdownToggle"; -import { dotsMenuIconSvg, notificationsIconSvg, walletIconSvg } from "../icons"; +import { dotsMenuIconSvg, walletIconSvg } from "../icons"; import { _t } from "../../../i18n"; import Link from "../../alink"; @@ -36,7 +36,7 @@ export const DeckToolbarBaseActions = ({
)} {global.usePrivate &&
setShowPurchaseDialog(true)}>{rocketSvg}
} - + )} {isExpanded || !activeUser ? ( diff --git a/src/common/components/discussion/__snapshots__/test1.spec.tsx.snap b/src/common/components/discussion/__snapshots__/test1.spec.tsx.snap index 2176675657b..05977fb6b6e 100644 --- a/src/common/components/discussion/__snapshots__/test1.spec.tsx.snap +++ b/src/common/components/discussion/__snapshots__/test1.spec.tsx.snap @@ -915,7 +915,7 @@ exports[`(1) Full render with active user 1`] = ` dangerouslySetInnerHTML={ Object { "__html": "

@esteemapp
-Bug report click here

", +Bug report click here

", } } /> @@ -2696,7 +2696,7 @@ exports[`(3) With selected item 1`] = ` dangerouslySetInnerHTML={ Object { "__html": "

@esteemapp
-Bug report click here

", +Bug report click here

", } } /> diff --git a/src/common/components/discussion/index.tsx b/src/common/components/discussion/index.tsx index b3ae099f0db..1135f363c89 100644 --- a/src/common/components/discussion/index.tsx +++ b/src/common/components/discussion/index.tsx @@ -1,4 +1,4 @@ -import React, { Component, useState, useEffect } from "react"; +import React, { Component, useEffect, useState } from "react"; import { History, Location } from "history"; @@ -7,9 +7,6 @@ import { Button, Form, FormControl } from "react-bootstrap"; import defaults from "../../constants/defaults.json"; import { renderPostBody, setProxyBase } from "@ecency/render-helper"; - -setProxyBase(defaults.imageServer); - import { Entry, EntryVote } from "../../store/entries/types"; import { Account, FullAccount } from "../../store/accounts/types"; import { Community, ROLES } from "../../store/communities/types"; @@ -18,9 +15,7 @@ import { Global } from "../../store/global/types"; import { User } from "../../store/users/types"; import { ActiveUser } from "../../store/active-user/types"; import { Discussion as DiscussionType, SortOrder } from "../../store/discussion/types"; -import { UI, ToggleType } from "../../store/ui/types"; - -import BaseComponent from "../base"; +import { ToggleType, UI } from "../../store/ui/types"; import ProfileLink from "../profile-link"; import EntryLink from "../entry-link"; import UserAvatar from "../user-avatar"; @@ -48,13 +43,7 @@ import { error } from "../feedback"; import _c from "../../util/fix-class-names"; -import { - commentSvg, - pencilOutlineSvg, - deleteForeverSvg, - menuDownSvg, - dotsHorizontal -} from "../../img/svg"; +import { commentSvg, deleteForeverSvg, dotsHorizontal, pencilOutlineSvg } from "../../img/svg"; import { version } from "../../../../package.json"; import { getFollowing } from "../../api/hive"; @@ -64,6 +53,8 @@ import MyDropDown from "../dropdown"; import { ProfilePopover } from "../profile-popover"; import "./_index.scss"; +setProxyBase(defaults.imageServer); + interface ItemBodyProps { entry: Entry; global: Global; @@ -382,7 +373,7 @@ export const Item = (props: ItemProps) => { const entryIsMuted = mutedData.includes(entry.author); const isComment = !!entry.parent_author; const ownEntry = activeUser && activeUser.username === entry.author; - const isHidden = entry?.net_rshares < -7000000000 && entry?.active_votes.length > 3; // 1000 HP + const isHidden = entry?.net_rshares < -7000000000 && entry?.active_votes?.length > 3; // 1000 HP const isMuted = entry?.stats?.gray && entry?.net_rshares >= 0 && entry?.author_reputation >= 0; const isLowReputation = diff --git a/src/common/components/editor-toolbar/index.tsx b/src/common/components/editor-toolbar/index.tsx index c144bbf0b64..26a7e83099b 100644 --- a/src/common/components/editor-toolbar/index.tsx +++ b/src/common/components/editor-toolbar/index.tsx @@ -7,7 +7,7 @@ import { User } from "../../store/users/types"; import { Global } from "../../store/global/types"; import Tooltip from "../tooltip"; -import EmojiPicker from "../emoji-picker"; +import { EmojiPicker } from "../emoji-picker"; import GifPicker from "../gif-picker"; import Gallery from "../gallery"; import Fragments from "../fragments"; @@ -65,6 +65,7 @@ interface State { shGif: boolean; showVideoUpload: boolean; showVideoGallery: boolean; + isMounted: boolean; } export const detectEvent = (eventType: string) => { @@ -86,7 +87,8 @@ export class EditorToolbar extends Component { mobileImage: false, shGif: false, showVideoUpload: false, - showVideoGallery: false + showVideoGallery: false, + isMounted: false }; holder = React.createRef(); @@ -155,6 +157,9 @@ export class EditorToolbar extends Component { window.addEventListener("blockquote", this.quote); window.addEventListener("image", this.toggleImage); window.addEventListener("customToolbarEvent", this.handleCustomToolbarEvent); + this.setState({ + isMounted: true + }); } componentWillUnmount() { @@ -576,13 +581,12 @@ export class EditorToolbar extends Component { )} -
+
{emoticonHappyOutlineSvg} - {showEmoji && ( + {showEmoji && this.state.isMounted && ( { - this.insertText(e, ""); - }} + anchor={document.querySelector("#editor-tool-emoji-picker")!!} + onSelect={(e) => this.insertText(e, "")} /> )}
diff --git a/src/common/components/emoji-picker/_index-old.scss b/src/common/components/emoji-picker/_index-old.scss new file mode 100644 index 00000000000..db99e703f4b --- /dev/null +++ b/src/common/components/emoji-picker/_index-old.scss @@ -0,0 +1,74 @@ +@import "src/style/colors"; +@import "src/style/variables"; +@import "src/style/bootstrap_vars"; +@import "src/style/mixins"; + +.emoji-picker { + width: 280px; + position: absolute; + right: 0; + z-index: 100; + border-bottom-right-radius: 8px; + border-bottom-left-radius: 8px; + padding: 14px 6px; + + @media (max-width: $sm-break) { + width: 160px; + } + + @include themify(day) { + background: darken($white-three, 5); + } + + @include themify(night) { + background: $dark-two; + } + + .emoji-cat-list { + height: 160px; + overflow: auto; + + .emoji-cat { + .cat-title { + font-weight: 500; + font-size: 14px; + margin: 4px 0 6px 0; + + @include themify(day) { + color: $charcoal-grey; + } + + @include themify(night) { + color: $pinkish-grey; + } + } + + .emoji-list { + @include clearfix(); + display: flex; + flex-wrap: wrap; + + .emoji { + cursor: pointer; + font-size: 18px; + margin: 3px 3px 1px 3px; + align-items: center; + justify-content: center; + display: flex; + font-family: "Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "Apple Color Emoji"; + border-bottom: 2px solid transparent; + + &:hover { + @include themify(day) { + border-bottom: 2px solid $white-three; + } + + @include themify(night) { + border-bottom: 2px solid lighten($dark-six, 14); + } + } + } + } + } + } +} diff --git a/src/common/components/emoji-picker/_index.scss b/src/common/components/emoji-picker/_index.scss index db99e703f4b..a4e81d69034 100644 --- a/src/common/components/emoji-picker/_index.scss +++ b/src/common/components/emoji-picker/_index.scss @@ -1,74 +1,30 @@ -@import "src/style/colors"; -@import "src/style/variables"; -@import "src/style/bootstrap_vars"; -@import "src/style/mixins"; +@import "src/style/vars_mixins"; -.emoji-picker { - width: 280px; +.emoji-picker-dialog { position: absolute; - right: 0; - z-index: 100; - border-bottom-right-radius: 8px; - border-bottom-left-radius: 8px; - padding: 14px 6px; + z-index: 201; - @media (max-width: $sm-break) { - width: 160px; + em-emoji-picker { + width: 300px; } - @include themify(day) { - background: darken($white-three, 5); - } - - @include themify(night) { - background: $dark-two; - } - - .emoji-cat-list { - height: 160px; - overflow: auto; - - .emoji-cat { - .cat-title { - font-weight: 500; - font-size: 14px; - margin: 4px 0 6px 0; - - @include themify(day) { - color: $charcoal-grey; - } - - @include themify(night) { - color: $pinkish-grey; - } - } - - .emoji-list { - @include clearfix(); - display: flex; - flex-wrap: wrap; - - .emoji { - cursor: pointer; - font-size: 18px; - margin: 3px 3px 1px 3px; - align-items: center; - justify-content: center; - display: flex; - font-family: "Segoe UI Emoji", "Segoe UI Symbol", "Segoe UI", "Apple Color Emoji"; - border-bottom: 2px solid transparent; - - &:hover { - @include themify(day) { - border-bottom: 2px solid $white-three; - } + @include media-breakpoint-down(sm) { + bottom: 0; + top: unset !important; + left: 0 !important; + width: 100%; + background: #fff; + align-items: center; + justify-content: center; + border-top: 1px solid var(--border-color); + position: fixed; + + @include themify(night) { + background-color: rgba(21, 22, 23); + } - @include themify(night) { - border-bottom: 2px solid lighten($dark-six, 14); - } - } - } - } + em-emoji-picker { + width: 100%; } } } diff --git a/src/common/components/emoji-picker/index-old.tsx b/src/common/components/emoji-picker/index-old.tsx new file mode 100644 index 00000000000..25f0d189bb7 --- /dev/null +++ b/src/common/components/emoji-picker/index-old.tsx @@ -0,0 +1,207 @@ +import React from "react"; +import { FormControl } from "react-bootstrap"; +import BaseComponent from "../base"; +import SearchBox from "../search-box"; +import { _t } from "../../i18n"; +import { getEmojiData } from "../../api/misc"; +import * as ls from "../../util/local-storage"; +import { insertOrReplace } from "../../util/input-util"; +import { EmojiPickerStyleProps } from "../chats/types/chat-types"; +import "./_index-old.scss"; + +interface Emoji { + a: string; + b: string; + j: string[]; +} + +interface EmojiCategory { + id: string; + name: string; + emojis: string[]; +} + +interface EmojiData { + categories: EmojiCategory[]; + emojis: Record; +} + +interface EmojiCacheItem { + id: string; + name: string; + keywords: string[]; +} + +interface Props { + fallback?: (e: string) => void; + style?: EmojiPickerStyleProps; +} + +interface State { + data: EmojiData | null; + cache: EmojiCacheItem[] | null; + filter: string; +} + +export default class EmojiPicker extends BaseComponent { + state: State = { + data: null, + cache: null, + filter: "" + }; + + _target: HTMLInputElement | null = null; + + componentDidMount() { + getEmojiData().then((data) => this.setData(data)); + + this.watchTarget(); // initial + + if (typeof window !== "undefined") { + window.addEventListener("focus", this.watchTarget, true); + } + } + + componentWillUnmount() { + super.componentWillUnmount(); + if (typeof window !== "undefined") { + window.removeEventListener("focus", this.watchTarget, true); + } + } + + watchTarget = () => { + if (document.activeElement?.classList.contains("accepts-emoji")) { + this._target = document.activeElement as HTMLInputElement; + } + }; + + setData = (data: EmojiData) => { + const cache: EmojiCacheItem[] = Object.keys(data.emojis).map((e) => { + const em = data.emojis[e]; + return { + id: e, + name: em.a.toLowerCase(), + keywords: em.j ? em.j : [] + }; + }); + + this.stateSet({ data, cache }); + }; + + filterChanged = (e: React.ChangeEvent) => { + this.setState({ filter: e.target.value }); + }; + + clicked = (id: string, native: string) => { + const recent = ls.get("recent-emoji", []); + if (!recent.includes(id)) { + const newRecent = [...new Set([id, ...recent])].slice(0, 18); + ls.set("recent-emoji", newRecent); + this.forceUpdate(); // Re-render recent list + } + + if (this._target) { + insertOrReplace(this._target, native); + } else { + const { fallback } = this.props; + if (fallback) fallback(native); + } + }; + + renderEmoji = (emoji: string) => { + const { data } = this.state; + const em = data!.emojis[emoji]; + if (!em) { + return null; + } + const unicodes = em.b.split("-"); + const codePoints = unicodes.map((u) => Number(`0x${u}`)); + const native = String.fromCodePoint(...codePoints); + + return ( +
{ + this.clicked(emoji, native); + }} + key={emoji} + className="emoji" + title={em.a} + > + {native} +
+ ); + }; + + render() { + const { data, cache, filter } = this.state; + if (!data || !cache) { + return null; + } + + const recent: string[] = ls.get("recent-emoji", []); + + const emojiPickerStyle = { + ...(this.props.style && this.props.style) + }; + + return ( +
+ + + {(() => { + if (filter) { + const results = cache + .filter( + (i) => + i.id.indexOf(filter) !== -1 || + i.name.indexOf(filter) !== -1 || + i.keywords.includes(filter) + ) + .map((i) => i.id); + + return ( +
+
+
+ {results.length === 0 && _t("emoji-picker.filter-no-match")} + {results.length > 0 && results.map((emoji) => this.renderEmoji(emoji))} +
+
+
+ ); + } else { + return ( +
+ {recent.length > 0 && ( +
+
{_t("emoji-picker.recently-used")}
+
+ {recent.map((emoji) => this.renderEmoji(emoji))} +
+
+ )} + + {data.categories.map((cat) => ( +
+
{cat.name}
+
+ {cat.emojis.map((emoji) => this.renderEmoji(emoji))} +
+
+ ))} +
+ ); + } + })()} +
+ ); + } +} diff --git a/src/common/components/emoji-picker/index.spec.tsx b/src/common/components/emoji-picker/index.spec.tsx index 3d5ade5679b..09ccc449af5 100644 --- a/src/common/components/emoji-picker/index.spec.tsx +++ b/src/common/components/emoji-picker/index.spec.tsx @@ -2,7 +2,7 @@ import React from "react"; import renderer from "react-test-renderer"; -import EmojiPicker from "./index"; +import EmojiPicker from "./index-old"; import emojiData from "../../../../public/emoji.json"; diff --git a/src/common/components/emoji-picker/index.tsx b/src/common/components/emoji-picker/index.tsx index 021649830b8..370196f7496 100644 --- a/src/common/components/emoji-picker/index.tsx +++ b/src/common/components/emoji-picker/index.tsx @@ -1,212 +1,106 @@ -import React from "react"; - -import { FormControl } from "react-bootstrap"; - -import BaseComponent from "../base"; -import SearchBox from "../search-box"; - -import { _t } from "../../i18n"; - -import { getEmojiData } from "../../api/misc"; - -import * as ls from "../../util/local-storage"; - -import { insertOrReplace } from "../../util/input-util"; +import React, { useEffect, useRef, useState } from "react"; +import { useQuery } from "@tanstack/react-query"; +import { QueryIdentifiers } from "../../core"; +import { Picker } from "emoji-mart"; +import { createPortal } from "react-dom"; +import useClickAway from "react-use/lib/useClickAway"; +import useMountedState from "react-use/lib/useMountedState"; import "./_index.scss"; -import { EmojiPickerStyleProps } from "../chats/types/chat-types"; - -interface Emoji { - a: string; - b: string; - j: string[]; -} - -interface EmojiCategory { - id: string; - name: string; - emojis: string[]; -} - -interface EmojiData { - categories: EmojiCategory[]; - emojis: Record; -} - -interface EmojiCacheItem { - id: string; - name: string; - keywords: string[]; -} +import { useMappedStore } from "../../store/use-mapped-store"; + +export const DEFAULT_EMOJI_DATA = { + categories: [], + emojis: {}, + aliases: {}, + sheet: { + cols: 0, + rows: 0 + } +}; interface Props { - fallback?: (e: string) => void; - style?: EmojiPickerStyleProps; -} - -interface State { - data: EmojiData | null; - cache: EmojiCacheItem[] | null; - filter: string; + anchor: Element | null; + onSelect: (e: string) => void; } -export default class EmojiPicker extends BaseComponent { - state: State = { - data: null, - cache: null, - filter: "" - }; - - _target: HTMLInputElement | null = null; - - componentDidMount() { - getEmojiData().then((data) => this.setData(data)); - - this.watchTarget(); // initial - - if (typeof window !== "undefined") { - window.addEventListener("focus", this.watchTarget, true); - } - } - componentWillUnmount() { - super.componentWillUnmount(); - if (typeof window !== "undefined") { - window.removeEventListener("focus", this.watchTarget, true); +/** + * Renders an emoji picker dialog. + * + * @param {Props} anchor - The anchor element to position the picker relative to. + * @param {function} onSelect - The callback function to be called when an emoji is selected. + * @return The rendered emoji picker dialog. + */ +export function EmojiPicker({ anchor, onSelect }: Props) { + const ref = useRef(null); + + const { global } = useMappedStore(); + + const [show, setShow] = useState(false); + const [position, setPosition] = useState({ x: 0, y: 0 }); + const [pickerInstance, setPickerInstance] = useState(); + + useClickAway(ref, () => { + setShow(false); + }); + + const { data } = useQuery( + [QueryIdentifiers.EMOJI_PICKER], + async () => { + try { + const data = await import(/* webpackChunkName: "emojis" */ "@emoji-mart/data"); + return data.default as typeof DEFAULT_EMOJI_DATA; + } catch (e) { + console.error("Failed to load emoji data"); + } + + return DEFAULT_EMOJI_DATA; + }, + { + initialData: DEFAULT_EMOJI_DATA } - } - - watchTarget = () => { - if (document.activeElement?.classList.contains("accepts-emoji")) { - this._target = document.activeElement as HTMLInputElement; + ); + + const isMounted = useMountedState(); + + useEffect(() => { + if (data.categories.length > 0) { + setPickerInstance( + new Picker({ + dynamicWidth: true, + onEmojiSelect: (e: { native: string }) => onSelect(e.native), + previewPosition: "none", + ref, + set: "apple", + theme: global.theme === "day" ? "light" : "dark" + }) + ); } - }; - - setData = (data: EmojiData) => { - const cache: EmojiCacheItem[] = Object.keys(data.emojis).map((e) => { - const em = data.emojis[e]; - return { - id: e, - name: em.a.toLowerCase(), - keywords: em.j ? em.j : [] - }; - }); - - this.stateSet({ data, cache }); - }; - - filterChanged = (e: React.ChangeEvent) => { - this.setState({ filter: e.target.value }); - }; - - clicked = (id: string, native: string) => { - const recent = ls.get("recent-emoji", []); - if (!recent.includes(id)) { - const newRecent = [...new Set([id, ...recent])].slice(0, 18); - ls.set("recent-emoji", newRecent); - this.forceUpdate(); // Re-render recent list + }, [data, global.theme]); + + useEffect(() => { + if (anchor) { + anchor.addEventListener("click", () => { + const { x, y } = anchor.getBoundingClientRect(); + setPosition({ x: x + window.scrollX, y: y + window.scrollY }); + setShow(true); + }); } + }, [anchor]); - if (this._target) { - insertOrReplace(this._target, native); - } else { - const { fallback } = this.props; - if (fallback) fallback(native); - } - }; - - renderEmoji = (emoji: string) => { - const { data } = this.state; - const em = data!.emojis[emoji]; - if (!em) { - return null; - } - const unicodes = em.b.split("-"); - const codePoints = unicodes.map((u) => Number(`0x${u}`)); - const native = String.fromCodePoint(...codePoints); - - return ( + return isMounted() ? ( + createPortal(
{ - this.clicked(emoji, native); + className="emoji-picker-dialog" + ref={ref} + style={{ + top: position.y + 40, + left: position.x, + display: show ? "flex" : "none" }} - key={emoji} - className="emoji" - title={em.a} - > - {native} -
- ); - }; - - render() { - const { data, cache, filter } = this.state; - if (!data || !cache) { - return null; - } - - const recent: string[] = ls.get("recent-emoji", []); - - const emojiPickerStyle = { - ...(this.props.style && this.props.style) // Merge the passed style props if available - }; - - return ( -
- - - {(() => { - if (filter) { - const results = cache - .filter( - (i) => - i.id.indexOf(filter) !== -1 || - i.name.indexOf(filter) !== -1 || - i.keywords.includes(filter) - ) - .map((i) => i.id); - - return ( -
-
-
- {results.length === 0 && _t("emoji-picker.filter-no-match")} - {results.length > 0 && results.map((emoji) => this.renderEmoji(emoji))} -
-
-
- ); - } else { - return ( -
- {recent.length > 0 && ( -
-
{_t("emoji-picker.recently-used")}
-
- {recent.map((emoji) => this.renderEmoji(emoji))} -
-
- )} - - {data.categories.map((cat) => ( -
-
{cat.name}
-
- {cat.emojis.map((emoji) => this.renderEmoji(emoji))} -
-
- ))} -
- ); - } - })()} -
- ); - } + />, + document.querySelector("#root")!! + ) + ) : ( + <> + ); } diff --git a/src/common/components/entry-menu/menu-items-generator.ts b/src/common/components/entry-menu/menu-items-generator.ts index 86cb6990598..69b1f12c02b 100644 --- a/src/common/components/entry-menu/menu-items-generator.ts +++ b/src/common/components/entry-menu/menu-items-generator.ts @@ -58,7 +58,7 @@ export function useMenuItemsGenerator( useEffect(() => { setCanMute( activeUser && community - ? !!community.team.find( + ? !!community.team?.find( (m) => m[0] === activeUser.username && [ROLES.OWNER.toString(), ROLES.ADMIN.toString(), ROLES.MOD.toString()].includes(m[1]) @@ -216,7 +216,7 @@ export function useMenuItemsGenerator( const isTeamManager = () => activeUser && community - ? !!community.team.find((m) => { + ? !!community.team?.find((m) => { return ( m[0] === activeUser.username && [ROLES.OWNER.toString(), ROLES.ADMIN.toString(), ROLES.MOD.toString()].includes(m[1]) diff --git a/src/common/components/entry-votes/index.tsx b/src/common/components/entry-votes/index.tsx index 44ac5fa5909..d36a2ad00bd 100644 --- a/src/common/components/entry-votes/index.tsx +++ b/src/common/components/entry-votes/index.tsx @@ -1,11 +1,9 @@ import React, { Component } from "react"; -import { Alert, Form, FormControl } from "react-bootstrap"; +import { Alert, Form, FormControl, Modal, Spinner } from "react-bootstrap"; import { History } from "history"; -import { Modal, Spinner } from "react-bootstrap"; - import { Global } from "../../store/global/types"; import { Entry } from "../../store/entries/types"; import { Account } from "../../store/accounts/types"; @@ -18,7 +16,7 @@ import ProfileLink from "../profile-link/index"; import Tooltip from "../tooltip"; import Pagination from "../pagination"; -import { Vote, getActiveVotes } from "../../api/hive"; +import { getActiveVotes, Vote } from "../../api/hive"; import parseAsset from "../../helper/parse-asset"; import parseDate, { dateToFormatted, dateToFullRelative } from "../../helper/parse-date"; @@ -159,7 +157,7 @@ export class EntryVotesDetail extends BaseComponent { }) .slice(start, end); const totalVotes = - (this.props.entry.active_votes && this.props.entry.active_votes.length) || + (this.props.entry.active_votes && this.props.entry.active_votes?.length) || this.props.entry.total_votes || 0; @@ -263,7 +261,12 @@ export class EntryVotes extends Component { }; componentDidUpdate(prevProps: Readonly) { - if (prevProps.entry?.active_votes?.length !== this.props.entry?.active_votes?.length) { + const hasDifferentVotes = + prevProps.entry?.active_votes?.length !== this.props.entry?.active_votes?.length; + const hasCurrentUserVote = this.props.entry?.active_votes?.find( + ({ voter }) => voter === this.props.activeUser?.username + ); + if (hasCurrentUserVote && hasDifferentVotes) { this.setState({ vote: true }); } } @@ -284,7 +287,7 @@ export class EntryVotes extends Component { render() { const { entry } = this.props; const { visible, searchText, searchTextDisabled, vote } = this.state; - const totalVotes = (entry.active_votes && entry.active_votes.length) || entry.total_votes || 0; + const totalVotes = (entry.active_votes && entry.active_votes?.length) || entry.total_votes || 0; const { voted } = this.isVoted(); let cls = _c(`heart-icon ${voted ? "voted" : ""} ${vote ? "vote-done" : ""} `); diff --git a/src/common/components/navbar/index.tsx b/src/common/components/navbar/index.tsx index e778c821fe6..b16c3cb2a93 100644 --- a/src/common/components/navbar/index.tsx +++ b/src/common/components/navbar/index.tsx @@ -9,7 +9,7 @@ import SwitchLang from "../switch-lang"; import ToolTip from "../tooltip"; import Search from "../search"; import Login from "../login"; -import UserNav from "../user-nav"; +import { UserNav } from "../user-nav"; import UserNotifications from "../notifications"; import Gallery from "../gallery"; import Drafts from "../drafts"; diff --git a/src/common/components/user-nav/index.spec.tsx b/src/common/components/user-nav/index.spec.tsx index 3a82e149c18..4c2860423b9 100644 --- a/src/common/components/user-nav/index.spec.tsx +++ b/src/common/components/user-nav/index.spec.tsx @@ -3,15 +3,14 @@ import { StaticRouter } from "react-router-dom"; import { createBrowserHistory, createLocation } from "history"; -import UserNav from "./index"; -import renderer from "react-test-renderer"; +import { UserNav } from "./index"; import { - notificationsInstance1, - UiInstance, - globalInstance, + dynamicPropsIntance1, fullAccountInstance, - dynamicPropsIntance1 + globalInstance, + notificationsInstance1, + UiInstance } from "../../helper/test-helper"; import { withStore } from "../../tests/with-store"; diff --git a/src/common/components/user-nav/index.tsx b/src/common/components/user-nav/index.tsx index 3b54616435d..dac40c6275c 100644 --- a/src/common/components/user-nav/index.tsx +++ b/src/common/components/user-nav/index.tsx @@ -1,193 +1,92 @@ -import React, { Component } from "react"; -import { History, Location } from "history"; -import { Link } from "react-router-dom"; - -import { Global } from "../../store/global/types"; -import { User } from "../../store/users/types"; -import { Account } from "../../store/accounts/types"; -import { ActiveUser } from "../../store/active-user/types"; -import { ToggleType, UI } from "../../store/ui/types"; -import { NotificationFilter, Notifications } from "../../store/notifications/types"; -import { DynamicProps } from "../../store/dynamic-props/types"; - +import React, { useState } from "react"; +import { _t } from "../../i18n"; +import { useMappedStore } from "../../store/use-mapped-store"; +import { useLocation } from "react-router"; +import "./_index.scss"; +import { bellOffSvg, bellSvg, chevronUpSvg, messangerSvg, rocketSvg } from "../../img/svg"; +import { downVotingPower, votingPower } from "../../api/hive"; +import { WalletBadge } from "./wallet-badge"; import ToolTip from "../tooltip"; -import UserAvatar from "../user-avatar"; import DropDown from "../dropdown"; import UserNotifications from "../notifications"; +import { PurchaseQrDialog } from "../purchase-qr"; +import { History } from "history"; +import UserAvatar from "../user-avatar"; import Gallery from "../gallery"; import Drafts from "../drafts"; import Bookmarks from "../bookmarks"; import Schedules from "../schedules"; import Fragments from "../fragments"; +import { Link } from "react-router-dom"; -import { _t } from "../../i18n"; - -import HiveWallet from "../../helper/hive-wallet"; - -import { - creditCardSvg, - gifCardSvg, - bellSvg, - bellOffSvg, - chevronUpSvg, - rocketSvg, - messangerSvg -} from "../../img/svg"; - -import { votingPower, downVotingPower } from "../../api/hive"; -import { - updateNotificationsSettings, - setNotificationsSettingsItem -} from "../../store/notifications"; -import { PurchaseQrDialog } from "../purchase-qr"; -import { useMappedStore } from "../../store/use-mapped-store"; -import { useLocation } from "react-router"; -import "./_index.scss"; - -export class WalletBadge extends Component<{ - activeUser: ActiveUser; - dynamicProps: DynamicProps; - icon?: JSX.Element; -}> { - render() { - const { activeUser, dynamicProps } = this.props; - - let hasUnclaimedRewards = false; - const { data: account } = activeUser; - - if (account.__loaded) { - hasUnclaimedRewards = new HiveWallet(account, dynamicProps).hasUnclaimedRewards; - } - - return ( - <> - - - {hasUnclaimedRewards && } - {this.props.icon ?? creditCardSvg} - - - - ); - } -} - -class PointsBadge extends Component<{ activeUser: ActiveUser }> { - render() { - const { activeUser } = this.props; - - let hasUnclaimedPoints = activeUser.points.uPoints !== "0.000"; - - return ( - <> - - - {hasUnclaimedPoints && } - {gifCardSvg} - - - - ); - } -} +export * from "./wallet-badge"; interface Props { - global: Global; - dynamicProps: DynamicProps; history: History; - location: Location; - users: User[]; - ui: UI; - activeUser: ActiveUser; - notifications: Notifications; - setActiveUser: (username: string | null) => void; - updateActiveUser: (data?: Account) => void; - deleteUser: (username: string) => void; - addAccount: (data: Account) => void; - fetchNotifications: (since: string | null) => void; - fetchUnreadNotificationCount: () => void; - setNotificationsFilter: (filter: NotificationFilter | null) => void; - markNotifications: (id: string | null) => void; - toggleUIProp: (what: ToggleType) => void; - muteNotifications: () => void; - unMuteNotifications: () => void; - updateNotificationsSettings: typeof updateNotificationsSettings; - setNotificationsSettingsItem: typeof setNotificationsSettingsItem; -} - -interface State { - gallery: boolean; - drafts: boolean; - bookmarks: boolean; - schedules: boolean; - fragments: boolean; - showPurchaseDialog: boolean; + icon?: JSX.Element; } -class UserNav extends Component { - state: State = { - gallery: false, - drafts: false, - bookmarks: false, - schedules: false, - fragments: false, - showPurchaseDialog: false - }; - - toggleLogin = () => { - const { toggleUIProp } = this.props; - toggleUIProp("login"); - }; - - toggleDrafts = () => { - const { drafts } = this.state; - this.setState({ drafts: !drafts }); - }; - - toggleGallery = () => { - const { gallery } = this.state; - this.setState({ gallery: !gallery }); - }; - - toggleBookmarks = () => { - const { bookmarks } = this.state; - this.setState({ bookmarks: !bookmarks }); - }; - - toggleSchedules = () => { - const { schedules } = this.state; - this.setState({ schedules: !schedules }); - }; - - toggleFragments = () => { - const { fragments } = this.state; - this.setState({ fragments: !fragments }); - }; - - toggleNotifications = () => { - const { toggleUIProp } = this.props; - toggleUIProp("notifications"); - }; - - goToSettings = () => { - const { activeUser, history } = this.props; - history.push(`/@${activeUser.username}/settings`); - }; - - render() { - const { gallery, drafts, bookmarks, schedules, fragments } = this.state; - const { activeUser, ui, notifications, global, dynamicProps } = this.props; - const { unread } = notifications; +export const UserNav = ({ history, icon }: Props) => { + const location = useLocation(); + const { global, ui, activeUser, notifications, setActiveUser, toggleUIProp } = useMappedStore(); + + const [gallery, setGallery] = useState(false); + const [drafts, setDrafts] = useState(false); + const [bookmarks, setBookmarks] = useState(false); + const [schedules, setSchedules] = useState(false); + const [fragments, setFragments] = useState(false); + const [showPurchaseDialog, setShowPurchaseDialog] = useState(false); + + const dropDownItems = [ + { + label: _t("user-nav.profile"), + href: `/@${activeUser?.username}` + }, + ...(global.usePrivate + ? [ + { + label: _t("user-nav.drafts"), + onClick: () => setDrafts(!drafts) + }, + { + label: _t("user-nav.gallery"), + onClick: () => setGallery(!gallery) + }, + { + label: _t("user-nav.bookmarks"), + onClick: () => setBookmarks(!bookmarks) + }, + { + label: _t("user-nav.schedules"), + onClick: () => setSchedules(!schedules) + }, + { + label: _t("user-nav.fragments"), + onClick: () => setFragments(!fragments) + } + ] + : []), + { + label: _t("user-nav.settings"), + onClick: () => history.push(`/@${activeUser?.username}/settings`) + }, + { + label: _t("g.login-as"), + onClick: () => toggleUIProp("login") + }, + { + label: _t("user-nav.logout"), + onClick: () => { + setActiveUser(null); + } + } + ]; - const preDropDownElem = activeUser.data.__loaded ? ( + const dropDownConfig = { + history: history, + label: , + items: dropDownItems, + preElem: activeUser?.data.__loaded ? (
{_t("user-nav.vote-power")}
@@ -203,160 +102,55 @@ class UserNav extends Component {
- ) : undefined; - - const dropDownItems = [ - { - label: _t("user-nav.profile"), - href: `/@${activeUser.username}` - }, - ...(global.usePrivate - ? [ - { - label: _t("user-nav.drafts"), - onClick: this.toggleDrafts - }, - { - label: _t("user-nav.gallery"), - onClick: this.toggleGallery - }, - { - label: _t("user-nav.bookmarks"), - onClick: this.toggleBookmarks - }, - { - label: _t("user-nav.schedules"), - onClick: this.toggleSchedules - }, - { - label: _t("user-nav.fragments"), - onClick: this.toggleFragments - } - ] - : []), - { - label: _t("user-nav.settings"), - onClick: this.goToSettings - }, - { - label: _t("g.login-as"), - onClick: this.toggleLogin - }, - { - label: _t("user-nav.logout"), - onClick: () => { - const { setActiveUser } = this.props; - setActiveUser(null); - } - } - ]; - - const dropDownConfig = { - history: this.props.history, - label: , - items: dropDownItems, - preElem: preDropDownElem - }; + ) : undefined + }; - return ( - <> -
- {global.usePrivate && ( -
this.setState({ showPurchaseDialog: true })} - className="user-points cursor-pointer" - > - {rocketSvg} -
- )} - + return ( + <> +
+ {global.usePrivate && ( +
setShowPurchaseDialog(true)} className="user-points cursor-pointer"> + {rocketSvg} +
+ )} + - {global.usePrivate && ( + {global.usePrivate ? ( + <> - - {unread > 0 && ( + toggleUIProp("notifications")}> + {notifications.unread > 0 && ( - {unread.toString().length < 3 ? unread : "..."} + {notifications.unread.toString().length < 3 ? notifications.unread : "..."} )} {global.notifications ? bellSvg : bellOffSvg} - )} - {global.usePrivate && ( {messangerSvg} - )} - - -
- {ui.notifications && } - {gallery && } - {drafts && } - {bookmarks && } - {schedules && } - {fragments && } - this.setState({ showPurchaseDialog: v })} - activeUser={activeUser} - location={this.props.location} - /> - - ); - } -} + + ) : ( + <> + )} -export default ({ history }: Pick) => { - const { - global, - dynamicProps, - users, - ui, - activeUser, - notifications, - setActiveUser, - updateActiveUser, - deleteUser, - addAccount, - fetchNotifications, - fetchUnreadNotificationCount, - setNotificationsFilter, - markNotifications, - toggleUIProp, - muteNotifications, - unMuteNotifications, - updateNotificationsSettings, - setNotificationsSettingsItem - } = useMappedStore(); - const location = useLocation(); - - return ( - + +
+ {ui.notifications && } + {gallery && setGallery(false)} />} + {drafts && setDrafts(false)} />} + {bookmarks && setBookmarks(false)} />} + {schedules && setSchedules(false)} />} + {fragments && setFragments(false)} />} + + ); }; diff --git a/src/common/components/user-nav/wallet-badge.tsx b/src/common/components/user-nav/wallet-badge.tsx new file mode 100644 index 00000000000..39adaa0b9c6 --- /dev/null +++ b/src/common/components/user-nav/wallet-badge.tsx @@ -0,0 +1,33 @@ +import React, { ReactNode, useEffect, useState } from "react"; +import { Link } from "react-router-dom"; +import HiveWallet from "../../helper/hive-wallet"; +import { _t } from "../../i18n"; +import { creditCardSvg } from "../../img/svg"; +import ToolTip from "../tooltip"; +import { useMappedStore } from "../../store/use-mapped-store"; + +export const WalletBadge = ({ icon }: { icon: ReactNode }) => { + const { activeUser, dynamicProps } = useMappedStore(); + + const [hasUnclaimedRewards, setHasUnclaimedRewards] = useState(false); + + useEffect(() => { + if (activeUser?.data?.__loaded) { + setHasUnclaimedRewards(new HiveWallet(activeUser.data, dynamicProps).hasUnclaimedRewards); + } + }, [activeUser]); + return ( + <> + + + {hasUnclaimedRewards && } + {icon ?? creditCardSvg} + + + + ); +}; diff --git a/src/common/components/video-gallery/index.scss b/src/common/components/video-gallery/index.scss index c6360b557d8..7c2fc534d80 100644 --- a/src/common/components/video-gallery/index.scss +++ b/src/common/components/video-gallery/index.scss @@ -131,13 +131,27 @@ } } +.video-center { + text-align: center; + width: auto; +} + +.video-info { + padding: 0.5rem; + @include themify(night) { + color: $gray-500; + } + @include themify(day) { + color: $gray-600; + } +} + .video-status-picker { display: flex; justify-content: space-between; align-items: center; - margin-bottom: 1rem; border-bottom: 1px solid var(--border-color); - padding: 1rem; + padding: 0 1rem; font-size: 0.875rem; .custom-dropdown .dropdown-btn .label { diff --git a/src/common/components/video-gallery/index.tsx b/src/common/components/video-gallery/index.tsx index 1806700b269..d857eaec775 100644 --- a/src/common/components/video-gallery/index.tsx +++ b/src/common/components/video-gallery/index.tsx @@ -66,7 +66,7 @@ const VideoGallery = ({ {!preFilter && !isEditing ? (
{isFetching && } + {!isFetching && items?.length != 0 && ( +
{_t("video-gallery.video-info")}
+ )}
{items?.map((item) => ( ))} - {!isFetching && items?.length === 0 && ( -
{_t("g.empty-list")}
- )}
+ {!isFetching && items?.length === 0 && ( +
{_t("g.empty-list")}
+ )}
diff --git a/src/common/components/video-gallery/video-gallery-item.tsx b/src/common/components/video-gallery/video-gallery-item.tsx index ca44f5f94a3..c4d8324689f 100644 --- a/src/common/components/video-gallery/video-gallery-item.tsx +++ b/src/common/components/video-gallery/video-gallery-item.tsx @@ -4,6 +4,8 @@ import { dateToFullRelative } from "../../helper/parse-date"; import React, { useEffect, useState } from "react"; import { ThreeSpeakVideo, useThreeSpeakVideo } from "../../api/threespeak"; import { Button } from "react-bootstrap"; +import { proxifyImageSrc } from "@ecency/render-helper"; +import { useMappedStore } from "../../store/use-mapped-store"; interface videoProps { status: string; @@ -30,6 +32,7 @@ export function VideoGalleryItem({ setShowGallery, setVideoMetadata }: Props) { + const { global } = useMappedStore(); const { data } = useThreeSpeakVideo("all"); const [showMoreInfo, setShowMoreInfo] = useState(false); @@ -52,7 +55,7 @@ export function VideoGalleryItem({ const embeddVideo = (video: videoProps) => { const speakFile = `[![](${video.thumbUrl})](${speakUrl}${video.owner}/${video.permlink})`; - const element = `
${speakFile}[Download](${video.filename.replace( + const element = `
${speakFile}[Source](${video.filename.replace( "ipfs://", "https://ipfs-3speak.b-cdn.net/ipfs/" )})
`; @@ -125,7 +128,10 @@ export function VideoGalleryItem({ return (
- +
diff --git a/src/common/components/video-upload-threespeak/index.scss b/src/common/components/video-upload-threespeak/index.scss index dc1def00a9c..989520fc46f 100644 --- a/src/common/components/video-upload-threespeak/index.scss +++ b/src/common/components/video-upload-threespeak/index.scss @@ -16,7 +16,7 @@ @include themify(night) { background-color: lighten($dark, 6); } - + } } @@ -88,11 +88,81 @@ label { .three-speak-video-uploading { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 1rem; - @include media-breakpoint-down(xs) { - grid-template-columns: 1fr; + .video-source { + display: flex; + gap: 1rem; + + > div { + width: 100%; + } + } +} + +.video-upload-recorder { + position: relative; + + .reset-btn { + position: absolute; + top: -2.65rem; + right: 0; + } + + .actions { + position: absolute; + bottom: 1rem; + left: 0; + right: 0; + z-index: 9; + display: grid; + grid-template-columns: 1fr 1fr 1fr; + justify-content: center; + + div { + display: flex; + align-items: center; + justify-content: center; + } + + .switch-camera { + opacity: 0.75; + cursor: pointer; + color: $white; + + &:hover { + opacity: 1; + } + } + + .record-btn { + color: $danger; + border: 0.25rem solid $white; + border-radius: 50%; + cursor: pointer; + + &:hover { + border-color: var(--border-color); + } + } + } + + video { + width: 100%; + border-radius: 1rem; + } + + .no-permission { + width: 100%; + height: 300px; + border-radius: 1rem; + background-color: var(--border-color); + display: flex; + align-items: center; + justify-content: center; + + p { + font-size: 1.125rem; + font-weight: bold; + } } } \ No newline at end of file diff --git a/src/common/components/video-upload-threespeak/index.tsx b/src/common/components/video-upload-threespeak/index.tsx index bb9e95e0e4e..1e3f0b99f2d 100644 --- a/src/common/components/video-upload-threespeak/index.tsx +++ b/src/common/components/video-upload-threespeak/index.tsx @@ -6,6 +6,9 @@ import "./index.scss"; import { VideoUploadItem } from "./video-upload-item"; import { createFile } from "../../util/create-file"; import { useMappedStore } from "../../store/use-mapped-store"; +import { recordVideoSvg } from "../../img/svg"; +import { VideoUploadRecorder } from "./video-upload-recorder"; +import useMountedState from "react-use/lib/useMountedState"; const DEFAULT_THUMBNAIL = require("./assets/thumbnail-play.jpg"); @@ -42,8 +45,11 @@ export const VideoUpload = (props: Props & React.HTMLAttributes) const [videoUrl, setVideoUrl] = useState(""); const [thumbUrl, setThumbUrl] = useState(""); const [duration, setDuration] = useState(""); + const [showRecorder, setShowRecorder] = useState(false); - const canUpload = videoUrl && videoPercentage === 100; + const canUpload = videoUrl; + + const isMounted = useMountedState(); // Reset on dialog hide useEffect(() => { @@ -60,6 +66,7 @@ export const VideoUpload = (props: Props & React.HTMLAttributes) setStep("upload"); setVideoPercentage(0); setThumbnailPercentage(0); + setShowRecorder(false); } }, [props.show]); @@ -108,19 +115,44 @@ export const VideoUpload = (props: Props & React.HTMLAttributes) const uploadVideoModal = (
-
- +
+

Video source

+ {showRecorder ? ( + setShowRecorder(false)} + /> + ) : ( +
+ {isMounted() && !selectedFile && "MediaRecorder" in window ? ( +
setShowRecorder(true)} + > + {recordVideoSvg} + {_t("video-upload.record-video")} +
+ ) : ( + <> + )} + +
+ )} +

Thumbnail

diff --git a/src/common/components/video-upload-threespeak/utils/index.ts b/src/common/components/video-upload-threespeak/utils/index.ts new file mode 100644 index 00000000000..b990ef0b9b8 --- /dev/null +++ b/src/common/components/video-upload-threespeak/utils/index.ts @@ -0,0 +1 @@ +export * from "./use-get-camera-list"; diff --git a/src/common/components/video-upload-threespeak/utils/use-get-camera-list.ts b/src/common/components/video-upload-threespeak/utils/use-get-camera-list.ts new file mode 100644 index 00000000000..912a0fdd9cf --- /dev/null +++ b/src/common/components/video-upload-threespeak/utils/use-get-camera-list.ts @@ -0,0 +1,18 @@ +import { useState } from "react"; +import useMount from "react-use/lib/useMount"; + +/** + * Retrieves a list of available cameras. + * + * @return {MediaDeviceInfo[]} The list of available cameras. + */ +export function useGetCameraList() { + const [list, setList] = useState([]); + + useMount(async () => { + const devices = await navigator.mediaDevices.enumerateDevices(); + setList(devices.filter(({ kind }) => kind === "videoinput")); + }); + + return list; +} diff --git a/src/common/components/video-upload-threespeak/video-upload-item.tsx b/src/common/components/video-upload-threespeak/video-upload-item.tsx index 37ecc896dcb..90db8f82564 100644 --- a/src/common/components/video-upload-threespeak/video-upload-item.tsx +++ b/src/common/components/video-upload-threespeak/video-upload-item.tsx @@ -5,7 +5,7 @@ import { ProgressBar } from "react-bootstrap"; interface Props { onFileChange: (event: React.ChangeEvent) => void; type: "video" | "thumbnail"; - accept: "video/*" | "image/*"; + accept: string; label: string; completed: number; } diff --git a/src/common/components/video-upload-threespeak/video-upload-recorder-actions.tsx b/src/common/components/video-upload-threespeak/video-upload-recorder-actions.tsx new file mode 100644 index 00000000000..f558e0cfbb9 --- /dev/null +++ b/src/common/components/video-upload-threespeak/video-upload-recorder-actions.tsx @@ -0,0 +1,66 @@ +import { circleSvg, rectSvg, switchCameraSvg } from "../../img/svg"; +import React, { useState } from "react"; +import { useGetCameraList } from "./utils"; + +interface Props { + noPermission: boolean; + mediaRecorder?: MediaRecorder; + onCameraSelect: (camera: MediaDeviceInfo) => void; +} + +export function VideoUploadRecorderActions({ noPermission, mediaRecorder, onCameraSelect }: Props) { + const cameraList = useGetCameraList(); + + const [currentCameraIndex, setCurrentCameraIndex] = useState(0); + const [recordStarted, setRecordStarted] = useState(false); + + const getNextCameraIndex = (index: number) => (index + 1) % cameraList.length; + + return ( +
+
+ {!recordStarted && cameraList.length > 1 ? ( +
{ + const nextCameraIndex = getNextCameraIndex(currentCameraIndex); + onCameraSelect(cameraList[nextCameraIndex]); + setCurrentCameraIndex(nextCameraIndex); + }} + > + {switchCameraSvg} +
+ ) : ( + <> + )} +
+ +
+ {recordStarted ? ( +
{ + mediaRecorder?.stop(); + setRecordStarted(false); + }} + > + {rectSvg} +
+ ) : ( +
{ + mediaRecorder?.start(); + setRecordStarted(true); + }} + > + {circleSvg} +
+ )} +
+
+
+ ); +} diff --git a/src/common/components/video-upload-threespeak/video-upload-recorder-no-permission.tsx b/src/common/components/video-upload-threespeak/video-upload-recorder-no-permission.tsx new file mode 100644 index 00000000000..95b907ae2d9 --- /dev/null +++ b/src/common/components/video-upload-threespeak/video-upload-recorder-no-permission.tsx @@ -0,0 +1,10 @@ +import { _t } from "../../i18n"; +import React from "react"; + +export function VideoUploadRecorderNoPermission() { + return ( +
+

{_t("video-upload.no-record-permission")}

+
+ ); +} diff --git a/src/common/components/video-upload-threespeak/video-upload-recorder.tsx b/src/common/components/video-upload-threespeak/video-upload-recorder.tsx new file mode 100644 index 00000000000..a3e9a762ea7 --- /dev/null +++ b/src/common/components/video-upload-threespeak/video-upload-recorder.tsx @@ -0,0 +1,188 @@ +import React, { useEffect, useRef, useState } from "react"; +import useMount from "react-use/lib/useMount"; +import { VideoUploadRecorderActions } from "./video-upload-recorder-actions"; +import { VideoUploadRecorderNoPermission } from "./video-upload-recorder-no-permission"; +import { Button } from "react-bootstrap"; +import { _t } from "../../i18n"; +import { useThreeSpeakVideoUpload } from "../../api/threespeak"; +import { error } from "../feedback"; +import { v4 } from "uuid"; +import { useUnmount } from "react-use"; + +interface Props { + setVideoUrl: (v: string) => void; + setFilevName: (v: string) => void; + setFilevSize: (v: number) => void; + setSelectedFile: (v: string) => void; + onReset: () => void; +} + +export function VideoUploadRecorder({ + setVideoUrl, + setFilevName, + setFilevSize, + onReset, + setSelectedFile +}: Props) { + const [stream, setStream] = useState(); + const [mediaRecorder, setMediaRecorder] = useState(); + const [recordedVideoSrc, setRecordedVideoSrc] = useState(); + const [recordedBlob, setRecordedBlob] = useState(); + const [noPermission, setNoPermission] = useState(false); + const [currentCamera, setCurrentCamera] = useState(); + + const ref = useRef(null); + + const { + mutateAsync: uploadVideo, + completed, + isLoading, + isSuccess + } = useThreeSpeakVideoUpload("video"); + + useMount(() => initStreamSafe()); + + useUnmount(() => { + stream?.getTracks().forEach((track) => track.stop()); + }); + + useEffect(() => { + initStreamSafe(); + }, [currentCamera]); + + useEffect(() => { + if (stream && ref.current) { + // @ts-ignore + ref.current?.srcObject = stream; + } + }, [stream, ref]); + + const initStream = async (mimeType: string) => { + setNoPermission(false); + + try { + const stream = await navigator.mediaDevices.getUserMedia({ + video: currentCamera ? { deviceId: currentCamera.deviceId } : true, + audio: true + }); + const mediaRecorder = new MediaRecorder(stream, { + mimeType + }); + + setMediaRecorder(mediaRecorder); + setStream(stream); + + mediaRecorder.addEventListener("dataavailable", (event) => { + if (event.data.size > 0) { + setRecordedVideoSrc(URL.createObjectURL(event.data)); + setRecordedBlob(event.data); + stream.getTracks().forEach((track) => track.stop()); + } + }); + } catch (e) { + setNoPermission(true); + throw e; + } + }; + + const initStreamSafe = async () => { + try { + await initStream("video/webm"); + } catch (e) { + await initStream("video/mp4"); + } + }; + + return ( +
+ {recordedBlob ? ( + + ) : ( + <> + )} + + {!noPermission && !recordedVideoSrc ? ( + { + stream + ?.getTracks() + .filter(({ kind }) => kind === "video") + .forEach((track) => track.stop()); + + setCurrentCamera(camera); + }} + /> + ) : ( + <> + )} + + {noPermission ? ( + + ) : ( + <> +
+ ); +} diff --git a/src/common/constants/dmca.json b/src/common/constants/dmca.json index 2c33cccbb85..e0fd6cdb409 100644 --- a/src/common/constants/dmca.json +++ b/src/common/constants/dmca.json @@ -1,302 +1,303 @@ [ - "aasdaura/.*", - "ah3p5idsebhn/.*", - "aishacorona/.*", - "arefinishingpros/.*", - "allenk/.*", - "anny08/.*", - "beeryallen/.*", - "billjd/.*", - "binance.support/.*", - "binance.help/.*", - "binanceehelp/.*", - "binancesupport/.*", - "binancesupport1/.*", - "binancesupportt/.*", - "binancetoll/.*", - "bio9kftyptni/.*", - "bitcoinsmarkett/.*", - "blockchaink/.*", - "blockdifind/.*", - "bqtumaeso0zl/.*", - "brbaramille/.*", - "btcbtcgvsagvcsa/.*", - "btccare/.*", - "btcservice/.*", - "btcsupportcare/.*", - "c4bqqi5jzf46/.*", - "callcbsupport/.*", - "callscoinbase/.*", - "carenumber/.*", - "caresuppoert/.*", - "caval/.*", - "cbservice/.*", - "cbsupport/.*", - "cbsupportservice/.*", - "cbsupportnumber/.*", - "cex.iosupport/.*", - "coinbaseeus/.*", - "coinbase.service/.*", - "coinbase-usa/.*", - "coinbasecare/.*", - "coinbascare/.*", - "coinbase.care/.*", - "coinbase.help/.*", - "coinbase.support/.*", - "coinbasecall/.*", - "coinbasecall1/.*", - "coinbasecalls/.*", - "coinbasedesk/.*", - "coinbasefree/.*", - "coinbasegold/.*", - "coinbasehelpline/.*", - "coinbasehelps/.*", - "coinbaselogin/.*", - "coinbaseloginn/.*", - "coinbaseno/.*", - "coinbasepay/.*", - "coinbasephn/.*", - "coinbasereal/.*", - "coinbasertss/.*", - "coinbaseservice/.*", - "coinbaseservicen/.*", - "coinbaseservices/.*", - "coinbasessup/.*", - "coinbasesupp/.*", - "coinbasesupport/.*", - "coinbasesupportc/.*", - "coinbasesupportp/.*", - "coinbasesupports/.*", - "coinbasesupportt/.*", - "coinbasesuprt/.*", - "coinbasetollfree/.*", - "coinbaseuk/.*", - "coinbaseus/.*", - "coinbaseus.com/.*", - "coinbaseusa/.*", - "coinbasewallet/.*", - "coinbashelp/.*", - "coinbassupport/.*", - "coinsbasesup/.*", - "coinbasesuprt/.*", - "coinbasetollfree/.*", - "coinbaseuk/.*", - "coinbbasepro/.*", - "coinbesesupport/.*", - "communicateus/.*", - "communityus/.*", - "costaricht/.*", - "contactmetamask/.*", - "cryptoservice/.*", - "cryptosupports/.*", - "cryptotokens/.*", - "cryptousero1/.*", - "cplahariya72/.*", - "customer.service/.*", - "customerservice/.*", - "customer800/.*", - "customercare/.*", - "customerlive/.*", - "customeronline/.*", - "customerservice/.*", - "customersupport/.*", - "customtoll/.*", - "customerwallet/.*", - "customtoll/.*", - "cxcbnxzcb/.*", - "damiwiy184/.*", - "danincomplete/warning-openledger-and-geneos-scam-alert-fraudulent-activity-detected", - "darkknight11/.*", - "dfdsfd45/.*", - "dialcoinbase/.*", - "djwtu/.*", - "ecency01/.*", - "ecencymaster/.*", - "ecensepop/.*", - "edwardspensor/.*", - "ellamason612/.*", - "ericahonolu/.*", - "faumaulloin/.*", - "ficih425/.*", - "foul1uxqcse6/.*", - "frankkohn/.*", - "geminiusa/.*", - "ggyivaiapyju/.*", - "genegg691/.*", - "ginas1900/.*", - "gunman4466/.*", - "gugytgydvvv/.*", - "harryxosborn/.*", - "hackmon90/.*", - "help.coinbase/.*", - "helpbinancee/.*", - "helpcoinbas/.*", - "helpcoinbase/.*", - "helplinesupport/.*", - "hikccbsc5k/.*", - "iag9479/.*", - "jacksparrowcz/.*", - "jacksparrowzx/.*", - "janiceadams/.*", - "jaibalaj123/.*", - "jakelaw915/.*", - "jallhlcv8/.*", - "jameesspaul/.*", - "jamesniton675/.*", - "janeliz72/.*", - "jayapartha/.*", - "jhagsdh265/.*", - "jimmyshergillxz/.*", - "joannegdunn/.*", - "jonathonsmithsz/.*", - "jonydevitis/.*", - "justinkanwal/.*", - "jundi1443/--r0jhik", - "jundi1/.*", - "kalimkopaaer/.*", - "karolinalowe/.*", - "kclentroaster/.*", - "kirstyxnaylor/.*", - "kissmenotddvf/.*", - "krakensupport/.*", - "leasha/.*", - "ledgernano/.*", - "ledgernanox/.*", - "ledgernanohelp/.*", - "ledgernanoy/.*", - "lindacare87/.*", - "lisachapaul/.*", - "lobstrsupport/.*", - "lobstrusa/.*", - "loginpending/.*", - "lumiwallet/.*", - "lylezmclean/.*", - "margueritec0668/the-possession-of-hannah-grace-2018-720p-1080p-brrip-dvdrip-high-quality", - "mariasmith/.*", - "marimcalister4/.*", - "markalan/.*", - "marquisea/.*", - "marsila/.*", - "metamask.support/.*", - "metamasklive/.*", - "metamaskliveus/.*", - "metamaskloginus/.*", - "metamasksupport/.*", - "metamasksupportu/.*", - "metamaskus/.*", - "melodi/.*", - "mmfuture/.*", - "mondkratzert5445/.*", - "moonpay.wallet/.*", - "moonpaysupport/.*", - "narniatailor/.*", - "nehemiahc/.*", - "nickfurrie/.*", - "nickfuryx/.*", - "npleasent/.*", - "oliviajames7/.*", - "oliver7219jeny/.*", - "omotayoaina/.*", - "onobel/.*", - "p90usskw36tv/.*", - "paulfjones966/.*", - "perciejacksondxc/.*", - "pesen05/.*", - "phbgg/.*", - "pintowallet/.*", - "poilebraubragra/.*", - "pramodranis/.*", - "psejsvtk9547/.*", - "qctaviwx/.*", - "rarec44537/.*", - "rajniraraa/.*", - "rcxrvaarejthw/.*", - "recoversupport/.*", - "rewqas568/.*", - "ronkasp/.*", - "samuel2000/.*", - "samuel2004/.*", - "samuel2005/.*", - "samuel2006/.*", - "samuel2007/.*", - "samuel2008/.*", - "samuel2009/.*", - "samuel2011/.*", - "samuel2012/.*", - "samuel2015/.*", - "samuel2016/.*", - "samuel2018/.*", - "samuel210/.*", - "samueldoctor2019/.*", - "service.customer/.*", - "servicecoinbase/.*", - "servicehelp/.*", - "sdeborah824/.*", - "shaunxcannon/.*", - "shahzifpc/nitro-pro-crack-keygen-torrent", - "shepardbernard2/.*", - "sofiazwayne/.*", - "starct053/.*", - "stephenyjohnsn/.*", - "supporthelp/coinbase-customer-service-number-1", - "support.coinbase/.*", - "support.gemini/.*", - "support.metamask/.*", - "support.wallet/.*", - "supportcoinbase/.*", - "supportcare/.*", - "support.tollfree/.*", - "supportdodge/.*", - "sscomm/.*", - "supporthelp/.*", - "support.binance/.*", - "supportnumber/.*", - "supportnumberbit/.*", - "supportnumberus/.*", - "supportrefund/.*", - "sylvestestalom/.*", - "synyppzvv3ub/.*", - "systembuster/.*", - "technicalusa/.*", - "techonoal/.*", - "tenda/.*", - "techsupport1/.*", - "tedsx7347848/.*", - "terimaachuma/.*", - "tklbidramu/.*", - "tollfree/.*", - "tollfrenumber/.*", - "trustcrypto/.*", - "trusttwallet/.*", - "trustwallett/.*", - "trustwalletuk/.*", - "trustwalletus/.*", - "ttja0lfaccoz/.*", - "tyuq3wg/.*", - "tzhpof/.*", - "uniswaphelp/.*", - "uniswapsupport/.*", - "usacoinbase/.*", - "usacbservice/.*", - "uscoinbase.care/.*", - "usercoinbaseapp/.*", - "ustrustwallet/.*", - "venomawn/.*", - "veronicaxwilson/.*", - "veudufideprei/.*", - "vfdbvfgbvdfgdf/.*", - "vnpst9lynksk/.*", - "vojsjh79vzqu/.*", - "vzo6b8fs6ifu/.*", - "walletcrypto/.*", - "wallet.trust/.*", - "walletuscoin/.*", - "walletusa/.*", - "wallet.usa/.*", - "walletusa/.*", - "yedanad275/.*", - "zakiartist85y/.*", - "zhgsildfh/.*", - "zkbvu0tcv2la/.*", - "xcoinbbaseproxx/.*" + "@aasdaura/.*", + "@ah3p5idsebhn/.*", + "@aishacorona/.*", + "@arefinishingpros/.*", + "@allenk/.*", + "@anny08/.*", + "@beeryallen/.*", + "@billjd/.*", + "@binance.support/.*", + "@binance.help/.*", + "@binanceehelp/.*", + "@binancesupport/.*", + "@binancesupport1/.*", + "@binancesupportt/.*", + "@binancetoll/.*", + "@bio9kftyptni/.*", + "@bitcoinsmarkett/.*", + "@blockchaink/.*", + "@blockdifind/.*", + "@bqtumaeso0zl/.*", + "@brbaramille/.*", + "@btcbtcgvsagvcsa/.*", + "@btccare/.*", + "@btcservice/.*", + "@btcsupportcare/.*", + "@c4bqqi5jzf46/.*", + "@callcbsupport/.*", + "@callscoinbase/.*", + "@carenumber/.*", + "@caresuppoert/.*", + "@caval/.*", + "@cbservice/.*", + "@cbsupport/.*", + "@cbsupportservice/.*", + "@cbsupportnumber/.*", + "@cex.iosupport/.*", + "@coinbaseeus/.*", + "@coinbase.service/.*", + "@coinbase-usa/.*", + "@coinbasecare/.*", + "@coinbascare/.*", + "@coinbase.care/.*", + "@coinbase.help/.*", + "@coinbase.support/.*", + "@coinbasecall/.*", + "@coinbasecall1/.*", + "@coinbasecalls/.*", + "@coinbasedesk/.*", + "@coinbasefree/.*", + "@coinbasegold/.*", + "@coinbasehelpline/.*", + "@coinbasehelps/.*", + "@coinbaselogin/.*", + "@coinbaseloginn/.*", + "@coinbaseno/.*", + "@coinbasepay/.*", + "@coinbasephn/.*", + "@coinbasereal/.*", + "@coinbasertss/.*", + "@coinbaseservice/.*", + "@coinbaseservicen/.*", + "@coinbaseservices/.*", + "@coinbasessup/.*", + "@coinbasesupp/.*", + "@coinbasesupport/.*", + "@coinbasesupportc/.*", + "@coinbasesupportp/.*", + "@coinbasesupports/.*", + "@coinbasesupportt/.*", + "@coinbasesuprt/.*", + "@coinbasetollfree/.*", + "@coinbaseuk/.*", + "@coinbaseus/.*", + "@coinbaseus.com/.*", + "@coinbaseusa/.*", + "@coinbasewallet/.*", + "@coinbashelp/.*", + "@coinbassupport/.*", + "@coinsbasesup/.*", + "@coinbasesuprt/.*", + "@coinbasetollfree/.*", + "@coinbaseuk/.*", + "@coinbbasepro/.*", + "@coinbesesupport/.*", + "@communicateus/.*", + "@communityus/.*", + "@costaricht/.*", + "@contactmetamask/.*", + "@cryptoservice/.*", + "@cryptosupports/.*", + "@cryptotokens/.*", + "@cryptousero1/.*", + "@cplahariya72/.*", + "@customer.service/.*", + "@customerservice/.*", + "@customer800/.*", + "@customercare/.*", + "@customerlive/.*", + "@customeronline/.*", + "@customerservice/.*", + "@customersupport/.*", + "@customtoll/.*", + "@customerwallet/.*", + "@customtoll/.*", + "@cxcbnxzcb/.*", + "@damiwiy184/.*", + "@danincomplete/warning-openledger-and-geneos-scam-alert-fraudulent-activity-detected", + "@darkknight11/.*", + "@dfdsfd45/.*", + "@dialcoinbase/.*", + "@djwtu/.*", + "@ecency01/.*", + "@ecencymaster/.*", + "@ecensepop/.*", + "@edwardspensor/.*", + "@ellamason612/.*", + "@ericahonolu/.*", + "@faumaulloin/.*", + "@ficih425/.*", + "@foul1uxqcse6/.*", + "@frankkohn/.*", + "@geminiusa/.*", + "@ggyivaiapyju/.*", + "@genegg691/.*", + "@ginas1900/.*", + "@gunman4466/.*", + "@gugytgydvvv/.*", + "@guthrie121/.*", + "@harryxosborn/.*", + "@hackmon90/.*", + "@help.coinbase/.*", + "@helpbinancee/.*", + "@helpcoinbas/.*", + "@helpcoinbase/.*", + "@helplinesupport/.*", + "@hikccbsc5k/.*", + "@iag9479/.*", + "@jacksparrowcz/.*", + "@jacksparrowzx/.*", + "@janiceadams/.*", + "@jaibalaj123/.*", + "@jakelaw915/.*", + "@jallhlcv8/.*", + "@jameesspaul/.*", + "@jamesniton675/.*", + "@janeliz72/.*", + "@jayapartha/.*", + "@jhagsdh265/.*", + "@jimmyshergillxz/.*", + "@joannegdunn/.*", + "@jonathonsmithsz/.*", + "@jonydevitis/.*", + "@justinkanwal/.*", + "@jundi1443/--r0jhik", + "@jundi1/.*", + "@kalimkopaaer/.*", + "@karolinalowe/.*", + "@kclentroaster/.*", + "@kirstyxnaylor/.*", + "@kissmenotddvf/.*", + "@krakensupport/.*", + "@leasha/.*", + "@ledgernano/.*", + "@ledgernanox/.*", + "@ledgernanohelp/.*", + "@ledgernanoy/.*", + "@lindacare87/.*", + "@lisachapaul/.*", + "@lobstrsupport/.*", + "@lobstrusa/.*", + "@loginpending/.*", + "@lumiwallet/.*", + "@lylezmclean/.*", + "@margueritec0668/the-possession-of-hannah-grace-2018-720p-1080p-brrip-dvdrip-high-quality", + "@mariasmith/.*", + "@marimcalister4/.*", + "@markalan/.*", + "@marquisea/.*", + "@marsila/.*", + "@metamask.support/.*", + "@metamasklive/.*", + "@metamaskliveus/.*", + "@metamaskloginus/.*", + "@metamasksupport/.*", + "@metamasksupportu/.*", + "@metamaskus/.*", + "@melodi/.*", + "@mmfuture/.*", + "@mondkratzert5445/.*", + "@moonpay.wallet/.*", + "@moonpaysupport/.*", + "@narniatailor/.*", + "@nehemiahc/.*", + "@nickfurrie/.*", + "@nickfuryx/.*", + "@npleasent/.*", + "@oliviajames7/.*", + "@oliver7219jeny/.*", + "@omotayoaina/.*", + "@onobel/.*", + "@p90usskw36tv/.*", + "@paulfjones966/.*", + "@perciejacksondxc/.*", + "@pesen05/.*", + "@phbgg/.*", + "@pintowallet/.*", + "@poilebraubragra/.*", + "@pramodranis/.*", + "@psejsvtk9547/.*", + "@qctaviwx/.*", + "@rarec44537/.*", + "@rajniraraa/.*", + "@rcxrvaarejthw/.*", + "@recoversupport/.*", + "@rewqas568/.*", + "@ronkasp/.*", + "@samuel2000/.*", + "@samuel2004/.*", + "@samuel2005/.*", + "@samuel2006/.*", + "@samuel2007/.*", + "@samuel2008/.*", + "@samuel2009/.*", + "@samuel2011/.*", + "@samuel2012/.*", + "@samuel2015/.*", + "@samuel2016/.*", + "@samuel2018/.*", + "@samuel210/.*", + "@samueldoctor2019/.*", + "@service.customer/.*", + "@servicecoinbase/.*", + "@servicehelp/.*", + "@sdeborah824/.*", + "@shaunxcannon/.*", + "@shahzifpc/nitro-pro-crack-keygen-torrent", + "@shepardbernard2/.*", + "@sofiazwayne/.*", + "@starct053/.*", + "@stephenyjohnsn/.*", + "@supporthelp/coinbase-customer-service-number-1", + "@support.coinbase/.*", + "@support.gemini/.*", + "@support.metamask/.*", + "@support.wallet/.*", + "@supportcoinbase/.*", + "@supportcare/.*", + "@support.tollfree/.*", + "@supportdodge/.*", + "@sscomm/.*", + "@supporthelp/.*", + "@support.binance/.*", + "@supportnumber/.*", + "@supportnumberbit/.*", + "@supportnumberus/.*", + "@supportrefund/.*", + "@sylvestestalom/.*", + "@synyppzvv3ub/.*", + "@systembuster/.*", + "@technicalusa/.*", + "@techonoal/.*", + "@tenda/.*", + "@techsupport1/.*", + "@tedsx7347848/.*", + "@terimaachuma/.*", + "@tklbidramu/.*", + "@tollfree/.*", + "@tollfrenumber/.*", + "@trustcrypto/.*", + "@trusttwallet/.*", + "@trustwallett/.*", + "@trustwalletuk/.*", + "@trustwalletus/.*", + "@ttja0lfaccoz/.*", + "@tyuq3wg/.*", + "@tzhpof/.*", + "@uniswaphelp/.*", + "@uniswapsupport/.*", + "@usacoinbase/.*", + "@usacbservice/.*", + "@uscoinbase.care/.*", + "@usercoinbaseapp/.*", + "@ustrustwallet/.*", + "@venomawn/.*", + "@veronicaxwilson/.*", + "@veudufideprei/.*", + "@vfdbvfgbvdfgdf/.*", + "@vnpst9lynksk/.*", + "@vojsjh79vzqu/.*", + "@vzo6b8fs6ifu/.*", + "@walletcrypto/.*", + "@wallet.trust/.*", + "@walletuscoin/.*", + "@walletusa/.*", + "@wallet.usa/.*", + "@walletusa/.*", + "@yedanad275/.*", + "@zakiartist85y/.*", + "@zhgsildfh/.*", + "@zkbvu0tcv2la/.*", + "@xcoinbbaseproxx/.*" ] diff --git a/src/common/constants/exchanges.ts b/src/common/constants/exchanges.ts index a6febe64a58..51bf442a3e6 100644 --- a/src/common/constants/exchanges.ts +++ b/src/common/constants/exchanges.ts @@ -13,7 +13,8 @@ const exchangeAccounts = [ "hitbtc-exchange", "poloniex", "upbit-exchange", - "onepagex" + "onepagex", + "bdhivesteem" ]; export default exchangeAccounts; diff --git a/src/common/core/caches/entries-cache.tsx b/src/common/core/caches/entries-cache.tsx index 3ae0f74629d..3bb51b22fb6 100644 --- a/src/common/core/caches/entries-cache.tsx +++ b/src/common/core/caches/entries-cache.tsx @@ -34,7 +34,7 @@ export const EntriesCacheManager = ({ children }: { children: any }) => { const updateCache = (entries: Entry[], skipInvalidation = false) => { entries.forEach((e) => { - if (dmca.some((rx: string) => new RegExp(rx).test(`${e.author}/${e.permlink}`))) { + if (dmca.some((rx: string) => new RegExp(rx).test(`@${e.author}/${e.permlink}`))) { e.body = "This post is not available due to a copyright/fraudulent claim."; e.title = ""; } @@ -199,7 +199,7 @@ export function useEntryCache( for (const k of groupKeys) { entry = entries[k].entries.find((x) => { - if (dmca.some((rx: string) => new RegExp(rx).test(`${x.author}/${x.permlink}`))) { + if (dmca.some((rx: string) => new RegExp(rx).test(`@${x.author}/${x.permlink}`))) { x.body = "This post is not available due to a copyright/fraudulent claim."; x.title = ""; } diff --git a/src/common/core/react-query.ts b/src/common/core/react-query.ts index f3db0fc44c1..de5ac6e4422 100644 --- a/src/common/core/react-query.ts +++ b/src/common/core/react-query.ts @@ -25,5 +25,7 @@ export enum QueryIdentifiers { THREE_SPEAK_VIDEO_LIST = "three-speak-video-list", THREE_SPEAK_VIDEO_LIST_FILTERED = "three-speak-video-list-filtered", DRAFTS = "drafts", - BY_DRAFT_ID = "by-draft-id" + BY_DRAFT_ID = "by-draft-id", + + EMOJI_PICKER = "emoji-picker" } diff --git a/src/common/helper/posting.ts b/src/common/helper/posting.ts index 5845e10b545..6d1963f7130 100644 --- a/src/common/helper/posting.ts +++ b/src/common/helper/posting.ts @@ -2,12 +2,10 @@ import getSlug from "speakingurl"; import { diff_match_patch } from "diff-match-patch"; -import { MetaData, CommentOptions, RewardType } from "../api/operations"; +import { BeneficiaryRoute, CommentOptions, MetaData, RewardType } from "../api/operations"; import isElectron from "../util/is-electron"; -import { BeneficiaryRoute } from "../api/operations"; - const permlinkRnd = () => (Math.random() + 1).toString(16).substring(2); export const createPermlink = (title: string, random: boolean = false): string => { @@ -95,6 +93,7 @@ export const makeCommentOptions = ( ): CommentOptions => { beneficiaries?.forEach((b) => delete b.src); + beneficiaries?.sort(); const opt: CommentOptions = { allow_curation_rewards: true, allow_votes: true, diff --git a/src/common/i18n/locales/ac-ace.json b/src/common/i18n/locales/ac-ace.json index 5184f2496bc..5e817662701 100644 --- a/src/common/i18n/locales/ac-ace.json +++ b/src/common/i18n/locales/ac-ace.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/ar-SA.json b/src/common/i18n/locales/ar-SA.json index b79a86c836f..33dabcf5279 100644 --- a/src/common/i18n/locales/ar-SA.json +++ b/src/common/i18n/locales/ar-SA.json @@ -48,7 +48,7 @@ "muted": "مكتوم", "trail": "درب", "controversial": "مثير للجدل", - "rising": "rising", + "rising": "صاعد", "found": "موجود", "feed": "المستجدات", "blog": "مدوّنات", @@ -58,22 +58,22 @@ "replies": "ردود", "done": "تم", "apply": "تطبيق", - "applying": "Applying...", + "applying": "استخدام...", "warning": "تحذير", "payout": "الدفعة", "learn-more": "تعلم المزيد في", "faq": "الأسئلة الشائعة", - "when": "When", - "price": "Price", + "when": "متى", + "price": "السعر", "more-results": "المزيد من النتائج", "close": "إغلاق", - "loading": "Loading", - "stop": "Stop", - "settings": "Settings", - "confirm": "Confirm", - "username": "Username", - "past-few-days": "the past few days", - "you": "You" + "loading": "تحميل", + "stop": "إيقاف", + "settings": "إعدادات", + "confirm": "تأكيد", + "username": "اسم المستخدم", + "past-few-days": "الأيام القليلة الماضية", + "you": "أنت" }, "confirm": { "title": "هل أنت متأكد؟", @@ -81,22 +81,22 @@ "cancel": "إلغاء" }, "announcements": { - "dismiss": "Dismiss", - "next": "Next", - "later": "Later" + "dismiss": "إهمال", + "next": "التالي", + "later": "لاحقاً" }, "floating-faq": { - "toggle-icon-info": "Click to expand", - "help": "Help", - "welcome": "Welcome to Ecency", - "need-help": "Need Help?", - "search-placeholder": "Search", - "suggestion": "Suggested articles", - "contact": "Contact us", - "submit": "Submit", - "username": "username", - "message": "message", - "no-results": "No results found" + "toggle-icon-info": "انقر للتوسيع", + "help": "المساعدة", + "welcome": "مرحبا بك في Ecency", + "need-help": "تحتاج للمساعدة؟", + "search-placeholder": "البحث", + "suggestion": "المقالات المقترحة", + "contact": "اتصل بنا", + "submit": "أرسل", + "username": "إسم المستخدم", + "message": "رسالة", + "no-results": "لم يتم العثور على نتائج" }, "entry-filter": { "filter-trending": "الشائع", @@ -109,8 +109,8 @@ "filter-feed-friends": "الأصدقاء", "filter-feed-subscriptions": "المجتمعات", "filter-global": "عام", - "filter-community": "My Community", - "filter-controversial": "Controversial", + "filter-community": "مجتمعي", + "filter-controversial": "مثير للجدل", "filter-rising": "صاعد", "filter-global-part1": "يحتوي هذا المستجد على ", "filter-global-part2": " مناقشات على كامل المنصة.", @@ -118,18 +118,18 @@ "filter-global-part4": " مناقشات من أشخاص تتابعهم. يمكنك متابعة المزيد من الأشخاص لملء هذه التغذية.", "filter-global-discover": "إكتشف أشخاص", "filter-global-join-communities": "انضم إلى مجتمعات", - "filter-today": "Today", - "filter-week": "This week", - "filter-month": "This month", - "filter-year": "This year", - "filter-alltime": "All time", - "filter-no-reblog": "Exclude reblog", - "filter-with-reblog": "Include reblog" + "filter-today": "اليوم", + "filter-week": "هذا الأسبوع", + "filter-month": "هذا الشهر", + "filter-year": "هذه السنة", + "filter-alltime": "كل الوقت", + "filter-no-reblog": "استبعاد إعادة التدوين", + "filter-with-reblog": "تضمين إعادة التدوين" }, "navbar": { "discover": "إكتشف", "communities": "المجتمعات", - "decks": "Decks", + "decks": "طوابق", "post": "إنشاء منشور", "day-theme": "الوضع العادي", "night-theme": "الوضع الليلي", @@ -170,7 +170,7 @@ "dark": "داكن", "switch-to": "تبديل", "vote-power": "قوة التصويت:", - "boost": "Boost" + "boost": "تعزيز" }, "intro": { "title": "التطلع إلى العظمة", @@ -185,7 +185,7 @@ "login-info-1": "نحن لا نخزن مفاتيحك الخاصة.", "login-info-2": "مزيد من المعلومات", "error-fields-required": "الرجاء إدخال اسم المستخدم والمفتاح الخاص", - "captcha-check-required": "Please verify if you are a human", + "captcha-check-required": "يرجى التحقق إذا كنت بشري", "error-public-key": "لقد أدخلت مفتاحًا عامًا. مطلوب كلمة المرور/WIF", "error-user-fetch": "تعذر جلب بيانات المستخدم", "error-user-not-found": "المستخدم غير موجود", @@ -198,8 +198,8 @@ "sign-up-text-2": "قم بالتسجيل الآن!" }, "sign-up": { - "header": "Rewarding communities", - "description": "Value Freespeech, Be in Charge, Achieve Financial freedom!", + "header": "مكافأة المجتمعات", + "description": "قدّر حرية التعبير، كن مسؤولاً، حقق الحرية المالية!", "bottom-description": "يتم إنشاء كل حساب على بلوكتشين و أنت فقط من يمتلك التحكم بذلك الحساب. حساب واحد لإدارة كل شيء على البلوكتشين و Ecency.", "learn-more": "تعرف على المزيد من الفوائد في الأسئلة الشائعة", "username": "اختر اسم مستخدم", @@ -212,41 +212,41 @@ "success-2": "اتبع التعليمات لإكمال عملية التسجيل.", "login-text-1": "هل لديك حساب؟", "login-text-2": "تسجيل الدخول", - "free-account": "Free account", - "free-account-desc": "Get a full-fledged Hive account, this method has basic checks to make sure botnet doesn't abuse our signup faucet.", - "register-free": "Register free", - "buy-account": "Buy account", - "buy-account-desc": "Get a full-fledged Hive account with no extra checks and few perks such as:", - "buy-account-li-1": "Instant creation without extra checks and waiting", - "buy-account-li-2": "3x RC compared to normal new accounts", - "buy-account-li-3": "300 Points to help you get started", - "qr-desc": "Scan with Ecency mobile app to continue", - "username-min-length-error": "Length of username should be at least 3 characters", - "username-max-length-error": "Length of username should be no more than 16 characters", - "username-no-ascii-first-letter-error": "First character should be a Letter", - "username-contains-symbols-error": "Should contain Letters, Numbers, Hyphen only", - "username-contains-double-hyphens": "Should not contain double Hyphens", - "username-exists": "This username already registered. Please, try another one", - "referral-invalid": "This referral is invalid. Please, try another one", - "referral-min-length-error": "Length of referral should be at least 3 characters", - "referral-max-length-error": "Length of referral should be no more than 16 characters", + "free-account": "حساب مجاني", + "free-account-desc": "احصل على حساب Hive كامل، تحتوي هذه الطريقة على فحوص أساسية للتأكد من أن الروبوتات لا تسئ استخدام صنبور التسجيل الخاص بنا.", + "register-free": "تسجيل مجاني", + "buy-account": "اشتري حساب", + "buy-account-desc": "احصل على حساب Hive كامل، دون فحوص إضافية إكراميات قليلة مثل:", + "buy-account-li-1": "إنشاء فوري دون عمليات فحص إضافية وانتظار", + "buy-account-li-2": "3 أضعاف رصيد المورد RC مقارنة بالحسابات الجديدة العادية", + "buy-account-li-3": "300 نقطة لمساعدتك على البَدْء", + "qr-desc": "امسح باستخدام تطبيق Ecency للجوال للمتابعة", + "username-min-length-error": "يجب أن يكون اسم المستخدم ٣ حروف على الأقل", + "username-max-length-error": "يجب أن لا يزيد طول اسم المستخدم عن ١٦ حرفاً", + "username-no-ascii-first-letter-error": "يجب أن يكون الرمز الأول حرفاً", + "username-contains-symbols-error": "يجب أن تحتوي على أحرف وأرقام وواصلة فقط", + "username-contains-double-hyphens": "يجب ألا تحتوي على واصلات مزدوجة", + "username-exists": "هذا البريد الإلكتروني موجود بالفعل. الرجاء إدخال بريد إلكتروني آخر", + "referral-invalid": "هذه الإحالة غير صالحة. من فضلك ، جرب واحدة أخرى", + "referral-min-length-error": "يجب أن يكون طول الإحالة ٣ حروف على الأقل", + "referral-max-length-error": "يجب أن لا يزيد طول الإحالة عن ١٦ حرفاً", "email-max-length-error": "يجب ألا يزيد طول البريد الإلكتروني عن 72 حرفًا" }, "onboard": { - "title-active-user": "Onboard a friend", - "title-visitor": "Ask from a friend", - "description-active-user": "You can create account for a friend.", - "description-visitor": "You can ask a friend to create an account for you.", - "creating": "Create for a friend", - "asking": " Ask from a friend", - "creating-description": "Pay with hive or account credit", - "asking-description": "Copy account link and send to friend.", - "username": "Username:", - "email": "Email:", - "referral": "Referral:", - "copy-key": "Make sure you copy or download your keys.", - "download-keys": "Download Keys", - "create-account-hive": "Pay with HIVE", + "title-active-user": "تأهيل صديق", + "title-visitor": "أطلب من صديق", + "description-active-user": "يمكنك إنشاء حساب لصديق.", + "description-visitor": "يمكنك أن تطلب من صديق إنشاء حساب لك.", + "creating": "إنشاء لصديق", + "asking": " أطلب من صديق", + "creating-description": "الدفع باستخدام hive أو رصيد حساب", + "asking-description": "نسخ رابط الحساب وإرساله إلى صديق.", + "username": "اسم المستخدم:", + "email": "البريد الإلكتروني:", + "referral": "الإحالة:", + "copy-key": "تأكد من نسخ أو تنزيل المفاتيح الخاصة بك.", + "download-keys": "تحميل المفاتيح", + "create-account-hive": "الدفع من خلال HIVE", "create-account-credit": "Pay with Account Credits ({{n}})", "confirm-details": "Confirm Details", "share": "(You can send code to a friend) Copy code", @@ -276,7 +276,7 @@ "owner": "Owner key:", "active": "Active key:", "posting": "Posting key:", - "memo": "Memo key:", + "memo": "مفتاح المذكرة:", "owner-use": "Change Password, Change Keys, Recover Account", "active-use": "Transfer Funds, Power up/down, Voting Witnesses/Proposals", "posting-use": "Post, Comment, Vote, Reblog, Follow, Profile updates", @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "أَدخِل", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "التحديث للحصول على الحالة المحدثة", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/az-AZ.json b/src/common/i18n/locales/az-AZ.json index 3d9e04af483..4adaa23b56b 100644 --- a/src/common/i18n/locales/az-AZ.json +++ b/src/common/i18n/locales/az-AZ.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/bg-BG.json b/src/common/i18n/locales/bg-BG.json index 6782988330a..e60729d3438 100644 --- a/src/common/i18n/locales/bg-BG.json +++ b/src/common/i18n/locales/bg-BG.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/bn-BD.json b/src/common/i18n/locales/bn-BD.json index 3d736360bce..5985b2b9f9c 100644 --- a/src/common/i18n/locales/bn-BD.json +++ b/src/common/i18n/locales/bn-BD.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/da-DK.json b/src/common/i18n/locales/da-DK.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/da-DK.json +++ b/src/common/i18n/locales/da-DK.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/de-DE.json b/src/common/i18n/locales/de-DE.json index 08e7da00fe0..ab7c090c7ee 100644 --- a/src/common/i18n/locales/de-DE.json +++ b/src/common/i18n/locales/de-DE.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/el-GR.json b/src/common/i18n/locales/el-GR.json index 6df3d3c2dc0..62a31e3e2e5 100644 --- a/src/common/i18n/locales/el-GR.json +++ b/src/common/i18n/locales/el-GR.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 3e5e2f4a6dd..c81a045eaa7 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1441,6 +1441,7 @@ }, "video-upload": { "choose-video": "Select a video", + "record-video": "Record a video", "choose-thumbnail": "Set a thumbnail(optional)", "continue": "Continue", "encode": "Send for encoding", @@ -1451,7 +1452,12 @@ "preview": "Preview", "to-gallery": "Go to gallery", "congrats": "Congratulations", - "publishing": "Don't refresh this page, wait for few seconds for video to process" + "publishing": "Don't refresh this page, wait for few seconds for video to process", + "no-record-permission": "You don't have permission to record video", + "confirm-and-upload": "Confirm and upload to 3Speak", + "uploading": "Uploading..{{n}}/{{total}}", + "uploaded": "Uploaded!", + "reset": "Reset" }, "video-gallery": { "all": "All", @@ -1472,7 +1478,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/es-ES.json b/src/common/i18n/locales/es-ES.json index a89e9278979..31b2819e5eb 100644 --- a/src/common/i18n/locales/es-ES.json +++ b/src/common/i18n/locales/es-ES.json @@ -1470,7 +1470,8 @@ "info-size": "Tamaño del archivo:", "insert-video": "Insertar", "insert-nsfw": "Insertar como NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refrescar para obtener el estado actualizado", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "Contenido NSFW", diff --git a/src/common/i18n/locales/et-EE.json b/src/common/i18n/locales/et-EE.json index 67e3a0514b9..cd7abeed895 100644 --- a/src/common/i18n/locales/et-EE.json +++ b/src/common/i18n/locales/et-EE.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/fa-IR.json b/src/common/i18n/locales/fa-IR.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/fa-IR.json +++ b/src/common/i18n/locales/fa-IR.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/fi-FI.json b/src/common/i18n/locales/fi-FI.json index 83f6cc410a8..13a398d30bb 100644 --- a/src/common/i18n/locales/fi-FI.json +++ b/src/common/i18n/locales/fi-FI.json @@ -58,7 +58,7 @@ "replies": "vastaukset", "done": "Valmis", "apply": "Käytä", - "applying": "Applying...", + "applying": "Asetetaan...", "warning": "Varoitus", "payout": "Palkkioita", "learn-more": "Lue lisää meidän", @@ -230,7 +230,7 @@ "referral-invalid": "Tämä suositus on virheellinen. Kokeile toista", "referral-min-length-error": "Käyttäjänimen pituuden on oltava vähintään 3 merkkiä", "referral-max-length-error": "Käyttäjänimen pituuden on oltava vähemmän kuin 16 merkkiä", - "email-max-length-error": "Length of email should be no more than 72 characters" + "email-max-length-error": "Sähköpostin pituus saa olla enintään 72 merkkiä" }, "onboard": { "title-active-user": "Onboard a friend", @@ -279,20 +279,20 @@ "memo": "Muistioavain:", "owner-use": "Vaihda Salasana, Vaihda Avaimet, Palauta Tili", "active-use": "Varainsiirrot, Tehonnus/Heikennys, Äänestystodistukset/Ehdotukset", - "posting-use": "Post, Comment, Vote, Reblog, Follow, Profile updates", - "memo-use": "Send/View encrypted messages on transfers", - "copy-tooltip": "Copy password", - "regenerate-password": "Regenerate password", - "copy-password": "Password copied successfully", - "copy-info-message": "Copy Link below and SEND to a friend", - "success-message": "You successfully created the account ", - "sign-title": "Sign transaction with ", - "failed-title": "Failed", - "failed-subtitle": "Transaction failed", - "failed-message": "Failed to create account", - "try-again": "Try again", - "sign-header-title": "Sign transaction", - "sign-sub-title": "Sign your transaction" + "posting-use": "Julkaise, Kommentoi, Äänestä, Jaa, Seuraa, Profiilipäivitykset", + "memo-use": "Lähetä/Näytä salatut viestit siirroissa", + "copy-tooltip": "Kopioi salasana", + "regenerate-password": "Luo uusi salasana", + "copy-password": "Salasana on tallennettu onnistuneesti", + "copy-info-message": "Kopioi linkki alta ja lähetä kaverille", + "success-message": "Tilin luominen onnistui ", + "sign-title": "Allekirjoita tapahtuma ", + "failed-title": "Epäonnistui", + "failed-subtitle": "Tapahtuma epäonnistui", + "failed-message": "Tunnuksen luonti epäonnistui", + "try-again": "Yritä uudelleen", + "sign-header-title": "Allekirjoita tapahtuma", + "sign-sub-title": "Allekirjoita tapahtumasi" }, "trending-tags": { "title": "Aiheet" @@ -467,12 +467,12 @@ "auth-required-title": "Valtuutus vaaditaan", "auth-required-desc": "Sinulla on oltava valtuus nähdä ilmoituksia", "wave": "Aalto", - "simple": "Simple", - "advanced": "Advanced", - "author": "Author", - "choose-one": "Choose one", - "user": "User", - "search-query": "Search query", + "simple": "Yksinkertainen", + "advanced": "Lisäasetukset", + "author": "Julkaisija", + "choose-one": "Valitse yksi", + "user": "Käyttäjä", + "search-query": "Hakulauseke", "columns": { "view-full-post": "Näytä koko julkaisu", "user": "Käyttäjä", @@ -506,9 +506,9 @@ "transfers": "Siirrot", "delegations": "Valtuutukset", "search-query": "Hakulauseke", - "enter-search-query": "Configure a search query and explore all posts", + "enter-search-query": "Kirjoita hakusana ja selaa kaikkia viestejä", "blogs": "Blogit", - "feeds": "Friends Feed", + "feeds": "Ystävien Syöte", "posts": "Julkaisut", "comments": "Kommentit", "all-history": "Koko historia", @@ -536,32 +536,32 @@ "hide-replies": "Piilota vastaukset", "filters": "Suodattimet", "market-swap-form": "Market swap form", - "market": "Market", + "market": "Markkinat", "swap-form": "Swapping form", "msf-description": "Swap your funds quickie with zero latency.", - "faq": "Help center", - "faq-subtitle": "FAQ", - "faq-description": "Find answer to all your questions about Ecency in our help center.", - "no-replies": "No replies yet", - "add-new-reply": "Add new reply", - "no-content": "No content", - "infinite-loading": "Loading 🌊...", - "end-reached": "You've reached end of 🌊.", - "feed-end-reached": "You've reached end of feed.", - "topics-subtitle": "The most popular", - "replied-to": "replied to", - "edit-wave": "Edit", - "updated": "Edited {{n}} ago", - "balance": "Balance", - "whats-new": "What's new", - "whats-new-description": "Keep in track the Ecency updates", - "updates": "Updates", - "release-list": "Release versions", - "current": "Current", - "infinite-failed": "Oops, fetching failed. Sit tight, we are retrying..." + "faq": "Tukikeskus", + "faq-subtitle": "UKK", + "faq-description": "Etsi vastaus kaikkiin kysymyksiisi Ecencystä meidän tukikeskuksesta.", + "no-replies": "Ei vastauksia", + "add-new-reply": "Lisää uusi vastaus", + "no-content": "Ei kohteita", + "infinite-loading": "Ladataan 🌊...", + "end-reached": "Olet saapunut loppuun 🌊.", + "feed-end-reached": "Olet saapunut syötteen loppuun.", + "topics-subtitle": "Suosituin", + "replied-to": "vastasi", + "edit-wave": "Muokkaa", + "updated": "Muokattu {{n}} sitten", + "balance": "Saldo", + "whats-new": "Uutta", + "whats-new-description": "Seuraa Ecencyn päivityksiä", + "updates": "Päivitykset", + "release-list": "Julkaisuversiot", + "current": "Nykyinen", + "infinite-failed": "Hups, nouto epäonnistui. Hetki pieni, yritämme uudelleen..." }, "threads-form": { - "save": "Save", + "save": "Tallenna", "publish": "Julkaise!", "create-regular-post": "Luo pitkä artikkeli", "input-placeholder": "Mitä tapahtuu?", @@ -641,7 +641,7 @@ "start-new-one": "Aloita uusi Vaihto", "signing": "Kirjataan..", "success-swap": "Vaihdettu onnistuneesti!", - "calculating-in": "Calculating in", + "calculating-in": "Lasketaan", "advanced": { "history": "Historia", "stake": "Tilauskanta", @@ -1438,44 +1438,45 @@ "description": "Lyhyt kuvaus" }, "video-upload": { - "choose-video": "Select a video", - "choose-thumbnail": "Set a thumbnail(optional)", - "continue": "Continue", + "choose-video": "Valitse video", + "choose-thumbnail": "Aseta pikkukuva(valinnainen)", + "continue": "Jatka", "encode": "Send for encoding", - "success": "Video succesfully uploaded", - "finished": "Finished", - "upload-video": "Upload video", - "video-gallery": "Video gallery", - "preview": "Preview", - "to-gallery": "Go to gallery", - "congrats": "Congratulations", - "publishing": "Don't refresh this page, wait for few seconds for video to process" + "success": "Video ladattu onnistuneesti", + "finished": "Valmis", + "upload-video": "Lataa video", + "video-gallery": "Videogalleria", + "preview": "Esikatselu", + "to-gallery": "Siirry galleriaan", + "congrats": "Onnittelut", + "publishing": "Älä päivitä tätä sivua, odota muutama sekunti videon käsittelemiseksi" }, "video-gallery": { - "all": "All", - "title": "Video gallery", - "published": "Published", + "all": "Kaikki", + "title": "Videogalleria", + "published": "Julkaistu", "encoding": "Encoding", "encoded": "Encoded", - "failed": "Failed", - "status-encoded": "Ready for posting", + "failed": "Epäonnistui", + "status-encoded": "Valmis lähettämiseen", "status-encoding": "Encoding", - "status-published": "Published", + "status-published": "Julkaistu", "status-failed": "Encoding failed", - "status-deleted": "Deleted", - "info-created": "Created:", - "status": "Status:", - "info-views": "Views:", - "info-duration": "Duration:", - "info-size": "File size:", - "insert-video": "Insert", - "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "status-deleted": "Poistettu", + "info-created": "Luotu:", + "status": "Tila:", + "info-views": "Katselut:", + "info-duration": "Kesto:", + "info-size": "Tiedostokoko:", + "insert-video": "Lisää", + "insert-nsfw": "Lisää NSFW", + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { - "title": "NSFW contents", - "warning": "Please check this if your content is NSFW", - "continue": "Continue" + "title": "NSFW-sisältö", + "warning": "Valitse tämä, jos sisältösi on NSFW", + "continue": "Jatka" }, "tag-selector": { "placeholder-empty": "Tunnisteet. Ensimmäisenä pääkategoria. Lajiteltava.", diff --git a/src/common/i18n/locales/fil-PH.json b/src/common/i18n/locales/fil-PH.json index f18395acc19..54e525caacf 100644 --- a/src/common/i18n/locales/fil-PH.json +++ b/src/common/i18n/locales/fil-PH.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/fr-FR.json b/src/common/i18n/locales/fr-FR.json index 6613796e4bb..c1c4a92225c 100644 --- a/src/common/i18n/locales/fr-FR.json +++ b/src/common/i18n/locales/fr-FR.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/he-IL.json b/src/common/i18n/locales/he-IL.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/he-IL.json +++ b/src/common/i18n/locales/he-IL.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/hi-IN.json b/src/common/i18n/locales/hi-IN.json index eeab0977219..e112ceb1153 100644 --- a/src/common/i18n/locales/hi-IN.json +++ b/src/common/i18n/locales/hi-IN.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/hr-HR.json b/src/common/i18n/locales/hr-HR.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/hr-HR.json +++ b/src/common/i18n/locales/hr-HR.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/hu-HU.json b/src/common/i18n/locales/hu-HU.json index 9638f0fb502..bad0eaf79f8 100644 --- a/src/common/i18n/locales/hu-HU.json +++ b/src/common/i18n/locales/hu-HU.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/id-ID.json b/src/common/i18n/locales/id-ID.json index 24e582c9002..7bb53d45b24 100644 --- a/src/common/i18n/locales/id-ID.json +++ b/src/common/i18n/locales/id-ID.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/it-IT.json b/src/common/i18n/locales/it-IT.json index 37184a0021f..2b26060bc06 100644 --- a/src/common/i18n/locales/it-IT.json +++ b/src/common/i18n/locales/it-IT.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/ja-JP.json b/src/common/i18n/locales/ja-JP.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/ja-JP.json +++ b/src/common/i18n/locales/ja-JP.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/ka-GE.json b/src/common/i18n/locales/ka-GE.json index f4b1d81990d..f6036ad8ffa 100644 --- a/src/common/i18n/locales/ka-GE.json +++ b/src/common/i18n/locales/ka-GE.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/kk-KZ.json b/src/common/i18n/locales/kk-KZ.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/kk-KZ.json +++ b/src/common/i18n/locales/kk-KZ.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/ko-KR.json b/src/common/i18n/locales/ko-KR.json index fdefc82983f..caf47822d6e 100644 --- a/src/common/i18n/locales/ko-KR.json +++ b/src/common/i18n/locales/ko-KR.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/ku-TR.json b/src/common/i18n/locales/ku-TR.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/ku-TR.json +++ b/src/common/i18n/locales/ku-TR.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/ky-KG.json b/src/common/i18n/locales/ky-KG.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/ky-KG.json +++ b/src/common/i18n/locales/ky-KG.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/lt-LT.json b/src/common/i18n/locales/lt-LT.json index 0c39fde67ce..2680d5d4c1c 100644 --- a/src/common/i18n/locales/lt-LT.json +++ b/src/common/i18n/locales/lt-LT.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/lv-LV.json b/src/common/i18n/locales/lv-LV.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/lv-LV.json +++ b/src/common/i18n/locales/lv-LV.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/ms-MY.json b/src/common/i18n/locales/ms-MY.json index 9616e01e3f7..7ae237f7ef0 100644 --- a/src/common/i18n/locales/ms-MY.json +++ b/src/common/i18n/locales/ms-MY.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/ne-NP.json b/src/common/i18n/locales/ne-NP.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/ne-NP.json +++ b/src/common/i18n/locales/ne-NP.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/nl-NL.json b/src/common/i18n/locales/nl-NL.json index f0c7f7bc8f9..b23c785d7a9 100644 --- a/src/common/i18n/locales/nl-NL.json +++ b/src/common/i18n/locales/nl-NL.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/no-NO.json b/src/common/i18n/locales/no-NO.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/no-NO.json +++ b/src/common/i18n/locales/no-NO.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/pa-IN.json b/src/common/i18n/locales/pa-IN.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/pa-IN.json +++ b/src/common/i18n/locales/pa-IN.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/pcm-NG.json b/src/common/i18n/locales/pcm-NG.json index 1e31a4aa9e3..64aae16e217 100644 --- a/src/common/i18n/locales/pcm-NG.json +++ b/src/common/i18n/locales/pcm-NG.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/pl-PL.json b/src/common/i18n/locales/pl-PL.json index b16672f9f76..18521e82a49 100644 --- a/src/common/i18n/locales/pl-PL.json +++ b/src/common/i18n/locales/pl-PL.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/pt-PT.json b/src/common/i18n/locales/pt-PT.json index 78ff3f50ef4..706ab4e12e8 100644 --- a/src/common/i18n/locales/pt-PT.json +++ b/src/common/i18n/locales/pt-PT.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/ro-RO.json b/src/common/i18n/locales/ro-RO.json index 1e6528cc65e..a80d99b589d 100644 --- a/src/common/i18n/locales/ro-RO.json +++ b/src/common/i18n/locales/ro-RO.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/ru-RU.json b/src/common/i18n/locales/ru-RU.json index 33a8bdf0bc4..50564f8abf5 100644 --- a/src/common/i18n/locales/ru-RU.json +++ b/src/common/i18n/locales/ru-RU.json @@ -230,7 +230,7 @@ "referral-invalid": "Этот реферал недействителен. Пожалуйста, попробуйте другого", "referral-min-length-error": "Длина имени реферала должна быть не менее трёх символов", "referral-max-length-error": "Длина имени реферала должна быть не более 16 символов", - "email-max-length-error": "Length of email should be no more than 72 characters" + "email-max-length-error": "Длина письма должна составлять не более 72 символов" }, "onboard": { "title-active-user": "Панель друга", @@ -1468,9 +1468,10 @@ "info-views": "Просмотров:", "info-duration": "Длительность:", "info-size": "Размер файла:", - "insert-video": "Insert", + "insert-video": "Вставить", "insert-nsfw": "Вставить как NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Обновить для получения обновленной информации о статусе", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW контент", diff --git a/src/common/i18n/locales/sk-SK.json b/src/common/i18n/locales/sk-SK.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/sk-SK.json +++ b/src/common/i18n/locales/sk-SK.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/sl-SI.json b/src/common/i18n/locales/sl-SI.json index 764522afda4..eb16b21c8f0 100644 --- a/src/common/i18n/locales/sl-SI.json +++ b/src/common/i18n/locales/sl-SI.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/sr-CS.json b/src/common/i18n/locales/sr-CS.json index 68d34585e2d..6dec7854d52 100644 --- a/src/common/i18n/locales/sr-CS.json +++ b/src/common/i18n/locales/sr-CS.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/sv-SE.json b/src/common/i18n/locales/sv-SE.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/sv-SE.json +++ b/src/common/i18n/locales/sv-SE.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/ta-IN.json b/src/common/i18n/locales/ta-IN.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/ta-IN.json +++ b/src/common/i18n/locales/ta-IN.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/th-TH.json b/src/common/i18n/locales/th-TH.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/th-TH.json +++ b/src/common/i18n/locales/th-TH.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/tr-TR.json b/src/common/i18n/locales/tr-TR.json index 8d2997770c1..6bd9dae6e42 100644 --- a/src/common/i18n/locales/tr-TR.json +++ b/src/common/i18n/locales/tr-TR.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/uk-UA.json b/src/common/i18n/locales/uk-UA.json index 59360c6b429..497a6b58bd2 100644 --- a/src/common/i18n/locales/uk-UA.json +++ b/src/common/i18n/locales/uk-UA.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/ur-IN.json b/src/common/i18n/locales/ur-IN.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/ur-IN.json +++ b/src/common/i18n/locales/ur-IN.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/ur-PK.json b/src/common/i18n/locales/ur-PK.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/ur-PK.json +++ b/src/common/i18n/locales/ur-PK.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/uz-UZ.json b/src/common/i18n/locales/uz-UZ.json index 155dfef928b..4bd162a8f38 100644 --- a/src/common/i18n/locales/uz-UZ.json +++ b/src/common/i18n/locales/uz-UZ.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/vi-VN.json b/src/common/i18n/locales/vi-VN.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/vi-VN.json +++ b/src/common/i18n/locales/vi-VN.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/yo-NG.json b/src/common/i18n/locales/yo-NG.json index 59cd49a8e70..979bfd9f680 100644 --- a/src/common/i18n/locales/yo-NG.json +++ b/src/common/i18n/locales/yo-NG.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/zh-CN.json b/src/common/i18n/locales/zh-CN.json index db058d6ab7d..5f6b032b977 100644 --- a/src/common/i18n/locales/zh-CN.json +++ b/src/common/i18n/locales/zh-CN.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/i18n/locales/zh-TW.json b/src/common/i18n/locales/zh-TW.json index e5eb1495e62..7412314c768 100644 --- a/src/common/i18n/locales/zh-TW.json +++ b/src/common/i18n/locales/zh-TW.json @@ -1470,7 +1470,8 @@ "info-size": "File size:", "insert-video": "Insert", "insert-nsfw": "Insert as NSFW", - "refresh": "Refresh to get updated status" + "refresh": "Refresh to get updated status", + "video-info": "Videos are stored and powered by SPK network, might have some storage and encoding fees." }, "nsfw-content": { "title": "NSFW contents", diff --git a/src/common/img/svg.tsx b/src/common/img/svg.tsx index 87163999531..07c32c55b35 100644 --- a/src/common/img/svg.tsx +++ b/src/common/img/svg.tsx @@ -2238,6 +2238,60 @@ export const uploadSvgV = ( ); + +export const recordVideoSvg = ( + + + + + + +); + +export const circleSvg = ( + + + +); + +export const rectSvg = ( + + + +); + +export const switchCameraSvg = ( + + + + + + + +); + export const chatSvg = ( { setIsComment(!!entry.parent_author); setIsOwnEntry(props.activeUser?.username === entry.author); setIsMuted(!!entry.stats?.gray && entry.net_rshares >= 0 && entry.author_reputation >= 0); - setIsHidden(entry?.net_rshares < -7000000000 && entry?.active_votes.length > 3); // 1000 HP + setIsHidden(entry?.net_rshares < -7000000000 && entry?.active_votes?.length > 3); // 1000 HP setIsLowReputation( !!entry.stats?.gray && entry.net_rshares >= 0 && entry.author_reputation < 0 ); diff --git a/src/common/pages/entry/index.tsx b/src/common/pages/entry/index.tsx index 21de1afe3f0..e28f1b95cd5 100644 --- a/src/common/pages/entry/index.tsx +++ b/src/common/pages/entry/index.tsx @@ -165,7 +165,7 @@ const EntryComponent = (props: Props) => { setIsComment(!!entry.parent_author); setIsOwnEntry(props.activeUser?.username === entry.author); setIsMuted(!!entry.stats?.gray && entry.net_rshares >= 0 && entry.author_reputation >= 0); - setIsHidden(entry?.net_rshares < -7000000000 && entry?.active_votes.length > 3); // 1000 HP + setIsHidden(entry?.net_rshares < -7000000000 && entry?.active_votes?.length > 3); // 1000 HP setIsLowReputation( !!entry.stats?.gray && entry.net_rshares >= 0 && entry.author_reputation < 0 ); diff --git a/src/common/pages/submit/api/publish.ts b/src/common/pages/submit/api/publish.ts index 35735fe0d82..9b6169634a5 100644 --- a/src/common/pages/submit/api/publish.ts +++ b/src/common/pages/submit/api/publish.ts @@ -77,7 +77,7 @@ export function usePublishApi(history: History, onClear: () => void) { c = await bridgeApi.getPostHeader(author, permlink); } catch (e) {} - if (c && c.author && !isThreespeak && speakPermlink === "") { + if (c && c.author) { // create permlink with random suffix permlink = createPermlink(title, true); } @@ -105,6 +105,10 @@ export function usePublishApi(history: History, onClear: () => void) { permlink = speakPermlink; // update speak video with title, body and tags await updateSpeakVideoInfo(activeUser.username, body, videoId, title, tags, isNsfw); + + // set specific metadata for 3speak + jsonMeta.app = "3speak/0.3.0"; + jsonMeta.type = "video"; } const options = makeCommentOptions(author, permlink, reward, beneficiaries); diff --git a/src/common/pages/submit/api/update.ts b/src/common/pages/submit/api/update.ts index da5d13a1ae5..4ea9a100bab 100644 --- a/src/common/pages/submit/api/update.ts +++ b/src/common/pages/submit/api/update.ts @@ -18,14 +18,7 @@ import { EntriesCacheContext } from "../../../core"; export function useUpdateApi(history: History, onClear: () => void) { const { activeUser } = useMappedStore(); - const { - videoId, - is3Speak: isThreespeak, - speakPermlink, - speakAuthor, - isNsfw, - videoMetadata - } = useThreeSpeakManager(); + const { videoMetadata } = useThreeSpeakManager(); const { updateCache } = useContext(EntriesCacheContext); diff --git a/src/common/pages/submit/hooks/body-versioning-manager.tsx b/src/common/pages/submit/hooks/body-versioning-manager.tsx new file mode 100644 index 00000000000..090577d10e1 --- /dev/null +++ b/src/common/pages/submit/hooks/body-versioning-manager.tsx @@ -0,0 +1,115 @@ +import React, { + createContext, + PropsWithChildren, + useContext, + useEffect, + useRef, + useState +} from "react"; +import md5 from "js-md5"; + +const BUFFER_SIZE = 100; + +interface QueueItem { + value: string; + hash: string; + metadata: Record; + status: "restored" | "new"; +} + +export const BodyContext = createContext<{ + body: string; + activeQueueItem?: QueueItem; + setBody: (value: string) => void; + updateMetadata: (value: Record) => void; +}>({ + body: "", + setBody: () => {}, + updateMetadata: () => {} +}); + +export function useBodyVersioningManager(onVersionChange?: (value: QueueItem) => void) { + const context = useContext(BodyContext); + + useEffect(() => { + if (context.activeQueueItem?.status === "restored") { + onVersionChange?.(context.activeQueueItem); + } + }, [context.activeQueueItem]); + + return context; +} + +/** + * Body versioning manager is a queue of all body changes + * + * Each queue item could contain additional metadata object + * Each body updating causes queue updating but if queue contains the same state then it will be restored + * + * Use cases: + * 1. If user added the video which contains metadata information and has removed it wrongly + * then he will do UNDO operation. In that case We may restore video metadata easily from the queue + * + * @note Active user changing causes queue clearing due to security reasons + * @note Active queue item is the last queue item + */ +export function BodyVersioningManager({ children }: PropsWithChildren) { + const [rawBody, setRawBody] = useState(""); + + const [historyQueue, setHistoryQueue] = useState([]); + const activeQueueItem = useRef({ + value: "", + hash: "", + metadata: {}, + status: "new" + }); + + useEffect(() => { + const hash = md5(rawBody); + const existingState = historyQueue.find((x) => x.hash === hash); + + const nextItem: QueueItem = { + value: rawBody, + hash, + metadata: activeQueueItem.current.metadata, + status: "new" + }; + if (existingState) { + nextItem.status = "restored"; + nextItem.metadata = JSON.parse(JSON.stringify(existingState.metadata)); + } + setHistoryQueue([...historyQueue, nextItem]); + activeQueueItem.current = nextItem; + }, [rawBody]); + + useEffect(() => { + if (historyQueue.length > BUFFER_SIZE) { + const temp = [...historyQueue]; + temp.shift(); + setHistoryQueue(temp); + } + }, [historyQueue]); + + const updateMetadata = (metadata: Record) => { + activeQueueItem.current = { + ...activeQueueItem.current, + metadata: { + ...activeQueueItem.current.metadata, + ...metadata + } + }; + }; + + return ( + + {children} + + ); +} diff --git a/src/common/pages/submit/hooks/index.ts b/src/common/pages/submit/hooks/index.ts index 169e1b8deda..aec3f531c08 100644 --- a/src/common/pages/submit/hooks/index.ts +++ b/src/common/pages/submit/hooks/index.ts @@ -5,3 +5,4 @@ export * from "./community-detector"; export * from "./entry-detector"; export * from "./api-draft-detector"; export * from "./three-speak-manager"; +export * from "./body-versioning-manager"; diff --git a/src/common/pages/submit/hooks/three-speak-manager.tsx b/src/common/pages/submit/hooks/three-speak-manager.tsx index 44876b54617..c11eb585edf 100644 --- a/src/common/pages/submit/hooks/three-speak-manager.tsx +++ b/src/common/pages/submit/hooks/three-speak-manager.tsx @@ -1,7 +1,8 @@ -import React, { createContext, ReactNode, useContext, useState } from "react"; +import React, { createContext, ReactNode, useContext, useEffect, useMemo, useState } from "react"; import { ThreeSpeakVideo } from "../../../api/threespeak"; import useLocalStorage from "react-use/lib/useLocalStorage"; import { PREFIX } from "../../../util/local-storage"; +import { useBodyVersioningManager } from "./body-versioning-manager"; export interface ThreeSpeakManagerContext { clear: () => void; @@ -19,9 +20,14 @@ export interface ThreeSpeakManagerContext { setVideoMetadata: (videoMetadata?: ThreeSpeakVideo) => void; isEditing: boolean; setIsEditing: (v: boolean) => void; -} -export interface ThreeSpeakManagerRef extends ThreeSpeakManagerContext {} + // getters + hasUnpublishedVideo: boolean; + + // funcs + has3SpeakVideo: (body: string) => boolean; + findAndClearUnpublished3SpeakVideo: (body: string) => void; +} export const ThreeSpeakVideoContext = createContext({ is3Speak: false, @@ -38,22 +44,106 @@ export const ThreeSpeakVideoContext = createContext({ setVideoMetadata: () => {}, clear: () => {}, isEditing: false, - setIsEditing: () => {} + setIsEditing: () => {}, + hasUnpublishedVideo: false, + has3SpeakVideo: () => false, + findAndClearUnpublished3SpeakVideo: () => {} }); export function useThreeSpeakManager() { return useContext(ThreeSpeakVideoContext); } +const THREE_SPEAK_VIDEO_PATTERN = + /\[!\[\]\(https:\/\/ipfs-3speak\.b-cdn\.net\/ipfs.*\)\]\(https:\/\/3speak\.tv\/watch\?.*\)\[Source\]\(https:\/\/ipfs-3speak\.b-cdn\.net\/ipfs\/(.*)\)/g; + export function ThreeSpeakManager(props: { children: ReactNode }) { - const [is3Speak, setIs3Speak] = useLocalStorage(PREFIX + "_sa_3s", false); - const [videoId, setVideoId] = useLocalStorage(PREFIX + "_sa_3s_vid", ""); - const [speakPermlink, setSpeakPermlink] = useLocalStorage(PREFIX + "_sa_3s_p", ""); - const [speakAuthor, setSpeakAuthor] = useLocalStorage(PREFIX + "_sa_3s_a", ""); - const [isNsfw, setIsNsfw] = useLocalStorage(PREFIX + "_sa_3s_n", false); - const [videoMetadata, setVideoMetadata] = useLocalStorage(PREFIX + "_sa_3s_vm"); + const [is3Speak, setIs3Speak, clearIs3Speak] = useLocalStorage(PREFIX + "_sa_3s", false); + const [videoId, setVideoId, clearVideoId] = useLocalStorage(PREFIX + "_sa_3s_vid", ""); + const [speakPermlink, setSpeakPermlink, clearSpeakPermlink] = useLocalStorage( + PREFIX + "_sa_3s_p", + "" + ); + const [speakAuthor, setSpeakAuthor, clearSpeakAuthor] = useLocalStorage(PREFIX + "_sa_3s_a", ""); + const [isNsfw, setIsNsfw, clearIsNsfw] = useLocalStorage(PREFIX + "_sa_3s_n", false); + const [videoMetadata, setVideoMetadata, clearVideoMetadata] = useLocalStorage( + PREFIX + "_sa_3s_vm" + ); const [isEditing, setIsEditing] = useState(false); + const hasUnpublishedVideo = useMemo( + () => (videoMetadata ? videoMetadata.status !== "published" : false), + [videoMetadata] + ); + + const bodyManager = useBodyVersioningManager(({ metadata }) => { + const { videoMetadata } = metadata as { + videoMetadata: { + videoMetadata: ThreeSpeakVideo; + is3Speak: boolean; + isNsfw: boolean; + videoId: string; + speakPermlink: string; + speakAuthor: string; + }; + }; + + if (videoMetadata) { + setVideoMetadata(videoMetadata.videoMetadata); + setIs3Speak(videoMetadata.is3Speak); + setVideoId(videoMetadata.videoId); + setSpeakPermlink(videoMetadata.speakPermlink); + setSpeakAuthor(videoMetadata.speakAuthor); + setIsNsfw(videoMetadata.isNsfw); + } + }); + + useEffect(() => { + bodyManager.updateMetadata({ + videoMetadata: { + is3Speak, + videoId, + speakPermlink, + speakAuthor, + isNsfw, + videoMetadata + } + }); + }, [speakAuthor]); + + const has3SpeakVideo = (body: string) => { + const groups = body.matchAll(THREE_SPEAK_VIDEO_PATTERN); + let has = false; + for (const group of groups) { + const match = group[1]; + + has = `ipfs://${match}` === videoMetadata?.filename; + } + + return has; + }; + + const findAndClearUnpublished3SpeakVideo = (body: string) => { + const groups = body.matchAll(THREE_SPEAK_VIDEO_PATTERN); + for (const group of groups) { + const match = group[1]; + + ///
[![](https://ipfs-3speak.b-cdn.net/ipfs/bafkreic3bnsxobtm2va6j3i6v44gc2p2wazi4iuiqqci23tdt7eba5ad5e)](https://3speak.tv/watch?v=demo.com/meohyozpiu)[Source](https://ipfs-3speak.b-cdn.net/ipfs/QmUu28DUQ6wQpH8sFt5pVb3ZDfnvib67BUdtyE8uD4p64j)
+ // Has unpublished video + if (`ipfs://${match}` === videoMetadata?.filename && videoMetadata?.status !== "published") { + body.replace( + new RegExp( + `\[!\[\]\(https:\/\/ipfs-3speak\.b-cdn\.net\/ipfs\/${videoMetadata?.thumbnail.replace( + "ipfs://", + "" + )}\)\]\(https:\/\/3speak\.tv\/watch\?.*\)\[Source\]\(https:\/\/ipfs-3speak\.b-cdn\.net\/ipfs\/${match}\)` + ), + "" + ); + } + } + }; + return ( { - setIs3Speak(false); - setVideoId(""); - setSpeakPermlink(""); - setSpeakAuthor(""); - setIsNsfw(false); - setVideoMetadata(undefined); + clearIs3Speak(); + clearIsNsfw(); + clearVideoId(); + clearVideoMetadata(); + clearSpeakPermlink(); + clearSpeakAuthor(); } }} > diff --git a/src/common/pages/submit/index.tsx b/src/common/pages/submit/index.tsx index 1bf5bfb6d34..7572b341641 100644 --- a/src/common/pages/submit/index.tsx +++ b/src/common/pages/submit/index.tsx @@ -5,9 +5,11 @@ import { MatchType, PostBase, VideoProps } from "./types"; import { pageMapDispatchToProps, pageMapStateToProps, PageProps } from "../common"; import { extractMetaData } from "../../helper/posting"; import { + BodyVersioningManager, ThreeSpeakManager, useAdvancedManager, useApiDraftDetector, + useBodyVersioningManager, useCommunityDetector, useEntryDetector, useLocalDraftManager, @@ -63,13 +65,14 @@ interface MatchProps { export function Submit(props: PageProps & MatchProps) { const postBodyRef = useRef(null); const threeSpeakManager = useThreeSpeakManager(); + const { body, setBody } = useBodyVersioningManager(); + const previousBody = usePrevious(body); const { activeUser } = useMappedStore(); const previousActiveUser = usePrevious(activeUser); const [title, setTitle] = useState(""); const [tags, setTags] = useState([]); - const [body, setBody] = useState(""); const [selectionTouched, setSelectionTouched] = useState(false); const [thumbnails, setThumbnails] = useState([]); const [selectedThumbnail, setSelectedThumbnail, removeThumbnail] = useLocalStorage( @@ -144,7 +147,7 @@ export function Submit(props: PageProps & MatchProps) { setTags( draft.tags .trim() - .split(",") + .split(/[ ,]+/) .filter((t) => !!t) ); setBody(draft.body); @@ -204,6 +207,9 @@ export function Submit(props: PageProps & MatchProps) { if (activeUser?.username !== previousActiveUser?.username && activeUser) { // delete active user from beneficiaries list setBeneficiaries(beneficiaries.filter((x) => x.account !== activeUser.username)); + + // clear not current user videos + threeSpeakManager.findAndClearUnpublished3SpeakVideo(body); } }, [activeUser]); @@ -219,6 +225,13 @@ export function Submit(props: PageProps & MatchProps) { updatePreview(); }, [title, body, tags]); + useEffect(() => { + if (!threeSpeakManager.has3SpeakVideo(body) && !!previousBody) { + threeSpeakManager.clear(); + console.log("clearing 3speak"); + } + }, [body]); + const updatePreview = (): void => { if (_updateTimer) { clearTimeout(_updateTimer); @@ -423,9 +436,9 @@ export function Submit(props: PageProps & MatchProps) { as="textarea" placeholder={_t("submit.body-placeholder")} value={body && body.length > 0 ? body : preview.body} - onChange={(e: { target: { value: React.SetStateAction } }) => - setBody(e.target.value) - } + onChange={(e: { target: { value: string } }) => { + setBody(e.target.value); + }} disableRows={true} maxrows={100} spellCheck={true} @@ -711,7 +724,7 @@ export function Submit(props: PageProps & MatchProps) { {editingEntry === null && ( <> - {props.global.usePrivate && ( + {props.global.usePrivate && !threeSpeakManager.hasUnpublishedVideo && ( {_t("submit.schedule")} @@ -818,9 +831,11 @@ export function Submit(props: PageProps & MatchProps) { const SubmitWithProviders = (props: PageProps & MatchProps) => { return ( - - - + + + + + ); }; diff --git a/src/desktop/app/components/navbar/index.tsx b/src/desktop/app/components/navbar/index.tsx index d66fd1cedbe..96485cdcc91 100644 --- a/src/desktop/app/components/navbar/index.tsx +++ b/src/desktop/app/components/navbar/index.tsx @@ -15,15 +15,14 @@ import { TrendingTags } from "../../../../common/store/trending-tags/types"; import { Account } from "../../../../common/store/accounts/types"; import { User } from "../../../../common/store/users/types"; import { ActiveUser } from "../../../../common/store/active-user/types"; -import { UI, ToggleType } from "../../../../common/store/ui/types"; +import { ToggleType, UI } from "../../../../common/store/ui/types"; import { NotificationFilter, Notifications } from "../../../../common/store/notifications/types"; import { DynamicProps } from "../../../../common/store/dynamic-props/types"; import ToolTip from "../../../../common/components/tooltip"; import Login from "../../../../common/components/login"; -import UserNav from "../../../../common/components/user-nav"; +import { UserNav } from "../../../../common/components/user-nav"; import DropDown from "../../../../common/components/dropdown"; -import SearchSuggester from "../../../../common/components/search-suggester"; import Updater from "../updater"; import SwitchLang from "../../../../common/components/switch-lang"; @@ -40,27 +39,25 @@ import routes from "../../../../common/routes"; import { version } from "../../../package.json"; import { - brightnessSvg, - pencilOutlineSvg, arrowLeftSvg, arrowRightSvg, - refreshSvg, - magnifySvg, + brightnessSvg, dotsHorizontal, - translateSvg + magnifySvg, + pencilOutlineSvg, + refreshSvg } from "../../../../common/img/svg"; import isElectron from "../../../../common/util/is-electron"; import "./_index.scss"; - -// why "require" instead "import" ? see: https://github.com/ReactTraining/react-router/issues/6203 - -const pathToRegexp = require("path-to-regexp"); import isCommunity from "../../../../common/helper/is-community"; -import { NotifyTypes } from "../../../../common/enums"; import { setNotificationsSettingsItem, updateNotificationsSettings } from "../../../../common/store/notifications"; + +// why "require" instead "import" ? see: https://github.com/ReactTraining/react-router/issues/6203 + +const pathToRegexp = require("path-to-regexp"); const logo = "./img/logo-circle.svg"; interface AddressBarProps { diff --git a/src/server/handlers/entry.tsx b/src/server/handlers/entry.tsx index 69af07e179a..6ebfe00e771 100644 --- a/src/server/handlers/entry.tsx +++ b/src/server/handlers/entry.tsx @@ -43,7 +43,7 @@ export default async (req: Request, res: Response) => { let entries = {}; if (entry) { - if (dmca.some((rx: string) => new RegExp(rx).test(`${entry?.author}/${entry?.permlink}`))) { + if (dmca.some((rx: string) => new RegExp(rx).test(`@${entry?.author}/${entry?.permlink}`))) { entry.body = "This post is not available due to a copyright/fraudulent claim."; entry.title = ""; } diff --git a/src/server/services/amp-pages.ts b/src/server/services/amp-pages.ts index f4c65f258a3..5e7a7faecb6 100644 --- a/src/server/services/amp-pages.ts +++ b/src/server/services/amp-pages.ts @@ -29,6 +29,7 @@ export async function getAsAMP( try { const cache = await redis.get(identifier); if (cache && !forceRender) { + await client.disconnect(); return cache; } } catch (e) { @@ -63,5 +64,6 @@ export async function getAsAMP( console.error(e); console.error("Redis is unavailable. AMP caches ignoring"); } + await client.disconnect(); return ampResult; } diff --git a/yarn.lock b/yarn.lock index b8cbfb470d5..50a5291d617 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1327,10 +1327,10 @@ xmldom "^0.5.0" xss "^1.0.8" -"@ecency/render-helper@^2.2.25": - version "2.2.25" - resolved "https://registry.yarnpkg.com/@ecency/render-helper/-/render-helper-2.2.25.tgz#a5244b0cf603a412d5d4d02c6c7ac7c3ea800030" - integrity sha512-aWfGKH8toNYXd00aCHS7i1Xwtej17Wind57rf9kJ92ycUP2E6t7mLIBRna4Y7O6HBuXL8BHPZRH+rol37BMUpg== +"@ecency/render-helper@^2.2.26": + version "2.2.26" + resolved "https://registry.yarnpkg.com/@ecency/render-helper/-/render-helper-2.2.26.tgz#9bc98d09e5dd35e70c3e0699eb40883fae4ab296" + integrity sha512-ahrJ5u2J0HMEJy0wYWDNixvpV70CHnj/uGcUvmipaJ22y6WJ7qUkLZI8aj0CK9DzY9DcP8Lzx1NWT/DLdCuZJw== dependencies: he "^1.2.0" lolight "^1.4.0" @@ -1344,6 +1344,11 @@ xmldom "^0.5.0" xss "^1.0.9" +"@emoji-mart/data@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@emoji-mart/data/-/data-1.1.2.tgz#777c976f8f143df47cbb23a7077c9ca9fe5fc513" + integrity sha512-1HP8BxD2azjqWJvxIaWAMyTySeZY0Osr83ukYjltPVkNXeJvTz7yDrPLBtnrD5uqJ3tg4CcLuuBW09wahqL/fg== + "@firebase/analytics@^0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@firebase/analytics/-/analytics-0.8.0.tgz#b5d595082f57d33842b1fd9025d88f83065e87fe" @@ -2020,6 +2025,11 @@ resolved "https://registry.yarnpkg.com/@types/diff-match-patch/-/diff-match-patch-1.0.32.tgz#d9c3b8c914aa8229485351db4865328337a3d09f" integrity sha512-bPYT5ECFiblzsVzyURaNhljBH2Gh1t9LowgUwciMrNAhFewLkHT2H0Mto07Y4/3KCOGZHRQll3CTtQZ0X11D/A== +"@types/dom-mediacapture-record@^1.0.16": + version "1.0.16" + resolved "https://registry.yarnpkg.com/@types/dom-mediacapture-record/-/dom-mediacapture-record-1.0.16.tgz#8a37eaa32c1810519843c6406325bb78618d1daf" + integrity sha512-GB4f8Es3aTEUjDT3ojQ8IWUhb6f5tlUoxvVw65ppkImG09QRRys6V6t56uLAynZCD3LisMQDuVObOKnnllDtCQ== + "@types/express-serve-static-core@^4.17.18": version "4.17.24" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz#ea41f93bf7e0d59cd5a76665068ed6aab6815c07" @@ -5148,6 +5158,11 @@ emittery@^0.7.1: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.2.tgz#25595908e13af0f5674ab419396e2fb394cdfa82" integrity sha512-A8OG5SR/ij3SsJdWDJdkkSYUjQdCUx6APQXem0SaEePBSRg4eymGYwBkKo1Y6DU+af/Jn2dBQqDBvjnr9Vi8nQ== +emoji-mart@^5.5.2: + version "5.5.2" + resolved "https://registry.yarnpkg.com/emoji-mart/-/emoji-mart-5.5.2.tgz#3ddbaf053139cf4aa217650078bc1c50ca8381af" + integrity sha512-Sqc/nso4cjxhOwWJsp9xkVm8OF5c+mJLZJFoFfzRuKO+yWiN7K8c96xmtughYb0d/fZ8UC6cLIQ/p4BR6Pv3/A== + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" From b3144d7d1f6d384a4288b18476103dda2cf34436 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Wed, 13 Sep 2023 18:34:05 +0500 Subject: [PATCH 065/179] Fix errors --- src/common/components/navbar/index.tsx | 2 +- src/common/components/user-nav/_index.scss | 3 --- src/common/components/user-nav/index.spec.tsx | 2 +- src/desktop/app/components/navbar/index.tsx | 2 +- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/common/components/navbar/index.tsx b/src/common/components/navbar/index.tsx index b16c3cb2a93..e778c821fe6 100644 --- a/src/common/components/navbar/index.tsx +++ b/src/common/components/navbar/index.tsx @@ -9,7 +9,7 @@ import SwitchLang from "../switch-lang"; import ToolTip from "../tooltip"; import Search from "../search"; import Login from "../login"; -import { UserNav } from "../user-nav"; +import UserNav from "../user-nav"; import UserNotifications from "../notifications"; import Gallery from "../gallery"; import Drafts from "../drafts"; diff --git a/src/common/components/user-nav/_index.scss b/src/common/components/user-nav/_index.scss index 2ef2fed069c..e80d67a2447 100644 --- a/src/common/components/user-nav/_index.scss +++ b/src/common/components/user-nav/_index.scss @@ -118,9 +118,6 @@ background: $dark-two; } - .label { - } - .power { display: flex; diff --git a/src/common/components/user-nav/index.spec.tsx b/src/common/components/user-nav/index.spec.tsx index 4c2860423b9..25a861501f1 100644 --- a/src/common/components/user-nav/index.spec.tsx +++ b/src/common/components/user-nav/index.spec.tsx @@ -3,7 +3,7 @@ import { StaticRouter } from "react-router-dom"; import { createBrowserHistory, createLocation } from "history"; -import { UserNav } from "./index"; +import UserNav from "./index"; import { dynamicPropsIntance1, diff --git a/src/desktop/app/components/navbar/index.tsx b/src/desktop/app/components/navbar/index.tsx index 96485cdcc91..f7ccdacb252 100644 --- a/src/desktop/app/components/navbar/index.tsx +++ b/src/desktop/app/components/navbar/index.tsx @@ -21,7 +21,7 @@ import { DynamicProps } from "../../../../common/store/dynamic-props/types"; import ToolTip from "../../../../common/components/tooltip"; import Login from "../../../../common/components/login"; -import { UserNav } from "../../../../common/components/user-nav"; +import UserNav from "../../../../common/components/user-nav"; import DropDown from "../../../../common/components/dropdown"; import Updater from "../updater"; import SwitchLang from "../../../../common/components/switch-lang"; From f9802bde8776d8956a2d93da50207e5e8588d8f7 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Wed, 13 Sep 2023 18:55:54 +0500 Subject: [PATCH 066/179] Snapshot update --- .../__snapshots__/index.spec.tsx.snap | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/src/common/components/user-nav/__snapshots__/index.spec.tsx.snap b/src/common/components/user-nav/__snapshots__/index.spec.tsx.snap index 308801eca8e..ab313809a10 100644 --- a/src/common/components/user-nav/__snapshots__/index.spec.tsx.snap +++ b/src/common/components/user-nav/__snapshots__/index.spec.tsx.snap @@ -58,6 +58,31 @@ exports[`(1) Default render 1`] = ` /> + + + + + + +
+ + + + + + +
+ + + + + + +
Date: Thu, 14 Sep 2023 12:11:30 +0500 Subject: [PATCH 067/179] Add Some other states in Context and used in components --- src/common/app.tsx | 2 +- .../chats/chat-context-provider.tsx | 57 ++++++- .../components/chats/chat-popup/index.tsx | 68 +-------- .../chats/chats-channel-messages/index.scss | 13 +- .../chats/chats-channel-messages/index.tsx | 58 +++++--- .../components/chats/join-chat/index.tsx | 38 ++--- .../chats/join-community-chat-btn/index.tsx | 81 ++-------- .../components/community-cover/index.tsx | 6 +- src/common/helper/message-service.ts | 20 --- src/common/pages/chats/index.tsx | 2 +- src/common/pages/community-functional.tsx | 140 ++++++------------ 11 files changed, 175 insertions(+), 310 deletions(-) diff --git a/src/common/app.tsx b/src/common/app.tsx index 94aaa913874..d8e586a8a8d 100644 --- a/src/common/app.tsx +++ b/src/common/app.tsx @@ -103,7 +103,7 @@ const App = (props: any) => { {/**/} - + diff --git a/src/common/components/chats/chat-context-provider.tsx b/src/common/components/chats/chat-context-provider.tsx index 87d3e91c869..adc908d3d33 100644 --- a/src/common/components/chats/chat-context-provider.tsx +++ b/src/common/components/chats/chat-context-provider.tsx @@ -3,7 +3,14 @@ import { Keys } from "../../../managers/message-manager-types"; import MessageService from "../../helper/message-service"; import { useMappedStore } from "../../store/use-mapped-store"; import { NostrKeysType } from "./types"; -import { getPrivateKey, getUserChatPublicKey } from "./utils"; +import { + createNoStrAccount, + getPrivateKey, + getUserChatPublicKey, + setProfileMetaData +} from "./utils"; +import * as ls from "../../util/local-storage"; +import { setNostrkeys } from "../../../managers/message-manager"; interface Context { activeUserKeys: NostrKeysType; @@ -12,6 +19,7 @@ interface Context { chatPrivKey: string; receiverPubKey: string; messageServiceInstance: MessageService | undefined; + hasUserJoinedChat: boolean; setRevealPrivKey: (d: boolean) => void; setShowSpinner: (d: boolean) => void; setChatPrivKey: (key: string) => void; @@ -19,10 +27,12 @@ interface Context { setReceiverPubKey: (key: string) => void; setMessageServiceInstance: (instance: MessageService | undefined) => void; initMessageServiceInstance: (keys: Keys) => void; + joinChat: () => void; } interface Props { children: JSX.Element | JSX.Element[]; + resetChat: () => void; } export const ChatContext = React.createContext({ @@ -32,16 +42,18 @@ export const ChatContext = React.createContext({ chatPrivKey: "", receiverPubKey: "", messageServiceInstance: undefined, + hasUserJoinedChat: false, setRevealPrivKey: () => {}, setShowSpinner: () => {}, setChatPrivKey: () => {}, setActiveUserKeys: () => {}, setReceiverPubKey: () => {}, setMessageServiceInstance: () => {}, - initMessageServiceInstance: () => {} + initMessageServiceInstance: () => {}, + joinChat: () => {} }); -export default function ChatContextProvider({ children }: Props) { +export default function ChatContextProvider(props: Props) { const { activeUser } = useMappedStore(); const [activeUserKeys, setActiveUserKeys] = useState({ pub: " ", priv: "" }); @@ -52,11 +64,30 @@ export default function ChatContextProvider({ children }: Props) { const [messageServiceInstance, setMessageServiceInstance] = useState( undefined ); + const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); + const [shouldUpdateProfile, setShouldUpdateProfile] = useState(false); useEffect(() => { getActiveUserKeys(); }, []); + useEffect(() => { + if (messageServiceInstance) { + getActiveUserKeys(); + setShouldUpdateProfile(false); + } + }, [messageServiceInstance]); + + useEffect(() => { + if (shouldUpdateProfile && messageServiceInstance) { + messageServiceInstance.updateProfile({ + name: activeUser?.username!, + about: "", + picture: "" + }); + } + }, [shouldUpdateProfile, messageServiceInstance]); + useEffect(() => { if (showSpinner) { setTimeout(() => { @@ -68,6 +99,7 @@ export default function ChatContextProvider({ children }: Props) { const getActiveUserKeys = async () => { const pubKey = await getUserChatPublicKey(activeUser?.username!); const privKey = getPrivateKey(activeUser?.username!); + setHasUserJoinedChat(!!pubKey); setChatPrivKey(privKey); const activeUserKeys = { pub: pubKey, @@ -91,6 +123,19 @@ export default function ChatContextProvider({ children }: Props) { return newMessageService; }; + const joinChat = async () => { + const { resetChat } = props; + resetChat(); + const keys = createNoStrAccount(); + ls.set(`${activeUser?.username}_nsPrivKey`, keys.priv); + await setProfileMetaData(activeUser, keys.pub); + setHasUserJoinedChat(true); + setNostrkeys(keys); + setChatPrivKey(keys.priv); + setShouldUpdateProfile(true); + setActiveUserKeys(keys); + }; + return ( - {children} + {props.children} ); } diff --git a/src/common/components/chats/chat-popup/index.tsx b/src/common/components/chats/chat-popup/index.tsx index 0f83e3e06d1..70b532709ed 100644 --- a/src/common/components/chats/chat-popup/index.tsx +++ b/src/common/components/chats/chat-popup/index.tsx @@ -84,14 +84,6 @@ interface Props { deleteDirectMessage: (peer: string, msgId: string) => void; } -export const profileUpdater = (messageServiceInstance: MessageService) => { - const detail = { - messageServiceInstance - }; - const ev = new CustomEvent("profileUpdater", { detail }); - window.dispatchEvent(ev); -}; - export default function ChatPopUp(props: Props) { const { activeUser, global, chat } = useMappedStore(); @@ -100,15 +92,11 @@ export default function ChatPopUp(props: Props) { revealPrivKey, activeUserKeys, showSpinner, + hasUserJoinedChat, setRevealPrivKey, setShowSpinner, - setActiveUserKeys + joinChat } = useContext(ChatContext); - // const messageServiceContext = useContext(MessageServiceContext); - - // const { messageServiceInstance } = messageServiceContext; - - // console.log("messageServiceInstance chatpop up", messageServiceInstance); const routerLocation = useLocation(); const prevActiveUser = usePrevious(activeUser); @@ -120,7 +108,6 @@ export default function ChatPopUp(props: Props) { const [isScrollToTop, setIsScrollToTop] = useState(false); const [isScrollToBottom, setIsScrollToBottom] = useState(false); const [showSearchUser, setShowSearchUser] = useState(false); - const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); const [inProgress, setInProgress] = useState(false); const [show, setShow] = useState(false); const [receiverPubKey, setReceiverPubKey] = useState(""); @@ -141,7 +128,6 @@ export default function ChatPopUp(props: Props) { const [noStrPrivKey, setNoStrPrivKey] = useState(""); const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); const [isTop, setIsTop] = useState(false); - const [shouldUpdateProfile, setShouldUpdateProfile] = useState(false); const [hasMore, setHasMore] = useState(true); const [removedUsers, setRemovedUsers] = useState([]); const [innerWidth, setInnerWidth] = useState(0); @@ -151,14 +137,6 @@ export default function ChatPopUp(props: Props) { console.log("chat in store", chat); }, [chat]); - useEffect(() => { - window.addEventListener("profileUpdater", noStrProfileUpdater); - - return () => { - window.removeEventListener("profileUpdater", noStrProfileUpdater); - }; - }, []); - useEffect(() => { if (currentChannel && chat.leftChannelsList.includes(currentChannel.id)) { setIsCommunity(false); @@ -178,19 +156,12 @@ export default function ChatPopUp(props: Props) { useEffect(() => { // deleteChatPublicKey(activeUser); - fetchProfileData(); setShow(!!activeUser?.username && !isChatPage); const noStrPrivKey = getPrivateKey(activeUser?.username!); setNoStrPrivKey(noStrPrivKey); setInnerWidth(window.innerWidth); }, []); - useEffect(() => { - if (shouldUpdateProfile && messageServiceInstance) { - profileUpdater(messageServiceInstance); - } - }, [shouldUpdateProfile, messageServiceInstance]); - useEffect(() => { const updated: ChannelUpdate = chat.updatedChannel .filter((x) => x.channelId === currentChannel?.id!) @@ -227,8 +198,7 @@ export default function ChatPopUp(props: Props) { useEffect(() => { if (messageServiceInstance) { - setHasUserJoinedChat(true); - setShouldUpdateProfile(false); + setIsSpinner(false); const noStrPrivKey = getPrivateKey(activeUser?.username!); setNoStrPrivKey(noStrPrivKey); } @@ -242,7 +212,7 @@ export default function ChatPopUp(props: Props) { useEffect(() => { const communities = getJoinedCommunities(chat.channels, chat.leftChannelsList); setCommunities(communities); - fetchProfileData(); + // fetchProfileData(); }, [chat.channels, chat.leftChannelsList]); useEffect(() => { @@ -295,7 +265,6 @@ export default function ChatPopUp(props: Props) { }, [removedUsers]); useEffect(() => { - fetchProfileData(); const msgsList = fetchDirectMessages(receiverPubKey!); const messages = msgsList.sort((a, b) => a.created - b.created); setDirectMessagesList(messages); @@ -369,16 +338,6 @@ export default function ChatPopUp(props: Props) { } }; - const noStrProfileUpdater = (e: Event) => { - const detail = (e as CustomEvent).detail; - console.log("detail", detail.messageServiceInstance); - detail.messageServiceInstance.updateProfile({ - name: activeUser?.username!, - about: "", - picture: "" - }); - }; - const fetchCommunity = async () => { const community = await getCommunity(communityName, activeUser?.username); setCurrentCommunity(community!); @@ -438,12 +397,6 @@ export default function ChatPopUp(props: Props) { setInProgress(false); }; - const fetchProfileData = async () => { - const profileData = await getProfileMetaData(activeUser?.username!); - const hasNoStrKey = profileData && profileData.hasOwnProperty(NOSTRKEY); - setHasUserJoinedChat(hasNoStrKey); - }; - const userClicked = (username: string) => { setIsCurrentUser(true); setCurrentUser(username); @@ -484,7 +437,6 @@ export default function ChatPopUp(props: Props) { }; const scrollerClicked = () => { - console.log("Scroller clicked"); chatBodyDivRef?.current?.scroll({ top: isCurrentUser || isCommunity ? chatBodyDivRef?.current?.scrollHeight : 0, behavior: "auto" @@ -515,18 +467,8 @@ export default function ChatPopUp(props: Props) { }; const handleJoinChat = async () => { - const { resetChat } = props; setIsSpinner(true); - resetChat(); - const keys = createNoStrAccount(); - ls.set(`${activeUser?.username}_nsPrivKey`, keys.priv); - setNoStrPrivKey(keys.priv); - await setProfileMetaData(activeUser, keys.pub); - setHasUserJoinedChat(true); - setNostrkeys(keys); - setShouldUpdateProfile(true); - setActiveUserKeys(keys); - setIsSpinner(false); + joinChat(); }; const chatButtonSpinner = ( diff --git a/src/common/components/chats/chats-channel-messages/index.scss b/src/common/components/chats/chats-channel-messages/index.scss index 92ea6c119d7..1d46f9692d1 100644 --- a/src/common/components/chats/chats-channel-messages/index.scss +++ b/src/common/components/chats/chats-channel-messages/index.scss @@ -62,6 +62,7 @@ &.gif { background: none; + padding: 0; img { max-width: 100%; } @@ -69,6 +70,7 @@ &.chat-image { background: none; + padding: 0; img { max-width: 100%; } @@ -79,7 +81,14 @@ max-width: 70% !important; word-wrap: break-word; padding: 10px; - border-radius: 0px 10px 10px; + border-radius: 0px 10px 10px 0px; + } + } + &.same-user-msg { + margin-left: 4rem; + padding-top: 1.5px; + .receiver-message-content { + // border-radius: 0; } } } @@ -157,6 +166,7 @@ } &.gif { background: none; + padding: 0; img { max-width: 100%; } @@ -164,6 +174,7 @@ &.chat-image { background: none; + padding: 0; img { max-width: 100%; } diff --git a/src/common/components/chats/chats-channel-messages/index.tsx b/src/common/components/chats/chats-channel-messages/index.tsx index cc8a1070dee..e44bc860c85 100644 --- a/src/common/components/chats/chats-channel-messages/index.tsx +++ b/src/common/components/chats/chats-channel-messages/index.tsx @@ -260,8 +260,6 @@ export default function ChatsChannelMessages(props: Props) { } break; case 3: - console.log("Current removed users", currentChannel?.removedUserIds); - console.log("removedUserId", removedUserId); const newUpdatedRemovedUsers = currentChannel && currentChannel?.removedUserIds!.filter((item) => item !== removedUserId); @@ -324,6 +322,16 @@ export default function ChatsChannelMessages(props: Props) { return <>; }; + const checkContiguousMessage = (msg: PublicMessage, i: number) => { + const prevMsg = publicMessages[i - 1]; + const msgAuthor = msg.creator; + const prevMsgAuthor = prevMsg ? prevMsg.creator : null; + if (msgAuthor === prevMsgAuthor) { + return true; + } + return false; + }; + return ( <>
@@ -331,6 +339,9 @@ export default function ChatsChannelMessages(props: Props) { activeUserKeys && publicMessages.map((pMsg, i) => { const dayAndMonth = getFormattedDateAndDay(pMsg, i); + + const isSameUserMessage = checkContiguousMessage(pMsg, i); + let renderedPreview = renderPostBody(pMsg.content, false, global.canUseWebp); renderedPreview = renderedPreview.replace(/]*>/g, ""); @@ -436,26 +447,31 @@ export default function ChatsChannelMessages(props: Props) { onMouseEnter={() => setHoveredMessageId(pMsg.id)} onMouseLeave={() => setHoveredMessageId("")} > -
- handleImageClick(pMsg.id, pMsg.creator)} - > - - - - -
+ {!isSameUserMessage && ( +
+ handleImageClick(pMsg.id, pMsg.creator)} + > + + + + +
+ )} + +
+ {!isSameUserMessage && ( +

+ {name} + {formatMessageTime(pMsg.created)} +

+ )} -
-

- {name} - {formatMessageTime(pMsg.created)} -

void; -} - -export default function JoinChat(props: Props) { - const { messageServiceInstance, setChatPrivKey, setActiveUserKeys } = useContext(ChatContext); - - const { activeUser } = useMappedStore(); +export default function JoinChat() { + const { messageServiceInstance, joinChat } = useContext(ChatContext); const [showSpinner, setShowSpinner] = useState(false); + useEffect(() => { + if (messageServiceInstance) { + setShowSpinner(false); + } + }, [messageServiceInstance]); + const handleJoinChat = async () => { - const { resetChat } = props; setShowSpinner(true); - resetChat(); - const keys = createNoStrAccount(); - ls.set(`${activeUser?.username}_nsPrivKey`, keys.priv); - setChatPrivKey(keys.priv); - await setProfileMetaData(activeUser, keys.pub); - setNostrkeys(keys); - messageServiceInstance?.updateProfile({ - name: activeUser?.username!, - about: "", - picture: "" - }); - setActiveUserKeys(keys); - setShowSpinner(false); + joinChat(); }; return ( diff --git a/src/common/components/chats/join-community-chat-btn/index.tsx b/src/common/components/chats/join-community-chat-btn/index.tsx index a9c1bc17a2e..109791f3800 100644 --- a/src/common/components/chats/join-community-chat-btn/index.tsx +++ b/src/common/components/chats/join-community-chat-btn/index.tsx @@ -3,76 +3,48 @@ import { Button, Spinner } from "react-bootstrap"; import { History } from "history"; import { Community, ROLES } from "../../../store/communities/types"; -import { ActiveUser } from "../../../store/active-user/types"; import { _t } from "../../../i18n"; import { useMappedStore } from "../../../store/use-mapped-store"; import { Channel, communityModerator } from "../../../../managers/message-manager-types"; -import * as ls from "../../../util/local-storage"; -import { setNostrkeys } from "../../../../managers/message-manager"; import { ChatContext } from "../chat-context-provider"; -import { profileUpdater } from "../chat-popup"; import { NOSTRKEY } from "../chat-popup/chat-constants"; -import { NostrKeysType } from "../types"; -import { - createNoStrAccount, - getProfileMetaData, - setChannelMetaData, - setProfileMetaData -} from "../utils"; +import { getProfileMetaData, setChannelMetaData } from "../utils"; interface Props { history: History; community: Community; - activeUser: ActiveUser | null; - resetChat: () => void; } export default function JoinCommunityChatBtn(props: Props) { - const { messageServiceInstance, setShowSpinner } = useContext(ChatContext); - const { chat } = useMappedStore(); + const { messageServiceInstance, activeUserKeys, hasUserJoinedChat, joinChat } = + useContext(ChatContext); + const { chat, activeUser } = useMappedStore(); const [inProgress, setInProgress] = useState(false); const [isCommunityChatJoined, setIsCommunityChatJoined] = useState(false); const [isChatEnabled, setIsChatEnabled] = useState(false); const [currentChannel, setCurrentChannel] = useState(); - const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); const [communityRoles, setCommunityRoles] = useState([]); - const [activeUserKeys, setActiveUserKeys] = useState(); const [loadCommunity, setLoadCommunity] = useState(false); const [initiateCommunityChat, setInitiateCommunityChat] = useState(false); - const [shouldUpdateProfile, setShouldUpdateProfile] = useState(false); useEffect(() => { fetchCommunityProfile(); - fetchUserProfileData(); }, [chat.channels, currentChannel, chat.leftChannelsList]); useEffect(() => { fetchCommunityProfile(); - }, [props.activeUser]); + }, [activeUser]); useEffect(() => { - if (activeUserKeys) { + if (activeUserKeys && activeUser?.username === props.community.name) { getCommunityRoles(); } }, [activeUserKeys]); - useEffect(() => { - if (shouldUpdateProfile && messageServiceInstance) { - profileUpdater(messageServiceInstance); - } - }, [shouldUpdateProfile, messageServiceInstance]); - - useEffect(() => { - if (messageServiceInstance && shouldUpdateProfile) { - setShouldUpdateProfile(false); - } - }, [messageServiceInstance]); - useEffect(() => { if (messageServiceInstance) { - fetchUserProfileData(); if (loadCommunity) { messageServiceInstance?.loadChannel(currentChannel?.id!); } @@ -93,15 +65,6 @@ export default function JoinCommunityChatBtn(props: Props) { fetchCurrentChannel(); }, [isCommunityChatJoined, props.community, chat.channels, chat.leftChannelsList]); - const fetchUserProfileData = async () => { - const profileData = await getProfileMetaData(props.activeUser?.username!); - const hasNoStrKey = profileData && profileData.hasOwnProperty(NOSTRKEY); - if (hasNoStrKey) { - setActiveUserKeys(profileData.nsKey); - } - setHasUserJoinedChat(hasNoStrKey); - }; - const fetchCommunityProfile = async () => { const communityProfile = await getProfileMetaData(props.community?.name); const haschannelMetaData = communityProfile && communityProfile.hasOwnProperty("channel"); @@ -127,7 +90,7 @@ export default function JoinCommunityChatBtn(props: Props) { const getCommunityRoles = async () => { let communityTeam: communityModerator[] = []; - const { community, activeUser } = props; + const { community } = props; const ownerData = await getProfileMetaData(community.name); const ownerRole = { name: activeUser!.username, @@ -190,10 +153,9 @@ export default function JoinCommunityChatBtn(props: Props) { }; const joinCommunityChat = () => { - console.log("Join community chat", messageServiceInstance); if (!hasUserJoinedChat) { - setShowSpinner(true); - handleJoinChat(); + setInProgress(true); + joinChat(); setLoadCommunity(true); setIsCommunityChatJoined(true); return; @@ -203,14 +165,13 @@ export default function JoinCommunityChatBtn(props: Props) { chat.leftChannelsList.filter((x) => x !== currentChannel?.id) ); } - setIsCommunityChatJoined(true); messageServiceInstance?.loadChannel(currentChannel?.id!); setIsCommunityChatJoined(true); }; const startCommunityChat = () => { if (!hasUserJoinedChat) { - handleJoinChat(); + joinChat(); setInitiateCommunityChat(true); setIsCommunityChatJoined(true); return; @@ -229,33 +190,13 @@ export default function JoinCommunityChatBtn(props: Props) { return {}; }; - const handleJoinChat = async () => { - const { resetChat, activeUser } = props; - setInProgress(true); - const keys = createNoStrAccount(); - setActiveUserKeys(keys); - ls.set(`${activeUser?.username}_nsPrivKey`, keys.priv); - await setProfileMetaData(props.activeUser, keys.pub); - setHasUserJoinedChat(true); - setNostrkeys(keys); - messageServiceInstance?.updateProfile({ - name: props.activeUser?.username!, - about: "", - picture: "" - }); - setInProgress(false); - fetchCommunityProfile(); - resetChat(); - setShouldUpdateProfile(true); - }; - const chatButtonSpinner = ( ); return ( <> - {props.community.name === props.activeUser?.username ? ( + {props.community.name === activeUser?.username ? ( isCommunityChatJoined ? ( ) : !isChatEnabled ? ( diff --git a/src/common/components/community-cover/index.tsx b/src/common/components/community-cover/index.tsx index 7d973086661..daa411dd7cf 100644 --- a/src/common/components/community-cover/index.tsx +++ b/src/common/components/community-cover/index.tsx @@ -139,7 +139,6 @@ interface Props { toggleUIProp: (what: ToggleType) => void; updateSubscriptions: (list: Subscription[]) => void; addAccount: (data: Account) => void; - resetChat: () => void; } export class CommunityCover extends Component { @@ -206,7 +205,7 @@ export class CommunityCover extends Component { {CommunityPostBtn({ ...this.props })} - +
{canUpdateCoverImage && ( { deleteUser: p.deleteUser, toggleUIProp: p.toggleUIProp, updateSubscriptions: p.updateSubscriptions, - addAccount: p.addAccount, - resetChat: p.resetChat + addAccount: p.addAccount }; return ; diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts index 2c54508493d..2867f49274a 100644 --- a/src/common/helper/message-service.ts +++ b/src/common/helper/message-service.ts @@ -92,7 +92,6 @@ class MessageService extends TypedEventEmitter { } private async init() { - console.log("Init run of message service"); this.eventQueue = []; this.eventQueueFlag = true; this.eventQueueBuffer = []; @@ -574,7 +573,6 @@ class MessageService extends TypedEventEmitter { sig: "" }) .then((event) => { - console.log("event failed", event); if (!event) { reject("Couldn't sign event!"); return; @@ -805,7 +803,6 @@ class MessageService extends TypedEventEmitter { } public close = () => { - console.log("Close run"); this.pool.close(this.readRelays); this.removeAllListeners(); }; @@ -842,20 +839,3 @@ class MessageService extends TypedEventEmitter { } export default MessageService; - -// export const initMessageService = (keys: Keys): MessageService | undefined => { -// if (window.messageService) { -// window.messageService.close(); -// window.messageService = undefined; -// } - -// // const { messageServiceInstance, setMessageServiceInstance } = useMessageServiceContext(); -// // console.log("I have to check this", messageServiceInstance, setMessageServiceInstance); - -// if (keys) { -// window.messageService = new MessageService(keys.priv, keys.pub); -// } -// console.log("raven instance created", window.messageService); - -// return window.messageService; -// }; diff --git a/src/common/pages/chats/index.tsx b/src/common/pages/chats/index.tsx index 85b684fc1d6..44c3f9fc9b9 100644 --- a/src/common/pages/chats/index.tsx +++ b/src/common/pages/chats/index.tsx @@ -100,7 +100,7 @@ export const Chats = (props: Props) => { ) ) : (
- +
)} diff --git a/src/common/pages/community-functional.tsx b/src/common/pages/community-functional.tsx index 4e5fa2bc9d2..a0d13c87023 100644 --- a/src/common/pages/community-functional.tsx +++ b/src/common/pages/community-functional.tsx @@ -6,13 +6,8 @@ import { getSubscriptions } from "../api/bridge"; import { EntryFilter, ListStyle } from "../store/global/types"; import { Channel } from "../../managers/message-manager-types"; import { usePrevious } from "../util/use-previous"; -import * as ls from "../util/local-storage"; -import { - createNoStrAccount, - getJoinedCommunities, - getProfileMetaData, - setProfileMetaData -} from "../components/chats/utils"; + +import { getJoinedCommunities } from "../components/chats/utils"; import { makeGroupKey } from "../store/entries"; import _ from "lodash"; import Meta from "../components/meta"; @@ -20,7 +15,6 @@ import ScrollToTop from "../components/scroll-to-top"; import Theme from "../components/theme"; import Feedback from "../components/feedback"; import defaults from "../constants/defaults.json"; -import { NOSTRKEY } from "../components/chats/chat-popup/chat-constants"; import CommunitySubscribers from "../components/community-subscribers"; import CommunityActivities from "../components/community-activities"; import LinearProgress from "../components/linear-progress"; @@ -46,10 +40,8 @@ import "./community.scss"; import { Button, Modal } from "react-bootstrap"; import LoginRequired from "../components/login-required"; import { useMappedStore } from "../store/use-mapped-store"; -import { setNostrkeys } from "../../managers/message-manager"; import { QueryIdentifiers, useCommunityCache } from "../core"; import { useQueryClient } from "@tanstack/react-query"; -import { profileUpdater } from "../components/chats/chat-popup"; import { ChatContext } from "../components/chats/chat-context-provider"; interface MatchParams { @@ -79,35 +71,18 @@ export const CommunityPage = (props: Props) => { const [searchDataLoading, setSearchDataLoading] = useState(getSearchParam().length > 0); const [searchData, setSearchData] = useState([]); const [isJoinCommunity, setIsJoinCommunity] = useState(false); - const [hasUserJoinedChat, setHasUserJoinedChat] = useState(true); const [inProgress, setInProgress] = useState(false); const [channelId, setChannelId] = useState(""); const [communities, setCommunities] = useState([]); const [isCommunityAlreadyJoined, setIsCommunityAlreadyJoined] = useState(false); const [isLoading, setIsLoading] = useState(false); - const [shouldUpdateProfile, setShouldUpdateProfile] = useState(false); + const [loadCommunity, setLoadCommunity] = useState(false); const prevMatch = usePrevious(props.match); const prevActiveUser = usePrevious(props.activeUser); const chatContext = useContext(ChatContext); - const { messageServiceInstance } = chatContext; - - useEffect(() => { - fetchUserProfileData(); - }, [props.activeUser]); - - useEffect(() => { - if (shouldUpdateProfile && messageServiceInstance) { - profileUpdater(messageServiceInstance); - } - }, [shouldUpdateProfile, messageServiceInstance]); - - useEffect(() => { - if (messageServiceInstance && shouldUpdateProfile) { - setShouldUpdateProfile(false); - } - }, [messageServiceInstance]); + const { messageServiceInstance, hasUserJoinedChat, joinChat } = chatContext; useEffect(() => { const communities = getJoinedCommunities(chat.channels, chat.leftChannelsList); @@ -119,6 +94,14 @@ export const CommunityPage = (props: Props) => { setIsCommunityAlreadyJoined(isAlreadyExists); }, [communities]); + useEffect(() => { + if (messageServiceInstance && loadCommunity) { + messageServiceInstance?.loadChannel(channelId); + setInProgress(false); + setIsJoinCommunity(false); + } + }, [loadCommunity, messageServiceInstance]); + useEffect(() => { setIsLoading(true); @@ -227,14 +210,6 @@ export const CommunityPage = (props: Props) => { delayedSearch(value); }; - const fetchUserProfileData = async () => { - if (props.activeUser) { - const profileData = await getProfileMetaData(props.activeUser?.username!); - const hasNoStrKey = profileData && profileData.hasOwnProperty(NOSTRKEY); - setHasUserJoinedChat(hasNoStrKey); - } - }; - const getMetaProps = () => { const { filter } = props.match.params; const ncount = props.notifications.unread > 0 ? `(${props.notifications.unread}) ` : ""; @@ -257,40 +232,50 @@ export const CommunityPage = (props: Props) => { ); - const handleJoinChat = async () => { - setInProgress(true); - const keys = createNoStrAccount(); - ls.set(`${props.activeUser?.username}_nsPrivKey`, keys.priv); - setNostrkeys(keys); - await setProfileMetaData(props.activeUser, keys.pub); - setHasUserJoinedChat(true); - setInProgress(false); - console.log("State has been changed"); - setShouldUpdateProfile(true); - }; - const joinCommunityChat = () => { - const communityIds = new Set(communities.map((community) => community.id)); - if (!communityIds.has(channelId)) { - const messageService = messageServiceInstance; - if (messageService) { - const updatedLeftChannelsList = chat.leftChannelsList.filter((x) => x !== channelId); - messageService.updateLeftChannelList(updatedLeftChannelsList); - messageService.loadChannel(channelId); - } + if (!hasUserJoinedChat) { + setInProgress(true); + joinChat(); + setLoadCommunity(true); + return; } - + if (chat.leftChannelsList.includes(channelId)) { + messageServiceInstance?.updateLeftChannelList( + chat.leftChannelsList.filter((x) => x !== channelId) + ); + } + messageServiceInstance?.loadChannel(channelId); setIsJoinCommunity(false); }; const joinCommunityModal = () => { - if (hasUserJoinedChat && !isCommunityAlreadyJoined) { + if (isCommunityAlreadyJoined) { + return ( + <> +
+
+

You have already joined this community

+
+
+

+ +

+ + ); + } else { return ( <>

Confirmaton

+ {inProgress && }
{

); - } else { - if (isCommunityAlreadyJoined) { - return ( - <> -
-
-

You have already joined this community

-
-
-

- -

- - ); - } else { - return ( - <> -
-
-

Please Join the Chat to Continue

-
-
- {inProgress && } -

- -

- - ); - } } }; From 972baa3ed137a673f789220c315e4342f6b8a084 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Thu, 14 Sep 2023 12:53:44 +0500 Subject: [PATCH 068/179] Add resend message functionality for DMs if any message failed to sent --- .../components/chats/chat-popup/index.tsx | 1 + .../chats/chats-channel-messages/index.tsx | 38 +-- .../chats/chats-direct-messages/index.tsx | 227 ++++++++++-------- .../chats/chats-messages-view/index.tsx | 10 +- 4 files changed, 136 insertions(+), 140 deletions(-) diff --git a/src/common/components/chats/chat-popup/index.tsx b/src/common/components/chats/chat-popup/index.tsx index 70b532709ed..bb6ed52ddd1 100644 --- a/src/common/components/chats/chat-popup/index.tsx +++ b/src/common/components/chats/chat-popup/index.tsx @@ -730,6 +730,7 @@ export default function ChatPopUp(props: Props) { directMessages={directMessagesList} currentUser={currentUser} isScrollToBottom={false} + deleteDirectMessage={props.deleteDirectMessage} /> ) : ( (); - // useEffect(() => { - // scrollToBottom(); - // console.log("Heloooooooo"); - // }, [publicMessages]); - useEffect(() => { if (prevGlobal?.theme !== global.theme) { setBackground(); @@ -153,22 +148,8 @@ export default function ChatsChannelMessages(props: Props) { const sendDM = (name: string, pubkey: string) => { if (dmMessage) { messageServiceInstance?.sendDirectMessage(pubkey, dmMessage); - - // setIsCurrentUser(true); - // setCurrentUser(name); - // setIsCommunity(false); - // setCommunityName(""); setClickedMessage(""); setDmMessage(""); - // console.log("Message is send to", name); - // if ( - // receiverPubKey && - // !props.chat.directContacts.some((contact) => contact.name === currentUser) && - // isCurrentUser - // ) { - // messageServiceInstance?.publishContacts(currentUser, receiverPubKey); - // } - if (from && from === CHATPAGE) { history?.push(`/chats/@${name}`); } @@ -181,7 +162,6 @@ export default function ChatsChannelMessages(props: Props) { const zoomInitializer = () => { const elements: HTMLElement[] = [...document.querySelectorAll(".chat-image img")]; - // .filter((x) => x.parentNode?.nodeName !== "A"); zoom = mediumZoom(elements); setBackground(); }; @@ -264,7 +244,6 @@ export default function ChatsChannelMessages(props: Props) { currentChannel && currentChannel?.removedUserIds!.filter((item) => item !== removedUserId); - console.log("newUpdatedRemovedUsers", newUpdatedRemovedUsers); updatedMetaData.removedUserIds = newUpdatedRemovedUsers; break; case 4: @@ -286,21 +265,6 @@ export default function ChatsChannelMessages(props: Props) { messageServiceInstance?.updateChannel(currentChannel!, updatedMetaData); currentChannelSetter({ ...currentChannel!, ...updatedMetaData }); setStep(0); - // if (operationType === HIDEMESSAGE) { - // setStep(0); - // setKeyDialog(false); - // fetchCommunityMessages( - // props.chat.publicMessages, - // currentChannel!, - // currentChannel?.hiddenMessageIds - // ); - // setHiddenMsgId(""); - // } - // if (operationType === BLOCKUSER || operationType === UNBLOCKUSER) { - // setStep(0); - // setKeyDialog(false); - // setRemovedUserID(""); - // } } catch (err) { error(_t("chat.error-updating-community")); } @@ -541,12 +505,14 @@ export default function ChatsChannelMessages(props: Props) { )} +
+ {pMsg.sent === 0 && ( diff --git a/src/common/components/chats/chats-direct-messages/index.tsx b/src/common/components/chats/chats-direct-messages/index.tsx index 1337e1c47c6..4e35a767d0b 100644 --- a/src/common/components/chats/chats-direct-messages/index.tsx +++ b/src/common/components/chats/chats-direct-messages/index.tsx @@ -1,28 +1,26 @@ -import React, { useContext, useEffect, useRef } from "react"; -import { DirectMessage } from "../../../../managers/message-manager-types"; +import React, { useContext, useEffect, useState } from "react"; +import usePrevious from "react-use/lib/usePrevious"; import mediumZoom, { Zoom } from "medium-zoom"; -import { Global, Theme } from "../../../store/global/types"; -import { - formatMessageTime, - formatMessageDate, - isMessageGif, - isMessageImage, - getUserChatPublicKey -} from "../utils"; -import { renderPostBody } from "@ecency/render-helper"; -import { useMappedStore } from "../../../store/use-mapped-store"; +import { Spinner } from "react-bootstrap"; import { Link } from "react-router-dom"; +import { formatMessageTime, formatMessageDate, isMessageGif, isMessageImage } from "../utils"; + +import { Theme } from "../../../store/global/types"; +import { DirectMessage } from "../../../../managers/message-manager-types"; + +import { useMappedStore } from "../../../store/use-mapped-store"; + import UserAvatar from "../../user-avatar"; import Tooltip from "../../tooltip"; +import { ChatContext } from "../chat-context-provider"; + import { failedMessageSvg, resendMessageSvg } from "../../../img/svg"; -import { Spinner } from "react-bootstrap"; -import "./index.scss"; -import { ActiveUser } from "../../../store/active-user/types"; -import usePrevious from "react-use/lib/usePrevious"; -import { NostrKeysType } from "../types"; +import { renderPostBody } from "@ecency/render-helper"; import { _t } from "../../../i18n"; -import { ChatContext } from "../chat-context-provider"; + +import "./index.scss"; +import ChatsConfirmationModal from "../chats-confirmation-modal"; interface Props { directMessages: DirectMessage[]; @@ -31,6 +29,7 @@ interface Props { isScrolled?: boolean; receiverPubKey: string; scrollToBottom?: () => void; + deleteDirectMessage: (peer: string, msgId: string) => void; } let zoom: Zoom | null = null; @@ -41,15 +40,18 @@ export default function ChatsDirectMessages(props: Props) { isScrolled, receiverPubKey, isScrollToBottom, - scrollToBottom + scrollToBottom, + deleteDirectMessage } = props; const { chat, global, activeUser } = useMappedStore(); - const { activeUserKeys } = useContext(ChatContext); + const { activeUserKeys, messageServiceInstance } = useContext(ChatContext); console.log("directMessages", directMessages); let prevGlobal = usePrevious(global); + const [step, setStep] = useState(0); + const [resendMessage, setResendMessage] = useState(); useEffect(() => { if (prevGlobal?.theme !== global.theme) { @@ -97,95 +99,120 @@ export default function ChatsDirectMessages(props: Props) { return <>; }; + const handleConfirm = () => { + switch (step) { + case 1: + if (resendMessage) { + deleteDirectMessage(receiverPubKey, resendMessage?.id); + messageServiceInstance?.sendDirectMessage(receiverPubKey!, resendMessage.content); + } + break; + default: + break; + } + setStep(0); + }; + return ( -
- {receiverPubKey ? ( - <> - {directMessages && - directMessages.map((msg, i) => { - const dayAndMonth = getFormattedDateAndDay(msg, i); - let renderedPreview = renderPostBody(msg.content, false, global.canUseWebp); - - renderedPreview = renderedPreview.replace(/]*>/g, ""); - renderedPreview = renderedPreview.replace(/<\/p>/g, ""); - - const isGif = isMessageGif(msg.content); - - const isImage = isMessageImage(msg.content); - - return ( - - {dayAndMonth} - {msg.creator !== activeUserKeys?.pub ? ( -
-
- - - - - + <> +
+ {receiverPubKey ? ( + <> + {directMessages && + directMessages.map((msg, i) => { + const dayAndMonth = getFormattedDateAndDay(msg, i); + let renderedPreview = renderPostBody(msg.content, false, global.canUseWebp); + + renderedPreview = renderedPreview.replace(/]*>/g, ""); + renderedPreview = renderedPreview.replace(/<\/p>/g, ""); + + const isGif = isMessageGif(msg.content); + + const isImage = isMessageImage(msg.content); + + return ( + + {dayAndMonth} + {msg.creator !== activeUserKeys?.pub ? ( +
+
+ + + + + +
+
+

{formatMessageTime(msg.created)}

+ +
+
+
+
-
-

{formatMessageTime(msg.created)}

- -
+ ) : ( +
+

{formatMessageTime(msg.created)}

+
+ {msg.sent === 2 && ( + + { + setStep(1); + setResendMessage(msg); + }} + > + {resendMessageSvg} + + + )}
-
-
-
- ) : ( -
-

{formatMessageTime(msg.created)}

-
- {msg.sent === 2 && ( - - { - // setKeyDialog(true); - // setStep(11); - // setResendMessage(msg); - }} - > - {resendMessageSvg} + {msg.sent === 0 && ( + + - - )} -
- {msg.sent === 0 && ( - - - - )} - {msg.sent === 2 && ( - - {failedMessageSvg} - - )} + )} + {msg.sent === 2 && ( + + {failedMessageSvg} + + )} +
-
- )} - - ); - })} - - ) : ( -

{_t("chat.not-joined")}

+ )} + + ); + })} + + ) : ( +

{_t("chat.not-joined")}

+ )} +
+ {step !== 0 && ( + { + setStep(0); + }} + onConfirm={handleConfirm} + /> )} -
+ ); } diff --git a/src/common/components/chats/chats-messages-view/index.tsx b/src/common/components/chats/chats-messages-view/index.tsx index 06d3c4cd816..dcced664dc5 100644 --- a/src/common/components/chats/chats-messages-view/index.tsx +++ b/src/common/components/chats/chats-messages-view/index.tsx @@ -37,7 +37,7 @@ const EmojiPickerStyle: EmojiPickerStyleProps = { interface Props { username: string; users: User[]; - history: History | null; + history: History; ui: UI; currentChannel: Channel; inProgress: boolean; @@ -48,6 +48,7 @@ interface Props { currentChannelSetter: (channel: Channel) => void; setInProgress: (d: boolean) => void; deletePublicMessage: (channelId: string, msgId: string) => void; + deleteDirectMessage: (peer: string, msgId: string) => void; } export default function ChatsMessagesView(props: Props) { @@ -55,9 +56,11 @@ export default function ChatsMessagesView(props: Props) { username, currentChannel, inProgress, + history, currentChannelSetter, setInProgress, - deletePublicMessage + deletePublicMessage, + deleteDirectMessage } = props; const { messageServiceInstance, receiverPubKey } = useContext(ChatContext); @@ -226,7 +229,6 @@ export default function ChatsMessagesView(props: Props) { {...props} publicMessages={publicMessages} currentChannel={currentChannel!} - activeUserKeys={activeUserKeys!} isScrollToBottom={isScrollToBottom} from={CHATPAGE} isScrolled={isScrolled} @@ -240,11 +242,11 @@ export default function ChatsMessagesView(props: Props) { )} From d4460a45541e296d06b7ee15c5aca6b143b96851 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Thu, 14 Sep 2023 16:37:09 +0500 Subject: [PATCH 069/179] Change position of message time in community chat --- .../chats/chats-channel-messages/index.scss | 133 +++++++++++++----- .../chats/chats-channel-messages/index.tsx | 26 ++-- 2 files changed, 118 insertions(+), 41 deletions(-) diff --git a/src/common/components/chats/chats-channel-messages/index.scss b/src/common/components/chats/chats-channel-messages/index.scss index 1d46f9692d1..6b972ed9fad 100644 --- a/src/common/components/chats/chats-channel-messages/index.scss +++ b/src/common/components/chats/chats-channel-messages/index.scss @@ -52,7 +52,12 @@ } .receiver-messag { display: flex; - .receiver-message-content { + + .receiver-message-wrapper { + box-sizing: content-box; + border-radius: 0 10px 10px 10px; + max-width: 70% !important; + word-wrap: break-word; @include themify(day) { background: #e4e6eb; } @@ -60,6 +65,27 @@ background: #cee2ff; } + .receiver-message-content { + color: #050505; + display: inline-block; + padding: 10px; + border-radius: 0px 10px 10px 0px; + } + + .receiver-msg-time { + @include themify(day) { + color: rgb(138, 141, 145); + } + @include themify(night) { + color: $charcoal-grey; + } + + font-size: 10px; + display: flex; + justify-content: flex-end; + margin: -5px 7px 3px 45px; + } + &.gif { background: none; padding: 0; @@ -75,39 +101,43 @@ max-width: 100%; } } - color: #050505; - // max-width: 290px; - display: inline-block; - max-width: 70% !important; - word-wrap: break-word; - padding: 10px; - border-radius: 0px 10px 10px 0px; + + &.gif, + &.chat-image { + .receiver-message-wrapper { + background: none; + } + .receiver-message-content { + padding: 0; + img { + padding-bottom: 10px; + } + } + .receiver-msg-time { + @include themify(day) { + color: rgb(138, 141, 145); + } + @include themify(night) { + color: $white-three; + } + } + } } } &.same-user-msg { margin-left: 4rem; padding-top: 1.5px; - .receiver-message-content { - // border-radius: 0; + .receiver-message-wrapper { + border-radius: 0 10px 10px 0; } } } } .sender { - margin-bottom: 0.17rem; - .sender-message-time { - color: rgb(138, 141, 145); - display: flex; - font-weight: 400; - justify-content: flex-end; - font-size: 12px; - margin: 0; - margin-right: 18px; - margin-bottom: 2px; - } + margin: 1px; + .sender-message { - // max-width: 320px; margin-left: auto; display: flex; @@ -143,19 +173,12 @@ margin-right: 7px; } - .sender-message-content { - border-radius: 10px 10px 0px; + .sender-message-wrapper { + box-sizing: content-box; + border-radius: 10px 10px 0 10px; max-width: 70%; word-wrap: break-word; - margin-bottom: 0; - color: $charcoal-grey; - font-size: 16px; - font-weight: 400; - padding: 8px 12px 8px 12px; - a { - text-decoration: underline; - color: white; - } + @include themify(day) { background-color: rgb(0, 132, 255); color: $white; @@ -164,8 +187,10 @@ background-color: $charcoal-grey; color: $white; } + &.gif { background: none; + padding: 0; img { max-width: 100%; @@ -179,6 +204,48 @@ max-width: 100%; } } + + &.gif, + &.chat-image { + .sender-message-time { + color: rgb(138, 141, 145); + } + .sender-message-content { + padding: 10px 0 0 5px; + img { + padding-bottom: 10px; + } + } + } + + .sender-message-content { + margin-bottom: 0; + color: $charcoal-grey; + font-size: 16px; + font-weight: 400; + padding: 8px 8px 8px 12px; + a { + text-decoration: underline; + color: white; + } + @include themify(day) { + color: $white; + } + @include themify(night) { + color: $white; + } + } + + .sender-message-time { + color: $white; + margin-bottom: 3px; + font-size: 10px; + display: flex; + justify-content: flex-end; + margin-right: 7px; + margin-left: 45px; + margin-top: -5px; + } } } } diff --git a/src/common/components/chats/chats-channel-messages/index.tsx b/src/common/components/chats/chats-channel-messages/index.tsx index a8d6ad09150..64adadcaa7c 100644 --- a/src/common/components/chats/chats-channel-messages/index.tsx +++ b/src/common/components/chats/chats-channel-messages/index.tsx @@ -432,17 +432,23 @@ export default function ChatsChannelMessages(props: Props) { {!isSameUserMessage && (

{name} - {formatMessageTime(pMsg.created)} + {/* {formatMessageTime(pMsg.created)} */}

)}
+ > +
+

{formatMessageTime(pMsg.created)}

+
+ {hoveredMessageId === pMsg.id && privilegedUsers.includes(activeUser?.username!) && ( @@ -470,7 +476,6 @@ export default function ChatsChannelMessages(props: Props) { onMouseEnter={() => setHoveredMessageId(pMsg.id)} onMouseLeave={() => setHoveredMessageId("")} > -

{formatMessageTime(pMsg.created)}

+ > +
+

{formatMessageTime(pMsg.created)}

+
{pMsg.sent === 0 && ( From 48615c1fde49c8d9b47e065d84570d7c70324d96 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Thu, 14 Sep 2023 19:28:23 +0500 Subject: [PATCH 070/179] Used store reducers from hook --- src/common/app.tsx | 2 +- .../chats/chat-context-provider.tsx | 4 +- .../components/chats/chat-popup/index.tsx | 17 +---- .../chats/chats-channel-messages/index.tsx | 6 +- .../chats/chats-confirmation-modal/index.scss | 13 ++++ .../chats/chats-confirmation-modal/index.tsx | 23 ++++-- .../chats/chats-direct-messages/index.tsx | 6 +- .../chats/chats-messages-view/index.tsx | 4 +- .../components/chats/import-chats/index.tsx | 30 +++----- src/managers/message-manager.tsx | 73 +++++++++---------- 10 files changed, 83 insertions(+), 95 deletions(-) create mode 100644 src/common/components/chats/chats-confirmation-modal/index.scss diff --git a/src/common/app.tsx b/src/common/app.tsx index d8e586a8a8d..222ccdbe6f8 100644 --- a/src/common/app.tsx +++ b/src/common/app.tsx @@ -197,7 +197,7 @@ const App = (props: any) => { - + diff --git a/src/common/components/chats/chat-context-provider.tsx b/src/common/components/chats/chat-context-provider.tsx index adc908d3d33..f1dfe0cc390 100644 --- a/src/common/components/chats/chat-context-provider.tsx +++ b/src/common/components/chats/chat-context-provider.tsx @@ -92,7 +92,7 @@ export default function ChatContextProvider(props: Props) { if (showSpinner) { setTimeout(() => { setShowSpinner(false); - }, 7000); + }, 6000); } }, [showSpinner]); @@ -105,7 +105,6 @@ export default function ChatContextProvider(props: Props) { pub: pubKey, priv: privKey }; - setShowSpinner(false); setActiveUserKeys(activeUserKeys); }; @@ -124,6 +123,7 @@ export default function ChatContextProvider(props: Props) { }; const joinChat = async () => { + console.log("Join chat run in context"); const { resetChat } = props; resetChat(); const keys = createNoStrAccount(); diff --git a/src/common/components/chats/chat-popup/index.tsx b/src/common/components/chats/chat-popup/index.tsx index bb6ed52ddd1..a37edb198a8 100644 --- a/src/common/components/chats/chat-popup/index.tsx +++ b/src/common/components/chats/chat-popup/index.tsx @@ -75,17 +75,14 @@ import MessageService from "../../../helper/message-service"; interface Props { history: History; - resetChat: () => void; setActiveUser: (username: string | null) => void; updateActiveUser: (data?: Account) => void; deleteUser: (username: string) => void; toggleUIProp: (what: ToggleType) => void; - deletePublicMessage: (channelId: string, msgId: string) => void; - deleteDirectMessage: (peer: string, msgId: string) => void; } export default function ChatPopUp(props: Props) { - const { activeUser, global, chat } = useMappedStore(); + const { activeUser, global, chat, resetChat } = useMappedStore(); const { messageServiceInstance, @@ -133,10 +130,6 @@ export default function ChatPopUp(props: Props) { const [innerWidth, setInnerWidth] = useState(0); const [isChatPage, setIsChatPage] = useState(false); - useEffect(() => { - console.log("chat in store", chat); - }, [chat]); - useEffect(() => { if (currentChannel && chat.leftChannelsList.includes(currentChannel.id)) { setIsCommunity(false); @@ -202,17 +195,11 @@ export default function ChatPopUp(props: Props) { const noStrPrivKey = getPrivateKey(activeUser?.username!); setNoStrPrivKey(noStrPrivKey); } - setTimeout(() => { - if (chat.channels.length === 0 && chat.directContacts.length === 0) { - setShowSpinner(false); - } - }, 5000); }, [typeof window !== "undefined" && messageServiceInstance]); useEffect(() => { const communities = getJoinedCommunities(chat.channels, chat.leftChannelsList); setCommunities(communities); - // fetchProfileData(); }, [chat.channels, chat.leftChannelsList]); useEffect(() => { @@ -448,7 +435,6 @@ export default function ChatPopUp(props: Props) { }; const handleRefreshSvgClick = () => { - const { resetChat } = props; resetChat(); handleBackArrowSvg(); if (getPrivateKey(activeUser?.username!)) { @@ -730,7 +716,6 @@ export default function ChatPopUp(props: Props) { directMessages={directMessagesList} currentUser={currentUser} isScrollToBottom={false} - deleteDirectMessage={props.deleteDirectMessage} /> ) : ( void; scrollToBottom?: () => void; currentChannelSetter: (channel: Channel) => void; - deletePublicMessage: (channelId: string, msgId: string) => void; } let zoom: Zoom | null = null; @@ -69,10 +68,9 @@ export default function ChatsChannelMessages(props: Props) { isScrolled, isActveUserRemoved, scrollToBottom, - currentChannelSetter, - deletePublicMessage + currentChannelSetter } = props; - const { chat, global, activeUser, ui, users } = useMappedStore(); + const { chat, global, activeUser, ui, users, deletePublicMessage } = useMappedStore(); const { messageServiceInstance, activeUserKeys } = useContext(ChatContext); diff --git a/src/common/components/chats/chats-confirmation-modal/index.scss b/src/common/components/chats/chats-confirmation-modal/index.scss new file mode 100644 index 00000000000..cf1333fb494 --- /dev/null +++ b/src/common/components/chats/chats-confirmation-modal/index.scss @@ -0,0 +1,13 @@ +@import "../../../../style/vars_mixins"; + +.join-community-dialog-body { + font-size: 18px; + margin-top: 12px; +} +.join-community-confirm-buttons { + text-align: right; + margin-top: 2rem; + .close-btn { + margin-right: 20px; + } +} diff --git a/src/common/components/chats/chats-confirmation-modal/index.tsx b/src/common/components/chats/chats-confirmation-modal/index.tsx index 1049e60aa12..04d440a7ac2 100644 --- a/src/common/components/chats/chats-confirmation-modal/index.tsx +++ b/src/common/components/chats/chats-confirmation-modal/index.tsx @@ -1,6 +1,10 @@ -import React from "react"; +import React, { useContext, useEffect, useState } from "react"; import { Button, Modal } from "react-bootstrap"; import { _t } from "../../../i18n"; +import LinearProgress from "../../linear-progress"; +import { ChatContext } from "../chat-context-provider"; + +import "./index.scss"; interface Props { actionType: string; @@ -10,6 +14,14 @@ interface Props { } const ChatsConfirmationModal = (props: Props) => { const { onClose, onConfirm, content, actionType } = props; + const { hasUserJoinedChat } = useContext(ChatContext); + const [inProgress, setInProgress] = useState(false); + + useEffect(() => { + if (hasUserJoinedChat && inProgress) { + setInProgress(false); + } + }, [hasUserJoinedChat]); const confirmationModalContent = ( <>
@@ -17,14 +29,12 @@ const ChatsConfirmationModal = (props: Props) => {

{actionType}

-
- {content} -
-

+ {inProgress && } +

{content}
+

)}{" "} {!isCommunity && !isCurrentUser && noStrPrivKey && ( -
+
setExpanded(true)}> { diff --git a/src/common/components/chats/chats-messages-box/index.tsx b/src/common/components/chats/chats-messages-box/index.tsx index 071f732c2ae..4fd33f2f380 100644 --- a/src/common/components/chats/chats-messages-box/index.tsx +++ b/src/common/components/chats/chats-messages-box/index.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useState } from "react"; +import React, { useEffect, useState } from "react"; import { match } from "react-router"; import { History } from "history"; import { Account } from "../../../store/accounts/types"; @@ -7,13 +7,12 @@ import { User } from "../../../store/users/types"; import ChatsMessagesHeader from "../chats-messages-header"; import ChatsMessagesView from "../chats-messages-view"; -import "./index.scss"; import { Channel, ChannelUpdate } from "../../../../managers/message-manager-types"; import LinearProgress from "../../linear-progress"; -import { NostrKeysType } from "../types"; import { formattedUserName } from "../utils"; import { useMappedStore } from "../../../store/use-mapped-store"; -import { ChatContext } from "../chat-context-provider"; + +import "./index.scss"; interface MatchParams { filter: string; @@ -26,22 +25,19 @@ interface MatchParams { interface Props { match: match; users: User[]; - history: History | null; + history: History; ui: UI; setActiveUser: (username: string | null) => void; updateActiveUser: (data?: Account) => void; deleteUser: (username: string) => void; toggleUIProp: (what: ToggleType) => void; - deletePublicMessage: (channelId: string, msgId: string) => void; } export default function ChatsMessagesBox(props: Props) { - const chatContext = useContext(ChatContext); - const { activeUserKeys } = chatContext; - const { activeUser, chat } = useMappedStore(); + const { chat } = useMappedStore(); const { channels, updatedChannel } = chat; - const { match, deletePublicMessage } = props; + const { match } = props; const username = match.params.username; const [maxHeight, setMaxHeight] = useState(0); @@ -109,7 +105,6 @@ export default function ChatsMessagesBox(props: Props) { inProgress={inProgress} currentChannelSetter={setCurrentChannel} setInProgress={setInProgress} - deletePublicMessage={deletePublicMessage} /> )} diff --git a/src/common/components/chats/chats-messages-header/index.tsx b/src/common/components/chats/chats-messages-header/index.tsx index 8a6340aabcc..f065123bbe3 100644 --- a/src/common/components/chats/chats-messages-header/index.tsx +++ b/src/common/components/chats/chats-messages-header/index.tsx @@ -13,7 +13,7 @@ import { formattedUserName } from "../utils"; interface Props { username: string; - history: History | null; + history: History; currentChannel: Channel; currentChannelSetter: (channe: Channel) => void; } diff --git a/src/common/components/chats/chats-messages-view/index.tsx b/src/common/components/chats/chats-messages-view/index.tsx index 0338c86a1aa..c05eab003c3 100644 --- a/src/common/components/chats/chats-messages-view/index.tsx +++ b/src/common/components/chats/chats-messages-view/index.tsx @@ -47,26 +47,13 @@ interface Props { toggleUIProp: (what: ToggleType) => void; currentChannelSetter: (channel: Channel) => void; setInProgress: (d: boolean) => void; - deletePublicMessage: (channelId: string, msgId: string) => void; - deleteDirectMessage: (peer: string, msgId: string) => void; } export default function ChatsMessagesView(props: Props) { - const { - username, - currentChannel, - inProgress, - history, - currentChannelSetter, - setInProgress, - deletePublicMessage, - deleteDirectMessage - } = props; + const { username, currentChannel, inProgress, currentChannelSetter, setInProgress } = props; const { messageServiceInstance, receiverPubKey } = useContext(ChatContext); - // console.log("Receiver pubkey in Message view component", receiverPubKey, setReceiverPubKey); - const messagesBoxRef = useRef(null); const { chat, activeUser } = useMappedStore(); @@ -233,7 +220,6 @@ export default function ChatsMessagesView(props: Props) { from={CHATPAGE} isScrolled={isScrolled} isActveUserRemoved={isActveUserRemoved} - // deletePublicMessage={deletePublicMessage} scrollToBottom={scrollToBottom} currentChannelSetter={currentChannelSetter} /> @@ -246,7 +232,6 @@ export default function ChatsMessagesView(props: Props) { isScrolled={isScrolled} receiverPubKey={receiverPubKey} isScrollToBottom={isScrollToBottom} - // deleteDirectMessage={deleteDirectMessage} scrollToBottom={scrollToBottom} /> )} diff --git a/src/common/components/chats/chats-sidebar/indes.tsx b/src/common/components/chats/chats-sidebar/indes.tsx index 11ea4684aea..e0c0d92d704 100644 --- a/src/common/components/chats/chats-sidebar/indes.tsx +++ b/src/common/components/chats/chats-sidebar/indes.tsx @@ -198,7 +198,10 @@ export default function ChatsSideBar(props: Props) { {userList.map((user) => ( setSearchText("")} + onClick={() => { + setSearchText(""); + setSearchInProgress(false); + }} key={user.account} >
{ +// const response = await getAccountFull(username); +// console.log("response", response); +// const { posting_json_metadata } = response; +// const profile = JSON.parse(posting_json_metadata!).profile; +// console.log("profile", profile); +// const { nsKey } = profile || {}; +// console.log("nsKey", nsKey); +// return nsKey; +// }; + export const getUserChatPublicKey = async (username: string) => { const response = await getAccountFull(username); - const { posting_json_metadata } = response; - const profile = JSON.parse(posting_json_metadata!).profile; - const { nsKey } = profile || {}; - // console.log("nsKey", nsKey); - return nsKey; + if (response && response.posting_json_metadata) { + const { posting_json_metadata } = response; + const profile = JSON.parse(posting_json_metadata).profile; + if (profile) { + const { nsKey } = profile || {}; + return nsKey; + } + } + return null; }; diff --git a/src/common/components/navbar/index.tsx b/src/common/components/navbar/index.tsx index e778c821fe6..7b7a83c0e98 100644 --- a/src/common/components/navbar/index.tsx +++ b/src/common/components/navbar/index.tsx @@ -9,7 +9,6 @@ import SwitchLang from "../switch-lang"; import ToolTip from "../tooltip"; import Search from "../search"; import Login from "../login"; -import UserNav from "../user-nav"; import UserNotifications from "../notifications"; import Gallery from "../gallery"; import Drafts from "../drafts"; @@ -48,6 +47,7 @@ import { useLocation } from "react-router"; import usePrevious from "react-use/lib/usePrevious"; import { Theme } from "../../store/global/types"; import "./_index.scss"; +import { UserNav } from "../user-nav"; const GLOBAL_FILTERS = [ "engine", diff --git a/src/common/components/user-nav/index.spec.tsx b/src/common/components/user-nav/index.spec.tsx index 25a861501f1..4c2860423b9 100644 --- a/src/common/components/user-nav/index.spec.tsx +++ b/src/common/components/user-nav/index.spec.tsx @@ -3,7 +3,7 @@ import { StaticRouter } from "react-router-dom"; import { createBrowserHistory, createLocation } from "history"; -import UserNav from "./index"; +import { UserNav } from "./index"; import { dynamicPropsIntance1, diff --git a/src/common/components/user-nav/index.tsx b/src/common/components/user-nav/index.tsx index 0683ee7d1a8..20473f3ee83 100644 --- a/src/common/components/user-nav/index.tsx +++ b/src/common/components/user-nav/index.tsx @@ -1,193 +1,92 @@ -import React, { Component } from "react"; -import { History, Location } from "history"; +import React, { useState } from "react"; +import { _t } from "../../i18n"; import { Link } from "react-router-dom"; - -import { Global } from "../../store/global/types"; -import { User } from "../../store/users/types"; -import { Account } from "../../store/accounts/types"; -import { ActiveUser } from "../../store/active-user/types"; -import { ToggleType, UI } from "../../store/ui/types"; -import { NotificationFilter, Notifications } from "../../store/notifications/types"; -import { DynamicProps } from "../../store/dynamic-props/types"; - +import { useMappedStore } from "../../store/use-mapped-store"; +import { useLocation } from "react-router"; +import "./_index.scss"; +import { bellOffSvg, bellSvg, chevronUpSvg, messangerSvg, rocketSvg } from "../../img/svg"; +import { downVotingPower, votingPower } from "../../api/hive"; +import { WalletBadge } from "./wallet-badge"; import ToolTip from "../tooltip"; -import UserAvatar from "../user-avatar"; import DropDown from "../dropdown"; import UserNotifications from "../notifications"; +import { PurchaseQrDialog } from "../purchase-qr"; +import { History } from "history"; +import UserAvatar from "../user-avatar"; import Gallery from "../gallery"; import Drafts from "../drafts"; import Bookmarks from "../bookmarks"; import Schedules from "../schedules"; import Fragments from "../fragments"; -import { _t } from "../../i18n"; - -import HiveWallet from "../../helper/hive-wallet"; - -import { - creditCardSvg, - gifCardSvg, - bellSvg, - bellOffSvg, - chevronUpSvg, - rocketSvg, - messangerSvg -} from "../../img/svg"; - -import { votingPower, downVotingPower } from "../../api/hive"; -import { - updateNotificationsSettings, - setNotificationsSettingsItem -} from "../../store/notifications"; -import { PurchaseQrDialog } from "../purchase-qr"; -import { useMappedStore } from "../../store/use-mapped-store"; -import { useLocation } from "react-router"; -import "./_index.scss"; - -export class WalletBadge extends Component<{ - activeUser: ActiveUser; - dynamicProps: DynamicProps; - icon?: JSX.Element; -}> { - render() { - const { activeUser, dynamicProps } = this.props; - - let hasUnclaimedRewards = false; - const { data: account } = activeUser; - - if (account.__loaded) { - hasUnclaimedRewards = new HiveWallet(account, dynamicProps).hasUnclaimedRewards; - } - - return ( - <> - - - {hasUnclaimedRewards && } - {this.props.icon ?? creditCardSvg} - - - - ); - } -} - -class PointsBadge extends Component<{ activeUser: ActiveUser }> { - render() { - const { activeUser } = this.props; - - let hasUnclaimedPoints = activeUser.points.uPoints !== "0.000"; - - return ( - <> - - - {hasUnclaimedPoints && } - {gifCardSvg} - - - - ); - } -} +export * from "./wallet-badge"; interface Props { - global: Global; - dynamicProps: DynamicProps; history: History; - location: Location; - users: User[]; - ui: UI; - activeUser: ActiveUser; - notifications: Notifications; - setActiveUser: (username: string | null) => void; - updateActiveUser: (data?: Account) => void; - deleteUser: (username: string) => void; - addAccount: (data: Account) => void; - fetchNotifications: (since: string | null) => void; - fetchUnreadNotificationCount: () => void; - setNotificationsFilter: (filter: NotificationFilter | null) => void; - markNotifications: (id: string | null) => void; - toggleUIProp: (what: ToggleType) => void; - muteNotifications: () => void; - unMuteNotifications: () => void; - updateNotificationsSettings: typeof updateNotificationsSettings; - setNotificationsSettingsItem: typeof setNotificationsSettingsItem; -} - -interface State { - gallery: boolean; - drafts: boolean; - bookmarks: boolean; - schedules: boolean; - fragments: boolean; - showPurchaseDialog: boolean; + icon?: JSX.Element; } -class UserNav extends Component { - state: State = { - gallery: false, - drafts: false, - bookmarks: false, - schedules: false, - fragments: false, - showPurchaseDialog: false - }; - - toggleLogin = () => { - const { toggleUIProp } = this.props; - toggleUIProp("login"); - }; - - toggleDrafts = () => { - const { drafts } = this.state; - this.setState({ drafts: !drafts }); - }; - - toggleGallery = () => { - const { gallery } = this.state; - this.setState({ gallery: !gallery }); - }; - - toggleBookmarks = () => { - const { bookmarks } = this.state; - this.setState({ bookmarks: !bookmarks }); - }; - - toggleSchedules = () => { - const { schedules } = this.state; - this.setState({ schedules: !schedules }); - }; - - toggleFragments = () => { - const { fragments } = this.state; - this.setState({ fragments: !fragments }); - }; - - toggleNotifications = () => { - const { toggleUIProp } = this.props; - toggleUIProp("notifications"); - }; - - goToSettings = () => { - const { activeUser, history } = this.props; - history.push(`/@${activeUser.username}/settings`); - }; - - render() { - const { gallery, drafts, bookmarks, schedules, fragments } = this.state; - const { activeUser, ui, notifications, global, dynamicProps } = this.props; - const { unread } = notifications; +export const UserNav = ({ history, icon }: Props) => { + const location = useLocation(); + const { global, ui, activeUser, notifications, setActiveUser, toggleUIProp } = useMappedStore(); + + const [gallery, setGallery] = useState(false); + const [drafts, setDrafts] = useState(false); + const [bookmarks, setBookmarks] = useState(false); + const [schedules, setSchedules] = useState(false); + const [fragments, setFragments] = useState(false); + const [showPurchaseDialog, setShowPurchaseDialog] = useState(false); + + const dropDownItems = [ + { + label: _t("user-nav.profile"), + href: `/@${activeUser?.username}` + }, + ...(global.usePrivate + ? [ + { + label: _t("user-nav.drafts"), + onClick: () => setDrafts(!drafts) + }, + { + label: _t("user-nav.gallery"), + onClick: () => setGallery(!gallery) + }, + { + label: _t("user-nav.bookmarks"), + onClick: () => setBookmarks(!bookmarks) + }, + { + label: _t("user-nav.schedules"), + onClick: () => setSchedules(!schedules) + }, + { + label: _t("user-nav.fragments"), + onClick: () => setFragments(!fragments) + } + ] + : []), + { + label: _t("user-nav.settings"), + onClick: () => history.push(`/@${activeUser?.username}/settings`) + }, + { + label: _t("g.login-as"), + onClick: () => toggleUIProp("login") + }, + { + label: _t("user-nav.logout"), + onClick: () => { + setActiveUser(null); + } + } + ]; - const preDropDownElem = activeUser.data.__loaded ? ( + const dropDownConfig = { + history: history, + label: , + items: dropDownItems, + preElem: activeUser?.data.__loaded ? (
{_t("user-nav.vote-power")}
@@ -203,160 +102,55 @@ class UserNav extends Component {
- ) : undefined; - - const dropDownItems = [ - { - label: _t("user-nav.profile"), - href: `/@${activeUser.username}` - }, - ...(global.usePrivate - ? [ - { - label: _t("user-nav.drafts"), - onClick: this.toggleDrafts - }, - { - label: _t("user-nav.gallery"), - onClick: this.toggleGallery - }, - { - label: _t("user-nav.bookmarks"), - onClick: this.toggleBookmarks - }, - { - label: _t("user-nav.schedules"), - onClick: this.toggleSchedules - }, - { - label: _t("user-nav.fragments"), - onClick: this.toggleFragments - } - ] - : []), - { - label: _t("user-nav.settings"), - onClick: this.goToSettings - }, - { - label: _t("g.login-as"), - onClick: this.toggleLogin - }, - { - label: _t("user-nav.logout"), - onClick: () => { - const { setActiveUser } = this.props; - setActiveUser(null); - } - } - ]; - - const dropDownConfig = { - history: this.props.history, - label: , - items: dropDownItems, - preElem: preDropDownElem - }; - - return ( - <> -
- {global.usePrivate && ( -
this.setState({ showPurchaseDialog: true })} - className="user-points cursor-pointer" - > - {rocketSvg} -
- )} - - - {global.usePrivate && ( - <> - - - {unread > 0 && ( - - {unread.toString().length < 3 ? unread : "..."} - - )} - {global.notifications ? bellSvg : bellOffSvg} - - - - - {messangerSvg} - - - - )} - - -
- {ui.notifications && } - {gallery && } - {drafts && } - {bookmarks && } - {schedules && } - {fragments && } - this.setState({ showPurchaseDialog: v })} - activeUser={activeUser} - location={this.props.location} - /> - - ); - } -} - -export default ({ history }: Pick) => { - const { - global, - dynamicProps, - users, - ui, - activeUser, - notifications, - setActiveUser, - updateActiveUser, - deleteUser, - addAccount, - fetchNotifications, - fetchUnreadNotificationCount, - setNotificationsFilter, - markNotifications, - toggleUIProp, - muteNotifications, - unMuteNotifications, - updateNotificationsSettings, - setNotificationsSettingsItem - } = useMappedStore(); - const location = useLocation(); + ) : undefined + }; return ( - + <> +
+ {global.usePrivate && ( +
setShowPurchaseDialog(true)} className="user-points cursor-pointer"> + {rocketSvg} +
+ )} + + + {global.usePrivate ? ( + <> + + toggleUIProp("notifications")}> + {notifications.unread > 0 && ( + + {notifications.unread.toString().length < 3 ? notifications.unread : "..."} + + )} + {global.notifications ? bellSvg : bellOffSvg} + + + + + {messangerSvg} + + + + ) : ( + <> + )} + + +
+ {ui.notifications && } + {gallery && setGallery(false)} />} + {drafts && setDrafts(false)} />} + {bookmarks && setBookmarks(false)} />} + {schedules && setSchedules(false)} />} + {fragments && setFragments(false)} />} + + ); }; diff --git a/src/common/pages/chats/index.tsx b/src/common/pages/chats/index.tsx index 44c3f9fc9b9..1354d39ca68 100644 --- a/src/common/pages/chats/index.tsx +++ b/src/common/pages/chats/index.tsx @@ -88,7 +88,7 @@ export const Chats = (props: Props) => {
) : ( // Handle the case when the user hasn't joined any community here - + )} ) : ( diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts index 6bbcb79a15c..04d16e296d5 100644 --- a/src/common/store/chat/index.ts +++ b/src/common/store/chat/index.ts @@ -327,6 +327,7 @@ export const replaceDirectMessage = (peer: string, data: DirectMessage) => (disp export const verifyDirectMessageSending = (peer: string, data: DirectMessage) => (dispatch: Dispatch) => { + console.log("verifyDirectMessageSending called"); dispatch(verifyDirectMessageSendingAct(peer, data)); }; diff --git a/src/desktop/app/components/navbar/index.tsx b/src/desktop/app/components/navbar/index.tsx index f7ccdacb252..96485cdcc91 100644 --- a/src/desktop/app/components/navbar/index.tsx +++ b/src/desktop/app/components/navbar/index.tsx @@ -21,7 +21,7 @@ import { DynamicProps } from "../../../../common/store/dynamic-props/types"; import ToolTip from "../../../../common/components/tooltip"; import Login from "../../../../common/components/login"; -import UserNav from "../../../../common/components/user-nav"; +import { UserNav } from "../../../../common/components/user-nav"; import DropDown from "../../../../common/components/dropdown"; import Updater from "../updater"; import SwitchLang from "../../../../common/components/switch-lang"; diff --git a/src/managers/message-manager.tsx b/src/managers/message-manager.tsx index 3ce46e0e1a7..0f4672a5ae7 100644 --- a/src/managers/message-manager.tsx +++ b/src/managers/message-manager.tsx @@ -19,6 +19,7 @@ import { getProfileMetaData, getPrivateKey } from "../common/components/chats/ut import { useMappedStore } from "../common/store/use-mapped-store"; import { ChatContext } from "../common/components/chats/chat-context-provider"; +import { useTimeoutFn } from "react-use"; export const setNostrkeys = (keys: NostrKeysType) => { const detail: NostrKeysType = { @@ -81,11 +82,21 @@ const MessageManager = () => { useEffect(() => { if (!messageServiceInstance && keys?.priv) { const messageService = initMessageServiceInstance(keys); - setMessageServiceInstance(messageService!); + console.log("messageService", messageService!!); + setMessageServiceInstance(messageService); setMessageService(messageService!); } }, [keys]); + // useEffect(() => { + // if (messageServiceInstance === undefined && keys?.priv) { + // const messageService = initMessageServiceInstance(keys); + // if (messageService) { + // setMessageService(messageService); + // } + // } + // }, [keys]); + useEffect(() => { if (activeUser) { getNostrKeys(activeUser); @@ -95,7 +106,8 @@ const MessageManager = () => { const createMSInstance = (e: Event) => { const detail = (e as CustomEvent).detail as NostrKeysType; const messageService = initMessageServiceInstance(detail); - setMessageServiceInstance(messageService!); + console.log("messageService", messageService!!); + setMessageServiceInstance(messageService); setMessageService(messageService!); }; @@ -187,7 +199,18 @@ const MessageManager = () => { const { peer, id } = m; setReplacedDirectMessagesBuffer((prevBuffer) => [...prevBuffer, id]); addDirectMessages(peer, m); - checkDirectMessageSending(peer, m); + const startTimeout = () => { + console.log("Start time out called"); + useTimeoutFn(() => { + console.log("Inner called"); + checkDirectMessageSending(peer, m); + }, 2000); + }; + + // Start the timeout + startTimeout(); + // const [startTimeout] = useTimeoutFn(checkDirectMessageSending(peer, m), 20000); + // checkDirectMessageSending(peer, m); }); }; @@ -210,9 +233,10 @@ const MessageManager = () => { }, [messageService, chat.directMessages]); const checkDirectMessageSending = (peer: string, data: DirectMessage) => { - setTimeout(() => { - verifyDirectMessageSending(peer, data); - }, 20000); + // setTimeout(() => { + console.log("checkDirectMessageSending called"); + verifyDirectMessageSending(peer, data); + // }, 20000); }; // // Direct message handler after sent From 144bbbdbe35ef4e9cafcb0dc8dbfd8263bef4d93 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Sat, 16 Sep 2023 15:40:13 +0500 Subject: [PATCH 072/179] Show Info message to user if any community chat is not enabled yet by the community owner --- .../chats/chat-context-provider.tsx | 34 +++++-------------- .../components/chats/chat-input/index.tsx | 25 +++++++++++++- .../chats/chat-popup/chat-constants.ts | 1 + .../components/chats/chat-popup/index.tsx | 4 +++ .../chats/chats-messages-box/index.tsx | 29 ++++++++++++++-- .../chats/chats-messages-view/index.tsx | 2 ++ .../chats/join-community-chat-btn/index.tsx | 4 +-- 7 files changed, 68 insertions(+), 31 deletions(-) diff --git a/src/common/components/chats/chat-context-provider.tsx b/src/common/components/chats/chat-context-provider.tsx index bfba6377d45..92fb0315ed3 100644 --- a/src/common/components/chats/chat-context-provider.tsx +++ b/src/common/components/chats/chat-context-provider.tsx @@ -18,15 +18,15 @@ interface Context { revealPrivKey: boolean; chatPrivKey: string; receiverPubKey: string; - messageServiceInstance: MessageService | undefined; + messageServiceInstance: MessageService | null; hasUserJoinedChat: boolean; setRevealPrivKey: (d: boolean) => void; setShowSpinner: (d: boolean) => void; setChatPrivKey: (key: string) => void; setActiveUserKeys: (keys: NostrKeysType) => void; setReceiverPubKey: (key: string) => void; - setMessageServiceInstance: (instance: MessageService | undefined) => void; - initMessageServiceInstance: (keys: Keys) => MessageService | undefined; + setMessageServiceInstance: (instance: MessageService | null) => void; + initMessageServiceInstance: (keys: Keys) => MessageService | null; joinChat: () => void; } @@ -41,7 +41,7 @@ export const ChatContext = React.createContext({ revealPrivKey: false, chatPrivKey: "", receiverPubKey: "", - messageServiceInstance: undefined, + messageServiceInstance: null, hasUserJoinedChat: false, setRevealPrivKey: () => {}, setShowSpinner: () => {}, @@ -49,7 +49,7 @@ export const ChatContext = React.createContext({ setActiveUserKeys: () => {}, setReceiverPubKey: () => {}, setMessageServiceInstance: () => {}, - initMessageServiceInstance: () => (({} as MessageService) || undefined), + initMessageServiceInstance: () => (({} as MessageService) || null), joinChat: () => {} }); @@ -61,9 +61,7 @@ export default function ChatContextProvider(props: Props) { const [chatPrivKey, setChatPrivKey] = useState(""); const [revealPrivKey, setRevealPrivKey] = useState(false); const [receiverPubKey, setReceiverPubKey] = useState(""); - const [messageServiceInstance, setMessageServiceInstance] = useState( - undefined - ); + const [messageServiceInstance, setMessageServiceInstance] = useState(null); const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); const [shouldUpdateProfile, setShouldUpdateProfile] = useState(false); @@ -92,7 +90,7 @@ export default function ChatContextProvider(props: Props) { if (showSpinner) { setTimeout(() => { setShowSpinner(false); - }, 6000); + }, 3000); } }, [showSpinner]); @@ -108,27 +106,13 @@ export default function ChatContextProvider(props: Props) { setActiveUserKeys(activeUserKeys); }; - // const initMessageServiceInstance = (keys: Keys) => { - // if (messageServiceInstance) { - // messageServiceInstance.close(); - // setMessageServiceInstance(undefined); - // } - - // let newMessageService: MessageService | undefined = undefined; - // if (keys) { - // newMessageService = new MessageService(keys.priv, keys.pub); - // setMessageServiceInstance(newMessageService); - // } - // return newMessageService; - // }; - const initMessageServiceInstance = (keys: Keys) => { if (messageServiceInstance) { messageServiceInstance.close(); - setMessageServiceInstance(undefined); + setMessageServiceInstance(null); } - let newMessageService: MessageService | undefined = undefined; + let newMessageService: MessageService | null = null; if (keys) { newMessageService = new MessageService(keys.priv, keys.pub); setMessageServiceInstance(newMessageService); diff --git a/src/common/components/chats/chat-input/index.tsx b/src/common/components/chats/chat-input/index.tsx index eca1dd740e9..c58061737e9 100644 --- a/src/common/components/chats/chat-input/index.tsx +++ b/src/common/components/chats/chat-input/index.tsx @@ -7,6 +7,7 @@ import { EmojiPickerStyleProps } from "../types"; import ClickAwayListener from "../../clickaway-listener"; import EmojiPicker from "../../emoji-picker/index-old"; +// import { EmojiPicker } from "../../emoji-picker"; import GifPicker from "../../gif-picker"; import { error } from "../../feedback"; import Tooltip from "../../tooltip"; @@ -49,9 +50,9 @@ export default function ChatInput(props: Props) { const [message, setMessage] = useState(""); const [shGif, setShGif] = useState(false); const [isMessageText, setIsMessageText] = useState(false); + // const [isMounted, setIsMounted] = useState(false); const { messageServiceInstance, chatPrivKey } = useContext(ChatContext); - // console.log("yaha check kar ka dekho", messageServiceInstance, chatPrivKey); const { isCommunity, @@ -65,6 +66,15 @@ export default function ChatInput(props: Props) { receiverPubKey } = props; + // useEffect(() => { + // setIsMounted(true); + // setShowEmojiPicker(true); + // return () => { + // setShowEmojiPicker(false); + // setIsMounted(false); + // }; + // }, []); + useEffect(() => { if (!isCurrentUser && !isCommunity) { setMessage(""); @@ -210,6 +220,19 @@ export default function ChatInput(props: Props) {
+ {/*
+
+ +
{emoticonHappyOutlineSvg}
+
+ {showEmojiPicker && isMounted && ( + handleEmojiSelection(e)} + /> + )} +
+
*/} {message.length === 0 && ( shGif && setShGif(false)}> diff --git a/src/common/components/chats/chat-popup/chat-constants.ts b/src/common/components/chats/chat-popup/chat-constants.ts index 5401fcde0b6..66d53a8a3ca 100644 --- a/src/common/components/chats/chat-popup/chat-constants.ts +++ b/src/common/components/chats/chat-popup/chat-constants.ts @@ -43,3 +43,4 @@ export const RESENDMESSAGE = "resendMessage"; export const CHAT = "chat"; export const COMMUNITY = "community"; export const CHATPAGE = "chatPage"; +export const CHANNEL = "channel"; diff --git a/src/common/components/chats/chat-popup/index.tsx b/src/common/components/chats/chat-popup/index.tsx index 54288038a1c..450fe4070a5 100644 --- a/src/common/components/chats/chat-popup/index.tsx +++ b/src/common/components/chats/chat-popup/index.tsx @@ -131,6 +131,10 @@ export default function ChatPopUp(props: Props) { const [innerWidth, setInnerWidth] = useState(0); const [isChatPage, setIsChatPage] = useState(false); + useEffect(() => { + console.log("chat in store", chat); + }, [chat]); + useEffect(() => { if (currentChannel && chat.leftChannelsList.includes(currentChannel.id)) { setIsCommunity(false); diff --git a/src/common/components/chats/chats-messages-box/index.tsx b/src/common/components/chats/chats-messages-box/index.tsx index 4fd33f2f380..64bd8289896 100644 --- a/src/common/components/chats/chats-messages-box/index.tsx +++ b/src/common/components/chats/chats-messages-box/index.tsx @@ -9,10 +9,11 @@ import ChatsMessagesView from "../chats-messages-view"; import { Channel, ChannelUpdate } from "../../../../managers/message-manager-types"; import LinearProgress from "../../linear-progress"; -import { formattedUserName } from "../utils"; +import { formattedUserName, getProfileMetaData } from "../utils"; import { useMappedStore } from "../../../store/use-mapped-store"; import "./index.scss"; +import { CHANNEL } from "../chat-popup/chat-constants"; interface MatchParams { filter: string; @@ -43,13 +44,25 @@ export default function ChatsMessagesBox(props: Props) { const [maxHeight, setMaxHeight] = useState(0); const [currentChannel, setCurrentChannel] = useState(); const [inProgress, setInProgress] = useState(false); + const [isCommunityChatEnabled, setIsCommunityChatEnabled] = useState(false); useEffect(() => { setMaxHeight(window.innerHeight - 68); }, [typeof window !== "undefined"]); useEffect(() => { - console.log("state has been updated", currentChannel); + if (username && !username.startsWith("@")) { + const isCommunity = chat.channels.some((channel) => channel.communityName === username); + if (!isCommunity) { + getCommunityProfile(); + } else { + setIsCommunityChatEnabled(true); + } + } + }, [username]); + + useEffect(() => { + console.log("Observe here currentChannel", currentChannel); }, [currentChannel]); useEffect(() => { @@ -82,6 +95,12 @@ export default function ChatsMessagesBox(props: Props) { } }; + const getCommunityProfile = async () => { + const communityProfile = await getProfileMetaData(username); + const haschannelMetaData = communityProfile && communityProfile.hasOwnProperty(CHANNEL); + setIsCommunityChatEnabled(haschannelMetaData); + }; + return ( <>
@@ -89,7 +108,7 @@ export default function ChatsMessagesBox(props: Props) {

Select a chat or start a new conversation

- ) : ( + ) : isCommunityChatEnabled ? ( <> + ) : ( +

+ Community chat not started yet +

)}
diff --git a/src/common/components/chats/chats-messages-view/index.tsx b/src/common/components/chats/chats-messages-view/index.tsx index c05eab003c3..a4edbce6b78 100644 --- a/src/common/components/chats/chats-messages-view/index.tsx +++ b/src/common/components/chats/chats-messages-view/index.tsx @@ -69,6 +69,8 @@ export default function ChatsMessagesView(props: Props) { const [removedUsers, setRemovedUsers] = useState([]); const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); + console.log("Current channel in messages view", props.currentChannel); + useEffect(() => { getActiveUserKeys(); isDirectUserOrCommunity(); diff --git a/src/common/components/chats/join-community-chat-btn/index.tsx b/src/common/components/chats/join-community-chat-btn/index.tsx index 109791f3800..2c6f41893f4 100644 --- a/src/common/components/chats/join-community-chat-btn/index.tsx +++ b/src/common/components/chats/join-community-chat-btn/index.tsx @@ -9,7 +9,7 @@ import { _t } from "../../../i18n"; import { useMappedStore } from "../../../store/use-mapped-store"; import { Channel, communityModerator } from "../../../../managers/message-manager-types"; import { ChatContext } from "../chat-context-provider"; -import { NOSTRKEY } from "../chat-popup/chat-constants"; +import { CHANNEL, NOSTRKEY } from "../chat-popup/chat-constants"; import { getProfileMetaData, setChannelMetaData } from "../utils"; interface Props { @@ -67,7 +67,7 @@ export default function JoinCommunityChatBtn(props: Props) { const fetchCommunityProfile = async () => { const communityProfile = await getProfileMetaData(props.community?.name); - const haschannelMetaData = communityProfile && communityProfile.hasOwnProperty("channel"); + const haschannelMetaData = communityProfile && communityProfile.hasOwnProperty(CHANNEL); setIsChatEnabled(haschannelMetaData); if (!currentChannel) { From cffd829afab2905f4238adff7dfcafc3fa27d5e2 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Sat, 16 Sep 2023 16:21:34 +0500 Subject: [PATCH 073/179] Minor fixes --- src/common/app.tsx | 6 +-- .../chats/chat-context-provider.tsx | 37 +++++++++---------- .../components/chats/chat-popup/index.tsx | 4 +- 3 files changed, 22 insertions(+), 25 deletions(-) diff --git a/src/common/app.tsx b/src/common/app.tsx index 222ccdbe6f8..4639441efb2 100644 --- a/src/common/app.tsx +++ b/src/common/app.tsx @@ -29,8 +29,8 @@ import { useMappedStore } from "./store/use-mapped-store"; import { EntriesCacheManager } from "./core"; import { UserActivityRecorder } from "./components/user-activity-recorder"; -import ChatContextProvider from "./components/chats/chat-context-provider"; -import ChatPopUp from "./components/chats/chat-popup"; +import { ChatContextProvider } from "./components/chats/chat-context-provider"; +import { ChatPopUp } from "./components/chats/chat-popup"; // Define lazy pages const ProfileContainer = loadable(() => import("./pages/profile-functional")); @@ -103,7 +103,7 @@ const App = (props: any) => { {/**/} - + diff --git a/src/common/components/chats/chat-context-provider.tsx b/src/common/components/chats/chat-context-provider.tsx index 92fb0315ed3..7770e1839de 100644 --- a/src/common/components/chats/chat-context-provider.tsx +++ b/src/common/components/chats/chat-context-provider.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from "react"; import { Keys } from "../../../managers/message-manager-types"; +import useDebounce from "react-use/lib/useDebounce"; import MessageService from "../../helper/message-service"; import { useMappedStore } from "../../store/use-mapped-store"; import { NostrKeysType } from "./types"; @@ -11,6 +12,7 @@ import { } from "./utils"; import * as ls from "../../util/local-storage"; import { setNostrkeys } from "../../../managers/message-manager"; +import { useMount } from "react-use"; interface Context { activeUserKeys: NostrKeysType; @@ -32,7 +34,6 @@ interface Context { interface Props { children: JSX.Element | JSX.Element[]; - resetChat: () => void; } export const ChatContext = React.createContext({ @@ -53,8 +54,8 @@ export const ChatContext = React.createContext({ joinChat: () => {} }); -export default function ChatContextProvider(props: Props) { - const { activeUser } = useMappedStore(); +export const ChatContextProvider = (props: Props) => { + const { activeUser, resetChat } = useMappedStore(); const [activeUserKeys, setActiveUserKeys] = useState({ pub: " ", priv: "" }); const [showSpinner, setShowSpinner] = useState(true); @@ -65,9 +66,9 @@ export default function ChatContextProvider(props: Props) { const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); const [shouldUpdateProfile, setShouldUpdateProfile] = useState(false); - useEffect(() => { + useMount(() => { getActiveUserKeys(); - }, []); + }); useEffect(() => { if (messageServiceInstance) { @@ -86,24 +87,22 @@ export default function ChatContextProvider(props: Props) { } }, [shouldUpdateProfile, messageServiceInstance]); - useEffect(() => { - if (showSpinner) { - setTimeout(() => { - setShowSpinner(false); - }, 3000); - } - }, [showSpinner]); + // useEffect(() => { + // if (showSpinner) { + // setTimeout(() => { + // setShowSpinner(false); + // }, 3000); + // } + // }, [showSpinner]); + + useDebounce(() => setShowSpinner(false), 6000, [showSpinner]); const getActiveUserKeys = async () => { const pubKey = await getUserChatPublicKey(activeUser?.username!); const privKey = getPrivateKey(activeUser?.username!); setHasUserJoinedChat(!!pubKey); setChatPrivKey(privKey); - const activeUserKeys = { - pub: pubKey, - priv: privKey - }; - setActiveUserKeys(activeUserKeys); + setActiveUserKeys({ pub: pubKey, priv: privKey }); }; const initMessageServiceInstance = (keys: Keys) => { @@ -122,8 +121,6 @@ export default function ChatContextProvider(props: Props) { }; const joinChat = async () => { - console.log("Join chat run in context"); - const { resetChat } = props; resetChat(); const keys = createNoStrAccount(); ls.set(`${activeUser?.username}_nsPrivKey`, keys.priv); @@ -158,4 +155,4 @@ export default function ChatContextProvider(props: Props) { {props.children} ); -} +}; diff --git a/src/common/components/chats/chat-popup/index.tsx b/src/common/components/chats/chat-popup/index.tsx index 450fe4070a5..3a30701cdc8 100644 --- a/src/common/components/chats/chat-popup/index.tsx +++ b/src/common/components/chats/chat-popup/index.tsx @@ -82,7 +82,7 @@ interface Props { toggleUIProp: (what: ToggleType) => void; } -export default function ChatPopUp(props: Props) { +export const ChatPopUp = (props: Props) => { const { activeUser, global, chat, resetChat } = useMappedStore(); const { @@ -943,4 +943,4 @@ export default function ChatPopUp(props: Props) { )} ); -} +}; From b65d66045cce4cf021faf3a857763a29c81d7b43 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Sat, 16 Sep 2023 19:10:46 +0500 Subject: [PATCH 074/179] Add option to user to join any community from chat extended page --- .../chats/chat-context-provider.tsx | 2 +- .../chats/chats-messages-box/index.scss | 7 + .../chats/chats-messages-box/index.tsx | 124 ++++++++++++------ .../components/chats/chats-sidebar/indes.tsx | 13 +- .../components/chats/chats-sidebar/index.scss | 2 +- 5 files changed, 98 insertions(+), 50 deletions(-) diff --git a/src/common/components/chats/chat-context-provider.tsx b/src/common/components/chats/chat-context-provider.tsx index 7770e1839de..51dc7751c3a 100644 --- a/src/common/components/chats/chat-context-provider.tsx +++ b/src/common/components/chats/chat-context-provider.tsx @@ -95,7 +95,7 @@ export const ChatContextProvider = (props: Props) => { // } // }, [showSpinner]); - useDebounce(() => setShowSpinner(false), 6000, [showSpinner]); + useDebounce(() => setShowSpinner(false), 3000, [showSpinner]); const getActiveUserKeys = async () => { const pubKey = await getUserChatPublicKey(activeUser?.username!); diff --git a/src/common/components/chats/chats-messages-box/index.scss b/src/common/components/chats/chats-messages-box/index.scss index fcf93fe9ca6..73d644d8d86 100644 --- a/src/common/components/chats/chats-messages-box/index.scss +++ b/src/common/components/chats/chats-messages-box/index.scss @@ -20,6 +20,9 @@ height: calc(100vh - 70px); font-size: 22px; font-weight: 700; + display: flex; + align-items: center; + justify-content: center; .start-chat { @include themify(day) { @@ -29,5 +32,9 @@ color: $white; } } + .info-message { + font-size: 16px; + // font-weight: 100; + } } } diff --git a/src/common/components/chats/chats-messages-box/index.tsx b/src/common/components/chats/chats-messages-box/index.tsx index 64bd8289896..789b4f0cf58 100644 --- a/src/common/components/chats/chats-messages-box/index.tsx +++ b/src/common/components/chats/chats-messages-box/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { match } from "react-router"; import { History } from "history"; import { Account } from "../../../store/accounts/types"; @@ -14,6 +14,8 @@ import { useMappedStore } from "../../../store/use-mapped-store"; import "./index.scss"; import { CHANNEL } from "../chat-popup/chat-constants"; +import { Button } from "react-bootstrap"; +import { ChatContext } from "../chat-context-provider"; interface MatchParams { filter: string; @@ -37,6 +39,8 @@ interface Props { export default function ChatsMessagesBox(props: Props) { const { chat } = useMappedStore(); + const { messageServiceInstance } = useContext(ChatContext); + const { channels, updatedChannel } = chat; const { match } = props; const username = match.params.username; @@ -45,26 +49,46 @@ export default function ChatsMessagesBox(props: Props) { const [currentChannel, setCurrentChannel] = useState(); const [inProgress, setInProgress] = useState(false); const [isCommunityChatEnabled, setIsCommunityChatEnabled] = useState(false); + const [isCommunityJoined, setIsCommunityChatJoined] = useState(false); + const [currentCommunity, setCurrentCommunity] = useState(); + const [hasLeftCommunity, setHasLeftCommunity] = useState(false); useEffect(() => { setMaxHeight(window.innerHeight - 68); }, [typeof window !== "undefined"]); + useEffect(() => { + const handleResize = () => { + setMaxHeight(window.innerHeight - 68); + }; + + window.addEventListener("resize", handleResize); + return () => { + window.removeEventListener("resize", handleResize); + }; + }, []); + + useEffect(() => { + if (currentCommunity && chat.leftChannelsList.includes(currentCommunity.id)) { + setHasLeftCommunity(true); + } + }, [currentCommunity]); + useEffect(() => { if (username && !username.startsWith("@")) { + getCommunityProfile(); const isCommunity = chat.channels.some((channel) => channel.communityName === username); if (!isCommunity) { - getCommunityProfile(); + setIsCommunityChatJoined(false); } else { setIsCommunityChatEnabled(true); } + } else { + setIsCommunityChatJoined(false); + setIsCommunityChatEnabled(true); } }, [username]); - useEffect(() => { - console.log("Observe here currentChannel", currentChannel); - }, [currentChannel]); - useEffect(() => { fetchCurrentChannel(formattedUserName(username)); }, [updatedChannel, username, channels]); @@ -97,41 +121,67 @@ export default function ChatsMessagesBox(props: Props) { const getCommunityProfile = async () => { const communityProfile = await getProfileMetaData(username); + setCurrentCommunity(communityProfile.channel); const haschannelMetaData = communityProfile && communityProfile.hasOwnProperty(CHANNEL); setIsCommunityChatEnabled(haschannelMetaData); }; + const handleJoinCommunity = () => { + if (currentCommunity) { + if (hasLeftCommunity) { + messageServiceInstance?.updateLeftChannelList( + chat.leftChannelsList.filter((x) => x !== currentCommunity?.id) + ); + } + messageServiceInstance?.loadChannel(currentCommunity?.id); + setIsCommunityChatJoined(true); + } + }; + return ( - <> -
- {match.url == "/chats" ? ( -
-

Select a chat or start a new conversation

-
- ) : isCommunityChatEnabled ? ( - <> - - {inProgress && } - - - ) : ( -

- Community chat not started yet -

- )} -
- +
+ {match.url === "/chats" ? ( +
+

Select a chat or start a new conversation

+
+ ) : ( + <> + {username.startsWith("@") || (isCommunityChatEnabled && isCommunityJoined) ? ( + <> + + {inProgress && } + + + ) : isCommunityChatEnabled && !isCommunityJoined ? ( +
+
+

+ {hasLeftCommunity + ? "You have left this community chat. Rejoin the chat now!" + : " You are not part of this community. Join the community chat now!"} +

+ +
+
+ ) : ( +

Community chat not started yet

+ )} + + )} +
); } diff --git a/src/common/components/chats/chats-sidebar/indes.tsx b/src/common/components/chats/chats-sidebar/indes.tsx index e0c0d92d704..97d1e94c769 100644 --- a/src/common/components/chats/chats-sidebar/indes.tsx +++ b/src/common/components/chats/chats-sidebar/indes.tsx @@ -11,17 +11,14 @@ import accountReputation from "../../../helper/account-reputation"; import { getCommunityLastMessage, getDirectLastMessage, getJoinedCommunities } from "../utils"; import { _t } from "../../../i18n"; import { arrowBackSvg, syncSvg } from "../../../img/svg"; -import { Chat, DirectContactsType } from "../../../store/chat/types"; import ChatsScroller from "../chats-scroller"; import LinearProgress from "../../linear-progress"; import Tooltip from "../../tooltip"; import UserAvatar from "../../user-avatar"; import "./index.scss"; -import DropDown, { MenuItem } from "../../dropdown"; -import { CHAT, DropDownStyle } from "../chat-popup/chat-constants"; import ChatsDropdownMenu from "../chats-dropdown-menu"; -import { AccountWithReputation, NostrKeysType } from "../types"; +import { AccountWithReputation } from "../types"; import { useMappedStore } from "../../../store/use-mapped-store"; import { ChatContext } from "../chat-context-provider"; import { getUserChatPublicKey, formattedUserName } from "../../../components/chats/utils"; @@ -63,8 +60,6 @@ export default function ChatsSideBar(props: Props) { const [isScrollToTop, setIsScrollToTop] = useState(false); const [communities, setCommunities] = useState([]); - console.log("setInProgress in chats sidebar", setShowSpinner); - useDebounce( async () => { if (searchText.length !== 0) { @@ -79,7 +74,7 @@ export default function ChatsSideBar(props: Props) { ); useEffect(() => { - if (username !== undefined) { + if (username) { if (username.startsWith("@")) { getReceiverPubKey(formattedUserName(username)); } @@ -89,12 +84,10 @@ export default function ChatsSideBar(props: Props) { useEffect(() => { const communities = getJoinedCommunities(channels, leftChannelsList); setCommunities(communities); - // fetchProfileData(); }, [channels, leftChannelsList]); const handleScroll = (event: React.UIEvent) => { var element = event.currentTarget; - console.log("element.scrollTop", element.scrollTop); if (element.scrollTop > 2) { setShowDivider(true); } else { @@ -120,13 +113,11 @@ export default function ChatsSideBar(props: Props) { }; const getReceiverPubKey = async (username: string) => { - console.log("Username ha yar", username); const peer = directContacts.find((x) => x.name === username)?.pubkey ?? ""; if (peer) { setReceiverPubKey(peer); } else { const pubkey = await getUserChatPublicKey(username); - console.log("Pubkey", pubkey); if (pubkey === undefined) { setReceiverPubKey(""); } else { diff --git a/src/common/components/chats/chats-sidebar/index.scss b/src/common/components/chats/chats-sidebar/index.scss index 9eeda140b33..4b5140bd4cd 100644 --- a/src/common/components/chats/chats-sidebar/index.scss +++ b/src/common/components/chats/chats-sidebar/index.scss @@ -218,7 +218,7 @@ } .chats-list { - height: calc(100vh - 220px); + // height: calc(100vh - 220px); overflow: auto; a { color: inherit; From 62d9c796e62c7957d49155fdeb05a6669995c495 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Sat, 16 Sep 2023 19:41:48 +0500 Subject: [PATCH 075/179] Fix in message-box --- src/common/components/chats/chats-messages-box/index.tsx | 1 + src/common/components/chats/chats-sidebar/index.scss | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/common/components/chats/chats-messages-box/index.tsx b/src/common/components/chats/chats-messages-box/index.tsx index 789b4f0cf58..d6dc21bb79d 100644 --- a/src/common/components/chats/chats-messages-box/index.tsx +++ b/src/common/components/chats/chats-messages-box/index.tsx @@ -81,6 +81,7 @@ export default function ChatsMessagesBox(props: Props) { if (!isCommunity) { setIsCommunityChatJoined(false); } else { + setIsCommunityChatJoined(true); setIsCommunityChatEnabled(true); } } else { diff --git a/src/common/components/chats/chats-sidebar/index.scss b/src/common/components/chats/chats-sidebar/index.scss index 4b5140bd4cd..9eeda140b33 100644 --- a/src/common/components/chats/chats-sidebar/index.scss +++ b/src/common/components/chats/chats-sidebar/index.scss @@ -218,7 +218,7 @@ } .chats-list { - // height: calc(100vh - 220px); + height: calc(100vh - 220px); overflow: auto; a { color: inherit; From 83cd4b95b64043fb82dcdf053a82615a8bfac90c Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Mon, 18 Sep 2023 19:30:31 +0500 Subject: [PATCH 076/179] WIP --- .../chats/chat-context-provider.tsx | 27 +++-- .../components/chats/chat-popup/index.tsx | 10 +- .../chats/chats-channel-messages/index.scss | 99 ++++++++++--------- .../chats/chats-channel-messages/index.tsx | 75 ++++++++------ .../chats-community-dropdown-menu/index.tsx | 9 +- .../chats/chats-direct-messages/index.scss | 15 ++- .../chats/chats-direct-messages/index.tsx | 87 +++++++++++----- .../chats/chats-messages-box/index.tsx | 52 ++++++---- .../chats/chats-messages-header/index.scss | 14 +++ .../chats/chats-messages-header/index.tsx | 26 ++--- .../chats/chats-messages-view/index.tsx | 6 +- .../chats/chats-side-profile/index.scss | 8 ++ .../chats/chats-side-profile/index.tsx | 23 +++++ .../components/chats/chats-sidebar/indes.tsx | 16 +-- .../chats/utils/check-contiguous-message.ts | 16 +++ src/common/components/chats/utils/index.ts | 1 + src/common/pages/chats/index.tsx | 19 +++- src/managers/message-manager.tsx | 26 ++--- 18 files changed, 337 insertions(+), 192 deletions(-) create mode 100644 src/common/components/chats/chats-side-profile/index.scss create mode 100644 src/common/components/chats/chats-side-profile/index.tsx create mode 100644 src/common/components/chats/utils/check-contiguous-message.ts diff --git a/src/common/components/chats/chat-context-provider.tsx b/src/common/components/chats/chat-context-provider.tsx index 51dc7751c3a..9b66d5a375f 100644 --- a/src/common/components/chats/chat-context-provider.tsx +++ b/src/common/components/chats/chat-context-provider.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { Keys } from "../../../managers/message-manager-types"; +import { Channel, Keys } from "../../../managers/message-manager-types"; import useDebounce from "react-use/lib/useDebounce"; import MessageService from "../../helper/message-service"; import { useMappedStore } from "../../store/use-mapped-store"; @@ -22,6 +22,8 @@ interface Context { receiverPubKey: string; messageServiceInstance: MessageService | null; hasUserJoinedChat: boolean; + currentChannel: Channel | null; + setCurrentChannel: (channel: Channel) => void; setRevealPrivKey: (d: boolean) => void; setShowSpinner: (d: boolean) => void; setChatPrivKey: (key: string) => void; @@ -44,6 +46,8 @@ export const ChatContext = React.createContext({ receiverPubKey: "", messageServiceInstance: null, hasUserJoinedChat: false, + currentChannel: null, + setCurrentChannel: () => {}, setRevealPrivKey: () => {}, setShowSpinner: () => {}, setChatPrivKey: () => {}, @@ -65,6 +69,11 @@ export const ChatContextProvider = (props: Props) => { const [messageServiceInstance, setMessageServiceInstance] = useState(null); const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); const [shouldUpdateProfile, setShouldUpdateProfile] = useState(false); + const [currentChannel, setCurrentChannel] = useState(null); + + useEffect(() => { + console.log("currentChannel", currentChannel); + }, [currentChannel]); useMount(() => { getActiveUserKeys(); @@ -87,13 +96,13 @@ export const ChatContextProvider = (props: Props) => { } }, [shouldUpdateProfile, messageServiceInstance]); - // useEffect(() => { - // if (showSpinner) { - // setTimeout(() => { - // setShowSpinner(false); - // }, 3000); - // } - // }, [showSpinner]); + useEffect(() => { + if (showSpinner) { + setTimeout(() => { + setShowSpinner(false); + }, 3000); + } + }, [showSpinner]); useDebounce(() => setShowSpinner(false), 3000, [showSpinner]); @@ -142,6 +151,8 @@ export const ChatContextProvider = (props: Props) => { chatPrivKey, messageServiceInstance, hasUserJoinedChat, + currentChannel, + setCurrentChannel, setRevealPrivKey, setShowSpinner, setChatPrivKey, diff --git a/src/common/components/chats/chat-popup/index.tsx b/src/common/components/chats/chat-popup/index.tsx index 3a30701cdc8..d584c598081 100644 --- a/src/common/components/chats/chat-popup/index.tsx +++ b/src/common/components/chats/chat-popup/index.tsx @@ -91,6 +91,8 @@ export const ChatPopUp = (props: Props) => { activeUserKeys, showSpinner, hasUserJoinedChat, + currentChannel, + setCurrentChannel, setRevealPrivKey, setShowSpinner, joinChat @@ -115,7 +117,6 @@ export const ChatPopUp = (props: Props) => { const [isCommunity, setIsCommunity] = useState(false); const [communityName, setCommunityName] = useState(""); const [currentCommunity, setCurrentCommunity] = useState(); - const [currentChannel, setCurrentChannel] = useState(); const [publicMessages, setPublicMessages] = useState([]); const [clickedMessage, setClickedMessage] = useState(""); const [keyDialog, setKeyDialog] = useState(false); @@ -652,12 +653,7 @@ export const ChatPopUp = (props: Props) => { )} {isCommunity && (
- +
)}{" "} {!isCommunity && !isCurrentUser && noStrPrivKey && ( diff --git a/src/common/components/chats/chats-channel-messages/index.scss b/src/common/components/chats/chats-channel-messages/index.scss index 6b972ed9fad..077c36ca6ba 100644 --- a/src/common/components/chats/chats-channel-messages/index.scss +++ b/src/common/components/chats/chats-channel-messages/index.scss @@ -8,22 +8,6 @@ font-size: 14px; } } - .profile-box { - padding: 15px; - .profile-box-content { - .user-avatar.large { - margin-bottom: 20px; - } - .profile-name { - margin-bottom: 10px; - font-size: 18px; - font-weight: 800; - } - .profile-box-buttons { - padding: 0 5px; - } - } - } .receiver { display: flex; @@ -72,19 +56,19 @@ border-radius: 0px 10px 10px 0px; } - .receiver-msg-time { - @include themify(day) { - color: rgb(138, 141, 145); - } - @include themify(night) { - color: $charcoal-grey; - } + // .receiver-msg-time { + // @include themify(day) { + // color: rgb(138, 141, 145); + // } + // @include themify(night) { + // color: $charcoal-grey; + // } - font-size: 10px; - display: flex; - justify-content: flex-end; - margin: -5px 7px 3px 45px; - } + // font-size: 10px; + // display: flex; + // justify-content: flex-end; + // margin: -5px 7px 3px 45px; + // } &.gif { background: none; @@ -113,14 +97,14 @@ padding-bottom: 10px; } } - .receiver-msg-time { - @include themify(day) { - color: rgb(138, 141, 145); - } - @include themify(night) { - color: $white-three; - } - } + // .receiver-msg-time { + // @include themify(day) { + // color: rgb(138, 141, 145); + // } + // @include themify(night) { + // color: $white-three; + // } + // } } } } @@ -207,9 +191,9 @@ &.gif, &.chat-image { - .sender-message-time { - color: rgb(138, 141, 145); - } + // .sender-message-time { + // color: rgb(138, 141, 145); + // } .sender-message-content { padding: 10px 0 0 5px; img { @@ -236,16 +220,16 @@ } } - .sender-message-time { - color: $white; - margin-bottom: 3px; - font-size: 10px; - display: flex; - justify-content: flex-end; - margin-right: 7px; - margin-left: 45px; - margin-top: -5px; - } + // .sender-message-time { + // color: $white; + // margin-bottom: 3px; + // font-size: 10px; + // display: flex; + // justify-content: flex-end; + // margin-right: 7px; + // margin-left: 45px; + // margin-top: -5px; + // } } } } @@ -266,3 +250,20 @@ } } } + +.profile-box { + padding: 15px; + .profile-box-content { + .user-avatar.large { + margin-bottom: 20px; + } + .profile-name { + margin: 5px 0 10px 0; + font-size: 18px; + font-weight: 800; + } + .profile-box-buttons { + padding: 0 5px; + } + } +} diff --git a/src/common/components/chats/chats-channel-messages/index.tsx b/src/common/components/chats/chats-channel-messages/index.tsx index bd569406446..7c37e6f221f 100644 --- a/src/common/components/chats/chats-channel-messages/index.tsx +++ b/src/common/components/chats/chats-channel-messages/index.tsx @@ -37,7 +37,13 @@ import { error } from "../../feedback"; import { CHATPAGE } from "../chat-popup/chat-constants"; import { Theme } from "../../../store/global/types"; import { NostrKeysType } from "../types"; -import { formatMessageTime, isMessageGif, isMessageImage, formatMessageDate } from "../utils"; +import { + formatMessageTime, + isMessageGif, + isMessageImage, + formatMessageDate, + checkContiguousMessage +} from "../utils"; import { ChatContext } from "../chat-context-provider"; interface Props { @@ -284,16 +290,6 @@ export default function ChatsChannelMessages(props: Props) { return <>; }; - const checkContiguousMessage = (msg: PublicMessage, i: number) => { - const prevMsg = publicMessages[i - 1]; - const msgAuthor = msg.creator; - const prevMsgAuthor = prevMsg ? prevMsg.creator : null; - if (msgAuthor === prevMsgAuthor) { - return true; - } - return false; - }; - return ( <>
@@ -302,7 +298,7 @@ export default function ChatsChannelMessages(props: Props) { publicMessages.map((pMsg, i) => { const dayAndMonth = getFormattedDateAndDay(pMsg, i); - const isSameUserMessage = checkContiguousMessage(pMsg, i); + const isSameUserMessage = checkContiguousMessage(pMsg, i, publicMessages); let renderedPreview = renderPostBody(pMsg.content, false, global.canUseWebp); @@ -435,18 +431,27 @@ export default function ChatsChannelMessages(props: Props) { )}
-
-

{formatMessageTime(pMsg.created)}

-
- + className={`receiver-message-wrapper ${isGif ? "gif" : ""} ${ + isImage ? "chat-image" : "" + }`} + > +
+ {/*

{formatMessageTime(pMsg.created)}

*/} +
+ {hoveredMessageId === pMsg.id && privilegedUsers.includes(activeUser?.username!) && ( @@ -509,17 +514,25 @@ export default function ChatsChannelMessages(props: Props) { )} -
-

{formatMessageTime(pMsg.created)}

-
+ className={`sender-message-wrapper ${isGif ? "gif" : ""} ${ + isImage ? "chat-image" : "" + }`} + > +
+ {/*

{formatMessageTime(pMsg.created)}

*/} +
+ {pMsg.sent === 0 && ( diff --git a/src/common/components/chats/chats-community-dropdown-menu/index.tsx b/src/common/components/chats/chats-community-dropdown-menu/index.tsx index ee9be8fb671..e41fbc79d05 100644 --- a/src/common/components/chats/chats-community-dropdown-menu/index.tsx +++ b/src/common/components/chats/chats-community-dropdown-menu/index.tsx @@ -31,15 +31,14 @@ interface Props { history: History; from?: string; username: string; - currentChannel: Channel; - currentChannelSetter: (channe: Channel) => void; } const roles = [ROLES.ADMIN, ROLES.MOD, ROLES.GUEST]; const ChatsCommunityDropdownMenu = (props: Props) => { const { activeUser, chat } = useMappedStore(); - const { history, currentChannelSetter, from, currentChannel } = props; + const { currentChannel, setCurrentChannel } = useContext(ChatContext); + const { history, from } = props; const [step, setStep] = useState(0); const [keyDialog, setKeyDialog] = useState(false); const [inProgress, setInProgress] = useState(false); @@ -115,7 +114,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => { const newUpdatedModerator = { ...newUpdatedChannel?.communityModerators![moderatorIndex!] }; newUpdatedModerator.role = selectedRole; newUpdatedChannel!.communityModerators![moderatorIndex!] = newUpdatedModerator; - currentChannelSetter(newUpdatedChannel); + setCurrentChannel(newUpdatedChannel); messageServiceInstance?.updateChannel(currentChannel, newUpdatedChannel); success("Roles updated succesfully"); } @@ -485,7 +484,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => { } try { messageServiceInstance?.updateChannel(currentChannel!, updatedMetaData); - currentChannelSetter({ ...currentChannel!, ...updatedMetaData }); + setCurrentChannel({ ...currentChannel!, ...updatedMetaData }); if (operationType === UNBLOCKUSER) { setStep(5); diff --git a/src/common/components/chats/chats-direct-messages/index.scss b/src/common/components/chats/chats-direct-messages/index.scss index 1a682e4a2e2..a75f1a15306 100644 --- a/src/common/components/chats/chats-direct-messages/index.scss +++ b/src/common/components/chats/chats-direct-messages/index.scss @@ -11,16 +11,16 @@ .receiver { display: flex; .user-img { - padding: 18px 8px 8px 16px; + padding: 8px 8px 8px 16px; } .community-user-img { - padding: 8px 8px 8px 16px; + padding: 8px 8px 0px 16px; .user-avatar.medium { cursor: pointer; } } .user-info { - padding-top: 8px; + padding-top: 20px; width: 100%; .user-msg-time { margin: 0; @@ -65,11 +65,18 @@ border-radius: 0px 10px 10px; } } + &.same-user { + padding-top: 1.5px; + margin-left: 4rem; + .receiver-message-content { + border-radius: 0px 10px 10px 10px; + } + } } } .sender { - margin-bottom: 0.17rem; + margin-bottom: 1.5px; .sender-message-time { color: rgb(138, 141, 145); display: flex; diff --git a/src/common/components/chats/chats-direct-messages/index.tsx b/src/common/components/chats/chats-direct-messages/index.tsx index 04c13d72c5d..af1d4c29f73 100644 --- a/src/common/components/chats/chats-direct-messages/index.tsx +++ b/src/common/components/chats/chats-direct-messages/index.tsx @@ -3,7 +3,13 @@ import usePrevious from "react-use/lib/usePrevious"; import mediumZoom, { Zoom } from "medium-zoom"; import { Spinner } from "react-bootstrap"; import { Link } from "react-router-dom"; -import { formatMessageTime, formatMessageDate, isMessageGif, isMessageImage } from "../utils"; +import { + formatMessageTime, + formatMessageDate, + isMessageGif, + isMessageImage, + checkContiguousMessage +} from "../utils"; import { Theme } from "../../../store/global/types"; import { DirectMessage } from "../../../../managers/message-manager-types"; @@ -97,6 +103,16 @@ export default function ChatsDirectMessages(props: Props) { return <>; }; + // const getFormattedDate = (msg: DirectMessage, i: number) => { + // const prevMsg = directMessages[i - 1]; + // const msgDate = formatMessageDate(msg.created); + // const prevMsgDate = prevMsg ? formatMessageDate(prevMsg.created) : null; + // if (msgDate !== prevMsgDate) { + // return true; + // } + // return false; + // }; + const handleConfirm = () => { switch (step) { case 1: @@ -128,34 +144,51 @@ export default function ChatsDirectMessages(props: Props) { const isImage = isMessageImage(msg.content); + const isSameUser = checkContiguousMessage(msg, i, directMessages); + + // const date = getFormattedDate(msg, i); + + // console.log('date', date, msg) + return ( {dayAndMonth} {msg.creator !== activeUserKeys?.pub ? (
-
- - - - - -
-
-

{formatMessageTime(msg.created)}

+ {!isSameUser && ( +
+ + + + + +
+ )} + +
+ {/*

{formatMessageTime(msg.created)}

*/}
-
+ +
+
) : (
-

{formatMessageTime(msg.created)}

+ {/*

{formatMessageTime(msg.created)}

*/}
)} -
+ +
+ {msg.sent === 0 && ( diff --git a/src/common/components/chats/chats-messages-box/index.tsx b/src/common/components/chats/chats-messages-box/index.tsx index d6dc21bb79d..6bda5f2d967 100644 --- a/src/common/components/chats/chats-messages-box/index.tsx +++ b/src/common/components/chats/chats-messages-box/index.tsx @@ -9,13 +9,14 @@ import ChatsMessagesView from "../chats-messages-view"; import { Channel, ChannelUpdate } from "../../../../managers/message-manager-types"; import LinearProgress from "../../linear-progress"; -import { formattedUserName, getProfileMetaData } from "../utils"; +import { formattedUserName, getJoinedCommunities, getProfileMetaData } from "../utils"; import { useMappedStore } from "../../../store/use-mapped-store"; import "./index.scss"; import { CHANNEL } from "../chat-popup/chat-constants"; import { Button } from "react-bootstrap"; import { ChatContext } from "../chat-context-provider"; +import { getCommunities } from "../../../api/bridge"; interface MatchParams { filter: string; @@ -39,14 +40,13 @@ interface Props { export default function ChatsMessagesBox(props: Props) { const { chat } = useMappedStore(); - const { messageServiceInstance } = useContext(ChatContext); + const { messageServiceInstance, currentChannel, setCurrentChannel } = useContext(ChatContext); const { channels, updatedChannel } = chat; const { match } = props; const username = match.params.username; const [maxHeight, setMaxHeight] = useState(0); - const [currentChannel, setCurrentChannel] = useState(); const [inProgress, setInProgress] = useState(false); const [isCommunityChatEnabled, setIsCommunityChatEnabled] = useState(false); const [isCommunityJoined, setIsCommunityChatJoined] = useState(false); @@ -58,6 +58,7 @@ export default function ChatsMessagesBox(props: Props) { }, [typeof window !== "undefined"]); useEffect(() => { + checkUserCommunityMembership(); const handleResize = () => { setMaxHeight(window.innerHeight - 68); }; @@ -76,22 +77,42 @@ export default function ChatsMessagesBox(props: Props) { useEffect(() => { if (username && !username.startsWith("@")) { - getCommunityProfile(); - const isCommunity = chat.channels.some((channel) => channel.communityName === username); - if (!isCommunity) { - setIsCommunityChatJoined(false); - } else { - setIsCommunityChatJoined(true); - setIsCommunityChatEnabled(true); - } + checkUserCommunityMembership(); } else { setIsCommunityChatJoined(false); setIsCommunityChatEnabled(true); } }, [username]); + useEffect(() => { + console.log( + "isCommunityChatEnabled", + isCommunityChatEnabled, + "isCommunityJoined", + isCommunityJoined, + "hasLeftCommunity", + hasLeftCommunity + ); + }, [isCommunityChatEnabled, isCommunityJoined, hasLeftCommunity]); + + const checkUserCommunityMembership = () => { + getCommunityProfile(); + const communities = getJoinedCommunities(chat.channels, chat.leftChannelsList); + const isCommunity = chat.channels.some((channel) => channel.communityName === username); + if (isCommunity) { + setIsCommunityChatEnabled(true); + const isJoined = communities.some((channel) => channel.communityName === username); + if (isJoined) { + setIsCommunityChatJoined(true); + } + } else { + setIsCommunityChatJoined(false); + } + }; + useEffect(() => { fetchCurrentChannel(formattedUserName(username)); + checkUserCommunityMembership(); }, [updatedChannel, username, channels]); const fetchCurrentChannel = (communityName: string) => { @@ -122,7 +143,7 @@ export default function ChatsMessagesBox(props: Props) { const getCommunityProfile = async () => { const communityProfile = await getProfileMetaData(username); - setCurrentCommunity(communityProfile.channel); + setCurrentCommunity(communityProfile?.channel); const haschannelMetaData = communityProfile && communityProfile.hasOwnProperty(CHANNEL); setIsCommunityChatEnabled(haschannelMetaData); }; @@ -149,12 +170,7 @@ export default function ChatsMessagesBox(props: Props) { <> {username.startsWith("@") || (isCommunityChatEnabled && isCommunityJoined) ? ( <> - + {inProgress && } void; } export default function ChatsMessagesHeader(props: Props) { - const { username, currentChannel, currentChannelSetter } = props; + const { username } = props; const { chat } = useMappedStore(); const isChannel = (username: string) => { @@ -43,18 +42,19 @@ export default function ChatsMessagesHeader(props: Props) { return (
-
- -

{formattedName(username, chat)}

-
+ +
+ +

{formattedName(username, chat)}

+
+ {isChannel(username) && (
- +
)}
diff --git a/src/common/components/chats/chats-messages-view/index.tsx b/src/common/components/chats/chats-messages-view/index.tsx index a4edbce6b78..ae38a3fa13c 100644 --- a/src/common/components/chats/chats-messages-view/index.tsx +++ b/src/common/components/chats/chats-messages-view/index.tsx @@ -69,8 +69,6 @@ export default function ChatsMessagesView(props: Props) { const [removedUsers, setRemovedUsers] = useState([]); const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); - console.log("Current channel in messages view", props.currentChannel); - useEffect(() => { getActiveUserKeys(); isDirectUserOrCommunity(); @@ -94,6 +92,7 @@ export default function ChatsMessagesView(props: Props) { }, [directUser, communityName, currentChannel, chat.directMessages]); useEffect(() => { + setIsScrollToBottom(false); setDirectUser(""); setCommunityName(""); isDirectUserOrCommunity(); @@ -166,7 +165,8 @@ export default function ChatsMessagesView(props: Props) { const user = chat.directContacts.find((item) => item.name === directUser); console.log("user", user?.pubkey); const messages = user && fetchDirectMessages(user?.pubkey, chat.directMessages); - setDirectMessages(messages!); + const directMessages = messages?.sort((a, b) => a.created - b.created); + setDirectMessages(directMessages!); console.log("Messages", messages); }; diff --git a/src/common/components/chats/chats-side-profile/index.scss b/src/common/components/chats/chats-side-profile/index.scss new file mode 100644 index 00000000000..fd2db536fd3 --- /dev/null +++ b/src/common/components/chats/chats-side-profile/index.scss @@ -0,0 +1,8 @@ +@import "../../../../style/vars_mixins"; + +.profile-side-bar { + // width: 40vw; + .side-profile { + padding: 20px; + } +} diff --git a/src/common/components/chats/chats-side-profile/index.tsx b/src/common/components/chats/chats-side-profile/index.tsx new file mode 100644 index 00000000000..6eb1ac38f42 --- /dev/null +++ b/src/common/components/chats/chats-side-profile/index.tsx @@ -0,0 +1,23 @@ +import React, { useContext, useEffect, useState } from "react"; +import UserAvatar from "../../user-avatar"; +import { ChatContext } from "../chat-context-provider"; +import { formattedUserName } from "../utils"; + +import "./index.scss"; + +interface Props { + username: string; +} +export const ChatsSideProfile = (props: Props) => { + const { currentChannel } = useContext(ChatContext); + const { username } = props; + console.log("Current channel in chats side profile", currentChannel); + console.log("Username in profile sidebar", props.username); + return ( +
+

+ +

+
+ ); +}; diff --git a/src/common/components/chats/chats-sidebar/indes.tsx b/src/common/components/chats/chats-sidebar/indes.tsx index 97d1e94c769..7cfb4d8c3d9 100644 --- a/src/common/components/chats/chats-sidebar/indes.tsx +++ b/src/common/components/chats/chats-sidebar/indes.tsx @@ -22,24 +22,15 @@ import { AccountWithReputation } from "../types"; import { useMappedStore } from "../../../store/use-mapped-store"; import { ChatContext } from "../chat-context-provider"; import { getUserChatPublicKey, formattedUserName } from "../../../components/chats/utils"; - -interface MatchParams { - filter: string; - name: string; - path: string; - url: string; - username: string; -} interface Props { - match: match; + username: string; history: History; - resetChat: () => void; } export default function ChatsSideBar(props: Props) { - const { chat } = useMappedStore(); + const { username } = props; + const { chat, resetChat } = useMappedStore(); const chatContext = useContext(ChatContext); const { channels, directContacts, leftChannelsList } = chat; - const { match, resetChat } = props; const { activeUserKeys, @@ -51,7 +42,6 @@ export default function ChatsSideBar(props: Props) { } = chatContext; const chatsSideBarRef = React.createRef(); - const username = match.params.username; const [showDivider, setShowDivider] = useState(false); const [searchText, setSearchText] = useState(""); diff --git a/src/common/components/chats/utils/check-contiguous-message.ts b/src/common/components/chats/utils/check-contiguous-message.ts new file mode 100644 index 00000000000..75c0fc9223d --- /dev/null +++ b/src/common/components/chats/utils/check-contiguous-message.ts @@ -0,0 +1,16 @@ +import { DirectMessage } from "./../../../../managers/message-manager-types"; +import { PublicMessage } from "../../../../managers/message-manager-types"; + +export const checkContiguousMessage = ( + msg: PublicMessage | DirectMessage, + i: number, + publicMessages: PublicMessage[] | DirectMessage[] +) => { + const prevMsg = publicMessages[i - 1]; + const msgAuthor = msg.creator; + const prevMsgAuthor = prevMsg ? prevMsg.creator : null; + if (msgAuthor === prevMsgAuthor) { + return true; + } + return false; +}; diff --git a/src/common/components/chats/utils/index.ts b/src/common/components/chats/utils/index.ts index d1b52969366..6ec55dfc912 100644 --- a/src/common/components/chats/utils/index.ts +++ b/src/common/components/chats/utils/index.ts @@ -15,3 +15,4 @@ export * from "./get-chat-private-key"; export * from "./get-joined-communities"; export * from "./copy-to-clipboard"; export * from "./use-fetch-direct-messages"; +export * from "./check-contiguous-message"; diff --git a/src/common/pages/chats/index.tsx b/src/common/pages/chats/index.tsx index 1354d39ca68..a8152d31759 100644 --- a/src/common/pages/chats/index.tsx +++ b/src/common/pages/chats/index.tsx @@ -14,6 +14,7 @@ import { useMappedStore } from "../../store/use-mapped-store"; import { ChatContext } from "../../components/chats/chat-context-provider"; import ImportChats from "../../components/chats/import-chats"; import JoinChat from "../../components/chats/join-chat"; +import { ChatsSideProfile } from "../../components/chats/chats-side-profile"; import "./index.scss"; @@ -31,10 +32,12 @@ interface Props extends PageProps { export const Chats = (props: Props) => { const { activeUser, global } = useMappedStore(); + const { match, history } = props; const [marginTop, setMarginTop] = useState(0); - const chatContext = useContext(ChatContext); - const { showSpinner, activeUserKeys, revealPrivKey, chatPrivKey } = chatContext; + const username = match.params.username; + + const { showSpinner, activeUserKeys, revealPrivKey, chatPrivKey } = useContext(ChatContext); console.log("inProgress in chats page", showSpinner); @@ -81,14 +84,20 @@ export const Chats = (props: Props) => { {activeUserKeys?.pub ? ( chatPrivKey ? ( <> - + {revealPrivKey ? (
) : ( - // Handle the case when the user hasn't joined any community here - + <> + + {match.url !== "/chats" && ( +
+ +
+ )} + )} ) : ( diff --git a/src/managers/message-manager.tsx b/src/managers/message-manager.tsx index 0f4672a5ae7..9f3d23eb23a 100644 --- a/src/managers/message-manager.tsx +++ b/src/managers/message-manager.tsx @@ -199,18 +199,18 @@ const MessageManager = () => { const { peer, id } = m; setReplacedDirectMessagesBuffer((prevBuffer) => [...prevBuffer, id]); addDirectMessages(peer, m); - const startTimeout = () => { - console.log("Start time out called"); - useTimeoutFn(() => { - console.log("Inner called"); - checkDirectMessageSending(peer, m); - }, 2000); - }; + // const startTimeout = () => { + // console.log("Start time out called"); + // useTimeoutFn(() => { + // console.log("Inner called"); + // checkDirectMessageSending(peer, m); + // }, 2000); + // }; // Start the timeout - startTimeout(); + // startTimeout(); // const [startTimeout] = useTimeoutFn(checkDirectMessageSending(peer, m), 20000); - // checkDirectMessageSending(peer, m); + checkDirectMessageSending(peer, m); }); }; @@ -233,10 +233,10 @@ const MessageManager = () => { }, [messageService, chat.directMessages]); const checkDirectMessageSending = (peer: string, data: DirectMessage) => { - // setTimeout(() => { - console.log("checkDirectMessageSending called"); - verifyDirectMessageSending(peer, data); - // }, 20000); + setTimeout(() => { + console.log("checkDirectMessageSending called"); + verifyDirectMessageSending(peer, data); + }, 20000); }; // // Direct message handler after sent From 64a02423a50f62f634e3540c683dc3b91f0087a5 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Tue, 19 Sep 2023 18:15:56 +0500 Subject: [PATCH 077/179] Show time uner message and show hide-svg on click when screen size is less than medium breakpoint --- .../chats/chat-popup/chat-constants.ts | 2 + .../chats/chats-channel-messages/index.scss | 80 ++++++++++--------- .../chats/chats-channel-messages/index.tsx | 78 +++++++++++++----- .../chats/chats-direct-messages/index.scss | 9 ++- .../chats/chats-direct-messages/index.tsx | 4 +- .../chats/chats-messages-box/index.scss | 2 +- .../chats/chats-messages-header/index.scss | 2 +- .../chats/chats-side-profile/index.scss | 2 +- .../chats/chats-side-profile/index.tsx | 2 +- .../components/chats/chats-sidebar/index.scss | 46 +++++++++++ src/common/pages/chats/index.tsx | 6 +- 11 files changed, 164 insertions(+), 69 deletions(-) diff --git a/src/common/components/chats/chat-popup/chat-constants.ts b/src/common/components/chats/chat-popup/chat-constants.ts index 66d53a8a3ca..76338f7b27c 100644 --- a/src/common/components/chats/chat-popup/chat-constants.ts +++ b/src/common/components/chats/chat-popup/chat-constants.ts @@ -28,6 +28,8 @@ export const GifImagesStyle = { }; export const CHAT_FILE_CONTENT_TYPES = ["jpg", "jpeg", "gif", "png"]; +export const PRIVILEGEDROLES = ["owner", "admin", "mod"]; +export const COMMUNITYADMINROLES = ["owner", "admin"]; export const NOSTRKEY = "nsKey"; export const UPLOADING = "Uploading"; diff --git a/src/common/components/chats/chats-channel-messages/index.scss b/src/common/components/chats/chats-channel-messages/index.scss index 077c36ca6ba..40532ce80eb 100644 --- a/src/common/components/chats/chats-channel-messages/index.scss +++ b/src/common/components/chats/chats-channel-messages/index.scss @@ -34,7 +34,7 @@ margin-right: 8px; } } - .receiver-messag { + .receiver-message { display: flex; .receiver-message-wrapper { @@ -50,25 +50,26 @@ } .receiver-message-content { + max-width: 100%; color: #050505; display: inline-block; padding: 10px; border-radius: 0px 10px 10px 0px; } - // .receiver-msg-time { - // @include themify(day) { - // color: rgb(138, 141, 145); - // } - // @include themify(night) { - // color: $charcoal-grey; - // } + .receiver-msg-time { + @include themify(day) { + color: rgb(138, 141, 145); + } + @include themify(night) { + color: $charcoal-grey; + } - // font-size: 10px; - // display: flex; - // justify-content: flex-end; - // margin: -5px 7px 3px 45px; - // } + font-size: 10px; + display: flex; + justify-content: flex-end; + margin: -5px 7px 3px 45px; + } &.gif { background: none; @@ -97,22 +98,22 @@ padding-bottom: 10px; } } - // .receiver-msg-time { - // @include themify(day) { - // color: rgb(138, 141, 145); - // } - // @include themify(night) { - // color: $white-three; - // } - // } + .receiver-msg-time { + @include themify(day) { + color: rgb(138, 141, 145); + } + @include themify(night) { + color: $white-three; + } + } } } } &.same-user-msg { margin-left: 4rem; - padding-top: 1.5px; + padding-top: 1px; .receiver-message-wrapper { - border-radius: 0 10px 10px 0; + border-radius: 10px; } } } @@ -159,7 +160,7 @@ .sender-message-wrapper { box-sizing: content-box; - border-radius: 10px 10px 0 10px; + border-radius: 10px 0px 10px 10px; max-width: 70%; word-wrap: break-word; @@ -191,9 +192,9 @@ &.gif, &.chat-image { - // .sender-message-time { - // color: rgb(138, 141, 145); - // } + .sender-message-time { + color: rgb(138, 141, 145); + } .sender-message-content { padding: 10px 0 0 5px; img { @@ -203,6 +204,7 @@ } .sender-message-content { + max-width: 100%; margin-bottom: 0; color: $charcoal-grey; font-size: 16px; @@ -220,16 +222,20 @@ } } - // .sender-message-time { - // color: $white; - // margin-bottom: 3px; - // font-size: 10px; - // display: flex; - // justify-content: flex-end; - // margin-right: 7px; - // margin-left: 45px; - // margin-top: -5px; - // } + .sender-message-time { + color: $white; + margin-bottom: 3px; + font-size: 10px; + display: flex; + justify-content: flex-end; + margin-right: 7px; + margin-left: 45px; + margin-top: -5px; + } + + &.same-user-message { + border-radius: 10px; + } } } } diff --git a/src/common/components/chats/chats-channel-messages/index.tsx b/src/common/components/chats/chats-channel-messages/index.tsx index 7c37e6f221f..fbc273861af 100644 --- a/src/common/components/chats/chats-channel-messages/index.tsx +++ b/src/common/components/chats/chats-channel-messages/index.tsx @@ -3,7 +3,6 @@ import mediumZoom, { Zoom } from "medium-zoom"; import { Channel, communityModerator, - DirectMessage, Profile, PublicMessage } from "../../../../managers/message-manager-types"; @@ -22,21 +21,15 @@ import { import UserAvatar from "../../user-avatar"; import FollowControls from "../../follow-controls"; import { Account } from "../../../store/accounts/types"; -import { ToggleType, UI } from "../../../store/ui/types"; -import { Global } from "../../../store/global/types"; +import { ToggleType } from "../../../store/ui/types"; import usePrevious from "react-use/lib/usePrevious"; import { _t } from "../../../i18n"; import Tooltip from "../../tooltip"; import { failedMessageSvg, hideSvg, resendMessageSvg } from "../../../img/svg"; - -import "./index.scss"; -import { User } from "../../../store/users/types"; -import { ActiveUser } from "../../../store/active-user/types"; import ChatsConfirmationModal from "../chats-confirmation-modal"; import { error } from "../../feedback"; -import { CHATPAGE } from "../chat-popup/chat-constants"; +import { CHATPAGE, COMMUNITYADMINROLES, PRIVILEGEDROLES } from "../chat-popup/chat-constants"; import { Theme } from "../../../store/global/types"; -import { NostrKeysType } from "../types"; import { formatMessageTime, isMessageGif, @@ -46,6 +39,9 @@ import { } from "../utils"; import { ChatContext } from "../chat-context-provider"; +import "./index.scss"; +import { useMount } from "react-use"; + interface Props { publicMessages: PublicMessage[]; currentChannel: Channel; @@ -94,6 +90,15 @@ export default function ChatsChannelMessages(props: Props) { const [privilegedUsers, setPrivilegedUsers] = useState([]); const [hiddenMsgId, setHiddenMsgId] = useState(""); const [resendMessage, setResendMessage] = useState(); + const [windowWidth, setWindowWidth] = useState(0); + const [showMessageActions, setShowMessageActions] = useState(false); + + useMount(() => { + setWindowWidth(window.innerWidth); + if (window.innerWidth <= 768) { + setShowMessageActions(true); + } + }); useEffect(() => { if (prevGlobal?.theme !== global.theme) { @@ -118,6 +123,14 @@ export default function ChatsChannelMessages(props: Props) { } }, [currentChannel]); + useEffect(() => { + window.addEventListener("resize", handleResize); + + return () => { + window.removeEventListener("resize", handleResize); + }; + }, []); + useEffect(() => { const handleClickOutside = (event: MouseEvent) => { const clickedElement = event.target as HTMLElement; @@ -160,6 +173,13 @@ export default function ChatsChannelMessages(props: Props) { } }; + const handleResize = () => { + setWindowWidth(window.innerWidth); + if (window.innerWidth <= 768) { + setShowMessageActions(true); + } + }; + const handleDMChange = (e: React.ChangeEvent) => { setDmMessage(e.target.value); }; @@ -190,14 +210,11 @@ export default function ChatsChannelMessages(props: Props) { }; const getPrivilegedUsers = (communityModerators: communityModerator[]) => { - const privilegedRoles = ["owner", "admin", "mod"]; - const communityAdminRoles = ["owner", "admin"]; - const privilegedUsers = communityModerators.filter((user) => - privilegedRoles.includes(user.role) + PRIVILEGEDROLES.includes(user.role) ); const communityAdmins = communityModerators.filter((user) => - communityAdminRoles.includes(user.role) + COMMUNITYADMINROLES.includes(user.role) ); const privilegedUserNames = privilegedUsers.map((user) => user.name); @@ -290,6 +307,15 @@ export default function ChatsChannelMessages(props: Props) { return <>; }; + const handelMessageActions = (msgId: string) => { + console.log("showMessageActions", showMessageActions, "msgId", msgId); + if (showMessageActions && hoveredMessageId !== msgId) { + setHoveredMessageId(msgId); + } else { + setHoveredMessageId(""); + } + }; + return ( <>
@@ -402,8 +428,8 @@ export default function ChatsChannelMessages(props: Props) {
setHoveredMessageId(pMsg.id)} - onMouseLeave={() => setHoveredMessageId("")} + onMouseEnter={() => !showMessageActions && setHoveredMessageId(pMsg.id)} + onMouseLeave={() => !showMessageActions && setHoveredMessageId("")} > {!isSameUserMessage && (
@@ -430,7 +456,10 @@ export default function ChatsChannelMessages(props: Props) {

)} -
+
handelMessageActions(pMsg.id)} + > - {/*

{formatMessageTime(pMsg.created)}

*/} + {windowWidth <= 768 && ( +

{formatMessageTime(pMsg.created)}

+ )}
{hoveredMessageId === pMsg.id && @@ -476,13 +507,14 @@ export default function ChatsChannelMessages(props: Props) {
setHoveredMessageId(pMsg.id)} - onMouseLeave={() => setHoveredMessageId("")} + onMouseEnter={() => !showMessageActions && setHoveredMessageId(pMsg.id)} + onMouseLeave={() => !showMessageActions && setHoveredMessageId("")} >
handelMessageActions(pMsg.id)} > {hoveredMessageId === pMsg.id && !isActveUserRemoved && ( @@ -523,14 +555,16 @@ export default function ChatsChannelMessages(props: Props) { >
- {/*

{formatMessageTime(pMsg.created)}

*/} + {windowWidth <= 768 && ( +

{formatMessageTime(pMsg.created)}

+ )}
diff --git a/src/common/components/chats/chats-direct-messages/index.scss b/src/common/components/chats/chats-direct-messages/index.scss index a75f1a15306..d4836f769c8 100644 --- a/src/common/components/chats/chats-direct-messages/index.scss +++ b/src/common/components/chats/chats-direct-messages/index.scss @@ -63,6 +63,10 @@ word-wrap: break-word; padding: 10px; border-radius: 0px 10px 10px; + + &.same-user-message { + border-radius: 10px; + } } } &.same-user { @@ -125,7 +129,7 @@ } .sender-message-content { - border-radius: 10px 10px 0px; + border-radius: 10px 0px 10px 10px; max-width: 70%; word-wrap: break-word; margin-bottom: 0; @@ -158,6 +162,9 @@ max-width: 100%; } } + &.same-user-message { + border-radius: 10px; + } } } } diff --git a/src/common/components/chats/chats-direct-messages/index.tsx b/src/common/components/chats/chats-direct-messages/index.tsx index af1d4c29f73..8f2f06d68b3 100644 --- a/src/common/components/chats/chats-direct-messages/index.tsx +++ b/src/common/components/chats/chats-direct-messages/index.tsx @@ -178,7 +178,7 @@ export default function ChatsDirectMessages(props: Props) { >
@@ -216,7 +216,7 @@ export default function ChatsDirectMessages(props: Props) { >
diff --git a/src/common/components/chats/chats-messages-box/index.scss b/src/common/components/chats/chats-messages-box/index.scss index 73d644d8d86..4b533466c7b 100644 --- a/src/common/components/chats/chats-messages-box/index.scss +++ b/src/common/components/chats/chats-messages-box/index.scss @@ -14,7 +14,7 @@ @include themify(night) { box-shadow: 0px -5px 5px $charcoal-grey; } - width: 60vw; + width: 100%; .no-chat-select { height: calc(100vh - 70px); diff --git a/src/common/components/chats/chats-messages-header/index.scss b/src/common/components/chats/chats-messages-header/index.scss index 80b5d265208..7c0ab8fdfd7 100644 --- a/src/common/components/chats/chats-messages-header/index.scss +++ b/src/common/components/chats/chats-messages-header/index.scss @@ -2,7 +2,7 @@ .chats-messages-header { grid-row: span 1; - width: 60vw; + width: 100%; height: 67px; @include themify(day) { border-bottom: 1px solid #e7e7e7; diff --git a/src/common/components/chats/chats-side-profile/index.scss b/src/common/components/chats/chats-side-profile/index.scss index fd2db536fd3..6375caa1db8 100644 --- a/src/common/components/chats/chats-side-profile/index.scss +++ b/src/common/components/chats/chats-side-profile/index.scss @@ -1,7 +1,7 @@ @import "../../../../style/vars_mixins"; .profile-side-bar { - // width: 40vw; + width: 400px; .side-profile { padding: 20px; } diff --git a/src/common/components/chats/chats-side-profile/index.tsx b/src/common/components/chats/chats-side-profile/index.tsx index 6eb1ac38f42..c24f010580a 100644 --- a/src/common/components/chats/chats-side-profile/index.tsx +++ b/src/common/components/chats/chats-side-profile/index.tsx @@ -15,7 +15,7 @@ export const ChatsSideProfile = (props: Props) => { console.log("Username in profile sidebar", props.username); return (
-

+

diff --git a/src/common/components/chats/chats-sidebar/index.scss b/src/common/components/chats/chats-sidebar/index.scss index 9eeda140b33..0baf0d1ed05 100644 --- a/src/common/components/chats/chats-sidebar/index.scss +++ b/src/common/components/chats/chats-sidebar/index.scss @@ -225,3 +225,49 @@ } } } + +@include media-breakpoint-down(sm) { + .chats-sidebar { + width: 45%; + + .chats-title { + margin: 15px 0px 5px 5px; + } + .chats-search { + padding: 0 5px 0 5px; + margin-bottom: 0; + } + .chats-list { + overflow-x: hidden; + .community-title, + .dm-title { + font-size: 1rem; + padding: 15px 0 10px 8px; + } + .community, + .dm { + margin: 0; + padding: 9.5px 0px 9.5px 5px; + .community-info, + .dm-info { + .community-name, + .dm-name { + padding: 3px 0 0 10px; + } + .community-last-message, + .dm-last-message { + padding: 4px 0 0 10px; + max-width: 35vw; + } + } + } + } + } +} +// @media (max-width: 300px) { +// .chats-sidebar { +// .community-info, .dm-info { +// display: none; +// } +// } +// } diff --git a/src/common/pages/chats/index.tsx b/src/common/pages/chats/index.tsx index a8152d31759..aafd4ff0d7d 100644 --- a/src/common/pages/chats/index.tsx +++ b/src/common/pages/chats/index.tsx @@ -92,11 +92,11 @@ export const Chats = (props: Props) => { ) : ( <> - {match.url !== "/chats" && ( -
+ {/* {match.url !== "/chats" && ( +
- )} + )} */} )} From 647f7ba78cdb6f21020d6f56a98e84784b30c560 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Fri, 22 Sep 2023 19:47:10 +0500 Subject: [PATCH 078/179] Issue fixes and Completed chat extended page --- .../chats/chat-context-provider.tsx | 84 ++++- .../components/chats/chat-input/index.tsx | 4 +- .../chats/chat-popup/chat-constants.ts | 4 +- .../components/chats/chat-popup/index.scss | 172 ++-------- .../components/chats/chat-popup/index.tsx | 295 ++++------------- .../chats/chats-channel-messages/index.scss | 4 + .../chats/chats-channel-messages/index.tsx | 40 +-- .../chats/chats-direct-messages/index.tsx | 18 +- .../chats/chats-messages-box/index.scss | 16 +- .../chats/chats-messages-box/index.tsx | 58 ++-- .../chats/chats-messages-header/index.scss | 55 +++- .../chats/chats-messages-header/index.tsx | 31 +- .../chats/chats-messages-view/index.tsx | 33 -- .../chats/chats-profile-box/index.tsx | 7 - .../chats/chats-scroller/index.scss | 2 +- .../components/chats/chats-sidebar/indes.tsx | 299 ++++++++++-------- .../components/chats/chats-sidebar/index.scss | 63 +++- .../components/chats/import-chats/index.tsx | 1 - .../chats/join-community-chat-btn/index.tsx | 61 ++-- .../chats/utils/upload-chat-public-key.ts | 14 +- .../components/manage-chat-key/index.scss | 22 +- .../components/manage-chat-key/index.tsx | 18 +- src/common/helper/message-service.ts | 4 - src/common/i18n/locales/en-US.json | 3 +- src/common/img/svg.tsx | 35 ++ src/common/pages/chats/index.scss | 2 +- src/common/pages/chats/index.tsx | 87 ++--- src/managers/message-manager.tsx | 45 +-- 28 files changed, 683 insertions(+), 794 deletions(-) diff --git a/src/common/components/chats/chat-context-provider.tsx b/src/common/components/chats/chat-context-provider.tsx index 9b66d5a375f..fc693ac07f7 100644 --- a/src/common/components/chats/chat-context-provider.tsx +++ b/src/common/components/chats/chat-context-provider.tsx @@ -8,7 +8,7 @@ import { createNoStrAccount, getPrivateKey, getUserChatPublicKey, - setProfileMetaData + uploadChatPublicKey } from "./utils"; import * as ls from "../../util/local-storage"; import { setNostrkeys } from "../../../managers/message-manager"; @@ -23,6 +23,11 @@ interface Context { messageServiceInstance: MessageService | null; hasUserJoinedChat: boolean; currentChannel: Channel | null; + showSideBar: boolean; + windowWidth: number; + maxHeight: number; + isActveUserRemoved: boolean; + setShowSideBar: (d: boolean) => void; setCurrentChannel: (channel: Channel) => void; setRevealPrivKey: (d: boolean) => void; setShowSpinner: (d: boolean) => void; @@ -47,6 +52,11 @@ export const ChatContext = React.createContext({ messageServiceInstance: null, hasUserJoinedChat: false, currentChannel: null, + showSideBar: true, + windowWidth: 0, + maxHeight: 0, + isActveUserRemoved: false, + setShowSideBar: () => {}, setCurrentChannel: () => {}, setRevealPrivKey: () => {}, setShowSpinner: () => {}, @@ -70,22 +80,59 @@ export const ChatContextProvider = (props: Props) => { const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); const [shouldUpdateProfile, setShouldUpdateProfile] = useState(false); const [currentChannel, setCurrentChannel] = useState(null); - - useEffect(() => { - console.log("currentChannel", currentChannel); - }, [currentChannel]); + const [showSideBar, setShowSideBar] = useState(true); + const [windowWidth, setWindowWidth] = useState(0); + const [maxHeight, setMaxHeight] = useState(0); + const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); useMount(() => { getActiveUserKeys(); + handleShowSideBar(); + setWindowWidth(window.innerWidth); + setMaxHeight(window.innerHeight - 66); }); useEffect(() => { + if (currentChannel && currentChannel.removedUserIds) { + setIsActiveUserRemoved(currentChannel.removedUserIds?.includes(activeUserKeys?.pub!)); + } + }, [currentChannel]); + + useEffect(() => { + console.log("messageServiceInstance", messageServiceInstance); + }, [messageServiceInstance]); + + useEffect(() => { + const handleResize = () => { + setWindowWidth(window.innerWidth); + setMaxHeight(window.innerHeight - 66); + }; + + window.addEventListener("resize", handleResize); + + return () => { + window.removeEventListener("resize", handleResize); + }; + }, []); + + useEffect(() => { + getActiveUserKeys(); if (messageServiceInstance) { - getActiveUserKeys(); setShouldUpdateProfile(false); } }, [messageServiceInstance]); + useEffect(() => { + console.log("showsidebar", showSideBar); + }, [showSideBar]); + + useEffect(() => { + window.addEventListener("resize", handleShowSideBar); + return () => { + window.removeEventListener("resize", handleShowSideBar); + }; + }, []); + useEffect(() => { if (shouldUpdateProfile && messageServiceInstance) { messageServiceInstance.updateProfile({ @@ -96,16 +143,17 @@ export const ChatContextProvider = (props: Props) => { } }, [shouldUpdateProfile, messageServiceInstance]); - useEffect(() => { - if (showSpinner) { - setTimeout(() => { - setShowSpinner(false); - }, 3000); - } - }, [showSpinner]); - useDebounce(() => setShowSpinner(false), 3000, [showSpinner]); + const handleShowSideBar = () => { + console.log("Function run", window.innerWidth); + if (window.innerWidth < 768) { + setShowSideBar(false); + } else { + setShowSideBar(true); + } + }; + const getActiveUserKeys = async () => { const pubKey = await getUserChatPublicKey(activeUser?.username!); const privKey = getPrivateKey(activeUser?.username!); @@ -125,7 +173,6 @@ export const ChatContextProvider = (props: Props) => { newMessageService = new MessageService(keys.priv, keys.pub); setMessageServiceInstance(newMessageService); } - console.log("newMessageService", newMessageService); return newMessageService; }; @@ -133,7 +180,7 @@ export const ChatContextProvider = (props: Props) => { resetChat(); const keys = createNoStrAccount(); ls.set(`${activeUser?.username}_nsPrivKey`, keys.priv); - await setProfileMetaData(activeUser, keys.pub); + await uploadChatPublicKey(activeUser, keys.pub); setHasUserJoinedChat(true); setNostrkeys(keys); setChatPrivKey(keys.priv); @@ -152,6 +199,11 @@ export const ChatContextProvider = (props: Props) => { messageServiceInstance, hasUserJoinedChat, currentChannel, + showSideBar, + windowWidth, + maxHeight, + isActveUserRemoved, + setShowSideBar, setCurrentChannel, setRevealPrivKey, setShowSpinner, diff --git a/src/common/components/chats/chat-input/index.tsx b/src/common/components/chats/chat-input/index.tsx index c58061737e9..f6386bddd50 100644 --- a/src/common/components/chats/chat-input/index.tsx +++ b/src/common/components/chats/chat-input/index.tsx @@ -33,7 +33,6 @@ import { ChatContext } from "../chat-context-provider"; interface Props { isCurrentUser: boolean; isCommunity: boolean; - isActveUserRemoved: boolean; currentChannel: Channel; currentUser: string; isCurrentUserJoined: boolean; @@ -52,12 +51,11 @@ export default function ChatInput(props: Props) { const [isMessageText, setIsMessageText] = useState(false); // const [isMounted, setIsMounted] = useState(false); - const { messageServiceInstance, chatPrivKey } = useContext(ChatContext); + const { messageServiceInstance, chatPrivKey, isActveUserRemoved } = useContext(ChatContext); const { isCommunity, isCurrentUser, - isActveUserRemoved, currentChannel, currentUser, isCurrentUserJoined, diff --git a/src/common/components/chats/chat-popup/chat-constants.ts b/src/common/components/chats/chat-popup/chat-constants.ts index 76338f7b27c..2e0ff4d8312 100644 --- a/src/common/components/chats/chat-popup/chat-constants.ts +++ b/src/common/components/chats/chat-popup/chat-constants.ts @@ -19,8 +19,8 @@ export const GifPickerStyle = { }; export const DropDownStyle = { - width: "40px", - height: "40px" + width: "35px", + height: "35px" }; export const GifImagesStyle = { diff --git a/src/common/components/chats/chat-popup/index.scss b/src/common/components/chats/chat-popup/index.scss index 1c56a18b60a..4e8590d9ec0 100644 --- a/src/common/components/chats/chat-popup/index.scss +++ b/src/common/components/chats/chat-popup/index.scss @@ -29,8 +29,8 @@ .chat-header { border-bottom: 1px solid $white-four; - padding-left: 6px; - padding-right: 6px; + padding-left: 10px; + padding-right: 4px; cursor: pointer; height: 53px; display: flex; @@ -50,6 +50,7 @@ .back-arrow-svg { border-radius: 50%; padding: 6px; + margin-top: 6px; } } @@ -59,23 +60,24 @@ .message-header-title { display: flex; width: 288px; - margin-left: 0.6rem; + margin-left: 0.1rem; .user-icon { - margin: 14px 11px 0 0; + margin: 17px 11px 0 0; } } .message-header-content { - margin: 0.8rem 0; + margin: 1rem 0; font-weight: 700; line-height: 24px; font-size: 21px; font-family: $font-family-sans-serif; - max-width: 190px; + max-width: 185px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + margin-bottom: 0; @include themify(day) { color: $charcoal-grey; @@ -91,16 +93,18 @@ align-items: center; justify-content: center; margin-top: 1.1rem; - .message-image { + .message-image, + .extended-image { margin-top: 2px; - .message-svg { - width: 40px; - height: 40px; + .message-svg, + .extended-svg { + width: 35px; + height: 35px; justify-content: center; text-align: center; border-radius: 50%; - padding-top: 13px; + padding-top: 10px; display: flex; svg { @@ -108,7 +112,11 @@ width: 20px; } } - .message-svg:hover { + .extended-svg { + padding-top: 8px; + } + .message-svg:hover, + .extended-svg:hover { background: rgba(29, 155, 240, 0.1); } } @@ -118,10 +126,11 @@ display: flex; justify-content: center; text-align: center; - padding-top: 6px; - width: 40px; - height: 40px; + padding-top: 3px; + width: 35px; + height: 35px; border-radius: 50%; + margin-bottom: 14px; } .arrow-svg:hover { @@ -290,76 +299,6 @@ background: $gunmetal; } } - .user-profile { - border-bottom-right-radius: 20px; - border-bottom-left-radius: 20px; - min-height: 266px; - - @include themify(day) { - background: $white-three; - } - @include themify(night) { - background: #172b44; - } - - padding: 0 16px 16px 16px; - margin: 0 16px 16px 16px; - - .user-logo { - display: flex; - justify-content: center; - align-items: center; - padding-top: 16px; - cursor: pointer; - } - .user-name { - font-size: 20px; - font-weight: 700; - @include themify(day) { - color: $charcoal-grey; - } - @include themify(night) { - color: $white; - } - } - .about { - text-align: center; - padding: 4px 0; - @include themify(day) { - color: $charcoal-grey; - } - @include themify(night) { - color: $white; - } - } - .joining-info { - justify-content: space-evenly; - } - .created-date { - padding-top: 8px; - font-size: 14px; - @include themify(day) { - color: $charcoal-grey; - } - @include themify(night) { - color: $white; - } - } - .followers { - font-size: 14px; - padding-top: 4px !important; - font-family: New Century Schoolbook; - } - } - - .user-profile:hover { - @include themify(day) { - background: $white-four; - } - @include themify(night) { - background: #0f223a; - } - } .scroller { position: sticky; @@ -450,66 +389,3 @@ } } } - -.chats-dialog { - .chat-modals-body { - padding: 2rem; - - .leave-dialog-body { - font-size: 18px; - margin-top: 12px; - } - .leave-confirm-buttons { - text-align: right !important; - .close-btn, - .confirm-btn { - margin-right: 20px; - } - } - - .import-chat-dialog-header { - display: flex; - .import-chat-dialog-titles { - margin-top: 9px; - } - .import-chat-main-title { - font-weight: bold; - } - } - .success-dialog-body { - margin: 2rem 0 1.4rem 0; - .success-dialog-content { - text-align: center; - margin-bottom: 1.4rem; - } - } - } - .add-dialog-header { - margin-bottom: 20px; - } - .community-chat-role-edit-dialog-content { - .add-user-role-form { - margin: 15px 0; - } - .user { - display: flex; - } - .username { - margin: 10px 0 0 10px; - } - } -} - -.chat-button { - position: fixed; - z-index: 99; - right: 6rem; - bottom: 20px; - width: 40px; - height: 40px; - border-radius: 50% !important; -} -.medium-zoom-overlay, -.medium-zoom-image--opened { - z-index: 999; -} diff --git a/src/common/components/chats/chat-popup/index.tsx b/src/common/components/chats/chat-popup/index.tsx index d584c598081..6926e5eca83 100644 --- a/src/common/components/chats/chat-popup/index.tsx +++ b/src/common/components/chats/chat-popup/index.tsx @@ -1,9 +1,9 @@ import React, { useContext, useEffect, useRef, useState } from "react"; import useDebounce from "react-use/lib/useDebounce"; import { useLocation } from "react-router"; -import { History } from "history"; -import { Button, Form, Modal, Spinner } from "react-bootstrap"; +import { Button, Form, Spinner } from "react-bootstrap"; import { Link } from "react-router-dom"; +import { history } from "../../../../common/store"; import { Community } from "../../../store/communities/types"; import { ToggleType } from "../../../store/ui/types"; @@ -18,7 +18,6 @@ import { import Tooltip from "../../tooltip"; import UserAvatar from "../../user-avatar"; import LinearProgress from "../../linear-progress"; -import { error } from "../../feedback"; import { setNostrkeys } from "../../../../managers/message-manager"; import ManageChatKey from "../../manage-chat-key"; import ChatInput from "../chat-input"; @@ -34,48 +33,36 @@ import { arrowBackSvg, chevronUpSvg, chevronDownSvgForSlider, - syncSvg + syncSvg, + extendedView } from "../../../img/svg"; -import { - NOSTRKEY, - NEWCHATACCOUNT, - CHATIMPORT, - RESENDMESSAGE, - EmojiPickerStyle, - GifPickerStyle -} from "./chat-constants"; - -import * as ls from "../../../util/local-storage"; +import { EmojiPickerStyle, GifPickerStyle } from "./chat-constants"; + import accountReputation from "../../../helper/account-reputation"; import { _t } from "../../../i18n"; import { usePrevious } from "../../../util/use-previous"; -import { getAccountFull, getAccountReputations } from "../../../api/hive"; +import { getAccountReputations } from "../../../api/hive"; import { getCommunity } from "../../../api/bridge"; import "./index.scss"; import ChatsDirectMessages from "../chats-direct-messages"; -import { AccountWithReputation, NostrKeysType } from "../types"; +import { AccountWithReputation } from "../types"; import { - createNoStrAccount, deleteChatPublicKey, fetchCommunityMessages, getCommunityLastMessage, getDirectLastMessage, getJoinedCommunities, getPrivateKey, - getProfileMetaData, - getUserChatPublicKey, - setProfileMetaData + getUserChatPublicKey } from "../utils"; import { useMappedStore } from "../../../store/use-mapped-store"; import { ChatContext } from "../chat-context-provider"; import ImportChats from "../import-chats"; -import MessageService from "../../../helper/message-service"; interface Props { - history: History; setActiveUser: (username: string | null) => void; updateActiveUser: (data?: Account) => void; deleteUser: (username: string) => void; @@ -92,6 +79,7 @@ export const ChatPopUp = (props: Props) => { showSpinner, hasUserJoinedChat, currentChannel, + windowWidth, setCurrentChannel, setRevealPrivKey, setShowSpinner, @@ -118,19 +106,13 @@ export const ChatPopUp = (props: Props) => { const [communityName, setCommunityName] = useState(""); const [currentCommunity, setCurrentCommunity] = useState(); const [publicMessages, setPublicMessages] = useState([]); - const [clickedMessage, setClickedMessage] = useState(""); - const [keyDialog, setKeyDialog] = useState(false); - const [step, setStep] = useState(0); const [communities, setCommunities] = useState([]); const [searchtext, setSearchText] = useState(""); const [userList, setUserList] = useState([]); - const [noStrPrivKey, setNoStrPrivKey] = useState(""); - const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); const [isTop, setIsTop] = useState(false); const [hasMore, setHasMore] = useState(true); - const [removedUsers, setRemovedUsers] = useState([]); - const [innerWidth, setInnerWidth] = useState(0); const [isChatPage, setIsChatPage] = useState(false); + const [isScrolled, setIsScrolled] = useState(false); useEffect(() => { console.log("chat in store", chat); @@ -156,11 +138,12 @@ export const ChatPopUp = (props: Props) => { useEffect(() => { // deleteChatPublicKey(activeUser); setShow(!!activeUser?.username && !isChatPage); - const noStrPrivKey = getPrivateKey(activeUser?.username!); - setNoStrPrivKey(noStrPrivKey); - setInnerWidth(window.innerWidth); }, []); + useEffect(() => { + console.log("Show", show); + }, [show]); + useEffect(() => { const updated: ChannelUpdate = chat.updatedChannel .filter((x) => x.channelId === currentChannel?.id!) @@ -198,10 +181,10 @@ export const ChatPopUp = (props: Props) => { useEffect(() => { if (messageServiceInstance) { setIsSpinner(false); - const noStrPrivKey = getPrivateKey(activeUser?.username!); - setNoStrPrivKey(noStrPrivKey); + } else { + console.log("Else run in chat-popup"); } - }, [typeof window !== "undefined" && messageServiceInstance]); + }, [messageServiceInstance]); useEffect(() => { const communities = getJoinedCommunities(chat.channels, chat.leftChannelsList); @@ -224,10 +207,10 @@ export const ChatPopUp = (props: Props) => { }, [global.theme, activeUser]); useEffect(() => { - if (directMessagesList.length !== 0) { + if (directMessagesList.length !== 0 && !isScrolled) { scrollerClicked(); } - if (!isScrollToBottom && publicMessages.length !== 0 && isCommunity) { + if (!isScrolled && publicMessages.length !== 0 && isCommunity) { scrollerClicked(); } }, [directMessagesList, publicMessages]); @@ -240,45 +223,31 @@ export const ChatPopUp = (props: Props) => { currentChannel, currentChannel.hiddenMessageIds ); - currentChannel?.removedUserIds && setRemovedUsers(currentChannel.removedUserIds); const messages = publicMessages.sort((a, b) => a.created - b.created); setPublicMessages(messages); } }, [currentChannel, isCommunity, chat.publicMessages]); useEffect(() => { - scrollerClicked(); - }, [isCurrentUser, isCommunity]); - - useEffect(() => { - if (removedUsers) { - const removed = removedUsers.includes(activeUserKeys?.pub!); - setIsActiveUserRemoved(removed); + if ((isCurrentUser || isCommunity) && show) { + scrollerClicked(); } - }, [removedUsers]); + }, [isCurrentUser, isCommunity, show]); useEffect(() => { const msgsList = fetchDirectMessages(receiverPubKey!); const messages = msgsList.sort((a, b) => a.created - b.created); setDirectMessagesList(messages); - const noStrPrivKey = getPrivateKey(activeUser?.username!); - setNoStrPrivKey(noStrPrivKey); }, [activeUser]); useEffect(() => { - if (isCommunity) { + if (isCommunity && show) { fetchCommunity(); + scrollerClicked(); fetchCurrentChannel(communityName); } - }, [isCommunity, communityName]); - - useEffect(() => { - window.addEventListener("resize", handleResize); - return () => { - window.removeEventListener("resize", handleResize); - }; - }, []); + }, [isCommunity, communityName, show]); useEffect(() => { if (currentUser) { @@ -327,7 +296,7 @@ export const ChatPopUp = (props: Props) => { setShow(false); setIsChatPage(true); } else { - setShow(true); + setShow(!!activeUser?.username); } }; @@ -362,10 +331,6 @@ export const ChatPopUp = (props: Props) => { } }; - const handleResize = () => { - setInnerWidth(window.innerWidth); - }; - const fetchDirectMessages = (peer: string) => { for (const item of chat.directMessages) { if (item.peer === peer) { @@ -376,20 +341,12 @@ export const ChatPopUp = (props: Props) => { }; const fetchCurrentUserData = async () => { - console.log("Fetch profile run"); - // const response = await getAccountFull(currentUser); - // const { posting_json_metadata } = response; - // const profile = JSON.parse(posting_json_metadata!).profile; - // const { nsKey } = profile || {}; const nsKey = await getUserChatPublicKey(currentUser); - console.log("nsKey", nsKey); if (nsKey) { setReceiverPubKey(nsKey); } else { setReceiverPubKey(""); } - - console.log("Hello reached"); setIsCurrentUserJoined(!!nsKey); setInProgress(false); }; @@ -423,6 +380,8 @@ export const ChatPopUp = (props: Props) => { const isScrollToBottom = (isCurrentUser || isCommunity) && element.scrollTop + chatBodyDivRef?.current?.clientHeight! < element.scrollHeight - 200; + const isScrolled = element.scrollTop + element.clientHeight <= element.scrollHeight - 20; + setIsScrolled(isScrolled); setIsScrollToTop(isScrollToTop); setIsScrollToBottom(isScrollToBottom); const scrollerTop = element.scrollTop <= 600 && publicMessages.length > 25; @@ -459,8 +418,6 @@ export const ChatPopUp = (props: Props) => { if (isCommunity && communityName) { fetchCurrentChannel(communityName); } - } else { - setNoStrPrivKey(""); } }; @@ -473,128 +430,38 @@ export const ChatPopUp = (props: Props) => { ); - const communityClicked = (community: string, name: string) => { + const communityClicked = (community: string) => { setIsCommunity(true); setCommunityName(community); }; - const finish = () => { - setStep(0); - setKeyDialog(false); - }; - - const handleConfirmButton = (actionType: string) => { - switch (actionType) { - case NEWCHATACCOUNT: - setInProgress(true); - deleteChatPublicKey(activeUser) - .then((updatedProfile) => { - handleJoinChat(); - setStep(10); - setInProgress(false); - }) - .catch((err) => { - error(err); - }); - break; - default: - break; - } - }; - - const successModal = (message: string) => { - return ( - <> -
-
2
-
-
{_t("manage-authorities.success-title")}
-
- {_t("manage-authorities.success-sub-title")} -
-
-
-
-
- - {message === CHATIMPORT - ? "Chats imported successfully" - : message === NEWCHATACCOUNT - ? "New Account created successfully" - : ""} - -
-
- - -
-
- - ); - }; - - const confirmationModal = (actionType: string) => { - return ( - <> -
-
-

- {actionType === NEWCHATACCOUNT ? "Warning" : "Confirmation"} -

-
-
- {inProgress && actionType === NEWCHATACCOUNT && } -
- {actionType === NEWCHATACCOUNT - ? "creating new account will reset your chats" - : "Are you sure?"} -
-

- - -

- - ); - }; - - const toggleKeyDialog = () => { - setKeyDialog(!keyDialog); - }; - const handleBackArrowSvg = () => { setCurrentUser(""); setIsCurrentUser(false); setCommunityName(""); setIsCommunity(false); - setClickedMessage(""); setShowSearchUser(false); setSearchText(""); setHasMore(true); setRevealPrivKey(false); }; + const handleExtendedView = () => { + if (!isCurrentUser && !isCommunity) { + history?.push("/chats"); + } else if (isCurrentUser) { + history?.push(`/chats/@${currentUser}`); + } else { + history?.push(`/chats/${communityName}`); + } + }; + return ( <> {show && (
@@ -633,19 +500,28 @@ export const ChatPopUp = (props: Props) => {

- {!currentUser && hasUserJoinedChat && noStrPrivKey && !isCommunity && !revealPrivKey && ( - <> -
- -

{addMessageSVG}

-
-
- - )} - {hasUserJoinedChat && noStrPrivKey && !revealPrivKey && ( +
+ +

{extendedView}

+
+
+ {!currentUser && + hasUserJoinedChat && + activeUserKeys?.priv && + !isCommunity && + !revealPrivKey && ( + <> +
+ +

{addMessageSVG}

+
+
+ + )} + {hasUserJoinedChat && activeUserKeys?.priv && !revealPrivKey && (
-

+

{syncSvg}

@@ -653,13 +529,13 @@ export const ChatPopUp = (props: Props) => { )} {isCommunity && (
- +
)}{" "} - {!isCommunity && !isCurrentUser && noStrPrivKey && ( + {!isCommunity && !isCurrentUser && activeUserKeys?.priv && (
setExpanded(true)}> { setRevealPrivKey(!revealPrivKey); }} @@ -685,13 +561,7 @@ export const ChatPopUp = (props: Props) => {
@@ -727,11 +597,11 @@ export const ChatPopUp = (props: Props) => { ) : ( )} @@ -786,7 +656,7 @@ export const ChatPopUp = (props: Props) => { {(chat.directContacts.length !== 0 || (chat.channels.length !== 0 && communities.length !== 0)) && !showSpinner && - noStrPrivKey ? ( + activeUserKeys?.priv ? ( {chat.channels.length !== 0 && communities.length !== 0 && ( <> @@ -807,9 +677,7 @@ export const ChatPopUp = (props: Props) => {
- communityClicked(channel.communityName!, channel.name) - } + onClick={() => communityClicked(channel.communityName!)} >

{channel.name}

@@ -851,7 +719,7 @@ export const ChatPopUp = (props: Props) => { ); })} - ) : !noStrPrivKey || noStrPrivKey.length === 0 || noStrPrivKey === null ? ( + ) : !activeUserKeys?.priv ? ( <> @@ -897,17 +765,11 @@ export const ChatPopUp = (props: Props) => {

)} - {isActveUserRemoved && isCommunity && ( -

- {_t("chat.blocked-user-message")} -

- )}
{(isCurrentUser || isCommunity) && ( { )}
)} - - {keyDialog && ( - - - - {step === 9 && confirmationModal(NEWCHATACCOUNT)} - {step === 11 && confirmationModal(RESENDMESSAGE)} - {step === 10 && successModal(NEWCHATACCOUNT)} - - - )} ); }; diff --git a/src/common/components/chats/chats-channel-messages/index.scss b/src/common/components/chats/chats-channel-messages/index.scss index 40532ce80eb..9045fe3358c 100644 --- a/src/common/components/chats/chats-channel-messages/index.scss +++ b/src/common/components/chats/chats-channel-messages/index.scss @@ -273,3 +273,7 @@ } } } +.medium-zoom-overlay, +.medium-zoom-image--opened { + z-index: 999; +} diff --git a/src/common/components/chats/chats-channel-messages/index.tsx b/src/common/components/chats/chats-channel-messages/index.tsx index fbc273861af..82f4d9dad41 100644 --- a/src/common/components/chats/chats-channel-messages/index.tsx +++ b/src/common/components/chats/chats-channel-messages/index.tsx @@ -50,7 +50,6 @@ interface Props { history: History; isScrollToBottom: boolean; isScrolled?: boolean; - isActveUserRemoved: boolean; setActiveUser: (username: string | null) => void; updateActiveUser: (data?: Account) => void; deleteUser: (username: string) => void; @@ -68,13 +67,13 @@ export default function ChatsChannelMessages(props: Props) { from, isScrollToBottom, isScrolled, - isActveUserRemoved, scrollToBottom, currentChannelSetter } = props; const { chat, global, activeUser, ui, users, deletePublicMessage } = useMappedStore(); - const { messageServiceInstance, activeUserKeys } = useContext(ChatContext); + const { messageServiceInstance, activeUserKeys, windowWidth, isActveUserRemoved } = + useContext(ChatContext); let prevGlobal = usePrevious(global); @@ -90,11 +89,10 @@ export default function ChatsChannelMessages(props: Props) { const [privilegedUsers, setPrivilegedUsers] = useState([]); const [hiddenMsgId, setHiddenMsgId] = useState(""); const [resendMessage, setResendMessage] = useState(); - const [windowWidth, setWindowWidth] = useState(0); + const [showMessageActions, setShowMessageActions] = useState(false); useMount(() => { - setWindowWidth(window.innerWidth); if (window.innerWidth <= 768) { setShowMessageActions(true); } @@ -116,6 +114,12 @@ export default function ChatsChannelMessages(props: Props) { } }, [publicMessages, isScrollToBottom, channelMessagesRef]); + useEffect(() => { + if (windowWidth <= 768) { + setShowMessageActions(true); + } + }, [windowWidth]); + useEffect(() => { if (currentChannel) { zoomInitializer(); @@ -123,14 +127,6 @@ export default function ChatsChannelMessages(props: Props) { } }, [currentChannel]); - useEffect(() => { - window.addEventListener("resize", handleResize); - - return () => { - window.removeEventListener("resize", handleResize); - }; - }, []); - useEffect(() => { const handleClickOutside = (event: MouseEvent) => { const clickedElement = event.target as HTMLElement; @@ -173,13 +169,6 @@ export default function ChatsChannelMessages(props: Props) { } }; - const handleResize = () => { - setWindowWidth(window.innerWidth); - if (window.innerWidth <= 768) { - setShowMessageActions(true); - } - }; - const handleDMChange = (e: React.ChangeEvent) => { setDmMessage(e.target.value); }; @@ -191,11 +180,11 @@ export default function ChatsChannelMessages(props: Props) { }; const setBackground = () => { - if (global.theme === Theme.day) { - zoom?.update({ background: "#ffffff" }); - } else { - zoom?.update({ background: "#131111" }); - } + // if (global.theme === Theme.day) { + // zoom?.update({ background: "#ffffff" }); + // } else { + // zoom?.update({ background: "#131111" }); + // } }; const handleImageClick = (msgId: string, pubkey: string) => { @@ -308,7 +297,6 @@ export default function ChatsChannelMessages(props: Props) { }; const handelMessageActions = (msgId: string) => { - console.log("showMessageActions", showMessageActions, "msgId", msgId); if (showMessageActions && hoveredMessageId !== msgId) { setHoveredMessageId(msgId); } else { diff --git a/src/common/components/chats/chats-direct-messages/index.tsx b/src/common/components/chats/chats-direct-messages/index.tsx index 8f2f06d68b3..55d1930a9be 100644 --- a/src/common/components/chats/chats-direct-messages/index.tsx +++ b/src/common/components/chats/chats-direct-messages/index.tsx @@ -48,11 +48,9 @@ export default function ChatsDirectMessages(props: Props) { scrollToBottom } = props; - const { chat, global, activeUser, deleteDirectMessage } = useMappedStore(); + const { global, activeUser, deleteDirectMessage } = useMappedStore(); const { activeUserKeys, messageServiceInstance } = useContext(ChatContext); - console.log("directMessages", directMessages); - let prevGlobal = usePrevious(global); const [step, setStep] = useState(0); const [resendMessage, setResendMessage] = useState(); @@ -103,16 +101,6 @@ export default function ChatsDirectMessages(props: Props) { return <>; }; - // const getFormattedDate = (msg: DirectMessage, i: number) => { - // const prevMsg = directMessages[i - 1]; - // const msgDate = formatMessageDate(msg.created); - // const prevMsgDate = prevMsg ? formatMessageDate(prevMsg.created) : null; - // if (msgDate !== prevMsgDate) { - // return true; - // } - // return false; - // }; - const handleConfirm = () => { switch (step) { case 1: @@ -146,10 +134,6 @@ export default function ChatsDirectMessages(props: Props) { const isSameUser = checkContiguousMessage(msg, i, directMessages); - // const date = getFormattedDate(msg, i); - - // console.log('date', date, msg) - return ( {dayAndMonth} diff --git a/src/common/components/chats/chats-messages-box/index.scss b/src/common/components/chats/chats-messages-box/index.scss index 4b533466c7b..43d34528bf8 100644 --- a/src/common/components/chats/chats-messages-box/index.scss +++ b/src/common/components/chats/chats-messages-box/index.scss @@ -23,15 +23,17 @@ display: flex; align-items: center; justify-content: center; - - .start-chat { - @include themify(day) { - color: #65676b; - } - @include themify(night) { - color: $white; + .start-chat-wrapper { + .start-chat { + @include themify(day) { + color: #65676b; + } + @include themify(night) { + color: $white; + } } } + .info-message { font-size: 16px; // font-weight: 100; diff --git a/src/common/components/chats/chats-messages-box/index.tsx b/src/common/components/chats/chats-messages-box/index.tsx index 6bda5f2d967..c63c078f136 100644 --- a/src/common/components/chats/chats-messages-box/index.tsx +++ b/src/common/components/chats/chats-messages-box/index.tsx @@ -17,6 +17,7 @@ import { CHANNEL } from "../chat-popup/chat-constants"; import { Button } from "react-bootstrap"; import { ChatContext } from "../chat-context-provider"; import { getCommunities } from "../../../api/bridge"; +import { useMount } from "react-use"; interface MatchParams { filter: string; @@ -40,34 +41,37 @@ interface Props { export default function ChatsMessagesBox(props: Props) { const { chat } = useMappedStore(); - const { messageServiceInstance, currentChannel, setCurrentChannel } = useContext(ChatContext); + const { + messageServiceInstance, + currentChannel, + windowWidth, + maxHeight, + setCurrentChannel, + setShowSideBar + } = useContext(ChatContext); const { channels, updatedChannel } = chat; const { match } = props; const username = match.params.username; - const [maxHeight, setMaxHeight] = useState(0); const [inProgress, setInProgress] = useState(false); const [isCommunityChatEnabled, setIsCommunityChatEnabled] = useState(false); const [isCommunityJoined, setIsCommunityChatJoined] = useState(false); const [currentCommunity, setCurrentCommunity] = useState(); const [hasLeftCommunity, setHasLeftCommunity] = useState(false); - useEffect(() => { - setMaxHeight(window.innerHeight - 68); - }, [typeof window !== "undefined"]); - - useEffect(() => { + useMount(() => { checkUserCommunityMembership(); - const handleResize = () => { - setMaxHeight(window.innerHeight - 68); - }; + }); - window.addEventListener("resize", handleResize); - return () => { - window.removeEventListener("resize", handleResize); - }; - }, []); + useEffect(() => { + console.log( + "isCommunityChatEnabled", + isCommunityChatEnabled, + "isCommunityJoined", + isCommunityJoined + ); + }, [isCommunityChatEnabled, isCommunityJoined]); useEffect(() => { if (currentCommunity && chat.leftChannelsList.includes(currentCommunity.id)) { @@ -84,17 +88,6 @@ export default function ChatsMessagesBox(props: Props) { } }, [username]); - useEffect(() => { - console.log( - "isCommunityChatEnabled", - isCommunityChatEnabled, - "isCommunityJoined", - isCommunityJoined, - "hasLeftCommunity", - hasLeftCommunity - ); - }, [isCommunityChatEnabled, isCommunityJoined, hasLeftCommunity]); - const checkUserCommunityMembership = () => { getCommunityProfile(); const communities = getJoinedCommunities(chat.channels, chat.leftChannelsList); @@ -161,10 +154,19 @@ export default function ChatsMessagesBox(props: Props) { }; return ( -
+
{match.url === "/chats" ? (
-

Select a chat or start a new conversation

+
+

Select a chat or start a new conversation

+ {windowWidth < 768 && } +
) : ( <> diff --git a/src/common/components/chats/chats-messages-header/index.scss b/src/common/components/chats/chats-messages-header/index.scss index 7c0ab8fdfd7..bda15d751c6 100644 --- a/src/common/components/chats/chats-messages-header/index.scss +++ b/src/common/components/chats/chats-messages-header/index.scss @@ -26,29 +26,52 @@ } .header-content { - .user-info { - display: inline-flex; - padding: 8px 10px; - margin: 5px 0 0 9px; - cursor: pointer; + .user-info-wrapper { + .expand-icon { + margin-top: 15px; + .expand-svg { + height: 35px; + width: 35px; + background: #f4f4f4; + margin-left: 10px; + border-radius: 10px; + cursor: pointer; + svg { + width: 35px; + height: 35px; + } - .username { - font-size: 21px; - font-family: Helvetica, Arial, sans-serif; - font-weight: 700; - margin: 7px 0 0 12px; + &:hover { + background: #e4e6eb; + } + } } - &:hover { - border-radius: 10px; - @include themify(day) { - background: #eeeeee; + + .user-info { + display: inline-flex; + padding: 8px 10px; + margin: 5px 0 0 0; + cursor: pointer; + + .username { + font-size: 21px; + font-family: Helvetica, Arial, sans-serif; + font-weight: 700; + margin: 7px 0 0 12px; } + &:hover { + border-radius: 10px; + @include themify(day) { + background: #eeeeee; + } - @include themify(night) { - background: $dusky-blue; + @include themify(night) { + background: $dusky-blue; + } } } } + .community-menu { border: none; width: 40px; diff --git a/src/common/components/chats/chats-messages-header/index.tsx b/src/common/components/chats/chats-messages-header/index.tsx index 9e7b8b99165..2bf04665272 100644 --- a/src/common/components/chats/chats-messages-header/index.tsx +++ b/src/common/components/chats/chats-messages-header/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { History } from "history"; import { _t } from "../../../i18n"; import { useMappedStore } from "../../../store/use-mapped-store"; @@ -11,6 +11,8 @@ import { CHATPAGE } from "../chat-popup/chat-constants"; import { Chat } from "../../../store/chat/types"; import { formattedUserName } from "../utils"; import Link from "../../alink"; +import { expandSideBar } from "../../../img/svg"; +import { ChatContext } from "../chat-context-provider"; interface Props { username: string; @@ -20,6 +22,7 @@ interface Props { export default function ChatsMessagesHeader(props: Props) { const { username } = props; const { chat } = useMappedStore(); + const { setShowSideBar } = useContext(ChatContext); const isChannel = (username: string) => { if (username.startsWith("@")) { @@ -42,16 +45,24 @@ export default function ChatsMessagesHeader(props: Props) { return (
- -
- -

{formattedName(username, chat)}

+
+
+

setShowSideBar(true)}> + {expandSideBar} +

- + +
+ +

{formattedName(username, chat)}

+
+ +
+ {isChannel(username) && (
diff --git a/src/common/components/chats/chats-messages-view/index.tsx b/src/common/components/chats/chats-messages-view/index.tsx index ae38a3fa13c..7cda969158a 100644 --- a/src/common/components/chats/chats-messages-view/index.tsx +++ b/src/common/components/chats/chats-messages-view/index.tsx @@ -61,16 +61,12 @@ export default function ChatsMessagesView(props: Props) { const [publicMessages, setPublicMessages] = useState([]); const [directMessages, setDirectMessages] = useState([]); const [communityName, setCommunityName] = useState(""); - const [activeUserKeys, setActiveUserKeys] = useState(); const [isScrollToBottom, setIsScrollToBottom] = useState(false); const [isTop, setIsTop] = useState(false); const [hasMore, setHasMore] = useState(true); const [isScrolled, setIsScrolled] = useState(false); - const [removedUsers, setRemovedUsers] = useState([]); - const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); useEffect(() => { - getActiveUserKeys(); isDirectUserOrCommunity(); }, []); @@ -87,7 +83,6 @@ export default function ChatsMessagesView(props: Props) { getDirectMessages(); } else if (communityName && currentChannel) { getChannelMessages(); - currentChannel?.removedUserIds && setRemovedUsers(currentChannel.removedUserIds); } }, [directUser, communityName, currentChannel, chat.directMessages]); @@ -104,13 +99,6 @@ export default function ChatsMessagesView(props: Props) { } }, [isTop]); - useEffect(() => { - if (removedUsers) { - const removed = removedUsers.includes(activeUserKeys?.pub!); - setIsActiveUserRemoved(removed); - } - }, [removedUsers]); - const fetchPrevMessages = () => { if (!hasMore || inProgress) return; @@ -118,7 +106,6 @@ export default function ChatsMessagesView(props: Props) { messageServiceInstance ?.fetchPrevMessages(currentChannel!.id, publicMessages[0].created) .then((num) => { - console.log("number", num); if (num < 25) { setHasMore(false); } @@ -129,16 +116,6 @@ export default function ChatsMessagesView(props: Props) { }); }; - const getActiveUserKeys = async () => { - const profileData = await getProfileMetaData(activeUser?.username!); - const noStrPrivKey = getPrivateKey(activeUser?.username!); - const activeUserKeys = { - pub: profileData?.nsKey, - priv: noStrPrivKey - }; - setActiveUserKeys(activeUserKeys); - }; - const isDirectUserOrCommunity = () => { if (username) { if (username && username.startsWith("@")) { @@ -163,15 +140,12 @@ export default function ChatsMessagesView(props: Props) { const getDirectMessages = () => { const user = chat.directContacts.find((item) => item.name === directUser); - console.log("user", user?.pubkey); const messages = user && fetchDirectMessages(user?.pubkey, chat.directMessages); const directMessages = messages?.sort((a, b) => a.created - b.created); setDirectMessages(directMessages!); - console.log("Messages", messages); }; const scrollToBottom = () => { - console.log("Scroll to bottom clicked", messagesBoxRef.current?.scrollHeight); messagesBoxRef && messagesBoxRef?.current?.scroll({ top: messagesBoxRef.current?.scrollHeight, @@ -181,16 +155,11 @@ export default function ChatsMessagesView(props: Props) { const handleScroll = (event: React.UIEvent) => { var element = event.currentTarget; - let srollHeight: number = (element.scrollHeight / 100) * 25; - // const isScrollToTop = !isCurrentUser && !isCommunity && element.scrollTop >= srollHeight; const isScrollToBottom = element.scrollTop + messagesBoxRef?.current?.clientHeight! < element.scrollHeight - 200; setIsScrollToBottom(isScrollToBottom); - // console.log(element.scrollTop, (element.scrollTop / 100) * 98); const isScrolled = element.scrollTop + element.clientHeight <= element.scrollHeight - 20; setIsScrolled(isScrolled); - // const isScrolled = element.scrollHeight/100 *3 - 50; - // console.log("isScrolled", isScrolled) const scrollerTop = element.scrollTop <= 600 && publicMessages.length > 25; if (communityName && scrollerTop) { setIsTop(true); @@ -221,7 +190,6 @@ export default function ChatsMessagesView(props: Props) { isScrollToBottom={isScrollToBottom} from={CHATPAGE} isScrolled={isScrolled} - isActveUserRemoved={isActveUserRemoved} scrollToBottom={scrollToBottom} currentChannelSetter={currentChannelSetter} /> @@ -252,7 +220,6 @@ export default function ChatsMessagesView(props: Props) { gifPickerStyle={EmojiPickerStyle} isCurrentUser={directUser ? true : false} isCommunity={communityName ? true : false} - isActveUserRemoved={isActveUserRemoved} currentUser={directUser} currentChannel={currentChannel!} isCurrentUserJoined={true} diff --git a/src/common/components/chats/chats-profile-box/index.tsx b/src/common/components/chats/chats-profile-box/index.tsx index 35f7e4e03d2..ea441a050df 100644 --- a/src/common/components/chats/chats-profile-box/index.tsx +++ b/src/common/components/chats/chats-profile-box/index.tsx @@ -6,7 +6,6 @@ import { dateToFormatted } from "../../../helper/parse-date"; import { _t } from "../../../i18n"; import { useMappedStore } from "../../../store/use-mapped-store"; import { getAccountFull } from "../../../api/hive"; -import { getCommunity } from "../../../api/bridge"; import { formattedUserName } from "../utils"; import { useCommunityCache } from "../../../core/caches/communities-cache"; @@ -35,12 +34,6 @@ export default function ChatsProfileBox(props: Props) { const { data: community } = useCommunityCache(username ? username! : communityName!); - // useEffect(() => { - // if (community) { - // console.log("Community 1st", community); - // } - // }, [community, communityName]); - useEffect(() => { fetchProfileData(); }, [username, isCommunity, isCurrentUser, communityName, currentUser]); diff --git a/src/common/components/chats/chats-scroller/index.scss b/src/common/components/chats/chats-scroller/index.scss index aefcae9b48f..fe337a59e70 100644 --- a/src/common/components/chats/chats-scroller/index.scss +++ b/src/common/components/chats/chats-scroller/index.scss @@ -6,7 +6,7 @@ height: 33px; border-radius: 50%; float: right; - z-index: 10; + z-index: 9; display: flex; align-items: center; justify-content: center; diff --git a/src/common/components/chats/chats-sidebar/indes.tsx b/src/common/components/chats/chats-sidebar/indes.tsx index 7cfb4d8c3d9..9885d8fbf61 100644 --- a/src/common/components/chats/chats-sidebar/indes.tsx +++ b/src/common/components/chats/chats-sidebar/indes.tsx @@ -10,7 +10,7 @@ import { getAccountReputations } from "../../../api/hive"; import accountReputation from "../../../helper/account-reputation"; import { getCommunityLastMessage, getDirectLastMessage, getJoinedCommunities } from "../utils"; import { _t } from "../../../i18n"; -import { arrowBackSvg, syncSvg } from "../../../img/svg"; +import { arrowBackSvg, closeSvg, syncSvg } from "../../../img/svg"; import ChatsScroller from "../chats-scroller"; import LinearProgress from "../../linear-progress"; import Tooltip from "../../tooltip"; @@ -36,6 +36,10 @@ export default function ChatsSideBar(props: Props) { activeUserKeys, revealPrivKey, chatPrivKey, + showSideBar, + windowWidth, + maxHeight, + setShowSideBar, setShowSpinner, setRevealPrivKey, setReceiverPubKey @@ -116,144 +120,179 @@ export default function ChatsSideBar(props: Props) { } }; + const handleSideBar = () => { + if (windowWidth < 768 && showSideBar) { + setShowSideBar(false); + } + }; + return ( -
-
-
- {revealPrivKey && ( - -
setRevealPrivKey(false)} - > - {arrowBackSvg} -
-
+ <> + {showSideBar && ( +
+ {windowWidth < 768 && ( +
setShowSideBar(false)} + > + {closeSvg} +
)} -

Chats

-
+
+
+ {revealPrivKey && windowWidth > 768 && ( + +
setRevealPrivKey(false)} + > + {arrowBackSvg} +
+
+ )} + +

Chats

+
-
-
- -

- {syncSvg} -

-
+
+
+ +

+ {syncSvg} +

+
+
+ {chatPrivKey && ( +
+ { + setRevealPrivKey(!revealPrivKey); + if (windowWidth < 768) { + setShowSideBar(false); + } + }} + {...props} + /> +
+ )} +
- {chatPrivKey && ( -
- { - setRevealPrivKey(!revealPrivKey); +
+ + { + setSearchText(e.target.value); + setSearchInProgress(true); + if (e.target.value.length === 0) { + setSearchInProgress(false); + setUserList([]); + } }} - {...props} /> -
+ +
+ {showDivider &&
} + {searchInProgress && } +
+ {searchText ? ( +
+ {userList.map((user) => ( + { + setSearchText(""); + setSearchInProgress(false); + }} + key={user.account} + > +
{ + setRevealPrivKey(false); + getReceiverPubKey(user.account); + handleSideBar(); + }} + > + + + + {user.account} + + ({accountReputation(user.reputation)}) + +
+ + ))} +
+ ) : ( + <> + {communities.length !== 0 &&

Communities

} + {communities.map((channel) => ( + +
{ + setRevealPrivKey(false); + handleSideBar(); + }} + > + +
+

{channel.name}

+

+ {getCommunityLastMessage(channel.id, chat.publicMessages)} +

+
+
+ + ))} + {directContacts.length !== 0 &&

DMs

} + {directContacts.map((contact) => ( + { + setReceiverPubKey(contact.pubkey); + handleSideBar(); + }} + > +
+ +
+

{contact.name}

+

+ {getDirectLastMessage(contact.pubkey, chat.directMessages)} +

+
+
+ + ))} + + )} +
+ {isScrollToTop && ( + )}
-
-
- - { - setSearchText(e.target.value); - setSearchInProgress(true); - if (e.target.value.length === 0) { - setSearchInProgress(false); - setUserList([]); - } - }} - /> - -
- {showDivider &&
} - {searchInProgress && } -
- {searchText ? ( -
- {userList.map((user) => ( - { - setSearchText(""); - setSearchInProgress(false); - }} - key={user.account} - > -
{ - handleRevealPrivKey(); - getReceiverPubKey(user.account); - }} - > - - - - {user.account} - ({accountReputation(user.reputation)}) -
- - ))} -
- ) : ( - <> - {communities.length !== 0 &&

Communities

} - {communities.map((channel) => ( - -
- -
-

{channel.name}

-

- {getCommunityLastMessage(channel.id, chat.publicMessages)} -

-
-
- - ))} - {directContacts.length !== 0 &&

DMs

} - {directContacts.map((contact) => ( - setReceiverPubKey(contact.pubkey)} - > -
- -
-

{contact.name}

-

- {getDirectLastMessage(contact.pubkey, chat.directMessages)} -

-
-
- - ))} - - )} -
- {isScrollToTop && ( - )} -
+ ); } diff --git a/src/common/components/chats/chats-sidebar/index.scss b/src/common/components/chats/chats-sidebar/index.scss index 0baf0d1ed05..127f1f2a5e3 100644 --- a/src/common/components/chats/chats-sidebar/index.scss +++ b/src/common/components/chats/chats-sidebar/index.scss @@ -9,10 +9,21 @@ background: $dark-two; box-shadow: 0px -5px 5px $charcoal-grey; } - width: 330px; + z-index: 10; + min-width: 330px; height: 100%; margin-top: 10px; + .close-sidebar { + margin: 10px 5px 0 0; + svg { + width: 30px; + height: 30px; + color: $steel-grey; + cursor: pointer; + } + } + .chats-title { margin: 30px 16px 12px 16px; @@ -226,9 +237,48 @@ } } -@include media-breakpoint-down(sm) { +// @include media-breakpoint-down(sm) { +// .chats-sidebar { +// width: 45%; + +// .chats-title { +// margin: 15px 0px 5px 5px; +// } +// .chats-search { +// padding: 0 5px 0 5px; +// margin-bottom: 0; +// } +// .chats-list { +// overflow-x: hidden; +// .community-title, +// .dm-title { +// font-size: 1rem; +// padding: 15px 0 10px 8px; +// } +// .community, +// .dm { +// margin: 0; +// padding: 9.5px 0px 9.5px 5px; +// .community-info, +// .dm-info { +// .community-name, +// .dm-name { +// padding: 3px 0 0 10px; +// } +// .community-last-message, +// .dm-last-message { +// padding: 4px 0 0 10px; +// max-width: 35vw; +// } +// } +// } +// } +// } +// } +//handle the width of the sidebar when screen size is less than 330px +@media (max-width: 320px) { .chats-sidebar { - width: 45%; + min-width: 260px; .chats-title { margin: 15px 0px 5px 5px; @@ -264,10 +314,3 @@ } } } -// @media (max-width: 300px) { -// .chats-sidebar { -// .community-info, .dm-info { -// display: none; -// } -// } -// } diff --git a/src/common/components/chats/import-chats/index.tsx b/src/common/components/chats/import-chats/index.tsx index 4ecf4688505..c02abd2aa64 100644 --- a/src/common/components/chats/import-chats/index.tsx +++ b/src/common/components/chats/import-chats/index.tsx @@ -12,7 +12,6 @@ import { setNostrkeys } from "../../../../managers/message-manager"; import "./index.scss"; import LinearProgress from "../../linear-progress"; import ChatsConfirmationModal from "../chats-confirmation-modal"; -import { createNoStrAccount, setProfileMetaData } from "../utils"; export default function ImportChats() { const { activeUser } = useMappedStore(); diff --git a/src/common/components/chats/join-community-chat-btn/index.tsx b/src/common/components/chats/join-community-chat-btn/index.tsx index 2c6f41893f4..9fe45261aa2 100644 --- a/src/common/components/chats/join-community-chat-btn/index.tsx +++ b/src/common/components/chats/join-community-chat-btn/index.tsx @@ -24,14 +24,18 @@ export default function JoinCommunityChatBtn(props: Props) { const [inProgress, setInProgress] = useState(false); const [isCommunityChatJoined, setIsCommunityChatJoined] = useState(false); const [isChatEnabled, setIsChatEnabled] = useState(false); - const [currentChannel, setCurrentChannel] = useState(); + const [currentCommunity, setCurrentCommunity] = useState(); const [communityRoles, setCommunityRoles] = useState([]); const [loadCommunity, setLoadCommunity] = useState(false); const [initiateCommunityChat, setInitiateCommunityChat] = useState(false); useEffect(() => { fetchCommunityProfile(); - }, [chat.channels, currentChannel, chat.leftChannelsList]); + }, [chat.channels, currentCommunity, chat.leftChannelsList]); + + useEffect(() => { + console.log("isChatEnabled", isChatEnabled, "isCommunityChatJoined", isCommunityChatJoined); + }, [isChatEnabled, isCommunityChatJoined]); useEffect(() => { fetchCommunityProfile(); @@ -44,20 +48,18 @@ export default function JoinCommunityChatBtn(props: Props) { }, [activeUserKeys]); useEffect(() => { + fetchCommunityProfile(); + checkIsChatJoined(); if (messageServiceInstance) { if (loadCommunity) { - messageServiceInstance?.loadChannel(currentChannel?.id!); + messageServiceInstance?.loadChannel(currentCommunity?.id!); + setInProgress(false); } if (initiateCommunityChat && communityRoles.length !== 0) { createCommunityChat(); } } - }, [ - typeof window !== "undefined" && messageServiceInstance, - loadCommunity, - communityRoles, - initiateCommunityChat - ]); + }, [messageServiceInstance, loadCommunity, communityRoles, initiateCommunityChat]); useEffect(() => { checkIsChatJoined(); @@ -66,26 +68,24 @@ export default function JoinCommunityChatBtn(props: Props) { }, [isCommunityChatJoined, props.community, chat.channels, chat.leftChannelsList]); const fetchCommunityProfile = async () => { + console.log("fetch community profile"); const communityProfile = await getProfileMetaData(props.community?.name); const haschannelMetaData = communityProfile && communityProfile.hasOwnProperty(CHANNEL); setIsChatEnabled(haschannelMetaData); - if (!currentChannel) { - setCurrentChannel(communityProfile.channel); + if (!currentCommunity) { + setCurrentCommunity(communityProfile.channel); } }; const checkIsChatJoined = () => { - for (const item of chat.channels) { - if ( - item.communityName === props.community.name && - !chat.leftChannelsList.includes(currentChannel?.id!) - ) { - setIsCommunityChatJoined(true); - } else { - setIsCommunityChatJoined(false); - } - } + setIsCommunityChatJoined( + chat.channels.some( + (item) => + item.communityName === props.community.name && + !chat.leftChannelsList.includes(currentCommunity?.id!) + ) + ); }; const getCommunityRoles = async () => { @@ -121,7 +121,6 @@ export default function JoinCommunityChatBtn(props: Props) { const createCommunityChat = async () => { const { community } = props; try { - setInProgress(true); const data = await messageServiceInstance?.createChannel({ name: community.title, about: community.description, @@ -132,6 +131,7 @@ export default function JoinCommunityChatBtn(props: Props) { removedUserIds: [] }); + console.log("Community data", data); const content = JSON.parse(data?.content!); const channelMetaData = { id: data?.id as string, @@ -143,7 +143,7 @@ export default function JoinCommunityChatBtn(props: Props) { picture: content.picture }; setChannelMetaData(community.name, channelMetaData).then(() => - setCurrentChannel(channelMetaData) + setCurrentCommunity(channelMetaData) ); } finally { setInProgress(false); @@ -153,27 +153,28 @@ export default function JoinCommunityChatBtn(props: Props) { }; const joinCommunityChat = () => { + setInProgress(true); if (!hasUserJoinedChat) { - setInProgress(true); joinChat(); setLoadCommunity(true); - setIsCommunityChatJoined(true); return; } - if (chat.leftChannelsList.includes(currentChannel?.id!)) { + if (chat.leftChannelsList.includes(currentCommunity?.id!)) { messageServiceInstance?.updateLeftChannelList( - chat.leftChannelsList.filter((x) => x !== currentChannel?.id) + chat.leftChannelsList.filter((x) => x !== currentCommunity?.id) ); } - messageServiceInstance?.loadChannel(currentChannel?.id!); + messageServiceInstance?.loadChannel(currentCommunity?.id!); setIsCommunityChatJoined(true); + setInProgress(false); }; const startCommunityChat = () => { + setInProgress(true); if (!hasUserJoinedChat) { joinChat(); setInitiateCommunityChat(true); - setIsCommunityChatJoined(true); + return; } else { createCommunityChat(); @@ -183,7 +184,7 @@ export default function JoinCommunityChatBtn(props: Props) { const fetchCurrentChannel = () => { for (const item of chat.channels) { if (item.communityName === props.community.name) { - setCurrentChannel(item); + setCurrentCommunity(item); return item; } } diff --git a/src/common/components/chats/utils/upload-chat-public-key.ts b/src/common/components/chats/utils/upload-chat-public-key.ts index c407bb527fb..5fe18c12a9c 100644 --- a/src/common/components/chats/utils/upload-chat-public-key.ts +++ b/src/common/components/chats/utils/upload-chat-public-key.ts @@ -2,15 +2,25 @@ import { getAccountFull } from "../../../api/hive"; import { updateProfile } from "../../../api/operations"; import { ActiveUser } from "../../../store/active-user/types"; -export const setProfileMetaData = async (activeUser: ActiveUser | null, noStrPubKey: string) => { +export const uploadChatPublicKey = async (activeUser: ActiveUser | null, noStrPubKey: string) => { const response = await getAccountFull(activeUser?.username!); const { posting_json_metadata } = response; - const profile = JSON.parse(posting_json_metadata!).profile; + const newProfile = { nsKey: noStrPubKey }; + if (!response || !response.posting_json_metadata) { + const newPostingData = { + posting_json_metadata: JSON.stringify({ profile: {} }) + }; + const profile = JSON.parse(newPostingData.posting_json_metadata)?.profile; + const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); + return updatedProfile; + } + + const profile = JSON.parse(posting_json_metadata!)?.profile; const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); return updatedProfile; }; diff --git a/src/common/components/manage-chat-key/index.scss b/src/common/components/manage-chat-key/index.scss index 777c9c5a110..ca206fdd106 100644 --- a/src/common/components/manage-chat-key/index.scss +++ b/src/common/components/manage-chat-key/index.scss @@ -3,12 +3,32 @@ @import "src/style/bootstrap_vars"; @import "src/style/mixins"; - .manage-chat-key { .private-key { margin-top: 2.5rem; margin-left: 14px; margin-right: 7px; + + .expand-icon { + margin: 13px 15px 0 0; + .expand-svg { + height: 35px; + width: 35px; + background: #e4e6eb; + margin-left: 10px; + border-radius: 10px; + cursor: pointer; + + svg { + width: 35px; + height: 35px; + } + + &:hover { + background: #ecf3ff; + } + } + } } .chat-priv-key { width: 100%; diff --git a/src/common/components/manage-chat-key/index.tsx b/src/common/components/manage-chat-key/index.tsx index a4007b0b7cf..454bdc5585c 100644 --- a/src/common/components/manage-chat-key/index.tsx +++ b/src/common/components/manage-chat-key/index.tsx @@ -1,7 +1,7 @@ import React, { useContext } from "react"; import { Form } from "react-bootstrap"; import { _t } from "../../i18n"; -import { copyContent } from "../../img/svg"; +import { copyContent, expandSideBar } from "../../img/svg"; import { ChatContext } from "../chats/chat-context-provider"; import { copyToClipboard } from "../chats/utils"; import { success } from "../feedback"; @@ -13,9 +13,7 @@ import "./index.scss"; export default function ManageChatKey() { const context = useContext(ChatContext); - const { chatPrivKey } = context; - - console.log("chatPrivKey in manage chat key", chatPrivKey); + const { chatPrivKey, windowWidth, setShowSideBar } = context; const copyPrivateKey = () => { copyToClipboard(chatPrivKey); @@ -25,8 +23,15 @@ export default function ManageChatKey() { return ( <>
-
- +
+ {windowWidth < 768 && ( +
+

setShowSideBar(true)}> + {expandSideBar} +

+
+ )} + {_t("chat.chat-priv-key")}
{ - console.log("SVG clicked"); copyPrivateKey(); }} > diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts index 2867f49274a..73810a7887c 100644 --- a/src/common/helper/message-service.ts +++ b/src/common/helper/message-service.ts @@ -242,7 +242,6 @@ class MessageService extends TypedEventEmitter { } public async updateLeftChannelList(channelIds: string[]) { - console.log("Update channel list"); const tags = [["d", "left-channel-list"]]; return this.publish(NewKinds.Arbitrary, tags, JSON.stringify(channelIds)); } @@ -446,7 +445,6 @@ class MessageService extends TypedEventEmitter { } public async updateChannel(channel: Channel, meta: Metadata) { - console.log("Update channel Run"); return this.findHealthyRelay(this.pool.seenOn(channel.id) as string[]).then((relay) => { return this.publish(Kind.ChannelMetadata, [["e", channel.id, relay]], JSON.stringify(meta)); }); @@ -530,7 +528,6 @@ class MessageService extends TypedEventEmitter { } public async updateProfile(profile: Metadata) { - console.log("Profile update run", profile); return this.publish(Kind.Metadata, [], JSON.stringify(profile)); } @@ -702,7 +699,6 @@ class MessageService extends TypedEventEmitter { : null; }) .filter(MessageService.notEmpty); - console.log("Channel creation event is emitted", channelCreations); if (channelCreations.length > 0) { this.emit(MessageEvents.ChannelCreation, channelCreations); } diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index c81a045eaa7..1cb83609c4b 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1861,7 +1861,8 @@ "join-community-chat": "Join Community Chat", "submit": "Submit", "chat-priv-key": "Chat Private key", - "copy-priv-key": "Copy Private key" + "copy-priv-key": "Copy Private key", + "extended-view": "Extended View" }, "add-image": { "title": "Add Image", diff --git a/src/common/img/svg.tsx b/src/common/img/svg.tsx index 07c32c55b35..0038326dd1b 100644 --- a/src/common/img/svg.tsx +++ b/src/common/img/svg.tsx @@ -2323,3 +2323,38 @@ export const messangerSvg = ( ); + +export const extendedView = ( + +); + +export const expandSideBar = ( + + + +); diff --git a/src/common/pages/chats/index.scss b/src/common/pages/chats/index.scss index 905f03fd7bf..80f0425fe8c 100644 --- a/src/common/pages/chats/index.scss +++ b/src/common/pages/chats/index.scss @@ -8,7 +8,7 @@ width: 100vw; height: 100vh; } - .chats-messages-box { + .chats-manage-key { .private-key { margin-top: 4.5rem; } diff --git a/src/common/pages/chats/index.tsx b/src/common/pages/chats/index.tsx index aafd4ff0d7d..d8b7c2e6651 100644 --- a/src/common/pages/chats/index.tsx +++ b/src/common/pages/chats/index.tsx @@ -37,9 +37,8 @@ export const Chats = (props: Props) => { const username = match.params.username; - const { showSpinner, activeUserKeys, revealPrivKey, chatPrivKey } = useContext(ChatContext); - - console.log("inProgress in chats page", showSpinner); + const { showSpinner, activeUserKeys, revealPrivKey, chatPrivKey, windowWidth } = + useContext(ChatContext); useEffect(() => { document.body.style.overflow = "hidden"; @@ -71,48 +70,58 @@ export const Chats = (props: Props) => { <> {global.isElectron ? : } +
- {showSpinner ? ( -
- -
- ) : ( - <> - {activeUserKeys?.pub ? ( - chatPrivKey ? ( - <> - - {revealPrivKey ? ( -
- + {activeUser ? ( + showSpinner ? ( +
+ +
+ ) : ( + <> + {activeUserKeys?.pub ? ( + chatPrivKey ? ( + <> + + {revealPrivKey ? ( +
+ +
+ ) : ( + <> + + {/* {match.url !== "/chats" && ( +
+ +
+ )} */} + + )} + + ) : ( + <> +
+
- ) : ( - <> - - {/* {match.url !== "/chats" && ( -
- -
- )} */} - - )} - + + ) ) : ( - <> -
- -
- - ) - ) : ( -
- -
- )} - +
+ +
+ )} + + ) + ) : ( +

+ Please login to continue the chat +

)}
diff --git a/src/managers/message-manager.tsx b/src/managers/message-manager.tsx index 9f3d23eb23a..88eda63a377 100644 --- a/src/managers/message-manager.tsx +++ b/src/managers/message-manager.tsx @@ -1,7 +1,6 @@ import React, { useContext, useEffect, useState } from "react"; import { ActiveUser } from "../common/store/active-user/types"; -import { DirectContactsType } from "../common/store/chat/types"; import { NostrKeysType } from "../common/components/chats/types"; import { DirectMessage, @@ -20,6 +19,7 @@ import { getProfileMetaData, getPrivateKey } from "../common/components/chats/ut import { useMappedStore } from "../common/store/use-mapped-store"; import { ChatContext } from "../common/components/chats/chat-context-provider"; import { useTimeoutFn } from "react-use"; +import { usePrevious } from "../common/util/use-previous"; export const setNostrkeys = (keys: NostrKeysType) => { const detail: NostrKeysType = { @@ -45,9 +45,12 @@ const MessageManager = () => { verifyPublicMessageSending, replaceDirectMessage, verifyDirectMessageSending, - addPreviousPublicMessages + addPreviousPublicMessages, + resetChat } = useMappedStore(); + const prevActiveUser = usePrevious(activeUser); + const [messageServiceReady, setMessageServiceReady] = useState(false); const [since, setSince] = useState(0); const [keys, setKeys] = useState(); @@ -80,33 +83,32 @@ const MessageManager = () => { }, []); useEffect(() => { - if (!messageServiceInstance && keys?.priv) { + console.log("active USer", activeUser); + }, [activeUser]); + + useEffect(() => { + if (keys?.priv) { const messageService = initMessageServiceInstance(keys); - console.log("messageService", messageService!!); setMessageServiceInstance(messageService); setMessageService(messageService!); + } else { + setMessageServiceInstance(null); } }, [keys]); - // useEffect(() => { - // if (messageServiceInstance === undefined && keys?.priv) { - // const messageService = initMessageServiceInstance(keys); - // if (messageService) { - // setMessageService(messageService); - // } - // } - // }, [keys]); - useEffect(() => { if (activeUser) { getNostrKeys(activeUser); } + if (prevActiveUser?.username !== activeUser?.username) { + resetChat(); + getNostrKeys(activeUser!); + } }, [activeUser]); const createMSInstance = (e: Event) => { const detail = (e as CustomEvent).detail as NostrKeysType; const messageService = initMessageServiceInstance(detail); - console.log("messageService", messageService!!); setMessageServiceInstance(messageService); setMessageService(messageService!); }; @@ -115,7 +117,7 @@ const MessageManager = () => { const profile = await getProfileMetaData(activeUser.username); const noStrPrivKey = getPrivateKey(activeUser.username); const keys = { - pub: profile.nsKey, + pub: profile?.nsKey, priv: noStrPrivKey }; setKeys(keys); @@ -199,17 +201,6 @@ const MessageManager = () => { const { peer, id } = m; setReplacedDirectMessagesBuffer((prevBuffer) => [...prevBuffer, id]); addDirectMessages(peer, m); - // const startTimeout = () => { - // console.log("Start time out called"); - // useTimeoutFn(() => { - // console.log("Inner called"); - // checkDirectMessageSending(peer, m); - // }, 2000); - // }; - - // Start the timeout - // startTimeout(); - // const [startTimeout] = useTimeoutFn(checkDirectMessageSending(peer, m), 20000); checkDirectMessageSending(peer, m); }); }; @@ -234,7 +225,6 @@ const MessageManager = () => { const checkDirectMessageSending = (peer: string, data: DirectMessage) => { setTimeout(() => { - console.log("checkDirectMessageSending called"); verifyDirectMessageSending(peer, data); }, 20000); }; @@ -295,7 +285,6 @@ const MessageManager = () => { // Channel creation handler const handleChannelCreation = (data: Channel[]) => { - console.log("handle channel creation run"); addChannels(data.filter((x) => !chat.channels.find((y) => y.id === x.id))); }; From 9fb0a134b6018aab80b6905fdab141ce414d6f9d Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Tue, 26 Sep 2023 16:23:10 +0500 Subject: [PATCH 079/179] Issue fixes found during testing --- .../chats/chat-context-provider.tsx | 11 +---- .../components/chats/chat-popup/index.scss | 23 ++++++++-- .../components/chats/chat-popup/index.tsx | 8 +--- .../chats/chats-channel-messages/index.scss | 4 ++ .../chats/chats-channel-messages/index.tsx | 46 +++++++++++-------- .../chats/chats-messages-box/index.tsx | 14 +----- .../chats/chats-profile-box/index.tsx | 1 - .../chats/chats-side-profile/index.scss | 8 ---- .../chats/chats-side-profile/index.tsx | 23 ---------- .../components/manage-chat-key/index.tsx | 14 ++++-- src/common/pages/chats/index.tsx | 8 +--- src/managers/message-manager.tsx | 4 -- 12 files changed, 67 insertions(+), 97 deletions(-) delete mode 100644 src/common/components/chats/chats-side-profile/index.scss delete mode 100644 src/common/components/chats/chats-side-profile/index.tsx diff --git a/src/common/components/chats/chat-context-provider.tsx b/src/common/components/chats/chat-context-provider.tsx index fc693ac07f7..0f1eb192945 100644 --- a/src/common/components/chats/chat-context-provider.tsx +++ b/src/common/components/chats/chat-context-provider.tsx @@ -98,10 +98,6 @@ export const ChatContextProvider = (props: Props) => { } }, [currentChannel]); - useEffect(() => { - console.log("messageServiceInstance", messageServiceInstance); - }, [messageServiceInstance]); - useEffect(() => { const handleResize = () => { setWindowWidth(window.innerWidth); @@ -122,10 +118,6 @@ export const ChatContextProvider = (props: Props) => { } }, [messageServiceInstance]); - useEffect(() => { - console.log("showsidebar", showSideBar); - }, [showSideBar]); - useEffect(() => { window.addEventListener("resize", handleShowSideBar); return () => { @@ -143,10 +135,9 @@ export const ChatContextProvider = (props: Props) => { } }, [shouldUpdateProfile, messageServiceInstance]); - useDebounce(() => setShowSpinner(false), 3000, [showSpinner]); + useDebounce(() => setShowSpinner(false), 5000, [showSpinner]); const handleShowSideBar = () => { - console.log("Function run", window.innerWidth); if (window.innerWidth < 768) { setShowSideBar(false); } else { diff --git a/src/common/components/chats/chat-popup/index.scss b/src/common/components/chats/chat-popup/index.scss index 4e8590d9ec0..137337d01f0 100644 --- a/src/common/components/chats/chat-popup/index.scss +++ b/src/common/components/chats/chat-popup/index.scss @@ -344,17 +344,25 @@ .import-chats { margin-top: 20px; } + + .manage-chat-key { + .private-key { + .form-group { + width: 90vw !important; + } + } + } } &.small-screen { right: 0; width: 100vw; display: grid; - grid-template-rows: repeat(18, 1fr); + grid-template-rows: repeat(100, 1fr); grid-template-columns: repeat(1, 1fr); .chat-header { - width: 94vw; - grid-row: span 1; + width: 100vw; + grid-row: span 5; } .back-arrow-svg { padding: 0; @@ -375,9 +383,14 @@ margin: 0; } .chat-body { - grid-row: span 16; + grid-row: span 95; height: 100%; + &.current-user, + .community { + grid-row: span 90; + } + .chat-content { .last-message { max-width: 75vw; @@ -385,7 +398,7 @@ } } .chat { - grid-row: span 1; + grid-row: span 5; } } } diff --git a/src/common/components/chats/chat-popup/index.tsx b/src/common/components/chats/chat-popup/index.tsx index 6926e5eca83..f60aae8e36a 100644 --- a/src/common/components/chats/chat-popup/index.tsx +++ b/src/common/components/chats/chat-popup/index.tsx @@ -140,10 +140,6 @@ export const ChatPopUp = (props: Props) => { setShow(!!activeUser?.username && !isChatPage); }, []); - useEffect(() => { - console.log("Show", show); - }, [show]); - useEffect(() => { const updated: ChannelUpdate = chat.updatedChannel .filter((x) => x.channelId === currentChannel?.id!) @@ -557,7 +553,7 @@ export const ChatPopUp = (props: Props) => {
{inProgress && } - {inProgress && !isCommunity && !isCurrentUser && } + {/* {inProgress && !isCommunity && !isCurrentUser && } */}
{ )} ) : revealPrivKey ? ( - + ) : ( - - ) : ( - <> - - - )} - - )} + {communityAdmins.includes(activeUser?.username!) && + name !== currentChannel.communityName && ( + <> + {currentChannel?.removedUserIds?.includes(pMsg.creator) ? ( + <> + + + ) : ( + <> + + + )} + + )}
{ setInProgress(false); return; } - try { - const profileData = await getProfileMetaData(user); - if (profileData && profileData.hasOwnProperty(NOSTRKEY)) { - const alreadyExists = currentChannel?.communityModerators?.some( - (moderator) => moderator.name === profileData.name - ); - if (alreadyExists) { - setAddRoleError("You have already assigned some rule to this user."); - setInProgress(false); - return; - } + const response = await getAccountFull(user); + if (!response) { + setAddRoleError("Account does not exist"); + return; + } + + if (!response.posting_json_metadata) { + setAddRoleError("This user hasn't joined the chat yet."); + return; + } + + const { posting_json_metadata } = response; + const profile = JSON.parse(posting_json_metadata).profile; + + if (!profile || !profile.hasOwnProperty(NOSTRKEY)) { + setAddRoleError("You cannot set this user because this user hasn't joined the chat yet."); + return; + } + + const alreadyExists = currentChannel?.communityModerators?.some( + (moderator) => moderator.name === response.name + ); + + if (alreadyExists) { + setAddRoleError("You have already assigned some rule to this user."); + setInProgress(false); + } else { const moderator = { name: user, - pubkey: profileData.nsKey, + pubkey: profile.nsKey, role: role }; setModerator(moderator); setAddRoleError(""); - } else { - setAddRoleError("You cannot set this user because this user hasn't joined the chat yet."); } } catch (err) { error(err as string); + } finally { + setInProgress(false); } - - setInProgress(false); }, 200, [user, role] @@ -414,20 +429,18 @@ const ChatsCommunityDropdownMenu = (props: Props) => { const successModal = (message: string) => { return ( <> -
+
2
-
-
{_t("manage-authorities.success-title")}
-
- {_t("manage-authorities.success-sub-title")} -
+
+
{_t("manage-authorities.success-title")}
+
{_t("manage-authorities.success-sub-title")}
-
+
{message === UNBLOCKUSER ? "User unblock successfully" : ""}
-
+
@@ -489,6 +502,9 @@ const ChatsCommunityDropdownMenu = (props: Props) => { setKeyDialog(true); setRemovedUserID(""); } + if (operationType === ADDROLE) { + setUser(""); + } } catch (err) { error(_t("chat.error-updating-community")); } diff --git a/src/common/components/chats/hooks/use-message-service-listener.tsx b/src/common/components/chats/hooks/use-message-service-listener.tsx index 2f4a2bab072..3c85a776bce 100644 --- a/src/common/components/chats/hooks/use-message-service-listener.tsx +++ b/src/common/components/chats/hooks/use-message-service-listener.tsx @@ -2,11 +2,11 @@ import { useEffect, useState } from "react"; import { Channel } from "../../../../managers/message-manager-types"; import MessageService from "../../../helper/message-service"; -function useMessageServiceListener( +export const useMessageServiceListener = ( messageServiceReady: boolean, messageService: MessageService | undefined, chatChannels: Channel[] -) { +) => { const [since, setSince] = useState(0); useEffect(() => { @@ -27,6 +27,4 @@ function useMessageServiceListener( clearTimeout(timer); }; }, [since, messageServiceReady, messageService, chatChannels]); -} - -export default useMessageServiceListener; +}; diff --git a/src/common/components/chats/join-community-chat-btn/index.tsx b/src/common/components/chats/join-community-chat-btn/index.tsx index b9c33db5963..b1e3dbda41c 100644 --- a/src/common/components/chats/join-community-chat-btn/index.tsx +++ b/src/common/components/chats/join-community-chat-btn/index.tsx @@ -139,9 +139,10 @@ export default function JoinCommunityChatBtn(props: Props) { about: content.about, picture: content.picture }; - setChannelMetaData(community.name, channelMetaData).then(() => - setCurrentCommunity(channelMetaData) - ); + const response = await setChannelMetaData(community.name, channelMetaData); + if (response) { + setCurrentCommunity(channelMetaData); + } } finally { setInProgress(false); setIsCommunityChatJoined(true); diff --git a/src/common/components/chats/utils/upload-channel-data.ts b/src/common/components/chats/utils/upload-channel-data.ts index 0a0f13477a6..8452caf2773 100644 --- a/src/common/components/chats/utils/upload-channel-data.ts +++ b/src/common/components/chats/utils/upload-channel-data.ts @@ -2,19 +2,17 @@ import { Channel } from "../../../../managers/message-manager-types"; import { getAccountFull } from "../../../api/hive"; import { updateProfile } from "../../../api/operations"; -export const setChannelMetaData = (username: string, channel: Channel) => { - return new Promise(async (resolve, reject) => { - try { - const response = await getAccountFull(username!); - const { posting_json_metadata } = response; - const profile = JSON.parse(posting_json_metadata!).profile; - const newProfile = { - channel: channel - }; - const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); - resolve(updatedProfile); - } catch (error) { - reject(error); - } - }); +export const setChannelMetaData = async (username: string, channel: Channel) => { + try { + const response = await getAccountFull(username!); + const { posting_json_metadata } = response; + const profile = JSON.parse(posting_json_metadata!).profile; + const newProfile = { + channel: channel + }; + const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); + return updatedProfile; + } catch (error) { + throw error; + } }; diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts index 73810a7887c..879d7341dc6 100644 --- a/src/common/helper/message-service.ts +++ b/src/common/helper/message-service.ts @@ -528,6 +528,7 @@ class MessageService extends TypedEventEmitter { } public async updateProfile(profile: Metadata) { + console.log("Update profile run"); return this.publish(Kind.Metadata, [], JSON.stringify(profile)); } @@ -582,9 +583,15 @@ class MessageService extends TypedEventEmitter { const pub = this.pool.publish(this.writeRelays, event); pub.on("ok", () => { resolve(event); + console.log("Event published", event); + // this.pushToEventBuffer(event); if (event.kind === Kind.Contacts) { + console.log("Direct contacts"); this.getContacts(); } + if (event.kind === Kind.Metadata) { + this.pushToEventBuffer(event); + } }); pub.on("failed", () => { @@ -610,6 +617,7 @@ class MessageService extends TypedEventEmitter { } pushToEventBuffer(event: Event) { + console.log("Push to event buffer run"); const cacheKey = `${event.id}_emitted`; if (this.nameCache[cacheKey] === undefined) { if (this.eventQueueFlag) { diff --git a/src/managers/message-manager.tsx b/src/managers/message-manager.tsx index 0b740588b20..ec15d8ad99a 100644 --- a/src/managers/message-manager.tsx +++ b/src/managers/message-manager.tsx @@ -20,7 +20,7 @@ import { useMappedStore } from "../common/store/use-mapped-store"; import { ChatContext } from "../common/components/chats/chat-context-provider"; import { useTimeoutFn } from "react-use"; import { usePrevious } from "../common/util/use-previous"; -import useMessageServiceListener from "../common/components/chats/hooks/use-message-service-listener"; +import { useMessageServiceListener } from "../common/components/chats/hooks/use-message-service-listener"; export const setNostrkeys = (keys: NostrKeysType) => { const detail: NostrKeysType = { From 72850c571c5e1c52c0f7aa22bf49b0ce41c7b5b5 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Tue, 3 Oct 2023 16:15:35 +0500 Subject: [PATCH 091/179] Removed consoles --- src/common/components/chats/chat-context-provider.tsx | 3 --- src/common/components/chats/chat-popup/index.tsx | 4 ---- 2 files changed, 7 deletions(-) diff --git a/src/common/components/chats/chat-context-provider.tsx b/src/common/components/chats/chat-context-provider.tsx index 4a010f93e6c..90a65488047 100644 --- a/src/common/components/chats/chat-context-provider.tsx +++ b/src/common/components/chats/chat-context-provider.tsx @@ -91,9 +91,6 @@ export const ChatContextProvider = (props: Props) => { setWindowWidth(window.innerWidth); setMaxHeight(window.innerHeight - 66); }); - useEffect(() => { - console.log("chat in context", chat); - }, []); useEffect(() => { if (currentChannel && currentChannel.removedUserIds) { diff --git a/src/common/components/chats/chat-popup/index.tsx b/src/common/components/chats/chat-popup/index.tsx index cef6b5ba4d7..bdde77f6f97 100644 --- a/src/common/components/chats/chat-popup/index.tsx +++ b/src/common/components/chats/chat-popup/index.tsx @@ -127,10 +127,6 @@ export const ChatPopUp = () => { setShow(!!activeUser?.username && !isChatPage); }, []); - useEffect(() => { - console.log("Chat in store", chat); - }, [chat]); - useEffect(() => { const updated: ChannelUpdate = chat.updatedChannel .filter((x) => x.channelId === currentChannel?.id!) From 66581175c286239bf67143cf8b03258a381ba5cb Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Tue, 3 Oct 2023 16:17:08 +0500 Subject: [PATCH 092/179] Removed consoles --- src/common/helper/message-service.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts index 879d7341dc6..10ad7a01976 100644 --- a/src/common/helper/message-service.ts +++ b/src/common/helper/message-service.ts @@ -528,7 +528,6 @@ class MessageService extends TypedEventEmitter { } public async updateProfile(profile: Metadata) { - console.log("Update profile run"); return this.publish(Kind.Metadata, [], JSON.stringify(profile)); } @@ -583,10 +582,7 @@ class MessageService extends TypedEventEmitter { const pub = this.pool.publish(this.writeRelays, event); pub.on("ok", () => { resolve(event); - console.log("Event published", event); - // this.pushToEventBuffer(event); if (event.kind === Kind.Contacts) { - console.log("Direct contacts"); this.getContacts(); } if (event.kind === Kind.Metadata) { @@ -617,7 +613,6 @@ class MessageService extends TypedEventEmitter { } pushToEventBuffer(event: Event) { - console.log("Push to event buffer run"); const cacheKey = `${event.id}_emitted`; if (this.nameCache[cacheKey] === undefined) { if (this.eventQueueFlag) { From 8c0ab8e79d32ec531d1a91459d162df3607d7660 Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Tue, 3 Oct 2023 17:35:03 +0500 Subject: [PATCH 093/179] Use useMount hook --- src/common/components/chats/chat-popup/index.tsx | 11 ++++++----- .../components/chats/chats-messages-view/index.tsx | 6 ++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/common/components/chats/chat-popup/index.tsx b/src/common/components/chats/chat-popup/index.tsx index bdde77f6f97..b985757ea62 100644 --- a/src/common/components/chats/chat-popup/index.tsx +++ b/src/common/components/chats/chat-popup/index.tsx @@ -59,6 +59,7 @@ import { import { useMappedStore } from "../../../store/use-mapped-store"; import { ChatContext } from "../chat-context-provider"; import ImportChats from "../import-chats"; +import { useMount } from "react-use"; export const ChatPopUp = () => { const { activeUser, global, chat, resetChat } = useMappedStore(); @@ -105,6 +106,11 @@ export const ChatPopUp = () => { const [isChatPage, setIsChatPage] = useState(false); const [isScrolled, setIsScrolled] = useState(false); + useMount(() => { + // deleteChatPublicKey(activeUser); + setShow(!!activeUser?.username && !isChatPage); + }); + useEffect(() => { if (currentChannel && chat.leftChannelsList.includes(currentChannel.id)) { setIsCommunity(false); @@ -122,11 +128,6 @@ export const ChatPopUp = () => { } }, [isChatPage]); - useEffect(() => { - // deleteChatPublicKey(activeUser); - setShow(!!activeUser?.username && !isChatPage); - }, []); - useEffect(() => { const updated: ChannelUpdate = chat.updatedChannel .filter((x) => x.channelId === currentChannel?.id!) diff --git a/src/common/components/chats/chats-messages-view/index.tsx b/src/common/components/chats/chats-messages-view/index.tsx index 7cda969158a..6f088103f5d 100644 --- a/src/common/components/chats/chats-messages-view/index.tsx +++ b/src/common/components/chats/chats-messages-view/index.tsx @@ -70,6 +70,12 @@ export default function ChatsMessagesView(props: Props) { isDirectUserOrCommunity(); }, []); + useEffect(() => { + if (publicMessages.length < 25) { + setHasMore(false); + } + }, [publicMessages]); + useEffect(() => { isDirectUserOrCommunity(); }, [chat.channels]); From e1e10ea8633e19043ab37419b6b676317e4fa82b Mon Sep 17 00:00:00 2001 From: Talha Saeed Date: Tue, 3 Oct 2023 17:42:55 +0500 Subject: [PATCH 094/179] Get receiver public key from context --- .../components/chats/chat-input/index.tsx | 6 ++---- .../components/chats/chat-popup/index.tsx | 3 +-- .../chats/chats-direct-messages/index.tsx | 13 +++---------- .../chats/chats-messages-view/index.tsx | 18 +++++------------- 4 files changed, 11 insertions(+), 29 deletions(-) diff --git a/src/common/components/chats/chat-input/index.tsx b/src/common/components/chats/chat-input/index.tsx index 4cadc8185a0..8d60dcc7d6e 100644 --- a/src/common/components/chats/chat-input/index.tsx +++ b/src/common/components/chats/chat-input/index.tsx @@ -36,7 +36,6 @@ interface Props { currentChannel: Channel; currentUser: string; isCurrentUserJoined: boolean; - receiverPubKey: string; emojiPickerStyles: EmojiPickerStyleProps; gifPickerStyle: EmojiPickerStyleProps; } @@ -50,7 +49,7 @@ export default function ChatInput(props: Props) { const [shGif, setShGif] = useState(false); const [isMessageText, setIsMessageText] = useState(false); - const { messageServiceInstance, isActveUserRemoved } = useContext(ChatContext); + const { messageServiceInstance, isActveUserRemoved, receiverPubKey } = useContext(ChatContext); const { isCommunity, @@ -59,8 +58,7 @@ export default function ChatInput(props: Props) { currentUser, isCurrentUserJoined, emojiPickerStyles, - gifPickerStyle, - receiverPubKey + gifPickerStyle } = props; useEffect(() => { diff --git a/src/common/components/chats/chat-popup/index.tsx b/src/common/components/chats/chat-popup/index.tsx index b985757ea62..ed9cf152eb0 100644 --- a/src/common/components/chats/chat-popup/index.tsx +++ b/src/common/components/chats/chat-popup/index.tsx @@ -570,7 +570,7 @@ export const ChatPopUp = () => { {isCurrentUser ? ( { isCurrentUser={isCurrentUser} isCommunity={isCommunity} currentUser={currentUser} - receiverPubKey={receiverPubKey} currentChannel={currentChannel!} isCurrentUserJoined={isCurrentUserJoined} emojiPickerStyles={EmojiPickerStyle} diff --git a/src/common/components/chats/chats-direct-messages/index.tsx b/src/common/components/chats/chats-direct-messages/index.tsx index 14dbd3f91b1..ca5f1b1ddad 100644 --- a/src/common/components/chats/chats-direct-messages/index.tsx +++ b/src/common/components/chats/chats-direct-messages/index.tsx @@ -34,23 +34,16 @@ interface Props { currentUser: string; isScrollToBottom: boolean; isScrolled?: boolean; - receiverPubKey: string; scrollToBottom?: () => void; } let zoom: Zoom | null = null; export default function ChatsDirectMessages(props: Props) { - const { - directMessages, - currentUser, - isScrolled, - receiverPubKey, - isScrollToBottom, - scrollToBottom - } = props; + const { directMessages, currentUser, isScrolled, isScrollToBottom, scrollToBottom } = props; const { global, activeUser, deleteDirectMessage } = useMappedStore(); - const { activeUserKeys, messageServiceInstance, windowWidth } = useContext(ChatContext); + const { activeUserKeys, messageServiceInstance, windowWidth, receiverPubKey } = + useContext(ChatContext); let prevGlobal = usePrevious(global); const [step, setStep] = useState(0); diff --git a/src/common/components/chats/chats-messages-view/index.tsx b/src/common/components/chats/chats-messages-view/index.tsx index 6f088103f5d..f68a78f514c 100644 --- a/src/common/components/chats/chats-messages-view/index.tsx +++ b/src/common/components/chats/chats-messages-view/index.tsx @@ -1,15 +1,9 @@ -import React, { RefObject, useContext, useEffect, useRef, useState } from "react"; +import React, { useContext, useEffect, useRef, useState } from "react"; import { Link } from "react-router-dom"; import { Channel, DirectMessage, PublicMessage } from "../../../../managers/message-manager-types"; -import { - fetchCommunityMessages, - fetchDirectMessages, - getPrivateKey, - getProfileMetaData -} from "../utils"; +import { fetchCommunityMessages, fetchDirectMessages } from "../utils"; import { History } from "history"; import { useMappedStore } from "../../../store/use-mapped-store"; -import { Global } from "../../../store/global/types"; import ChatsProfileBox from "../chats-profile-box"; import "./index.scss"; @@ -22,7 +16,7 @@ import { User } from "../../../store/users/types"; import ChatInput from "../chat-input"; import ChatsScroller from "../chats-scroller"; import { CHATPAGE } from "../chat-popup/chat-constants"; -import { EmojiPickerStyleProps, NostrKeysType } from "../types"; +import { EmojiPickerStyleProps } from "../types"; import { ChatContext } from "../chat-context-provider"; const EmojiPickerStyle: EmojiPickerStyleProps = { @@ -52,11 +46,11 @@ interface Props { export default function ChatsMessagesView(props: Props) { const { username, currentChannel, inProgress, currentChannelSetter, setInProgress } = props; - const { messageServiceInstance, receiverPubKey } = useContext(ChatContext); + const { messageServiceInstance } = useContext(ChatContext); const messagesBoxRef = useRef(null); - const { chat, activeUser } = useMappedStore(); + const { chat } = useMappedStore(); const [directUser, setDirectUser] = useState(""); const [publicMessages, setPublicMessages] = useState([]); const [directMessages, setDirectMessages] = useState([]); @@ -206,7 +200,6 @@ export default function ChatsMessagesView(props: Props) { directMessages={directMessages && directMessages} currentUser={directUser!} isScrolled={isScrolled} - receiverPubKey={receiverPubKey} isScrollToBottom={isScrollToBottom} scrollToBottom={scrollToBottom} /> @@ -221,7 +214,6 @@ export default function ChatsMessagesView(props: Props) { )}
Date: Tue, 3 Oct 2023 18:48:19 +0500 Subject: [PATCH 095/179] Change styling in edit community role modal --- .../components/chats/chats-community-dropdown-menu/index.scss | 4 ++-- .../components/chats/chats-community-dropdown-menu/index.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/common/components/chats/chats-community-dropdown-menu/index.scss b/src/common/components/chats/chats-community-dropdown-menu/index.scss index d1c23632f86..c428319e091 100644 --- a/src/common/components/chats/chats-community-dropdown-menu/index.scss +++ b/src/common/components/chats/chats-community-dropdown-menu/index.scss @@ -2,8 +2,8 @@ .chats-dialog { .chat-modals-body { - .add-dialog-header { - margin-bottom: 2rem; + .community-chat-role-edit-dialog-content { + margin-top: 2rem; } .success-dialog-header { .success-dialog-titles { diff --git a/src/common/components/chats/chats-community-dropdown-menu/index.tsx b/src/common/components/chats/chats-community-dropdown-menu/index.tsx index d2c4c62a92d..36548d45930 100644 --- a/src/common/components/chats/chats-community-dropdown-menu/index.tsx +++ b/src/common/components/chats/chats-community-dropdown-menu/index.tsx @@ -265,10 +265,10 @@ const ChatsCommunityDropdownMenu = (props: Props) => {

{_t("chat.edit-community-roles")}

+ {inProgress && }
- {inProgress && }
@@ -317,7 +317,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => {
{currentChannel?.communityModerators?.length !== 0 ? ( <> - +
From d5c122bef3f8eb66954ff498289141212a88da51 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Sun, 15 Oct 2023 16:16:39 +0600 Subject: [PATCH 096/179] Fixed TW migration conflicts and starting working on chats popup fixes --- src/common/app.tsx | 4 +- .../components/chats/chat-popup/index.tsx | 765 ------------------ .../chats/chats-dropdown-menu/index.tsx | 48 -- .../chats/chats-messages-view/index.scss | 21 - .../chats/utils/upload-chat-public-key.ts | 26 - .../components/community-cover/index.tsx | 2 +- .../components/emoji-picker/index-old.tsx | 2 +- src/common/components/login/index.tsx | 2 +- .../components/manage-auth-icon/index.tsx | 4 +- .../components/manage-chat-key/index.tsx | 68 -- .../chats/chat-context-provider.tsx | 0 .../chats/chat-input/index.scss | 2 +- .../chats/chat-input/index.tsx | 55 +- .../chats/chat-popup/chat-constants.ts | 0 .../chat-popup/chat-popup-direct-messages.tsx | 114 +++ .../chats/chat-popup/chat-popup-header.tsx | 150 ++++ .../chat-popup/chat-popup-messages-list.tsx | 69 ++ .../chat-popup/chat-popup-search-user.tsx | 63 ++ .../chats/chat-popup/index.scss | 178 +--- .../features/chats/chat-popup/index.tsx | 490 +++++++++++ .../chats/chats-channel-messages/index.scss | 28 +- .../chats/chats-channel-messages/index.tsx | 91 +-- .../chats-community-dropdown-menu/index.scss | 2 +- .../chats-community-dropdown-menu/index.tsx | 105 ++- .../chats/chats-confirmation-modal/index.scss | 5 +- .../chats/chats-confirmation-modal/index.tsx | 16 +- .../chats/chats-direct-messages/index.scss | 27 +- .../chats/chats-direct-messages/index.tsx | 16 +- .../chats/chats-dropdown-menu/index.tsx | 47 ++ .../chats/chats-messages-box/index.scss | 6 +- .../chats/chats-messages-box/index.tsx | 5 +- .../chats/chats-messages-header/index.scss | 22 +- .../chats/chats-messages-header/index.tsx | 14 +- .../chats/chats-messages-view/index.scss | 12 + .../chats/chats-messages-view/index.tsx | 12 +- .../chats/chats-profile-box/index.scss | 14 +- .../chats/chats-profile-box/index.tsx | 5 +- .../chats/chats-scroller/index.scss | 8 +- .../chats/chats-scroller/index.tsx | 2 +- .../chats/chats-sidebar/indes.tsx | 24 +- .../chats/chats-sidebar/index.scss | 30 +- .../hooks/use-message-service-listener.tsx | 0 .../chats/import-chats/index.scss | 2 +- .../chats/import-chats/index.tsx | 44 +- .../chats/join-chat/index.tsx | 17 +- .../chats/join-community-chat-btn/index.tsx | 38 +- .../chats}/manage-chat-key/index.scss | 7 +- .../features/chats/manage-chat-key/index.tsx | 37 + src/common/features/chats/queries/index.ts | 2 + src/common/features/chats/queries/queries.ts | 3 + .../chats/queries/search-users-query.ts | 20 + .../chats/types/chat-types.ts | 0 .../chats/types/index.ts | 0 .../chats/utils/check-contiguous-message.ts | 8 +- .../chats/utils/copy-to-clipboard.ts | 0 .../chats/utils/create-nostr-account.ts | 0 .../chats/utils/delete-chat-public-key.ts | 0 .../chats/utils/fetch-profile-metadata.ts | 4 +- .../utils/format-message-date-and-day.ts | 2 +- .../chats/utils/format-message-time.ts | 0 .../chats/utils/formatted-user-name.ts | 0 .../chats/utils/get-chat-private-key.ts | 0 .../chats/utils/get-joined-communities.ts | 0 .../chats/utils/get-user-chat-public-key.ts | 0 .../chats/utils/index.ts | 0 .../chats/utils/is-message-gif.ts | 0 .../chats/utils/is-message-image.ts | 0 .../chats/utils/upload-channel-data.ts | 3 +- .../chats/utils/upload-chat-public-key.ts | 13 + .../utils/use-fetch-community-messages.ts | 3 +- .../chats/utils/use-fetch-direct-messages.ts | 0 .../utils/use-get-community-last-message.ts | 0 .../utils/use-get-direct-last-message.ts | 0 src/common/i18n/locales/en-US.json | 10 +- src/common/img/svg.tsx | 123 ++- src/common/pages/chats/index.tsx | 16 +- src/common/pages/community-functional.tsx | 49 +- src/managers/message-manager.tsx | 8 +- yarn.lock | 277 ++++++- 79 files changed, 1698 insertions(+), 1542 deletions(-) delete mode 100644 src/common/components/chats/chat-popup/index.tsx delete mode 100644 src/common/components/chats/chats-dropdown-menu/index.tsx delete mode 100644 src/common/components/chats/chats-messages-view/index.scss delete mode 100644 src/common/components/chats/utils/upload-chat-public-key.ts delete mode 100644 src/common/components/manage-chat-key/index.tsx rename src/common/{components => features}/chats/chat-context-provider.tsx (100%) rename src/common/{components => features}/chats/chat-input/index.scss (96%) rename src/common/{components => features}/chats/chat-input/index.tsx (89%) rename src/common/{components => features}/chats/chat-popup/chat-constants.ts (100%) create mode 100644 src/common/features/chats/chat-popup/chat-popup-direct-messages.tsx create mode 100644 src/common/features/chats/chat-popup/chat-popup-header.tsx create mode 100644 src/common/features/chats/chat-popup/chat-popup-messages-list.tsx create mode 100644 src/common/features/chats/chat-popup/chat-popup-search-user.tsx rename src/common/{components => features}/chats/chat-popup/index.scss (61%) create mode 100644 src/common/features/chats/chat-popup/index.tsx rename src/common/{components => features}/chats/chats-channel-messages/index.scss (91%) rename src/common/{components => features}/chats/chats-channel-messages/index.tsx (90%) rename src/common/{components => features}/chats/chats-community-dropdown-menu/index.scss (87%) rename src/common/{components => features}/chats/chats-community-dropdown-menu/index.tsx (86%) rename src/common/{components => features}/chats/chats-confirmation-modal/index.scss (63%) rename src/common/{components => features}/chats/chats-confirmation-modal/index.tsx (81%) rename src/common/{components => features}/chats/chats-direct-messages/index.scss (90%) rename src/common/{components => features}/chats/chats-direct-messages/index.tsx (97%) create mode 100644 src/common/features/chats/chats-dropdown-menu/index.tsx rename src/common/{components => features}/chats/chats-messages-box/index.scss (86%) rename src/common/{components => features}/chats/chats-messages-box/index.tsx (98%) rename src/common/{components => features}/chats/chats-messages-header/index.scss (83%) rename src/common/{components => features}/chats/chats-messages-header/index.tsx (87%) create mode 100644 src/common/features/chats/chats-messages-view/index.scss rename src/common/{components => features}/chats/chats-messages-view/index.tsx (95%) rename src/common/{components => features}/chats/chats-profile-box/index.scss (83%) rename src/common/{components => features}/chats/chats-profile-box/index.tsx (96%) rename src/common/{components => features}/chats/chats-scroller/index.scss (77%) rename src/common/{components => features}/chats/chats-scroller/index.tsx (95%) rename src/common/{components => features}/chats/chats-sidebar/indes.tsx (95%) rename src/common/{components => features}/chats/chats-sidebar/index.scss (89%) rename src/common/{components => features}/chats/hooks/use-message-service-listener.tsx (100%) rename src/common/{components => features}/chats/import-chats/index.scss (67%) rename src/common/{components => features}/chats/import-chats/index.tsx (70%) rename src/common/{components => features}/chats/join-chat/index.tsx (64%) rename src/common/{components => features}/chats/join-community-chat-btn/index.tsx (89%) rename src/common/{components => features/chats}/manage-chat-key/index.scss (84%) create mode 100644 src/common/features/chats/manage-chat-key/index.tsx create mode 100644 src/common/features/chats/queries/index.ts create mode 100644 src/common/features/chats/queries/queries.ts create mode 100644 src/common/features/chats/queries/search-users-query.ts rename src/common/{components => features}/chats/types/chat-types.ts (100%) rename src/common/{components => features}/chats/types/index.ts (100%) rename src/common/{components => features}/chats/utils/check-contiguous-message.ts (55%) rename src/common/{components => features}/chats/utils/copy-to-clipboard.ts (100%) rename src/common/{components => features}/chats/utils/create-nostr-account.ts (100%) rename src/common/{components => features}/chats/utils/delete-chat-public-key.ts (100%) rename src/common/{components => features}/chats/utils/fetch-profile-metadata.ts (74%) rename src/common/{components => features}/chats/utils/format-message-date-and-day.ts (87%) rename src/common/{components => features}/chats/utils/format-message-time.ts (100%) rename src/common/{components => features}/chats/utils/formatted-user-name.ts (100%) rename src/common/{components => features}/chats/utils/get-chat-private-key.ts (100%) rename src/common/{components => features}/chats/utils/get-joined-communities.ts (100%) rename src/common/{components => features}/chats/utils/get-user-chat-public-key.ts (100%) rename src/common/{components => features}/chats/utils/index.ts (100%) rename src/common/{components => features}/chats/utils/is-message-gif.ts (100%) rename src/common/{components => features}/chats/utils/is-message-image.ts (100%) rename src/common/{components => features}/chats/utils/upload-channel-data.ts (82%) create mode 100644 src/common/features/chats/utils/upload-chat-public-key.ts rename src/common/{components => features}/chats/utils/use-fetch-community-messages.ts (82%) rename src/common/{components => features}/chats/utils/use-fetch-direct-messages.ts (100%) rename src/common/{components => features}/chats/utils/use-get-community-last-message.ts (100%) rename src/common/{components => features}/chats/utils/use-get-direct-last-message.ts (100%) diff --git a/src/common/app.tsx b/src/common/app.tsx index 676ca704362..86ff6df6710 100644 --- a/src/common/app.tsx +++ b/src/common/app.tsx @@ -29,8 +29,8 @@ import { useMappedStore } from "./store/use-mapped-store"; import { EntriesCacheManager } from "./core"; import { UserActivityRecorder } from "./components/user-activity-recorder"; -import { ChatContextProvider } from "./components/chats/chat-context-provider"; -import { ChatPopUp } from "./components/chats/chat-popup"; +import { ChatContextProvider } from "./features/chats/chat-context-provider"; +import { ChatPopUp } from "./features/chats/chat-popup"; // Define lazy pages const ProfileContainer = loadable(() => import("./pages/profile-functional")); diff --git a/src/common/components/chats/chat-popup/index.tsx b/src/common/components/chats/chat-popup/index.tsx deleted file mode 100644 index ed9cf152eb0..00000000000 --- a/src/common/components/chats/chat-popup/index.tsx +++ /dev/null @@ -1,765 +0,0 @@ -import React, { useContext, useEffect, useRef, useState } from "react"; -import useDebounce from "react-use/lib/useDebounce"; -import { useLocation } from "react-router"; -import { Button, Form, Spinner } from "react-bootstrap"; -import { Link } from "react-router-dom"; -import { history } from "../../../../common/store"; - -import { Community } from "../../../store/communities/types"; -import { - Channel, - ChannelUpdate, - DirectMessage, - PublicMessage -} from "../../../../managers/message-manager-types"; - -import Tooltip from "../../tooltip"; -import UserAvatar from "../../user-avatar"; -import LinearProgress from "../../linear-progress"; -import { setNostrkeys } from "../../../../managers/message-manager"; -import ManageChatKey from "../../manage-chat-key"; -import ChatInput from "../chat-input"; -import ChatsProfileBox from "../chats-profile-box"; -import ChatsDropdownMenu from "../chats-dropdown-menu"; -import ChatsCommunityDropdownMenu from "../chats-community-dropdown-menu"; -import ChatsChannelMessages from "../chats-channel-messages"; - -import { - addMessageSVG, - expandArrow, - collapseArrow, - arrowBackSvg, - chevronUpSvg, - chevronDownSvgForSlider, - syncSvg, - extendedView -} from "../../../img/svg"; - -import { EmojiPickerStyle, GifPickerStyle } from "./chat-constants"; - -import accountReputation from "../../../helper/account-reputation"; -import { _t } from "../../../i18n"; -import { usePrevious } from "../../../util/use-previous"; - -import { getAccountReputations } from "../../../api/hive"; -import { getCommunity } from "../../../api/bridge"; - -import "./index.scss"; -import ChatsDirectMessages from "../chats-direct-messages"; -import { AccountWithReputation } from "../types"; -import { - deleteChatPublicKey, - fetchCommunityMessages, - getCommunityLastMessage, - getDirectLastMessage, - getJoinedCommunities, - getPrivateKey, - getUserChatPublicKey -} from "../utils"; -import { useMappedStore } from "../../../store/use-mapped-store"; -import { ChatContext } from "../chat-context-provider"; -import ImportChats from "../import-chats"; -import { useMount } from "react-use"; - -export const ChatPopUp = () => { - const { activeUser, global, chat, resetChat } = useMappedStore(); - - const { - messageServiceInstance, - revealPrivKey, - activeUserKeys, - showSpinner, - hasUserJoinedChat, - currentChannel, - windowWidth, - setCurrentChannel, - setRevealPrivKey, - setShowSpinner, - joinChat - } = useContext(ChatContext); - - const routerLocation = useLocation(); - const prevActiveUser = usePrevious(activeUser); - const chatBodyDivRef = useRef(null); - - const [expanded, setExpanded] = useState(false); - const [currentUser, setCurrentUser] = useState(""); - const [isCurrentUser, setIsCurrentUser] = useState(false); - const [isScrollToTop, setIsScrollToTop] = useState(false); - const [isScrollToBottom, setIsScrollToBottom] = useState(false); - const [showSearchUser, setShowSearchUser] = useState(false); - const [inProgress, setInProgress] = useState(false); - const [show, setShow] = useState(false); - const [receiverPubKey, setReceiverPubKey] = useState(""); - const [isSpinner, setIsSpinner] = useState(false); - const [directMessagesList, setDirectMessagesList] = useState([]); - const [isCurrentUserJoined, setIsCurrentUserJoined] = useState(true); - const [isCommunity, setIsCommunity] = useState(false); - const [communityName, setCommunityName] = useState(""); - const [currentCommunity, setCurrentCommunity] = useState(); - const [publicMessages, setPublicMessages] = useState([]); - const [communities, setCommunities] = useState([]); - const [searchtext, setSearchText] = useState(""); - const [userList, setUserList] = useState([]); - const [isTop, setIsTop] = useState(false); - const [hasMore, setHasMore] = useState(true); - const [isChatPage, setIsChatPage] = useState(false); - const [isScrolled, setIsScrolled] = useState(false); - - useMount(() => { - // deleteChatPublicKey(activeUser); - setShow(!!activeUser?.username && !isChatPage); - }); - - useEffect(() => { - if (currentChannel && chat.leftChannelsList.includes(currentChannel.id)) { - setIsCommunity(false); - setCommunityName(""); - } - }, [chat.leftChannelsList]); - - useEffect(() => { - handleRouterChange(); - }, [routerLocation]); - - useEffect(() => { - if (isChatPage) { - setShow(false); - } - }, [isChatPage]); - - useEffect(() => { - const updated: ChannelUpdate = chat.updatedChannel - .filter((x) => x.channelId === currentChannel?.id!) - .sort((a, b) => b.created - a.created)[0]; - if (currentChannel && updated) { - const publicMessages: PublicMessage[] = fetchCommunityMessages( - chat.publicMessages, - currentChannel, - updated?.hiddenMessageIds - ); - const messages = publicMessages.sort((a, b) => a.created - b.created); - setPublicMessages(messages); - const channel = { - name: updated.name, - about: updated.about, - picture: updated.picture, - communityName: updated.communityName, - communityModerators: updated.communityModerators, - id: updated.channelId, - creator: updated.creator, - created: currentChannel?.created!, - hiddenMessageIds: updated.hiddenMessageIds, - removedUserIds: updated.removedUserIds - }; - setCurrentChannel(channel); - } - }, [chat.updatedChannel]); - - useEffect(() => { - if (isTop) { - fetchPrevMessages(); - } - }, [isTop]); - - useEffect(() => { - if (messageServiceInstance) { - setIsSpinner(false); - } - }, [messageServiceInstance]); - - useEffect(() => { - const communities = getJoinedCommunities(chat.channels, chat.leftChannelsList); - setCommunities(communities); - }, [chat.channels, chat.leftChannelsList]); - - useEffect(() => { - const msgsList = fetchDirectMessages(receiverPubKey!); - const messages = msgsList.sort((a, b) => a.created - b.created); - setDirectMessagesList(messages); - }, [chat.directMessages]); - - useEffect(() => { - if (prevActiveUser?.username !== activeUser?.username) { - setIsCommunity(false); - setIsCurrentUser(false); - setCurrentUser(""); - setCommunityName(""); - } - }, [global.theme, activeUser]); - - useEffect(() => { - if (directMessagesList.length !== 0 && !isScrolled) { - scrollerClicked(); - } - if (!isScrolled && publicMessages.length !== 0 && isCommunity) { - scrollerClicked(); - } - }, [directMessagesList, publicMessages]); - - useEffect(() => { - if (currentChannel && isCommunity) { - messageServiceInstance?.fetchChannel(currentChannel.id); - const publicMessages: PublicMessage[] = fetchCommunityMessages( - chat.publicMessages, - currentChannel, - currentChannel.hiddenMessageIds - ); - const messages = publicMessages.sort((a, b) => a.created - b.created); - setPublicMessages(messages); - } - }, [currentChannel, isCommunity, chat.publicMessages]); - - useEffect(() => { - if ((isCurrentUser || isCommunity) && show) { - scrollerClicked(); - } - }, [isCurrentUser, isCommunity, show]); - - useEffect(() => { - const msgsList = fetchDirectMessages(receiverPubKey!); - const messages = msgsList.sort((a, b) => a.created - b.created); - setDirectMessagesList(messages); - }, [activeUser]); - - useEffect(() => { - if (isCommunity && show) { - fetchCommunity(); - - scrollerClicked(); - fetchCurrentChannel(communityName); - } - }, [isCommunity, communityName, show]); - - useEffect(() => { - if (currentUser) { - const isCurrentUserFound = chat.directContacts.find( - (contact) => contact.name === currentUser - ); - if (isCurrentUserFound) { - setReceiverPubKey(isCurrentUserFound.pubkey); - setIsCurrentUserJoined(true); - } else { - setInProgress(true); - fetchCurrentUserData(); - } - - const peer = chat.directContacts.find((x) => x.name === currentUser)?.pubkey ?? ""; - const msgsList = fetchDirectMessages(peer!); - const messages = msgsList.sort((a, b) => a.created - b.created); - setDirectMessagesList(messages); - if (!messageServiceInstance) { - setNostrkeys(activeUserKeys!); - } - } else { - setIsCurrentUserJoined(true); - setInProgress(false); - } - }, [currentUser]); - - useDebounce( - async () => { - if (searchtext.length !== 0) { - const resp = await getAccountReputations(searchtext, 30); - const sortedByReputation = resp.sort((a, b) => (a.reputation > b.reputation ? -1 : 1)); - setUserList(sortedByReputation); - setInProgress(false); - } else { - setInProgress(false); - setUserList([]); - } - }, - 500, - [searchtext] - ); - - const handleRouterChange = () => { - if (routerLocation.pathname.match("/chats")) { - setShow(false); - setIsChatPage(true); - } else { - setShow(!!activeUser?.username); - } - }; - - const fetchCommunity = async () => { - const community = await getCommunity(communityName, activeUser?.username); - setCurrentCommunity(community!); - }; - - const fetchCurrentChannel = (communityName: string) => { - const channel = chat.channels.find((channel) => channel.communityName === communityName); - if (channel) { - const updated: ChannelUpdate = chat.updatedChannel - .filter((x) => x.channelId === channel.id) - .sort((a, b) => b.created - a.created)[0]; - if (updated) { - const channel = { - name: updated.name, - about: updated.about, - picture: updated.picture, - communityName: updated.communityName, - communityModerators: updated.communityModerators, - id: updated.channelId, - creator: updated.creator, - created: currentChannel?.created!, - hiddenMessageIds: updated.hiddenMessageIds, - removedUserIds: updated.removedUserIds - }; - setCurrentChannel(channel); - } else { - setCurrentChannel(channel); - } - } - }; - - const fetchDirectMessages = (peer: string) => { - for (const item of chat.directMessages) { - if (item.peer === peer) { - return Object.values(item.chat); - } - } - return []; - }; - - const fetchCurrentUserData = async () => { - const nsKey = await getUserChatPublicKey(currentUser); - if (nsKey) { - setReceiverPubKey(nsKey); - } else { - setReceiverPubKey(""); - } - setIsCurrentUserJoined(!!nsKey); - setInProgress(false); - }; - - const userClicked = (username: string) => { - setIsCurrentUser(true); - setCurrentUser(username); - }; - - const fetchPrevMessages = () => { - if (!hasMore || inProgress) return; - - setInProgress(true); - messageServiceInstance - ?.fetchPrevMessages(currentChannel!.id, publicMessages[0].created) - .then((num) => { - if (num < 25) { - setHasMore(false); - } - }) - .finally(() => { - setInProgress(false); - setIsTop(false); - }); - }; - - const handleScroll = (event: React.UIEvent) => { - var element = event.currentTarget; - let srollHeight: number = (element.scrollHeight / 100) * 25; - const isScrollToTop = !isCurrentUser && !isCommunity && element.scrollTop >= srollHeight; - const isScrollToBottom = - (isCurrentUser || isCommunity) && - element.scrollTop + chatBodyDivRef?.current?.clientHeight! < element.scrollHeight - 200; - const isScrolled = element.scrollTop + element.clientHeight <= element.scrollHeight - 20; - setIsScrolled(isScrolled); - setIsScrollToTop(isScrollToTop); - setIsScrollToBottom(isScrollToBottom); - const scrollerTop = element.scrollTop <= 600 && publicMessages.length > 25; - if (isCommunity && scrollerTop) { - setIsTop(true); - } else { - setIsTop(false); - } - }; - - const scrollerClicked = () => { - chatBodyDivRef?.current?.scroll({ - top: isCurrentUser || isCommunity ? chatBodyDivRef?.current?.scrollHeight : 0, - behavior: "auto" - }); - }; - - const handleMessageSvgClick = () => { - setShowSearchUser(!showSearchUser); - setExpanded(true); - }; - - const handleRefreshSvgClick = () => { - setExpanded(true); - resetChat(); - handleBackArrowSvg(); - if (getPrivateKey(activeUser?.username!)) { - setShowSpinner(true); - const keys = { - pub: activeUserKeys?.pub!, - priv: getPrivateKey(activeUser?.username!) - }; - setNostrkeys(keys); - if (isCommunity && communityName) { - fetchCurrentChannel(communityName); - } - } - }; - - const handleJoinChat = async () => { - setIsSpinner(true); - joinChat(); - }; - - const chatButtonSpinner = ( - - ); - - const communityClicked = (community: string) => { - setIsCommunity(true); - setCommunityName(community); - }; - - const handleBackArrowSvg = () => { - setCurrentUser(""); - setIsCurrentUser(false); - setCommunityName(""); - setIsCommunity(false); - setShowSearchUser(false); - setSearchText(""); - setHasMore(true); - setRevealPrivKey(false); - }; - - const handleExtendedView = () => { - if (!isCurrentUser && !isCommunity) { - history?.push("/chats"); - } else if (isCurrentUser) { - history?.push(`/chats/@${currentUser}`); - } else { - history?.push(`/chats/${communityName}`); - } - }; - - return ( - <> - {show && ( -
-
- {(currentUser || communityName || showSearchUser || revealPrivKey) && expanded && ( - -
- - {" "} - {arrowBackSvg} - -
-
- )} -
setExpanded(!expanded)}> - {(currentUser || isCommunity) && ( -

- -

- )} - -

- {currentUser - ? currentUser - : isCommunity - ? currentCommunity?.title - : showSearchUser - ? "New Message" - : revealPrivKey - ? "Manage chat key" - : "Messages"} -

-
-
-
- -

{extendedView}

-
-
- {!currentUser && - hasUserJoinedChat && - activeUserKeys?.priv && - !isCommunity && - !revealPrivKey && ( - <> -
- -

{addMessageSVG}

-
-
- - )} - {hasUserJoinedChat && activeUserKeys?.priv && !revealPrivKey && ( -
- -

- {syncSvg} -

-
-
- )} - {isCommunity && ( -
- -
- )}{" "} - {!isCommunity && !isCurrentUser && activeUserKeys?.priv && ( -
setExpanded(true)}> - { - setRevealPrivKey(!revealPrivKey); - }} - /> -
- )} -
- -

{ - setExpanded(!expanded); - }} - > - {expanded ? expandArrow : collapseArrow} -

-
-
-
-
- {inProgress && } -
- {hasUserJoinedChat && !revealPrivKey ? ( - <> - {currentUser.length !== 0 || communityName.length !== 0 ? ( -
- <> - {" "} - - - - {isCurrentUser ? ( - - ) : ( - - )} - -
- ) : showSearchUser ? ( - <> -
- - { - setSearchText(e.target.value); - setInProgress(true); - }} - /> - -
-
- {userList.map((user, index) => { - return ( -
{ - setCurrentUser(user.account); - setIsCurrentUser(true); - }} - > -
- - - -
- -
-

{user.account}

-

- ({accountReputation(user.reputation)}) -

-
-
- ); - })} -
- - ) : ( - <> - {(chat.directContacts.length !== 0 || - (chat.channels.length !== 0 && communities.length !== 0)) && - !showSpinner && - activeUserKeys?.priv ? ( - - {chat.channels.length !== 0 && communities.length !== 0 && ( - <> -
{_t("chat.communities")}
- {communities.map((channel) => { - return ( -
- -
- - - -
- - -
communityClicked(channel.communityName!)} - > -

{channel.name}

-

- {getCommunityLastMessage(channel.id, chat.publicMessages)} -

-
-
- ); - })} - {chat.directContacts.length !== 0 && ( -
{_t("chat.dms")}
- )} - - )} - {chat.directContacts.map((user) => { - return ( -
- -
- - - -
- - -
{ - userClicked(user.name); - setReceiverPubKey(user.pubkey); - }} - > -

{user.name}

-

- {getDirectLastMessage(user.pubkey, chat.directMessages)} -

-
-
- ); - })} -
- ) : !activeUserKeys?.priv ? ( - <> - - - ) : showSpinner ? ( -
- -

Loading...

-
- ) : ( - <> -

{_t("chat.no-chat")}

-
- -
- - )} - - )} - - ) : revealPrivKey ? ( - - ) : ( - - )} - - {((isScrollToTop && !isCurrentUser) || - ((isCurrentUser || isCommunity) && isScrollToBottom)) && ( - -
- {isCurrentUser || isCommunity ? chevronDownSvgForSlider : chevronUpSvg} -
-
- )} -
- {(isCurrentUser || isCommunity) && ( - - )} -
- )} - - ); -}; diff --git a/src/common/components/chats/chats-dropdown-menu/index.tsx b/src/common/components/chats/chats-dropdown-menu/index.tsx deleted file mode 100644 index ae335b04534..00000000000 --- a/src/common/components/chats/chats-dropdown-menu/index.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import React from "react"; -import { History } from "history"; -import DropDown, { MenuItem } from "../../dropdown"; - -import { KebabMenu, chatKeySvg } from "../../../img/svg"; -import { _t } from "../../../i18n"; -import { DropDownStyle } from "../chat-popup/chat-constants"; - -interface Props { - history: History | null; - onManageChatKey?: () => void; -} - -const ChatsDropdownMenu = (props: Props) => { - const onManageChatKey = () => { - const { onManageChatKey } = props; - if (onManageChatKey) { - onManageChatKey(); - } - }; - - const menuItems: MenuItem[] = [ - { - label: "Manage Chat key", - onClick: onManageChatKey, - icon: chatKeySvg - } - ]; - - const menuConfig = { - history: props.history, - label: "", - icon: KebabMenu, - items: menuItems - }; - - return ( - - ); -}; - -export default ChatsDropdownMenu; diff --git a/src/common/components/chats/chats-messages-view/index.scss b/src/common/components/chats/chats-messages-view/index.scss deleted file mode 100644 index 92ca90be431..00000000000 --- a/src/common/components/chats/chats-messages-view/index.scss +++ /dev/null @@ -1,21 +0,0 @@ -@import "../../../../style/vars_mixins"; - -.chats-messages-view { - @include themify(day) { - border-bottom: 1px solid $white-three; - } - @include themify(night) { - border-bottom: 1px solid $charcoal-grey; - } - - grid-row: span 16; - overflow: scroll; - overflow-x: hidden; - a::after { - display: none; - } - - &.no-scroll { - overflow: hidden; - } -} diff --git a/src/common/components/chats/utils/upload-chat-public-key.ts b/src/common/components/chats/utils/upload-chat-public-key.ts deleted file mode 100644 index 5fe18c12a9c..00000000000 --- a/src/common/components/chats/utils/upload-chat-public-key.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { getAccountFull } from "../../../api/hive"; -import { updateProfile } from "../../../api/operations"; -import { ActiveUser } from "../../../store/active-user/types"; - -export const uploadChatPublicKey = async (activeUser: ActiveUser | null, noStrPubKey: string) => { - const response = await getAccountFull(activeUser?.username!); - - const { posting_json_metadata } = response; - - const newProfile = { - nsKey: noStrPubKey - }; - - if (!response || !response.posting_json_metadata) { - const newPostingData = { - posting_json_metadata: JSON.stringify({ profile: {} }) - }; - const profile = JSON.parse(newPostingData.posting_json_metadata)?.profile; - const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); - return updatedProfile; - } - - const profile = JSON.parse(posting_json_metadata!)?.profile; - const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); - return updatedProfile; -}; diff --git a/src/common/components/community-cover/index.tsx b/src/common/components/community-cover/index.tsx index a380e6bac79..47fba56af53 100644 --- a/src/common/components/community-cover/index.tsx +++ b/src/common/components/community-cover/index.tsx @@ -13,7 +13,7 @@ import { setProxyBase } from "@ecency/render-helper"; import BaseComponent from "../base"; import SubscriptionBtn from "../subscription-btn"; import CommunityPostBtn from "../community-post-btn"; -import JoinCommunityChatBtn from "../chats/join-community-chat-btn"; +import JoinCommunityChatBtn from "../../features/chats/join-community-chat-btn"; import Tooltip from "../tooltip"; import ImageUploadDialog from "../image-upload"; diff --git a/src/common/components/emoji-picker/index-old.tsx b/src/common/components/emoji-picker/index-old.tsx index c4f58498bb8..124fd93d84b 100644 --- a/src/common/components/emoji-picker/index-old.tsx +++ b/src/common/components/emoji-picker/index-old.tsx @@ -5,7 +5,7 @@ import { _t } from "../../i18n"; import { getEmojiData } from "../../api/misc"; import * as ls from "../../util/local-storage"; import { insertOrReplace } from "../../util/input-util"; -import { EmojiPickerStyleProps } from "../chats/types/chat-types"; +import { EmojiPickerStyleProps } from "../../features/chats/types/chat-types"; import "./_index-old.scss"; interface Emoji { diff --git a/src/common/components/login/index.tsx b/src/common/components/login/index.tsx index f0779a55ec6..0db85268759 100644 --- a/src/common/components/login/index.tsx +++ b/src/common/components/login/index.tsx @@ -24,7 +24,7 @@ import { usrActivity } from "../../api/private-api"; import { hsTokenRenew } from "../../api/auth-api"; import { formatError, grantPostingPermission } from "../../api/operations"; import { getRefreshToken } from "../../helper/user-token"; -import { getPrivateKey, getProfileMetaData } from "../../components/chats/utils"; +import { getPrivateKey, getProfileMetaData } from "../../features/chats/utils"; import ReCAPTCHA from "react-google-recaptcha"; import { addAccountAuthority, signBuffer } from "../../helper/keychain"; diff --git a/src/common/components/manage-auth-icon/index.tsx b/src/common/components/manage-auth-icon/index.tsx index 50b8a1dc723..4fc214c9b9d 100644 --- a/src/common/components/manage-auth-icon/index.tsx +++ b/src/common/components/manage-auth-icon/index.tsx @@ -5,7 +5,7 @@ import { PrivateKey, PublicKey } from "@hiveio/dhive"; import { actionType } from "../manage-authority/types"; import DropDown, { MenuItem } from "../dropdown"; -import { KebabMenu, revokeSvg, copyOutlinSvg, keyOutlineSvg } from "../../img/svg"; +import { copyOutlinSvg, kebabMenuSvg, keyOutlineSvg, revokeSvg } from "../../img/svg"; import { _t } from "../../i18n"; import "./index.scss"; @@ -79,7 +79,7 @@ const ManageAuthIcon = (props: Props) => { const menuConfig = { history: props.history, label: "", - icon: KebabMenu, + icon: kebabMenuSvg, items: menuItems }; diff --git a/src/common/components/manage-chat-key/index.tsx b/src/common/components/manage-chat-key/index.tsx deleted file mode 100644 index 2b0daa7de80..00000000000 --- a/src/common/components/manage-chat-key/index.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import React, { useContext } from "react"; -import { Form } from "react-bootstrap"; -import { _t } from "../../i18n"; -import { History } from "history"; -import { copyContent, expandSideBar } from "../../img/svg"; -import { ChatContext } from "../chats/chat-context-provider"; -import { copyToClipboard } from "../chats/utils"; -import { success } from "../feedback"; -import Tooltip from "../tooltip"; - -import "./index.scss"; - -interface Props { - history: History; -} - -export default function ManageChatKey(props: Props) { - const context = useContext(ChatContext); - - console.log("Props", props.history); - - const { chatPrivKey, windowWidth, setShowSideBar } = context; - - const { history } = props; - - const copyPrivateKey = () => { - copyToClipboard(chatPrivKey); - success("Key copied into clipboad"); - }; - - return ( - <> -
-
- {windowWidth < 768 && history.location.pathname.includes("/chats") && ( -
-

setShowSideBar(true)}> - {expandSideBar} -

-
- )} - - {_t("chat.chat-priv-key")} -
- - -

{ - copyPrivateKey(); - }} - > - {copyContent} -

-
-
-
-
- {/* */} -
- - ); -} diff --git a/src/common/components/chats/chat-context-provider.tsx b/src/common/features/chats/chat-context-provider.tsx similarity index 100% rename from src/common/components/chats/chat-context-provider.tsx rename to src/common/features/chats/chat-context-provider.tsx diff --git a/src/common/components/chats/chat-input/index.scss b/src/common/features/chats/chat-input/index.scss similarity index 96% rename from src/common/components/chats/chat-input/index.scss rename to src/common/features/chats/chat-input/index.scss index a93f6b97fd1..896d8fd2441 100644 --- a/src/common/components/chats/chat-input/index.scss +++ b/src/common/features/chats/chat-input/index.scss @@ -1,4 +1,4 @@ -@import "../../../../style/vars_mixins"; +@import "src/style/vars_mixins"; .chat { grid-row: span 1; diff --git a/src/common/components/chats/chat-input/index.tsx b/src/common/features/chats/chat-input/index.tsx similarity index 89% rename from src/common/components/chats/chat-input/index.tsx rename to src/common/features/chats/chat-input/index.tsx index 8d60dcc7d6e..8bd4dd7cf7e 100644 --- a/src/common/components/chats/chat-input/index.tsx +++ b/src/common/features/chats/chat-input/index.tsx @@ -1,21 +1,18 @@ import React, { useContext, useEffect, useRef, useState } from "react"; -import { Form, FormControl, InputGroup } from "react-bootstrap"; import axios from "axios"; - import { Channel } from "../../../../managers/message-manager-types"; import { EmojiPickerStyleProps } from "../types"; - -import ClickAwayListener from "../../clickaway-listener"; -import EmojiPicker from "../../emoji-picker/index-old"; +import ClickAwayListener from "../../../components/clickaway-listener"; +import EmojiPicker from "../../../components/emoji-picker/index-old"; // import { EmojiPicker } from "../../emoji-picker"; -import GifPicker from "../../gif-picker"; -import { error } from "../../feedback"; -import Tooltip from "../../tooltip"; +import GifPicker from "../../../components/gif-picker"; +import { error } from "../../../components/feedback"; +import Tooltip from "../../../components/tooltip"; import { + chatBoxImageSvg, emoticonHappyOutlineSvg, gifIcon, - chatBoxImageSvg, messageSendSvg } from "../../../img/svg"; import { CHAT_FILE_CONTENT_TYPES, GifImagesStyle, UPLOADING } from "../chat-popup/chat-constants"; @@ -29,6 +26,9 @@ import { addImage } from "../../../api/private-api"; import "./index.scss"; import { ChatContext } from "../chat-context-provider"; +import { Form } from "@ui/form"; +import { FormControl, InputGroup } from "@ui/input"; +import { Button } from "@ui/button"; interface Props { isCurrentUser: boolean; @@ -170,11 +170,6 @@ export default function ChatInput(props: Props) { } }; - const handleMessage = (e: React.ChangeEvent) => { - setMessage(e.target.value); - setIsMessageText(e.target.value.length !== 0); - }; - return ( <>
- - + } + > + { + setMessage(e.target.value); + setIsMessageText(e.target.value.length !== 0); + }} required={true} type="text" placeholder={_t("chat.start-chat-placeholder")} @@ -289,15 +299,6 @@ export default function ChatInput(props: Props) { className="chat-input" disabled={(isCurrentUser && !receiverPubKey) || isActveUserRemoved} /> - - {messageSendSvg} -
diff --git a/src/common/components/chats/chat-popup/chat-constants.ts b/src/common/features/chats/chat-popup/chat-constants.ts similarity index 100% rename from src/common/components/chats/chat-popup/chat-constants.ts rename to src/common/features/chats/chat-popup/chat-constants.ts diff --git a/src/common/features/chats/chat-popup/chat-popup-direct-messages.tsx b/src/common/features/chats/chat-popup/chat-popup-direct-messages.tsx new file mode 100644 index 00000000000..f4034a12ec6 --- /dev/null +++ b/src/common/features/chats/chat-popup/chat-popup-direct-messages.tsx @@ -0,0 +1,114 @@ +import React, { useContext, useEffect, useState } from "react"; +import { _t } from "../../../i18n"; +import { Link } from "react-router-dom"; +import { getCommunityLastMessage, getDirectLastMessage, getJoinedCommunities } from "../utils"; +import ImportChats from "../import-chats"; +import { Spinner } from "@ui/spinner"; +import { Button } from "@ui/button"; +import { useMappedStore } from "../../../store/use-mapped-store"; +import { ChatContext } from "../chat-context-provider"; +import { Channel } from "../../../../managers/message-manager-types"; +import UserAvatar from "../../../components/user-avatar"; + +interface Props { + communityClicked: (v: string) => void; + userClicked: (v: string) => void; + setReceiverPubKey: (v: string) => void; + setShowSearchUser: (v: boolean) => void; +} + +export function ChatPopupDirectMessages({ + communityClicked, + userClicked, + setReceiverPubKey, + setShowSearchUser +}: Props) { + const { chat } = useMappedStore(); + const { activeUserKeys, showSpinner } = useContext(ChatContext); + + const [communities, setCommunities] = useState([]); + + useEffect(() => { + const communities = getJoinedCommunities(chat.channels, chat.leftChannelsList); + setCommunities(communities); + }, [chat.channels, chat.leftChannelsList]); + + return ( + <> + {(chat.directContacts.length !== 0 || + (chat.channels.length !== 0 && communities.length !== 0)) && + !showSpinner && + activeUserKeys?.priv ? ( + + {chat.channels.length !== 0 && communities.length !== 0 && ( + <> +
{_t("chat.communities")}
+ {communities.map((channel) => ( +
+ +
+ + + +
+ + +
communityClicked(channel.communityName!)} + > +

{channel.name}

+

+ {getCommunityLastMessage(channel.id, chat.publicMessages)} +

+
+
+ ))} + {chat.directContacts.length !== 0 && ( +
{_t("chat.dms")}
+ )} + + )} + {chat.directContacts.map((user) => ( +
+ +
+ + + +
+ + +
{ + userClicked(user.name); + setReceiverPubKey(user.pubkey); + }} + > +

{user.name}

+

+ {getDirectLastMessage(user.pubkey, chat.directMessages)} +

+
+
+ ))} +
+ ) : !activeUserKeys?.priv ? ( + + ) : showSpinner ? ( +
+ +

Loading...

+
+ ) : ( + <> +

{_t("chat.no-chat")}

+
+ +
+ + )} + + ); +} diff --git a/src/common/features/chats/chat-popup/chat-popup-header.tsx b/src/common/features/chats/chat-popup/chat-popup-header.tsx new file mode 100644 index 00000000000..f19de84d3c1 --- /dev/null +++ b/src/common/features/chats/chat-popup/chat-popup-header.tsx @@ -0,0 +1,150 @@ +import Tooltip from "../../../components/tooltip"; +import { _t } from "../../../i18n"; +import { Button } from "@ui/button"; +import { + addMessageSvg, + arrowBackSvg, + duotoneRefreshSvg, + expandArrow, + extendedView +} from "../../../img/svg"; +import ChatsCommunityDropdownMenu from "../chats-community-dropdown-menu"; +import { history } from "../../../store"; +import ChatsDropdownMenu from "../chats-dropdown-menu"; +import { classNameObject } from "../../../helper/class-name-object"; +import React, { useContext } from "react"; +import UserAvatar from "../../../components/user-avatar"; +import { Community } from "../../../store/communities"; +import { ChatContext } from "../chat-context-provider"; + +interface Props { + currentUser: string; + communityName: string; + showSearchUser: boolean; + expanded: boolean; + canSendMessage: boolean; + isCommunity: boolean; + isCurrentUser: boolean; + handleBackArrowSvg: () => void; + handleMessageSvgClick: () => void; + handleRefreshSvgClick: () => void; + handleExtendedView: () => void; + setExpanded: (v: boolean) => void; + currentCommunity?: Community; +} + +export function ChatPopupHeader({ + currentUser, + communityName, + showSearchUser, + expanded, + canSendMessage, + isCommunity, + isCurrentUser, + handleBackArrowSvg, + handleMessageSvgClick, + handleRefreshSvgClick, + handleExtendedView, + setExpanded, + currentCommunity +}: Props) { + const { revealPrivKey, activeUserKeys, hasUserJoinedChat, setRevealPrivKey } = + useContext(ChatContext); + + return ( +
+
+ {(currentUser || communityName || showSearchUser || revealPrivKey) && expanded && ( + +
+
+ +
+
+ ); +} diff --git a/src/common/features/chats/chat-popup/chat-popup-messages-list.tsx b/src/common/features/chats/chat-popup/chat-popup-messages-list.tsx new file mode 100644 index 00000000000..2e5430f5754 --- /dev/null +++ b/src/common/features/chats/chat-popup/chat-popup-messages-list.tsx @@ -0,0 +1,69 @@ +import { Link } from "react-router-dom"; +import ChatsProfileBox from "../chats-profile-box"; +import ChatsDirectMessages from "../chats-direct-messages"; +import ChatsChannelMessages from "../chats-channel-messages"; +import { history } from "../../../store"; +import React, { useContext } from "react"; +import { ChatContext } from "../chat-context-provider"; +import { Community } from "../../../store/communities"; +import { DirectMessage, PublicMessage } from "../../../../managers/message-manager-types"; + +interface Props { + isCurrentUser: boolean; + currentUser: string; + isCommunity: boolean; + communityName: string; + currentCommunity?: Community; + directMessagesList: DirectMessage[]; + publicMessages: PublicMessage[]; +} + +export function ChatPopupMessagesList({ + isCommunity, + currentUser, + isCurrentUser, + currentCommunity, + communityName, + directMessagesList, + publicMessages +}: Props) { + const { currentChannel, setCurrentChannel } = useContext(ChatContext); + + return ( +
+ {" "} + + + + {isCurrentUser ? ( + + ) : ( + + )} +
+ ); +} diff --git a/src/common/features/chats/chat-popup/chat-popup-search-user.tsx b/src/common/features/chats/chat-popup/chat-popup-search-user.tsx new file mode 100644 index 00000000000..df7a16050f9 --- /dev/null +++ b/src/common/features/chats/chat-popup/chat-popup-search-user.tsx @@ -0,0 +1,63 @@ +import { FormControl, InputGroup } from "@ui/input"; +import { _t } from "../../../i18n"; +import accountReputation from "../../../helper/account-reputation"; +import React, { useState } from "react"; +import UserAvatar from "../../../components/user-avatar"; +import useDebounce from "react-use/lib/useDebounce"; +import { useSearchUsersQuery } from "../queries"; +import { Spinner } from "@ui/spinner"; + +interface Props { + setCurrentUser: (v: string) => void; + setIsCurrentUser: (v: boolean) => void; +} + +export function ChatPopupSearchUser({ setCurrentUser, setIsCurrentUser }: Props) { + const [search, setSearch] = useState(""); + const { data, isLoading, refetch } = useSearchUsersQuery(search); + + useDebounce(() => refetch(), 500, [search]); + + return ( + <> +
+
+ : "@"}> + setSearch(e.target.value)} + /> + +
+
+
+ {data.map((user, index) => { + return ( +
{ + setCurrentUser(user.account); + setIsCurrentUser(true); + }} + > +
+ + + +
+ +
+

{user.account}

+

({accountReputation(user.reputation)})

+
+
+ ); + })} +
+ + ); +} diff --git a/src/common/components/chats/chat-popup/index.scss b/src/common/features/chats/chat-popup/index.scss similarity index 61% rename from src/common/components/chats/chat-popup/index.scss rename to src/common/features/chats/chat-popup/index.scss index 137337d01f0..00333fbfd3d 100644 --- a/src/common/components/chats/chat-popup/index.scss +++ b/src/common/features/chats/chat-popup/index.scss @@ -1,4 +1,4 @@ -@import "../../../../style/vars_mixins"; +@import "src/style/vars_mixins"; .chatbox-container { position: fixed; @@ -15,11 +15,11 @@ @include themify(day) { box-shadow: rgb(101 119 134 / 20%) 0px 0px 15px, rgb(101 119 134 / 15%) 0px 0px 3px 1px; - background: $white; + @apply bg-white; } @include themify(night) { - background: $dark; + @apply bg-dark-default; box-shadow: rgb(255 255 255 / 20%) 0px 0px 15px, rgb(255 255 255 / 15%) 0px 0px 3px 1px; } @@ -27,133 +27,6 @@ height: 530px; } - .chat-header { - border-bottom: 1px solid $white-four; - padding-left: 10px; - padding-right: 4px; - cursor: pointer; - height: 53px; - display: flex; - justify-content: space-between; - @include themify(day) { - border-bottom: 1px solid $white-four; - } - - @include themify(night) { - border-bottom: 1px solid $dark-indigo; - } - - .back-arrow-image { - justify-content: center; - display: flex; - align-items: center; - .back-arrow-svg { - border-radius: 50%; - padding: 6px; - margin-top: 6px; - } - } - - .back-arrow-svg:hover { - background: rgba(29, 155, 240, 0.1); - } - .message-header-title { - display: flex; - width: 288px; - margin-left: 0.1rem; - .user-icon { - margin: 17px 11px 0 0; - } - } - - .message-header-content { - margin: 1rem 0; - font-weight: 700; - line-height: 24px; - font-size: 21px; - font-family: $font-family-sans-serif; - - max-width: 185px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - margin-bottom: 0; - - @include themify(day) { - color: $charcoal-grey; - } - - @include themify(night) { - color: $white-two; - } - } - - .actionable-imgs { - display: flex; - align-items: center; - justify-content: center; - margin-top: 1.1rem; - .message-image, - .extended-image { - margin-top: 2px; - - .message-svg, - .extended-svg { - width: 35px; - height: 35px; - justify-content: center; - text-align: center; - border-radius: 50%; - padding-top: 10px; - display: flex; - - svg { - height: 20px; - width: 20px; - } - } - .extended-svg { - padding-top: 8px; - } - .message-svg:hover, - .extended-svg:hover { - background: rgba(29, 155, 240, 0.1); - } - } - .arrow-image { - margin: 1.5 0.3rem 0 0; - .arrow-svg { - display: flex; - justify-content: center; - text-align: center; - padding-top: 3px; - width: 35px; - height: 35px; - border-radius: 50%; - margin-bottom: 14px; - } - - .arrow-svg:hover { - background: rgba(29, 155, 240, 0.1); - } - } - .community-menu, - .simple-menu { - border: none; - margin-bottom: 13px; - svg { - @include themify(day) { - color: $charcoal-grey; - } - - @include themify(night) { - color: $white-two; - } - } - } - } - } - .chat-body { position: relative; height: 470px; @@ -173,8 +46,10 @@ padding-bottom: 10px; height: 412px; margin-bottom: 8px; + @apply border-b; + @include themify(day) { - border-bottom: 1px solid $white-three; + @apply border-light-300; } @include themify(night) { border-bottom: 1px solid #172b44; @@ -184,8 +59,10 @@ &.community { height: 412px; margin-bottom: 8px; + @apply border-b; + @include themify(day) { - border-bottom: 1px solid $white-three; + @apply border-light-300; } @include themify(night) { border-bottom: 1px solid #172b44; @@ -201,21 +78,20 @@ justify-content: center; align-items: center; } + .no-chat { display: flex; align-items: center; justify-content: center; margin-top: 20%; } + .start-chat-btn { display: flex; align-items: center; justify-content: center; } - .user-search-bar { - margin: 12px; - } .user-search-suggestion-list { .search-content { padding: 0.7rem; @@ -234,21 +110,24 @@ .search-user-title { display: flex; margin-left: 1rem; + .search-username { margin: 7px 0 0 0; font-size: 20px; font-weight: 700; } + .search-reputation { margin: 9px 0 0 6px; } } + &:hover { @include themify(day) { - background: $white-four; + @apply bg-light-400; } @include themify(night) { - background: $charcoal-grey; + @apply bg-gray-charcoal; } } } @@ -262,6 +141,7 @@ .user-img { padding: 13px 16px; } + .user-title { padding-top: 6px; width: -webkit-fill-available; @@ -269,17 +149,18 @@ .username { padding-top: 8px; @include themify(day) { - color: $charcoal-grey; + @apply text-gray-charcoal; } @include themify(night) { - color: $white-two; + @apply text-light-200; } font-size: 18px; font-weight: 700; margin-bottom: 0.2rem !important; } + .last-message { max-width: 320px; white-space: nowrap; @@ -296,7 +177,7 @@ background: #eeeeee; } @include themify(night) { - background: $gunmetal; + @apply bg-gunmetal; } } @@ -311,9 +192,10 @@ align-items: center; justify-content: center; cursor: pointer; + @apply border-b; @include themify(day) { background: #e4e6eb; - border: 1px solid $white-five; + @apply border-light-400; } @include themify(night) { background: #0f223a; @@ -321,10 +203,10 @@ svg { @include themify(day) { - color: $dark-sky-blue; + @apply text-blue-dark-sky; } @include themify(night) { - color: $white; + @apply text-white; } width: 20px; @@ -360,28 +242,35 @@ display: grid; grid-template-rows: repeat(100, 1fr); grid-template-columns: repeat(1, 1fr); + .chat-header { width: 100vw; grid-row: span 5; } + .back-arrow-svg { padding: 0; } + .message-header-content { max-width: 40vw !important; } + .sender-message-content, .receiver-message-content { max-width: 75vw !important; } + &.expanded { height: 100vh; } + .message-header-title { width: 100vw; - margin-right: 0.3rem; margin: 0; + margin-right: 0.3rem; } + .chat-body { grid-row: span 95; height: 100%; @@ -397,6 +286,7 @@ } } } + .chat { grid-row: span 5; } diff --git a/src/common/features/chats/chat-popup/index.tsx b/src/common/features/chats/chat-popup/index.tsx new file mode 100644 index 00000000000..b9581fe24ae --- /dev/null +++ b/src/common/features/chats/chat-popup/index.tsx @@ -0,0 +1,490 @@ +import React, { useContext, useEffect, useMemo, useRef, useState } from "react"; +import { useLocation } from "react-router"; +import { history } from "../../../store"; +import { Community } from "../../../store/communities"; +import { + ChannelUpdate, + DirectMessage, + PublicMessage +} from "../../../../managers/message-manager-types"; +import Tooltip from "../../../components/tooltip"; +import LinearProgress from "../../../components/linear-progress"; +import { setNostrkeys } from "../../../../managers/message-manager"; +import ManageChatKey from "../manage-chat-key"; +import ChatInput from "../chat-input"; +import { chevronDownSvgForSlider, chevronUpSvg } from "../../../img/svg"; +import { EmojiPickerStyle, GifPickerStyle } from "./chat-constants"; +import { _t } from "../../../i18n"; +import { usePrevious } from "../../../util/use-previous"; +import { getCommunity } from "../../../api/bridge"; +import "./index.scss"; +import { fetchCommunityMessages, getPrivateKey, getUserChatPublicKey } from "../utils"; +import { useMappedStore } from "../../../store/use-mapped-store"; +import { ChatContext } from "../chat-context-provider"; +import { useMount } from "react-use"; +import { Spinner } from "@ui/spinner"; +import { Button } from "@ui/button"; +import { classNameObject } from "../../../helper/class-name-object"; +import { ChatPopupHeader } from "./chat-popup-header"; +import { ChatPopupMessagesList } from "./chat-popup-messages-list"; +import { ChatPopupSearchUser } from "./chat-popup-search-user"; +import { ChatPopupDirectMessages } from "./chat-popup-direct-messages"; + +export const ChatPopUp = () => { + const { activeUser, global, chat, resetChat } = useMappedStore(); + + const { + messageServiceInstance, + revealPrivKey, + activeUserKeys, + showSpinner, + hasUserJoinedChat, + currentChannel, + windowWidth, + setCurrentChannel, + setRevealPrivKey, + setShowSpinner, + joinChat + } = useContext(ChatContext); + + const routerLocation = useLocation(); + const prevActiveUser = usePrevious(activeUser); + const chatBodyDivRef = useRef(null); + + const [expanded, setExpanded] = useState(false); + const [currentUser, setCurrentUser] = useState(""); + const [isCurrentUser, setIsCurrentUser] = useState(false); + const [isScrollToTop, setIsScrollToTop] = useState(false); + const [isScrollToBottom, setIsScrollToBottom] = useState(false); + const [showSearchUser, setShowSearchUser] = useState(false); + const [inProgress, setInProgress] = useState(false); + const [show, setShow] = useState(false); + const [receiverPubKey, setReceiverPubKey] = useState(""); + const [isSpinner, setIsSpinner] = useState(false); + const [directMessagesList, setDirectMessagesList] = useState([]); + const [isCurrentUserJoined, setIsCurrentUserJoined] = useState(true); + const [isCommunity, setIsCommunity] = useState(false); + const [communityName, setCommunityName] = useState(""); + const [currentCommunity, setCurrentCommunity] = useState(); + const [publicMessages, setPublicMessages] = useState([]); + const [isTop, setIsTop] = useState(false); + const [hasMore, setHasMore] = useState(true); + const [isChatPage, setIsChatPage] = useState(false); + const [isScrolled, setIsScrolled] = useState(false); + + const canSendMessage = useMemo( + () => + !currentUser && hasUserJoinedChat && !!activeUserKeys?.priv && !isCommunity && !revealPrivKey, + [currentUser, hasUserJoinedChat, activeUserKeys, isCommunity, revealPrivKey] + ); + + useMount(() => { + // deleteChatPublicKey(activeUser); + setShow(!!activeUser?.username && !isChatPage); + }); + + useEffect(() => { + if (currentChannel && chat.leftChannelsList.includes(currentChannel.id)) { + setIsCommunity(false); + setCommunityName(""); + } + }, [chat.leftChannelsList]); + + useEffect(() => { + handleRouterChange(); + }, [routerLocation]); + + useEffect(() => { + if (isChatPage) { + setShow(false); + } + }, [isChatPage]); + + useEffect(() => { + const updated: ChannelUpdate = chat.updatedChannel + .filter((x) => x.channelId === currentChannel?.id!) + .sort((a, b) => b.created - a.created)[0]; + if (currentChannel && updated) { + const publicMessages: PublicMessage[] = fetchCommunityMessages( + chat.publicMessages, + currentChannel, + updated?.hiddenMessageIds + ); + const messages = publicMessages.sort((a, b) => a.created - b.created); + setPublicMessages(messages); + const channel = { + name: updated.name, + about: updated.about, + picture: updated.picture, + communityName: updated.communityName, + communityModerators: updated.communityModerators, + id: updated.channelId, + creator: updated.creator, + created: currentChannel?.created!, + hiddenMessageIds: updated.hiddenMessageIds, + removedUserIds: updated.removedUserIds + }; + setCurrentChannel(channel); + } + }, [chat.updatedChannel]); + + useEffect(() => { + if (isTop) { + fetchPrevMessages(); + } + }, [isTop]); + + useEffect(() => { + if (messageServiceInstance) { + setIsSpinner(false); + } + }, [messageServiceInstance]); + + useEffect(() => { + const msgsList = fetchDirectMessages(receiverPubKey!); + const messages = msgsList.sort((a, b) => a.created - b.created); + setDirectMessagesList(messages); + }, [chat.directMessages]); + + useEffect(() => { + if (prevActiveUser?.username !== activeUser?.username) { + setIsCommunity(false); + setIsCurrentUser(false); + setCurrentUser(""); + setCommunityName(""); + } + }, [global.theme, activeUser]); + + useEffect(() => { + if (directMessagesList.length !== 0 && !isScrolled) { + scrollerClicked(); + } + if (!isScrolled && publicMessages.length !== 0 && isCommunity) { + scrollerClicked(); + } + }, [directMessagesList, publicMessages]); + + useEffect(() => { + if (currentChannel && isCommunity) { + messageServiceInstance?.fetchChannel(currentChannel.id); + const publicMessages: PublicMessage[] = fetchCommunityMessages( + chat.publicMessages, + currentChannel, + currentChannel.hiddenMessageIds + ); + const messages = publicMessages.sort((a, b) => a.created - b.created); + setPublicMessages(messages); + } + }, [currentChannel, isCommunity, chat.publicMessages]); + + useEffect(() => { + if ((isCurrentUser || isCommunity) && show) { + scrollerClicked(); + } + }, [isCurrentUser, isCommunity, show]); + + useEffect(() => { + const msgsList = fetchDirectMessages(receiverPubKey!); + const messages = msgsList.sort((a, b) => a.created - b.created); + setDirectMessagesList(messages); + }, [activeUser]); + + useEffect(() => { + if (isCommunity && show) { + fetchCommunity(); + + scrollerClicked(); + fetchCurrentChannel(communityName); + } + }, [isCommunity, communityName, show]); + + useEffect(() => { + if (currentUser) { + const isCurrentUserFound = chat.directContacts.find( + (contact) => contact.name === currentUser + ); + if (isCurrentUserFound) { + setReceiverPubKey(isCurrentUserFound.pubkey); + setIsCurrentUserJoined(true); + } else { + setInProgress(true); + fetchCurrentUserData(); + } + + const peer = chat.directContacts.find((x) => x.name === currentUser)?.pubkey ?? ""; + const msgsList = fetchDirectMessages(peer!); + const messages = msgsList.sort((a, b) => a.created - b.created); + setDirectMessagesList(messages); + if (!messageServiceInstance) { + setNostrkeys(activeUserKeys!); + } + } else { + setIsCurrentUserJoined(true); + setInProgress(false); + } + }, [currentUser]); + + const handleRouterChange = () => { + if (routerLocation.pathname.match("/chats")) { + setShow(false); + setIsChatPage(true); + } else { + setShow(!!activeUser?.username); + } + }; + + const fetchCommunity = async () => { + const community = await getCommunity(communityName, activeUser?.username); + setCurrentCommunity(community!); + }; + + const fetchCurrentChannel = (communityName: string) => { + const channel = chat.channels.find((channel) => channel.communityName === communityName); + if (channel) { + const updated: ChannelUpdate = chat.updatedChannel + .filter((x) => x.channelId === channel.id) + .sort((a, b) => b.created - a.created)[0]; + if (updated) { + const channel = { + name: updated.name, + about: updated.about, + picture: updated.picture, + communityName: updated.communityName, + communityModerators: updated.communityModerators, + id: updated.channelId, + creator: updated.creator, + created: currentChannel?.created!, + hiddenMessageIds: updated.hiddenMessageIds, + removedUserIds: updated.removedUserIds + }; + setCurrentChannel(channel); + } else { + setCurrentChannel(channel); + } + } + }; + + const fetchDirectMessages = (peer: string) => { + for (const item of chat.directMessages) { + if (item.peer === peer) { + return Object.values(item.chat); + } + } + return []; + }; + + const fetchCurrentUserData = async () => { + const nsKey = await getUserChatPublicKey(currentUser); + if (nsKey) { + setReceiverPubKey(nsKey); + } else { + setReceiverPubKey(""); + } + setIsCurrentUserJoined(!!nsKey); + setInProgress(false); + }; + + const userClicked = (username: string) => { + setIsCurrentUser(true); + setCurrentUser(username); + }; + + const fetchPrevMessages = () => { + if (!hasMore || inProgress) return; + + setInProgress(true); + messageServiceInstance + ?.fetchPrevMessages(currentChannel!.id, publicMessages[0].created) + .then((num) => { + if (num < 25) { + setHasMore(false); + } + }) + .finally(() => { + setInProgress(false); + setIsTop(false); + }); + }; + + const handleScroll = (event: React.UIEvent) => { + var element = event.currentTarget; + let srollHeight: number = (element.scrollHeight / 100) * 25; + const isScrollToTop = !isCurrentUser && !isCommunity && element.scrollTop >= srollHeight; + const isScrollToBottom = + (isCurrentUser || isCommunity) && + element.scrollTop + chatBodyDivRef?.current?.clientHeight! < element.scrollHeight - 200; + const isScrolled = element.scrollTop + element.clientHeight <= element.scrollHeight - 20; + setIsScrolled(isScrolled); + setIsScrollToTop(isScrollToTop); + setIsScrollToBottom(isScrollToBottom); + const scrollerTop = element.scrollTop <= 600 && publicMessages.length > 25; + if (isCommunity && scrollerTop) { + setIsTop(true); + } else { + setIsTop(false); + } + }; + + const scrollerClicked = () => { + chatBodyDivRef?.current?.scroll({ + top: isCurrentUser || isCommunity ? chatBodyDivRef?.current?.scrollHeight : 0, + behavior: "auto" + }); + }; + + const handleMessageSvgClick = () => { + setShowSearchUser(!showSearchUser); + setExpanded(true); + }; + + const handleRefreshSvgClick = () => { + setExpanded(true); + resetChat(); + handleBackArrowSvg(); + if (getPrivateKey(activeUser?.username!)) { + setShowSpinner(true); + const keys = { + pub: activeUserKeys?.pub!, + priv: getPrivateKey(activeUser?.username!) + }; + setNostrkeys(keys); + if (isCommunity && communityName) { + fetchCurrentChannel(communityName); + } + } + }; + + const handleJoinChat = async () => { + setIsSpinner(true); + joinChat(); + }; + + const communityClicked = (community: string) => { + setIsCommunity(true); + setCommunityName(community); + }; + + const handleBackArrowSvg = () => { + setCurrentUser(""); + setIsCurrentUser(false); + setCommunityName(""); + setIsCommunity(false); + setShowSearchUser(false); + setHasMore(true); + setRevealPrivKey(false); + }; + + const handleExtendedView = () => { + if (!isCurrentUser && !isCommunity) { + history?.push("/chats"); + } else if (isCurrentUser) { + history?.push(`/chats/@${currentUser}`); + } else { + history?.push(`/chats/${communityName}`); + } + }; + + return ( + <> + {show && ( +
+ + {inProgress && } +
+ {hasUserJoinedChat && !revealPrivKey ? ( + <> + {currentUser.length !== 0 || communityName.length !== 0 ? ( + + ) : showSearchUser ? ( + + ) : ( + + )} + + ) : revealPrivKey ? ( + + ) : ( + + )} + + {((isScrollToTop && !isCurrentUser) || + ((isCurrentUser || isCommunity) && isScrollToBottom)) && ( + +
+ {isCurrentUser || isCommunity ? chevronDownSvgForSlider : chevronUpSvg} +
+
+ )} +
+ {(isCurrentUser || isCommunity) && ( + + )} +
+ )} + + ); +}; diff --git a/src/common/components/chats/chats-channel-messages/index.scss b/src/common/features/chats/chats-channel-messages/index.scss similarity index 91% rename from src/common/components/chats/chats-channel-messages/index.scss rename to src/common/features/chats/chats-channel-messages/index.scss index d0a7ab77edf..b5d08a1da9a 100644 --- a/src/common/components/chats/chats-channel-messages/index.scss +++ b/src/common/features/chats/chats-channel-messages/index.scss @@ -1,4 +1,4 @@ -@import "../../../../style/vars_mixins"; +@import "src/style/vars_mixins"; .channel-messages { padding-bottom: 15px; @@ -62,7 +62,7 @@ color: rgb(138, 141, 145); } @include themify(night) { - color: $charcoal-grey; + @apply text-gray-charcoal; } font-size: 10px; @@ -103,7 +103,7 @@ color: rgb(138, 141, 145); } @include themify(night) { - color: $white-three; + @apply text-light-300; } } } @@ -138,10 +138,10 @@ cursor: pointer; svg { @include themify(day) { - fill: $light-black; + @apply fill-gray-600; } @include themify(night) { - fill: $white; + @apply fill-white; } } } @@ -151,7 +151,7 @@ svg { width: 16px; height: 16px; - fill: $red !important; + @apply fill-red; } } @@ -170,11 +170,10 @@ @include themify(day) { background-color: rgb(0, 132, 255); - color: $white; + @apply text-white; } @include themify(night) { - background-color: $charcoal-grey; - color: $white; + @apply text-white bg-gray-charcoal; } &.gif { @@ -210,24 +209,19 @@ .sender-message-content { max-width: 100%; margin-bottom: 0; - color: $charcoal-grey; + @apply text-gray-charcoal; font-size: 16px; font-weight: 400; padding: 8px 8px 8px 12px; + @apply text-white; a { text-decoration: underline; color: white; } - @include themify(day) { - color: $white; - } - @include themify(night) { - color: $white; - } } .sender-message-time { - color: $white; + @apply text-white; margin-bottom: 3px; font-size: 10px; display: flex; diff --git a/src/common/components/chats/chats-channel-messages/index.tsx b/src/common/features/chats/chats-channel-messages/index.tsx similarity index 90% rename from src/common/components/chats/chats-channel-messages/index.tsx rename to src/common/features/chats/chats-channel-messages/index.tsx index d05d642c000..2879349f771 100644 --- a/src/common/components/chats/chats-channel-messages/index.tsx +++ b/src/common/features/chats/chats-channel-messages/index.tsx @@ -1,4 +1,4 @@ -import React, { useRef, useState, RefObject, useEffect, useContext } from "react"; +import React, { RefObject, useContext, useEffect, useRef, useState } from "react"; import { useMount } from "react-use"; import mediumZoom, { Zoom } from "medium-zoom"; import { @@ -10,36 +10,32 @@ import { import { History } from "history"; import { renderPostBody } from "@ecency/render-helper"; import { useMappedStore } from "../../../store/use-mapped-store"; -import { - Button, - Form, - FormControl, - InputGroup, - OverlayTrigger, - Popover, - Spinner -} from "react-bootstrap"; -import UserAvatar from "../../user-avatar"; -import FollowControls from "../../follow-controls"; +import UserAvatar from "../../../components/user-avatar"; +import FollowControls from "../../../components/follow-controls"; import usePrevious from "react-use/lib/usePrevious"; import { _t } from "../../../i18n"; -import Tooltip from "../../tooltip"; +import Tooltip from "../../../components/tooltip"; import { failedMessageSvg, hideSvg, resendMessageSvg } from "../../../img/svg"; import ChatsConfirmationModal from "../chats-confirmation-modal"; -import { error } from "../../feedback"; +import { error } from "../../../components/feedback"; import { CHATPAGE, COMMUNITYADMINROLES, PRIVILEGEDROLES } from "../chat-popup/chat-constants"; import { Theme } from "../../../store/global/types"; import { + checkContiguousMessage, + formatMessageDate, + formatMessageDateAndDay, formatMessageTime, isMessageGif, - isMessageImage, - formatMessageDate, - checkContiguousMessage, - formatMessageDateAndDay + isMessageImage } from "../utils"; import { ChatContext } from "../chat-context-provider"; import "./index.scss"; +import { Popover, PopoverContent } from "@ui/popover"; +import { Button } from "@ui/button"; +import { Form } from "@ui/form"; +import { FormControl } from "@ui/input"; +import { Spinner } from "@ui/spinner"; interface Props { publicMessages: PublicMessage[]; @@ -175,10 +171,6 @@ export default function ChatsChannelMessages(props: Props) { } }; - const handleDMChange = (e: React.ChangeEvent) => { - setDmMessage(e.target.value); - }; - const zoomInitializer = () => { const elements: HTMLElement[] = [...document.querySelectorAll(".chat-image img")]; zoom = mediumZoom(elements); @@ -316,8 +308,8 @@ export default function ChatsChannelMessages(props: Props) { const name = getProfileName(pMsg.creator, chat.profiles); const popover = ( - - + +
}>
@@ -351,7 +343,6 @@ export default function ChatsChannelMessages(props: Props) { {currentChannel?.removedUserIds?.includes(pMsg.creator) ? ( <>
- + ); @@ -422,18 +409,18 @@ export default function ChatsChannelMessages(props: Props) { > {!isSameUserMessage || (isSameUserMessage && dayAndMonth) ? (
- handleImageClick(pMsg.id, pMsg.creator)} - > - - - - + {/* handleImageClick(pMsg.id, pMsg.creator)}*/} + {/*>*/} + {/* */} + {/* */} + {/* */} + {/**/}
) : ( <> @@ -568,7 +555,7 @@ export default function ChatsChannelMessages(props: Props) { {pMsg.sent === 0 && ( - + )} {pMsg.sent === 2 && ( diff --git a/src/common/components/chats/chats-community-dropdown-menu/index.scss b/src/common/features/chats/chats-community-dropdown-menu/index.scss similarity index 87% rename from src/common/components/chats/chats-community-dropdown-menu/index.scss rename to src/common/features/chats/chats-community-dropdown-menu/index.scss index c428319e091..ec44864509c 100644 --- a/src/common/components/chats/chats-community-dropdown-menu/index.scss +++ b/src/common/features/chats/chats-community-dropdown-menu/index.scss @@ -1,4 +1,4 @@ -@import "../../../../style/vars_mixins"; +@import "src/style/vars_mixins"; .chats-dialog { .chat-modals-body { diff --git a/src/common/components/chats/chats-community-dropdown-menu/index.tsx b/src/common/features/chats/chats-community-dropdown-menu/index.tsx similarity index 86% rename from src/common/components/chats/chats-community-dropdown-menu/index.tsx rename to src/common/features/chats/chats-community-dropdown-menu/index.tsx index 36548d45930..07d42ceb806 100644 --- a/src/common/components/chats/chats-community-dropdown-menu/index.tsx +++ b/src/common/features/chats/chats-community-dropdown-menu/index.tsx @@ -1,9 +1,9 @@ import React, { useContext, useEffect, useState } from "react"; import { History } from "history"; -import DropDown, { MenuItem } from "../../dropdown"; +import DropDown, { MenuItem } from "../../../components/dropdown"; import useDebounce from "react-use/lib/useDebounce"; -import { KebabMenu, linkSvg, chatLeaveSvg, editSVG, removeUserSvg } from "../../../img/svg"; +import { chatLeaveSvg, editSVG, kebabMenuSvg, linkSvg, removeUserSvg } from "../../../img/svg"; import { _t } from "../../../i18n"; import { ADDROLE, @@ -15,16 +15,18 @@ import { } from "../chat-popup/chat-constants"; import { useMappedStore } from "../../../store/use-mapped-store"; import { Channel, communityModerator } from "../../../../managers/message-manager-types"; -import { error, success } from "../../feedback"; -import { Button, Form, Modal, Row, Col, InputGroup, FormControl } from "react-bootstrap"; -import LinearProgress from "../../linear-progress"; +import { error, success } from "../../../components/feedback"; +import LinearProgress from "../../../components/linear-progress"; import { ROLES } from "../../../store/communities"; -import UserAvatar from "../../user-avatar"; -import { copyToClipboard, getProfileMetaData } from "../utils"; +import UserAvatar from "../../../components/user-avatar"; +import { copyToClipboard } from "../utils"; import { ChatContext } from "../chat-context-provider"; import "./index.scss"; import { getAccountFull } from "../../../api/hive"; +import { Button } from "@ui/button"; +import { FormControl, InputGroup } from "@ui/input"; +import { Modal, ModalBody, ModalHeader } from "@ui/modal"; interface Props { history: History; @@ -68,12 +70,6 @@ const ChatsCommunityDropdownMenu = (props: Props) => { setKeyDialog(!keyDialog); }; - const userChanged = (e: React.ChangeEvent) => { - const { value: user } = e.target; - setUser(user); - setInProgress(true); - }; - const getCommunityAdmins = () => { const communityAdminRoles = ["owner", "admin"]; const communityAdmins = currentChannel?.communityModerators?.filter((user) => @@ -90,11 +86,6 @@ const ChatsCommunityDropdownMenu = (props: Props) => { setBlockedUsers(blockedUsers); }; - const roleChanged = (e: React.ChangeEvent) => { - const { value: role } = e.target; - setRole(role); - }; - const handleBlockedUsers = () => { setKeyDialog(true); setStep(3); @@ -220,7 +211,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => { const communityMenuConfig = { history: props.history, label: "", - icon: KebabMenu, + icon: kebabMenuSvg, items: communityMenuItems }; @@ -229,17 +220,14 @@ const ChatsCommunityDropdownMenu = (props: Props) => { <>
-

Confirmation

+

{_t("communities-create.confirmation")}

-
- Are you sure? -
-

+

{_t("confirm.title")}
+

- - - {_t("community-role-edit.username")} - -
- - - @ - - +
{_t("community-role-edit.username")}
+
+ + { + setUser(e.target.value); + setInProgress(true); + }} className={addRoleError ? "is-invalid" : ""} /> - {addRoleError && {addRoleError}} - - - - - {_t("community-role-edit.role")} - -
- + {addRoleError &&
{addRoleError}
} + + +
+
{_t("community-role-edit.role")}
+
+ ) => setRole(e.target.value)} + > {roles.map((r, i) => ( ))} - - - + +
+
{_t("community.roles-account")}
{importPrivKey && (
-
{ - e.preventDefault(); - }} - > - - - {keySvg} - - e.preventDefault()}> + {_t("chat.submit")}} + > + setPrivKey(e.target.value)} /> - - - {inProgress && } - {error && {error}} + {error &&
{error}
}
)} {} -
- +
+
diff --git a/src/common/components/chats/join-chat/index.tsx b/src/common/features/chats/join-chat/index.tsx similarity index 64% rename from src/common/components/chats/join-chat/index.tsx rename to src/common/features/chats/join-chat/index.tsx index b79cbfd204a..47ba2890b8d 100644 --- a/src/common/components/chats/join-chat/index.tsx +++ b/src/common/features/chats/join-chat/index.tsx @@ -1,6 +1,8 @@ import React, { useContext, useEffect, useState } from "react"; -import { Button, Spinner } from "react-bootstrap"; import { ChatContext } from "../chat-context-provider"; +import { Button } from "@ui/button"; +import { Spinner } from "@ui/spinner"; +import { _t } from "../../../i18n"; export default function JoinChat() { const { messageServiceInstance, joinChat } = useContext(ChatContext); @@ -20,12 +22,13 @@ export default function JoinChat() { return (
-

You haven't joined the chat yet. Please join the chat to start chatting.

-
); diff --git a/src/common/components/chats/join-community-chat-btn/index.tsx b/src/common/features/chats/join-community-chat-btn/index.tsx similarity index 89% rename from src/common/components/chats/join-community-chat-btn/index.tsx rename to src/common/features/chats/join-community-chat-btn/index.tsx index b1e3dbda41c..34342e34718 100644 --- a/src/common/components/chats/join-community-chat-btn/index.tsx +++ b/src/common/features/chats/join-community-chat-btn/index.tsx @@ -1,16 +1,14 @@ import React, { useContext, useEffect, useState } from "react"; -import { Button, Spinner } from "react-bootstrap"; import { History } from "history"; - -import { Community, ROLES } from "../../../store/communities/types"; - +import { Community, ROLES } from "../../../store/communities"; import { _t } from "../../../i18n"; - import { useMappedStore } from "../../../store/use-mapped-store"; import { Channel, communityModerator } from "../../../../managers/message-manager-types"; import { ChatContext } from "../chat-context-provider"; import { CHANNEL, NOSTRKEY } from "../chat-popup/chat-constants"; import { getProfileMetaData, setChannelMetaData } from "../utils"; +import { Spinner } from "@ui/spinner"; +import { Button } from "@ui/button"; interface Props { history: History; @@ -189,36 +187,40 @@ export default function JoinCommunityChatBtn(props: Props) { return {}; }; - const chatButtonSpinner = ( - - ); - return ( <> {props.community.name === activeUser?.username ? ( isCommunityChatJoined ? ( - + ) : !isChatEnabled ? ( - ) : !isCommunityChatJoined && isChatEnabled && hasUserJoinedChat ? ( - ) : ( <> ) ) : isChatEnabled && !isCommunityChatJoined ? ( - ) : isCommunityChatJoined ? ( - + ) : ( <> )} diff --git a/src/common/components/manage-chat-key/index.scss b/src/common/features/chats/manage-chat-key/index.scss similarity index 84% rename from src/common/components/manage-chat-key/index.scss rename to src/common/features/chats/manage-chat-key/index.scss index ca206fdd106..cea50e61b95 100644 --- a/src/common/components/manage-chat-key/index.scss +++ b/src/common/features/chats/manage-chat-key/index.scss @@ -1,7 +1,4 @@ -@import "src/style/colors"; -@import "src/style/variables"; -@import "src/style/bootstrap_vars"; -@import "src/style/mixins"; +@import "src/style/vars_mixins"; .manage-chat-key { .private-key { @@ -46,7 +43,7 @@ color: #a1a1a1; } @include themify(night) { - color: $white-two; + @apply text-light-200; } } } diff --git a/src/common/features/chats/manage-chat-key/index.tsx b/src/common/features/chats/manage-chat-key/index.tsx new file mode 100644 index 00000000000..7b8d18acde0 --- /dev/null +++ b/src/common/features/chats/manage-chat-key/index.tsx @@ -0,0 +1,37 @@ +import React, { useContext } from "react"; +import { _t } from "../../../i18n"; +import { History } from "history"; +import { expandSideBar } from "../../../img/svg"; +import { ChatContext } from "../chat-context-provider"; +import "./index.scss"; +import { InputGroupCopyClipboard } from "@ui/input"; + +interface Props { + history: History; +} + +export default function ManageChatKey({ history }: Props) { + const context = useContext(ChatContext); + const { chatPrivKey, windowWidth, setShowSideBar } = context; + + return ( + <> +
+
+ {windowWidth < 768 && history.location.pathname.includes("/chats") && ( +
+

setShowSideBar(true)}> + {expandSideBar} +

+
+ )} +
+
{_t("chat.chat-priv-key")}
+ +
+
+ {/* */} +
+ + ); +} diff --git a/src/common/features/chats/queries/index.ts b/src/common/features/chats/queries/index.ts new file mode 100644 index 00000000000..abb89894ef7 --- /dev/null +++ b/src/common/features/chats/queries/index.ts @@ -0,0 +1,2 @@ +export * from "./search-users-query"; +export * from "./queries"; diff --git a/src/common/features/chats/queries/queries.ts b/src/common/features/chats/queries/queries.ts new file mode 100644 index 00000000000..1ccc0df4abc --- /dev/null +++ b/src/common/features/chats/queries/queries.ts @@ -0,0 +1,3 @@ +export enum ChatQueries { + SEARCH_USER = "search-user" +} diff --git a/src/common/features/chats/queries/search-users-query.ts b/src/common/features/chats/queries/search-users-query.ts new file mode 100644 index 00000000000..8171fbad7be --- /dev/null +++ b/src/common/features/chats/queries/search-users-query.ts @@ -0,0 +1,20 @@ +import { useQuery } from "@tanstack/react-query"; +import { ChatQueries } from "./queries"; +import { getAccountReputations } from "../../../api/hive"; + +export function useSearchUsersQuery(query: string) { + return useQuery( + [ChatQueries.SEARCH_USER, query], + async () => { + if (!query) { + return []; + } + const response = await getAccountReputations(query); + response.sort((a, b) => (a.reputation > b.reputation ? -1 : 1)); + return response; + }, + { + initialData: [] + } + ); +} diff --git a/src/common/components/chats/types/chat-types.ts b/src/common/features/chats/types/chat-types.ts similarity index 100% rename from src/common/components/chats/types/chat-types.ts rename to src/common/features/chats/types/chat-types.ts diff --git a/src/common/components/chats/types/index.ts b/src/common/features/chats/types/index.ts similarity index 100% rename from src/common/components/chats/types/index.ts rename to src/common/features/chats/types/index.ts diff --git a/src/common/components/chats/utils/check-contiguous-message.ts b/src/common/features/chats/utils/check-contiguous-message.ts similarity index 55% rename from src/common/components/chats/utils/check-contiguous-message.ts rename to src/common/features/chats/utils/check-contiguous-message.ts index 75c0fc9223d..95dee746833 100644 --- a/src/common/components/chats/utils/check-contiguous-message.ts +++ b/src/common/features/chats/utils/check-contiguous-message.ts @@ -1,5 +1,4 @@ -import { DirectMessage } from "./../../../../managers/message-manager-types"; -import { PublicMessage } from "../../../../managers/message-manager-types"; +import { DirectMessage, PublicMessage } from "../../../../managers/message-manager-types"; export const checkContiguousMessage = ( msg: PublicMessage | DirectMessage, @@ -9,8 +8,5 @@ export const checkContiguousMessage = ( const prevMsg = publicMessages[i - 1]; const msgAuthor = msg.creator; const prevMsgAuthor = prevMsg ? prevMsg.creator : null; - if (msgAuthor === prevMsgAuthor) { - return true; - } - return false; + return msgAuthor === prevMsgAuthor; }; diff --git a/src/common/components/chats/utils/copy-to-clipboard.ts b/src/common/features/chats/utils/copy-to-clipboard.ts similarity index 100% rename from src/common/components/chats/utils/copy-to-clipboard.ts rename to src/common/features/chats/utils/copy-to-clipboard.ts diff --git a/src/common/components/chats/utils/create-nostr-account.ts b/src/common/features/chats/utils/create-nostr-account.ts similarity index 100% rename from src/common/components/chats/utils/create-nostr-account.ts rename to src/common/features/chats/utils/create-nostr-account.ts diff --git a/src/common/components/chats/utils/delete-chat-public-key.ts b/src/common/features/chats/utils/delete-chat-public-key.ts similarity index 100% rename from src/common/components/chats/utils/delete-chat-public-key.ts rename to src/common/features/chats/utils/delete-chat-public-key.ts diff --git a/src/common/components/chats/utils/fetch-profile-metadata.ts b/src/common/features/chats/utils/fetch-profile-metadata.ts similarity index 74% rename from src/common/components/chats/utils/fetch-profile-metadata.ts rename to src/common/features/chats/utils/fetch-profile-metadata.ts index 56988efb3cf..7a38f8f76be 100644 --- a/src/common/components/chats/utils/fetch-profile-metadata.ts +++ b/src/common/features/chats/utils/fetch-profile-metadata.ts @@ -4,8 +4,6 @@ export const getProfileMetaData = async (username: string) => { const response = await getAccountFull(username); const { posting_json_metadata } = response; if (posting_json_metadata) { - const profile = JSON.parse(posting_json_metadata!).profile; - - return profile; + return JSON.parse(posting_json_metadata!).profile; } }; diff --git a/src/common/components/chats/utils/format-message-date-and-day.ts b/src/common/features/chats/utils/format-message-date-and-day.ts similarity index 87% rename from src/common/components/chats/utils/format-message-date-and-day.ts rename to src/common/features/chats/utils/format-message-date-and-day.ts index a0821a17bbd..d76b1c51152 100644 --- a/src/common/components/chats/utils/format-message-date-and-day.ts +++ b/src/common/features/chats/utils/format-message-date-and-day.ts @@ -1,4 +1,4 @@ -import { PublicMessage } from "./../../../../managers/message-manager-types"; +import { PublicMessage } from "../../../../managers/message-manager-types"; import { DirectMessage } from "../../../../managers/message-manager-types"; import { formatMessageDate } from "./format-message-time"; diff --git a/src/common/components/chats/utils/format-message-time.ts b/src/common/features/chats/utils/format-message-time.ts similarity index 100% rename from src/common/components/chats/utils/format-message-time.ts rename to src/common/features/chats/utils/format-message-time.ts diff --git a/src/common/components/chats/utils/formatted-user-name.ts b/src/common/features/chats/utils/formatted-user-name.ts similarity index 100% rename from src/common/components/chats/utils/formatted-user-name.ts rename to src/common/features/chats/utils/formatted-user-name.ts diff --git a/src/common/components/chats/utils/get-chat-private-key.ts b/src/common/features/chats/utils/get-chat-private-key.ts similarity index 100% rename from src/common/components/chats/utils/get-chat-private-key.ts rename to src/common/features/chats/utils/get-chat-private-key.ts diff --git a/src/common/components/chats/utils/get-joined-communities.ts b/src/common/features/chats/utils/get-joined-communities.ts similarity index 100% rename from src/common/components/chats/utils/get-joined-communities.ts rename to src/common/features/chats/utils/get-joined-communities.ts diff --git a/src/common/components/chats/utils/get-user-chat-public-key.ts b/src/common/features/chats/utils/get-user-chat-public-key.ts similarity index 100% rename from src/common/components/chats/utils/get-user-chat-public-key.ts rename to src/common/features/chats/utils/get-user-chat-public-key.ts diff --git a/src/common/components/chats/utils/index.ts b/src/common/features/chats/utils/index.ts similarity index 100% rename from src/common/components/chats/utils/index.ts rename to src/common/features/chats/utils/index.ts diff --git a/src/common/components/chats/utils/is-message-gif.ts b/src/common/features/chats/utils/is-message-gif.ts similarity index 100% rename from src/common/components/chats/utils/is-message-gif.ts rename to src/common/features/chats/utils/is-message-gif.ts diff --git a/src/common/components/chats/utils/is-message-image.ts b/src/common/features/chats/utils/is-message-image.ts similarity index 100% rename from src/common/components/chats/utils/is-message-image.ts rename to src/common/features/chats/utils/is-message-image.ts diff --git a/src/common/components/chats/utils/upload-channel-data.ts b/src/common/features/chats/utils/upload-channel-data.ts similarity index 82% rename from src/common/components/chats/utils/upload-channel-data.ts rename to src/common/features/chats/utils/upload-channel-data.ts index 8452caf2773..0eb3562df2a 100644 --- a/src/common/components/chats/utils/upload-channel-data.ts +++ b/src/common/features/chats/utils/upload-channel-data.ts @@ -10,8 +10,7 @@ export const setChannelMetaData = async (username: string, channel: Channel) => const newProfile = { channel: channel }; - const updatedProfile = await updateProfile(response, { ...profile, ...newProfile }); - return updatedProfile; + return await updateProfile(response, { ...profile, ...newProfile }); } catch (error) { throw error; } diff --git a/src/common/features/chats/utils/upload-chat-public-key.ts b/src/common/features/chats/utils/upload-chat-public-key.ts new file mode 100644 index 00000000000..31154282d9c --- /dev/null +++ b/src/common/features/chats/utils/upload-chat-public-key.ts @@ -0,0 +1,13 @@ +import { getAccountFull } from "../../../api/hive"; +import { updateProfile } from "../../../api/operations"; +import { ActiveUser } from "../../../store/active-user/types"; + +export const uploadChatPublicKey = async (activeUser: ActiveUser | null, noStrPubKey: string) => { + const response = await getAccountFull(activeUser?.username!); + + debugger; + return await updateProfile(response, { + ...JSON.parse(response?.posting_json_metadata ? response.posting_json_metadata : "{}"), + nsKey: noStrPubKey + }); +}; diff --git a/src/common/components/chats/utils/use-fetch-community-messages.ts b/src/common/features/chats/utils/use-fetch-community-messages.ts similarity index 82% rename from src/common/components/chats/utils/use-fetch-community-messages.ts rename to src/common/features/chats/utils/use-fetch-community-messages.ts index 8c0bc6f0c63..4dae981e7ff 100644 --- a/src/common/components/chats/utils/use-fetch-community-messages.ts +++ b/src/common/features/chats/utils/use-fetch-community-messages.ts @@ -9,10 +9,9 @@ export const fetchCommunityMessages = ( const hideMessageIds = hiddenMessageIds || currentChannel?.hiddenMessageIds || []; for (const item of publicMessages) { if (item.channelId === currentChannel.id) { - const filteredPublicMessages = Object.values(item.PublicMessage).filter( + return Object.values(item.PublicMessage).filter( (message) => !hideMessageIds.includes(message.id) ); - return filteredPublicMessages; } } return []; diff --git a/src/common/components/chats/utils/use-fetch-direct-messages.ts b/src/common/features/chats/utils/use-fetch-direct-messages.ts similarity index 100% rename from src/common/components/chats/utils/use-fetch-direct-messages.ts rename to src/common/features/chats/utils/use-fetch-direct-messages.ts diff --git a/src/common/components/chats/utils/use-get-community-last-message.ts b/src/common/features/chats/utils/use-get-community-last-message.ts similarity index 100% rename from src/common/components/chats/utils/use-get-community-last-message.ts rename to src/common/features/chats/utils/use-get-community-last-message.ts diff --git a/src/common/components/chats/utils/use-get-direct-last-message.ts b/src/common/features/chats/utils/use-get-direct-last-message.ts similarity index 100% rename from src/common/components/chats/utils/use-get-direct-last-message.ts rename to src/common/features/chats/utils/use-get-direct-last-message.ts diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 39434fa30ad..9b27af3dd0f 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -76,7 +76,9 @@ "you": "You", "copy-clipboard": "Copy to clipboard", "first": "First", - "last": "Last" + "last": "Last", + "ok": "OK", + "join": "Join" }, "confirm": { "title": "Are you sure?", @@ -751,7 +753,8 @@ "roles-role": "Role", "roles-account-title": "Title", "roles-add": "Add User", - "join-community-chat": "Join Community Chat" + "join-community-chat": "Join Community Chat", + "already-joined-community": "You have already joined this community" }, "community-cover": { "rewards": "rewards", @@ -1871,7 +1874,8 @@ "submit": "Submit", "chat-priv-key": "Chat Private key", "copy-priv-key": "Copy Private key", - "extended-view": "Extended View" + "extended-view": "Extended View", + "you-haven-t-joined": "You haven't joined the chat yet. Please join the chat to start chatting." }, "add-image": { "title": "Add Image", diff --git a/src/common/img/svg.tsx b/src/common/img/svg.tsx index c54729e2978..2d4806f80c6 100644 --- a/src/common/img/svg.tsx +++ b/src/common/img/svg.tsx @@ -1852,7 +1852,7 @@ export const sellStakeSvg = ( ); -export const KebabMenu = ( +export const kebabMenuSvg = ( ); -export const addMessageSVG = ( - +export const addMessageSvg = ( + + ); @@ -2057,22 +2065,6 @@ export const expandArrow = ( ); -export const collapseArrow = ( - - - -); - export const arrowBackSvg = ( ); + +export const duotoneRefreshSvg = ( + + + + +); diff --git a/src/common/pages/chats/index.tsx b/src/common/pages/chats/index.tsx index 19429a54cec..53f729c429e 100644 --- a/src/common/pages/chats/index.tsx +++ b/src/common/pages/chats/index.tsx @@ -1,21 +1,21 @@ import React, { useContext, useEffect, useState } from "react"; import { connect } from "react-redux"; import { match } from "react-router"; -import { Spinner } from "react-bootstrap"; import NavBar from "../../components/navbar"; import NavBarElectron from "../../../desktop/app/components/navbar"; import { pageMapDispatchToProps, pageMapStateToProps, PageProps } from "../common"; -import ChatsSideBar from "../../components/chats/chats-sidebar/indes"; -import ChatsMessagesBox from "../../components/chats/chats-messages-box"; +import ChatsSideBar from "../../features/chats/chats-sidebar/indes"; +import ChatsMessagesBox from "../../features/chats/chats-messages-box"; -import ManageChatKey from "../../components/manage-chat-key"; +import ManageChatKey from "../../features/chats/manage-chat-key"; import Feedback from "../../components/feedback"; import { useMappedStore } from "../../store/use-mapped-store"; -import { ChatContext } from "../../components/chats/chat-context-provider"; -import ImportChats from "../../components/chats/import-chats"; -import JoinChat from "../../components/chats/join-chat"; +import { ChatContext } from "../../features/chats/chat-context-provider"; +import ImportChats from "../../features/chats/import-chats"; +import JoinChat from "../../features/chats/join-chat"; import "./index.scss"; +import { Spinner } from "@ui/spinner"; interface MatchParams { filter: string; @@ -77,7 +77,7 @@ export const Chats = (props: Props) => { {activeUser ? ( showSpinner ? (
- +

Loading...

diff --git a/src/common/pages/community-functional.tsx b/src/common/pages/community-functional.tsx index a0d13c87023..6a22d6b70b0 100644 --- a/src/common/pages/community-functional.tsx +++ b/src/common/pages/community-functional.tsx @@ -7,7 +7,7 @@ import { EntryFilter, ListStyle } from "../store/global/types"; import { Channel } from "../../managers/message-manager-types"; import { usePrevious } from "../util/use-previous"; -import { getJoinedCommunities } from "../components/chats/utils"; +import { getJoinedCommunities } from "../features/chats/utils"; import { makeGroupKey } from "../store/entries"; import _ from "lodash"; import Meta from "../components/meta"; @@ -37,12 +37,13 @@ import { EntryListContent } from "../components/entry-list"; import { connect } from "react-redux"; import { withPersistentScroll } from "../components/with-persistent-scroll"; import "./community.scss"; -import { Button, Modal } from "react-bootstrap"; import LoginRequired from "../components/login-required"; import { useMappedStore } from "../store/use-mapped-store"; import { QueryIdentifiers, useCommunityCache } from "../core"; import { useQueryClient } from "@tanstack/react-query"; -import { ChatContext } from "../components/chats/chat-context-provider"; +import { ChatContext } from "../features/chats/chat-context-provider"; +import { Button } from "@ui/button"; +import { Modal, ModalBody, ModalHeader } from "@ui/modal"; interface MatchParams { filter: string; @@ -252,18 +253,12 @@ export const CommunityPage = (props: Props) => { if (isCommunityAlreadyJoined) { return ( <> -
-
-

You have already joined this community

-
+
+

{_t("community.already-joined-community")}

-

-

@@ -271,7 +266,7 @@ export const CommunityPage = (props: Props) => { } else { return ( <> -
+

Confirmaton

@@ -281,21 +276,14 @@ export const CommunityPage = (props: Props) => { className="join-community-dialog-body" style={{ fontSize: "18px", marginTop: "12px" }} > - Are you sure? + {_t("confirm.title")}

- -

@@ -312,12 +300,11 @@ export const CommunityPage = (props: Props) => { show={true} centered={true} onHide={() => setIsJoinCommunity(false)} - keyboard={false} - className="authorities-dialog modal-thin-header" + className="authorities-dialog" size="lg" > - - {joinCommunityModal()} + + {joinCommunityModal()} )} diff --git a/src/managers/message-manager.tsx b/src/managers/message-manager.tsx index ec15d8ad99a..f44cb723c73 100644 --- a/src/managers/message-manager.tsx +++ b/src/managers/message-manager.tsx @@ -1,7 +1,7 @@ import React, { useContext, useEffect, useState } from "react"; import { ActiveUser } from "../common/store/active-user/types"; -import { NostrKeysType } from "../common/components/chats/types"; +import { NostrKeysType } from "../common/features/chats/types"; import { DirectMessage, Profile, @@ -14,13 +14,13 @@ import { } from "./message-manager-types"; import MessageService, { MessageEvents } from "../common/helper/message-service"; -import { getProfileMetaData, getPrivateKey } from "../common/components/chats/utils"; +import { getProfileMetaData, getPrivateKey } from "../common/features/chats/utils"; import { useMappedStore } from "../common/store/use-mapped-store"; -import { ChatContext } from "../common/components/chats/chat-context-provider"; +import { ChatContext } from "../common/features/chats/chat-context-provider"; import { useTimeoutFn } from "react-use"; import { usePrevious } from "../common/util/use-previous"; -import { useMessageServiceListener } from "../common/components/chats/hooks/use-message-service-listener"; +import { useMessageServiceListener } from "../common/features/chats/hooks/use-message-service-listener"; export const setNostrkeys = (keys: NostrKeysType) => { const detail: NostrKeysType = { diff --git a/yarn.lock b/yarn.lock index 3e6ff0b4ac3..b9628cdd3b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1606,6 +1606,15 @@ graceful-fs "^4.2.4" source-map "^0.6.0" +"@jest/source-map@^29.4.3": + version "29.6.3" + resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.6.3.tgz#d90ba772095cf37a34a5eb9413f1b562a08554c4" + integrity sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw== + dependencies: + "@jridgewell/trace-mapping" "^0.3.18" + callsites "^3.0.0" + graceful-fs "^4.2.9" + "@jest/test-result@^24.9.0": version "24.9.0" resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.9.0.tgz#11796e8aa9dbf88ea025757b3152595ad06ba0ca" @@ -1699,6 +1708,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.7.tgz#30cd49820a962aff48c8fffc5cd760151fca61fe" integrity sha512-8cXDaBBHOr2pQ7j77Y6Vp5VDT2sIqWyWQ56TjEq4ih/a4iST3dItRe8Q9fp0rrIl9DoKhWQtUQz/YpOxLkXbNA== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + "@jridgewell/set-array@^1.0.0": version "1.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.1.tgz#36a6acc93987adcf0ba50c66908bd0b70de8afea" @@ -1714,6 +1728,19 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.13.tgz#b6461fb0c2964356c469e115f504c95ad97ab88c" integrity sha512-GryiOJmNcWbovBxTfZSF71V/mXbgcV3MewDe3kIMCLyIh5e7SKAeUZs+rMnJ8jkMolZ/4/VsdBmMrw3l+VdZ3w== +"@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.18": + version "0.3.19" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@jridgewell/trace-mapping@^0.3.9": version "0.3.13" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.13.tgz#dcfe3e95f224c8fe97a87a5235defec999aa92ea" @@ -1753,6 +1780,33 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@noble/ciphers@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-0.2.0.tgz#a12cda60f3cf1ab5d7c77068c3711d2366649ed7" + integrity sha512-6YBxJDAapHSdd3bLDv6x2wRPwq4QFMUaB3HvljNBUTThDd12eSm7/3F+2lnfzx2jvM+S6Nsy0jEt9QbPqSwqRw== + +"@noble/curves@1.1.0", "@noble/curves@~1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.1.0.tgz#f13fc667c89184bc04cccb9b11e8e7bae27d8c3d" + integrity sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA== + dependencies: + "@noble/hashes" "1.3.1" + +"@noble/hashes@1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.1.tgz#8831ef002114670c603c458ab8b11328406953a9" + integrity sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA== + +"@noble/hashes@~1.3.0", "@noble/hashes@~1.3.1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + +"@noble/secp256k1@^1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" + integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1843,6 +1897,33 @@ resolved "https://registry.yarnpkg.com/@redis/time-series/-/time-series-1.0.4.tgz#af85eb080f6934580e4d3b58046026b6c2b18717" integrity sha512-ThUIgo2U/g7cCuZavucQTQzA9g9JbDDY2f64u3AbAoz/8vE2lt2U37LamDUVChhaDA3IRT9R6VvJwqnUfTJzng== +"@scure/base@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" + integrity sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA== + +"@scure/base@~1.1.0": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.3.tgz#8584115565228290a6c6c4961973e0903bb3df2f" + integrity sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q== + +"@scure/bip32@1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.1.tgz#7248aea723667f98160f593d621c47e208ccbb10" + integrity sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A== + dependencies: + "@noble/curves" "~1.1.0" + "@noble/hashes" "~1.3.1" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.1.tgz#5cee8978656b272a917b7871c981e0541ad6ac2a" + integrity sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg== + dependencies: + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + "@sinonjs/commons@^1.7.0": version "1.8.3" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" @@ -2893,12 +2974,12 @@ assert-plus@^1.0.0: integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= assert@^1.1.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" - integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== + version "1.5.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.1.tgz#038ab248e4ff078e7bc2485ba6e6388466c78f76" + integrity sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A== dependencies: - object-assign "^4.1.1" - util "0.10.3" + object.assign "^4.1.4" + util "^0.10.4" assign-symbols@^1.0.0: version "1.0.0" @@ -3233,9 +3314,9 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.8, bn.js@^4.11.9: integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== bn.js@^5.0.0, bn.js@^5.1.1: - version "5.2.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" - integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== body-parser@1.19.2: version "1.19.2" @@ -3489,7 +3570,7 @@ builtin-modules@^1.1.1: builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" - integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= + integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== bytebuffer@^5.0.1: version "5.0.1" @@ -3750,7 +3831,7 @@ cheerio@^1.0.0-rc.11: parse5-htmlparser2-tree-adapter "^7.0.0" tslib "^2.4.0" -"chokidar@>=3.0.0 <4.0.0", chokidar@^3.3.0, chokidar@^3.4.1: +"chokidar@>=3.0.0 <4.0.0", chokidar@^3.3.0: version "3.5.2" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== @@ -3784,7 +3865,7 @@ chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" -chokidar@^3.5.3: +chokidar@^3.4.1, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -4165,7 +4246,7 @@ console-browserify@^1.1.0: constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" - integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= + integrity sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ== content-disposition@0.5.4: version "0.5.4" @@ -4688,9 +4769,9 @@ custom-event@^1.0.1: integrity sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU= cyclist@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" - integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= + version "1.0.2" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.2.tgz#673b5f233bf34d8e602b949429f8171d9121bea3" + integrity sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA== d@1, d@^1.0.1: version "1.0.1" @@ -4780,6 +4861,15 @@ default-gateway@^4.2.0: execa "^1.0.0" ip-regex "^2.1.0" +define-data-property@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -4787,6 +4877,15 @@ define-properties@^1.1.2, define-properties@^1.1.3: dependencies: object-keys "^1.0.12" +define-properties@^1.1.4: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + define-property@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" @@ -4838,9 +4937,9 @@ dependency-graph@^0.11.0: integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg== des.js@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" - integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== + version "1.1.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da" + integrity sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg== dependencies: inherits "^2.0.1" minimalistic-assert "^1.0.0" @@ -5890,7 +5989,7 @@ fresh@0.5.2: from2@^2.1.0: version "2.3.0" resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" - integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= + integrity sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g== dependencies: inherits "^2.0.1" readable-stream "^2.0.0" @@ -5988,6 +6087,16 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: has "^1.0.3" has-symbols "^1.0.1" +get-intrinsic@^1.1.3, get-intrinsic@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -6153,11 +6262,23 @@ globby@^6.1.0: pify "^2.0.0" pinkie-promise "^2.0.0" +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4: version "4.2.6" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== +graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" @@ -6214,11 +6335,28 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + has-symbols@^1.0.1, has-symbols@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -6523,7 +6661,7 @@ http-proxy@^1.17.0: https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" - integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= + integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== https-proxy-agent@5.0.0, https-proxy-agent@^5.0.0: version "5.0.0" @@ -6718,11 +6856,6 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, i resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" - integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= - inherits@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -7203,6 +7336,11 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isomorphic-ws@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz#e5529148912ecb9b451b46ed44d53dae1ce04bbf" + integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw== + istanbul-lib-coverage@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" @@ -8369,6 +8507,11 @@ minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== +minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + minipass-collect@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" @@ -8434,13 +8577,20 @@ mkdirp@1.x, mkdirp@^1.0.3, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1: +mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== dependencies: minimist "^1.2.5" +mkdirp@^0.5.3: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + mockdate@^3.0.2: version "3.0.5" resolved "https://registry.yarnpkg.com/mockdate/-/mockdate-3.0.5.tgz#789be686deb3149e7df2b663d2bc4392bc3284fb" @@ -8768,6 +8918,28 @@ normalize-url@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== +nostr-relaypool@^0.6.28: + version "0.6.28" + resolved "https://registry.yarnpkg.com/nostr-relaypool/-/nostr-relaypool-0.6.28.tgz#50f1dc21c11bda1fca2a6d785d78fd6f6ead9a57" + integrity sha512-kyLChBunf6IB2O3MSPHBe9iSokBGyqZHvAJXZ5+eh9C4znAu7iY8L/yh0V4Ta/P9TtzDna3QTEgFWVan4m0vBA== + dependencies: + "@jest/source-map" "^29.4.3" + isomorphic-ws "^5.0.0" + nostr-tools "^1.10.0" + safe-stable-stringify "^2.4.2" + +nostr-tools@^1.10.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.16.0.tgz#5867f1d8bd055a5a3b27aadb199457dceb244314" + integrity sha512-sx/aOl0gmkeHVoIVbyOhEQhzF88NsrBXMC8bsjhPASqA6oZ8uSOAyEGgRLMfC3SKgzQD5Gr6KvDoAahaD6xKcg== + dependencies: + "@noble/ciphers" "^0.2.0" + "@noble/curves" "1.1.0" + "@noble/hashes" "1.3.1" + "@scure/base" "1.1.1" + "@scure/bip32" "1.3.1" + "@scure/bip39" "1.2.1" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -8892,6 +9064,16 @@ object.assign@^4.1.0, object.assign@^4.1.2: has-symbols "^1.0.1" object-keys "^1.1.1" +object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz#1bd63aeacf0d5d2d2f31b5e393b03a7c601a23f7" @@ -8997,7 +9179,7 @@ original@^1.0.0: os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" - integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= + integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A== os-locale@^3.0.0: version "3.1.0" @@ -10031,7 +10213,7 @@ punycode@1.3.2: punycode@^1.2.4: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" @@ -10086,7 +10268,7 @@ query-string@^6.13.1: querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" - integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= + integrity sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA== querystring@0.2.0: version "0.2.0" @@ -10619,7 +10801,7 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -10632,6 +10814,19 @@ read-pkg@^5.2.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^2.0.0, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + readable-stream@^3.0.6, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" @@ -11092,6 +11287,11 @@ safe-regex@^1.1.0: dependencies: ret "~0.1.10" +safe-stable-stringify@^2.4.2: + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -11335,7 +11535,7 @@ set-value@^2.0.0, set-value@^2.0.1: setimmediate@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== setprototypeof@1.1.0: version "1.1.0" @@ -12365,7 +12565,7 @@ tmpl@1.0.x: to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" - integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= + integrity sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA== to-fast-properties@^2.0.0: version "2.0.0" @@ -12556,7 +12756,7 @@ tsutils@^3.9.1: tty-browserify@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" - integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= + integrity sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw== tus-js-client@*, tus-js-client@^3.1.0: version "3.1.0" @@ -12626,7 +12826,7 @@ typedarray-to-buffer@^3.1.5: typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== typescript@4.1.6: version "4.1.6" @@ -12852,14 +13052,7 @@ util.promisify@~1.0.0: has-symbols "^1.0.1" object.getownpropertydescriptors "^2.1.0" -util@0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" - integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= - dependencies: - inherits "2.0.1" - -util@^0.10.3: +util@^0.10.3, util@^0.10.4: version "0.10.4" resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== From b766e13ffe67a71d90c514c2b7cdd885350b5547 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Wed, 18 Oct 2023 21:48:21 +0600 Subject: [PATCH 097/179] Chat: separated direct message component --- .../chats/chat-popup/chat-direct-message.tsx | 24 +++++++ .../chat-popup/chat-popup-direct-messages.tsx | 63 ++++++------------- .../features/chats/chat-popup/index.scss | 47 ++------------ .../features/chats/manage-chat-key/index.tsx | 38 ++++++++--- src/common/i18n/locales/en-US.json | 2 +- 5 files changed, 77 insertions(+), 97 deletions(-) create mode 100644 src/common/features/chats/chat-popup/chat-direct-message.tsx diff --git a/src/common/features/chats/chat-popup/chat-direct-message.tsx b/src/common/features/chats/chat-popup/chat-direct-message.tsx new file mode 100644 index 00000000000..60365fb3d90 --- /dev/null +++ b/src/common/features/chats/chat-popup/chat-direct-message.tsx @@ -0,0 +1,24 @@ +import { Link } from "react-router-dom"; +import React from "react"; +import UserAvatar from "../../../components/user-avatar"; + +interface Props { + username: string; + lastMessage: string; + userClicked: (username: string) => void; +} + +export function ChatDirectMessage({ username, userClicked, lastMessage }: Props) { + return ( +
+ + + + +
userClicked(username)}> +

{username}

+

{lastMessage}

+
+
+ ); +} diff --git a/src/common/features/chats/chat-popup/chat-popup-direct-messages.tsx b/src/common/features/chats/chat-popup/chat-popup-direct-messages.tsx index f4034a12ec6..ef412807fb9 100644 --- a/src/common/features/chats/chat-popup/chat-popup-direct-messages.tsx +++ b/src/common/features/chats/chat-popup/chat-popup-direct-messages.tsx @@ -1,6 +1,5 @@ import React, { useContext, useEffect, useState } from "react"; import { _t } from "../../../i18n"; -import { Link } from "react-router-dom"; import { getCommunityLastMessage, getDirectLastMessage, getJoinedCommunities } from "../utils"; import ImportChats from "../import-chats"; import { Spinner } from "@ui/spinner"; @@ -8,7 +7,7 @@ import { Button } from "@ui/button"; import { useMappedStore } from "../../../store/use-mapped-store"; import { ChatContext } from "../chat-context-provider"; import { Channel } from "../../../../managers/message-manager-types"; -import UserAvatar from "../../../components/user-avatar"; +import { ChatDirectMessage } from "./chat-direct-message"; interface Props { communityClicked: (v: string) => void; @@ -39,30 +38,17 @@ export function ChatPopupDirectMessages({ (chat.channels.length !== 0 && communities.length !== 0)) && !showSpinner && activeUserKeys?.priv ? ( - + <> {chat.channels.length !== 0 && communities.length !== 0 && ( <>
{_t("chat.communities")}
{communities.map((channel) => ( -
- -
- - - -
- - -
communityClicked(channel.communityName!)} - > -

{channel.name}

-

- {getCommunityLastMessage(channel.id, chat.publicMessages)} -

-
-
+ communityClicked(channel.communityName!)} + /> ))} {chat.directContacts.length !== 0 && (
{_t("chat.dms")}
@@ -70,30 +56,17 @@ export function ChatPopupDirectMessages({ )} {chat.directContacts.map((user) => ( -
- -
- - - -
- - -
{ - userClicked(user.name); - setReceiverPubKey(user.pubkey); - }} - > -

{user.name}

-

- {getDirectLastMessage(user.pubkey, chat.directMessages)} -

-
-
+ { + setReceiverPubKey(user.pubkey); + userClicked(v); + }} + key={user.pubkey} + lastMessage={getDirectLastMessage(user.pubkey, chat.directMessages)} + /> ))} -
+ ) : !activeUserKeys?.priv ? ( ) : showSpinner ? ( diff --git a/src/common/features/chats/chat-popup/index.scss b/src/common/features/chats/chat-popup/index.scss index 00333fbfd3d..b1ee5b4d25e 100644 --- a/src/common/features/chats/chat-popup/index.scss +++ b/src/common/features/chats/chat-popup/index.scss @@ -5,7 +5,7 @@ z-index: 99; right: 13rem; bottom: 0rem; - height: 53px; + height: 50px; width: 410px; border-top-right-radius: 16px; @@ -133,45 +133,6 @@ } } - .chat-content { - display: flex; - cursor: pointer; - border-bottom: 0.5px solid rgba(134, 150, 160, 0.15); - - .user-img { - padding: 13px 16px; - } - - .user-title { - padding-top: 6px; - width: -webkit-fill-available; - - .username { - padding-top: 8px; - @include themify(day) { - @apply text-gray-charcoal; - } - - @include themify(night) { - @apply text-light-200; - } - - font-size: 18px; - font-weight: 700; - margin-bottom: 0.2rem !important; - } - - .last-message { - max-width: 320px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - margin-bottom: 0; - font-size: 14px; - } - } - } - .chat-content:hover { @include themify(day) { background: #eeeeee; @@ -228,10 +189,10 @@ } .manage-chat-key { + @apply p-4; + .private-key { - .form-group { - width: 90vw !important; - } + margin: 0; } } } diff --git a/src/common/features/chats/manage-chat-key/index.tsx b/src/common/features/chats/manage-chat-key/index.tsx index 7b8d18acde0..aa8cc86e9fa 100644 --- a/src/common/features/chats/manage-chat-key/index.tsx +++ b/src/common/features/chats/manage-chat-key/index.tsx @@ -1,36 +1,58 @@ -import React, { useContext } from "react"; +import React, { useContext, useEffect, useRef, useState } from "react"; import { _t } from "../../../i18n"; import { History } from "history"; import { expandSideBar } from "../../../img/svg"; import { ChatContext } from "../chat-context-provider"; import "./index.scss"; import { InputGroupCopyClipboard } from "@ui/input"; +import qrcode from "qrcode"; +import { classNameObject } from "../../../helper/class-name-object"; interface Props { history: History; } export default function ManageChatKey({ history }: Props) { - const context = useContext(ChatContext); - const { chatPrivKey, windowWidth, setShowSideBar } = context; + const { chatPrivKey, windowWidth, setShowSideBar } = useContext(ChatContext); + + const qrImgRef = useRef(null); + const [isQrShow, setIsQrShow] = useState(false); + + useEffect(() => { + compileQR(chatPrivKey); + }, [chatPrivKey]); + + const compileQR = async (key: string) => { + if (qrImgRef.current) { + qrImgRef.current.src = await qrcode.toDataURL(key, { width: 300 }); + setIsQrShow(true); + } + }; return ( <>
-
+
{windowWidth < 768 && history.location.pathname.includes("/chats") && ( -
+

setShowSideBar(true)}> {expandSideBar}

)} -
-
{_t("chat.chat-priv-key")}
+
+
{_t("chat.chat-priv-key")}
+
- {/* */}
); diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 9b27af3dd0f..291d0c587d7 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1872,7 +1872,7 @@ "start-community-chat": "Start Community Chat", "join-community-chat": "Join Community Chat", "submit": "Submit", - "chat-priv-key": "Chat Private key", + "chat-priv-key": "Please, copy this key or save QR code to your device for joining to same account in another devices", "copy-priv-key": "Copy Private key", "extended-view": "Extended View", "you-haven-t-joined": "You haven't joined the chat yet. Please join the chat to start chatting." From a34928f987647d3889746a699324a6ae62a2786c Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Thu, 19 Oct 2023 23:46:14 +0600 Subject: [PATCH 098/179] Chat: moved components to own directory --- src/common/app.tsx | 2 +- .../components/community-cover/index.tsx | 2 +- .../{ => components}/chat-input/index.scss | 4 + .../{ => components}/chat-input/index.tsx | 97 +++++-------------- .../chat-popup/chat-constants.ts | 0 .../chat-popup/chat-direct-message.tsx | 9 +- .../chat-popup/chat-popup-direct-messages.tsx | 10 +- .../chat-popup/chat-popup-header.tsx | 16 +-- .../chat-popup/chat-popup-messages-list.tsx | 8 +- .../chat-popup/chat-popup-search-user.tsx | 8 +- .../{ => components}/chat-popup/index.scss | 0 .../{ => components}/chat-popup/index.tsx | 28 +++--- .../chats-channel-messages/index.scss | 31 ++++++ .../chats-channel-messages/index.tsx | 22 ++--- .../chats-community-dropdown-menu/index.scss | 2 + .../chats-community-dropdown-menu/index.tsx | 24 ++--- .../chats-confirmation-modal/index.scss | 1 + .../chats-confirmation-modal/index.tsx | 7 +- .../chats-direct-messages/index.scss | 24 +++++ .../chats-direct-messages/index.tsx | 18 ++-- .../chats-dropdown-menu/index.tsx | 6 +- .../chats-messages-box/index.scss | 1 + .../chats-messages-box/index.tsx | 16 +-- .../chats-messages-header/index.scss | 4 + .../chats-messages-header/index.tsx | 14 +-- .../chats-messages-view/index.scss | 0 .../chats-messages-view/index.tsx | 23 +++-- .../chats-profile-box/index.scss | 5 + .../chats-profile-box/index.tsx | 12 +-- .../chats-scroller/index.scss | 0 .../{ => components}/chats-scroller/index.tsx | 7 +- .../{ => components}/chats-sidebar/indes.tsx | 27 +++--- .../{ => components}/chats-sidebar/index.scss | 17 ++++ .../{ => components}/import-chats/index.scss | 0 .../{ => components}/import-chats/index.tsx | 18 ++-- .../{ => components}/join-chat/index.tsx | 4 +- .../join-community-chat-btn/index.tsx | 12 +-- .../manage-chat-key/index.scss | 3 + .../manage-chat-key/index.tsx | 8 +- src/common/features/chats/mutations/index.ts | 1 + src/common/features/chats/mutations/upload.ts | 66 +++++++++++++ .../features/chats/utils/is-message-gif.ts | 2 +- src/common/pages/chats/index.tsx | 10 +- 43 files changed, 346 insertions(+), 223 deletions(-) rename src/common/features/chats/{ => components}/chat-input/index.scss (99%) rename src/common/features/chats/{ => components}/chat-input/index.tsx (76%) rename src/common/features/chats/{ => components}/chat-popup/chat-constants.ts (100%) rename src/common/features/chats/{ => components}/chat-popup/chat-direct-message.tsx (65%) rename src/common/features/chats/{ => components}/chat-popup/chat-popup-direct-messages.tsx (90%) rename src/common/features/chats/{ => components}/chat-popup/chat-popup-header.tsx (91%) rename src/common/features/chats/{ => components}/chat-popup/chat-popup-messages-list.tsx (86%) rename src/common/features/chats/{ => components}/chat-popup/chat-popup-search-user.tsx (89%) rename src/common/features/chats/{ => components}/chat-popup/index.scss (100%) rename src/common/features/chats/{ => components}/chat-popup/index.tsx (95%) rename src/common/features/chats/{ => components}/chats-channel-messages/index.scss (99%) rename src/common/features/chats/{ => components}/chats-channel-messages/index.tsx (97%) rename src/common/features/chats/{ => components}/chats-community-dropdown-menu/index.scss (99%) rename src/common/features/chats/{ => components}/chats-community-dropdown-menu/index.tsx (95%) rename src/common/features/chats/{ => components}/chats-confirmation-modal/index.scss (99%) rename src/common/features/chats/{ => components}/chats-confirmation-modal/index.tsx (91%) rename src/common/features/chats/{ => components}/chats-direct-messages/index.scss (99%) rename src/common/features/chats/{ => components}/chats-direct-messages/index.tsx (94%) rename src/common/features/chats/{ => components}/chats-dropdown-menu/index.tsx (82%) rename src/common/features/chats/{ => components}/chats-messages-box/index.scss (99%) rename src/common/features/chats/{ => components}/chats-messages-box/index.tsx (92%) rename src/common/features/chats/{ => components}/chats-messages-header/index.scss (99%) rename src/common/features/chats/{ => components}/chats-messages-header/index.tsx (83%) rename src/common/features/chats/{ => components}/chats-messages-view/index.scss (100%) rename src/common/features/chats/{ => components}/chats-messages-view/index.tsx (92%) rename src/common/features/chats/{ => components}/chats-profile-box/index.scss (99%) rename src/common/features/chats/{ => components}/chats-profile-box/index.tsx (91%) rename src/common/features/chats/{ => components}/chats-scroller/index.scss (100%) rename src/common/features/chats/{ => components}/chats-scroller/index.tsx (89%) rename src/common/features/chats/{ => components}/chats-sidebar/indes.tsx (92%) rename src/common/features/chats/{ => components}/chats-sidebar/index.scss (99%) rename src/common/features/chats/{ => components}/import-chats/index.scss (100%) rename src/common/features/chats/{ => components}/import-chats/index.tsx (85%) rename src/common/features/chats/{ => components}/join-chat/index.tsx (89%) rename src/common/features/chats/{ => components}/join-community-chat-btn/index.tsx (94%) rename src/common/features/chats/{ => components}/manage-chat-key/index.scss (99%) rename src/common/features/chats/{ => components}/manage-chat-key/index.tsx (88%) create mode 100644 src/common/features/chats/mutations/index.ts create mode 100644 src/common/features/chats/mutations/upload.ts diff --git a/src/common/app.tsx b/src/common/app.tsx index 86ff6df6710..cd2686c37d6 100644 --- a/src/common/app.tsx +++ b/src/common/app.tsx @@ -30,7 +30,7 @@ import { EntriesCacheManager } from "./core"; import { UserActivityRecorder } from "./components/user-activity-recorder"; import { ChatContextProvider } from "./features/chats/chat-context-provider"; -import { ChatPopUp } from "./features/chats/chat-popup"; +import { ChatPopUp } from "./features/chats/components/chat-popup"; // Define lazy pages const ProfileContainer = loadable(() => import("./pages/profile-functional")); diff --git a/src/common/components/community-cover/index.tsx b/src/common/components/community-cover/index.tsx index 47fba56af53..856d20b6a5f 100644 --- a/src/common/components/community-cover/index.tsx +++ b/src/common/components/community-cover/index.tsx @@ -13,7 +13,7 @@ import { setProxyBase } from "@ecency/render-helper"; import BaseComponent from "../base"; import SubscriptionBtn from "../subscription-btn"; import CommunityPostBtn from "../community-post-btn"; -import JoinCommunityChatBtn from "../../features/chats/join-community-chat-btn"; +import JoinCommunityChatBtn from "../../features/chats/components/join-community-chat-btn"; import Tooltip from "../tooltip"; import ImageUploadDialog from "../image-upload"; diff --git a/src/common/features/chats/chat-input/index.scss b/src/common/features/chats/components/chat-input/index.scss similarity index 99% rename from src/common/features/chats/chat-input/index.scss rename to src/common/features/chats/components/chat-input/index.scss index 896d8fd2441..c3eafef54c7 100644 --- a/src/common/features/chats/chat-input/index.scss +++ b/src/common/features/chats/components/chat-input/index.scss @@ -30,8 +30,10 @@ } } } + .chatbox-image { cursor: pointer; + .chatbox-image-icon { margin: 7px 0 0 7px; } @@ -51,12 +53,14 @@ background: #cee2ff; } } + .msg-svg { padding: 8px; &.active { cursor: pointer; } + &.active:hover { background: rgba(29, 155, 240, 0.1); border-radius: 50%; diff --git a/src/common/features/chats/chat-input/index.tsx b/src/common/features/chats/components/chat-input/index.tsx similarity index 76% rename from src/common/features/chats/chat-input/index.tsx rename to src/common/features/chats/components/chat-input/index.tsx index 8bd4dd7cf7e..c1b991d91fc 100644 --- a/src/common/features/chats/chat-input/index.tsx +++ b/src/common/features/chats/components/chat-input/index.tsx @@ -1,34 +1,29 @@ import React, { useContext, useEffect, useRef, useState } from "react"; -import axios from "axios"; -import { Channel } from "../../../../managers/message-manager-types"; -import { EmojiPickerStyleProps } from "../types"; -import ClickAwayListener from "../../../components/clickaway-listener"; -import EmojiPicker from "../../../components/emoji-picker/index-old"; +import { Channel } from "../../../../../managers/message-manager-types"; +import { EmojiPickerStyleProps } from "../../types"; +import ClickAwayListener from "../../../../components/clickaway-listener"; +import EmojiPicker from "../../../../components/emoji-picker/index-old"; // import { EmojiPicker } from "../../emoji-picker"; -import GifPicker from "../../../components/gif-picker"; -import { error } from "../../../components/feedback"; -import Tooltip from "../../../components/tooltip"; +import GifPicker from "../../../../components/gif-picker"; +import { error } from "../../../../components/feedback"; +import Tooltip from "../../../../components/tooltip"; import { chatBoxImageSvg, emoticonHappyOutlineSvg, gifIcon, messageSendSvg -} from "../../../img/svg"; +} from "../../../../img/svg"; import { CHAT_FILE_CONTENT_TYPES, GifImagesStyle, UPLOADING } from "../chat-popup/chat-constants"; -import { classNameObject } from "../../../helper/class-name-object"; - -import { useMappedStore } from "../../../store/use-mapped-store"; -import { _t } from "../../../i18n"; -import { getAccessToken } from "../../../helper/user-token"; -import { uploadImage } from "../../../api/misc"; -import { addImage } from "../../../api/private-api"; - +import { classNameObject } from "../../../../helper/class-name-object"; +import { useMappedStore } from "../../../../store/use-mapped-store"; +import { _t } from "../../../../i18n"; import "./index.scss"; -import { ChatContext } from "../chat-context-provider"; +import { ChatContext } from "../../chat-context-provider"; import { Form } from "@ui/form"; import { FormControl, InputGroup } from "@ui/input"; import { Button } from "@ui/button"; +import { useChatFileUpload } from "../../mutations"; interface Props { isCurrentUser: boolean; @@ -40,26 +35,25 @@ interface Props { gifPickerStyle: EmojiPickerStyleProps; } -export default function ChatInput(props: Props) { +export default function ChatInput({ + isCommunity, + isCurrentUser, + currentChannel, + currentUser, + isCurrentUserJoined, + emojiPickerStyles, + gifPickerStyle +}: Props) { const fileInputRef = useRef(null); const { global, activeUser, chat } = useMappedStore(); + const { messageServiceInstance, isActveUserRemoved, receiverPubKey } = useContext(ChatContext); const [showEmojiPicker, setShowEmojiPicker] = useState(false); const [message, setMessage] = useState(""); const [shGif, setShGif] = useState(false); const [isMessageText, setIsMessageText] = useState(false); - const { messageServiceInstance, isActveUserRemoved, receiverPubKey } = useContext(ChatContext); - - const { - isCommunity, - isCurrentUser, - currentChannel, - currentUser, - isCurrentUserJoined, - emojiPickerStyles, - gifPickerStyle - } = props; + const { mutateAsync } = useChatFileUpload(setMessage, setIsMessageText); useEffect(() => { if (!isCurrentUser && !isCommunity) { @@ -123,52 +117,11 @@ export default function ChatInput(props: Props) { e.preventDefault(); } - if (files.length > 1 && isElectron) { - let isWindows = process.platform === "win32"; - if (isWindows) { - files = files.reverse(); - } - } - - files.forEach((file) => upload(file)); + files.forEach((file) => mutateAsync(file)); // reset input e.target.value = ""; }; - const upload = async (file: File) => { - const username = activeUser?.username!; - - const tempImgTag = `![Uploading ${file.name} #${Math.floor(Math.random() * 99)}]()\n\n`; - - setMessage(tempImgTag); - - let imageUrl: string; - try { - let token = getAccessToken(username); - if (token) { - const resp = await uploadImage(file, token); - imageUrl = resp.url; - - if (global.usePrivate && imageUrl.length > 0) { - addImage(username, imageUrl).then(); - } - - const imgTag = imageUrl.length > 0 && `![](${imageUrl})\n\n`; - - imgTag && setMessage(imgTag); - setIsMessageText(true); - } else { - error(_t("editor-toolbar.image-error-cache")); - } - } catch (e) { - if (axios.isAxiosError(e) && e.response?.status === 413) { - error(_t("editor-toolbar.image-error-size")); - } else { - error(_t("editor-toolbar.image-error")); - } - return; - } - }; return ( <> diff --git a/src/common/features/chats/chat-popup/chat-constants.ts b/src/common/features/chats/components/chat-popup/chat-constants.ts similarity index 100% rename from src/common/features/chats/chat-popup/chat-constants.ts rename to src/common/features/chats/components/chat-popup/chat-constants.ts diff --git a/src/common/features/chats/chat-popup/chat-direct-message.tsx b/src/common/features/chats/components/chat-popup/chat-direct-message.tsx similarity index 65% rename from src/common/features/chats/chat-popup/chat-direct-message.tsx rename to src/common/features/chats/components/chat-popup/chat-direct-message.tsx index 60365fb3d90..931cfc66c19 100644 --- a/src/common/features/chats/chat-popup/chat-direct-message.tsx +++ b/src/common/features/chats/components/chat-popup/chat-direct-message.tsx @@ -1,6 +1,6 @@ import { Link } from "react-router-dom"; import React from "react"; -import UserAvatar from "../../../components/user-avatar"; +import UserAvatar from "../../../../components/user-avatar"; interface Props { username: string; @@ -10,12 +10,15 @@ interface Props { export function ChatDirectMessage({ username, userClicked, lastMessage }: Props) { return ( -
+
userClicked(username)} + > -
userClicked(username)}> +

{username}

{lastMessage}

diff --git a/src/common/features/chats/chat-popup/chat-popup-direct-messages.tsx b/src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx similarity index 90% rename from src/common/features/chats/chat-popup/chat-popup-direct-messages.tsx rename to src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx index ef412807fb9..df283f2a4d2 100644 --- a/src/common/features/chats/chat-popup/chat-popup-direct-messages.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx @@ -1,12 +1,12 @@ import React, { useContext, useEffect, useState } from "react"; -import { _t } from "../../../i18n"; -import { getCommunityLastMessage, getDirectLastMessage, getJoinedCommunities } from "../utils"; +import { _t } from "../../../../i18n"; +import { getCommunityLastMessage, getDirectLastMessage, getJoinedCommunities } from "../../utils"; import ImportChats from "../import-chats"; import { Spinner } from "@ui/spinner"; import { Button } from "@ui/button"; -import { useMappedStore } from "../../../store/use-mapped-store"; -import { ChatContext } from "../chat-context-provider"; -import { Channel } from "../../../../managers/message-manager-types"; +import { useMappedStore } from "../../../../store/use-mapped-store"; +import { ChatContext } from "../../chat-context-provider"; +import { Channel } from "../../../../../managers/message-manager-types"; import { ChatDirectMessage } from "./chat-direct-message"; interface Props { diff --git a/src/common/features/chats/chat-popup/chat-popup-header.tsx b/src/common/features/chats/components/chat-popup/chat-popup-header.tsx similarity index 91% rename from src/common/features/chats/chat-popup/chat-popup-header.tsx rename to src/common/features/chats/components/chat-popup/chat-popup-header.tsx index f19de84d3c1..e1bfdc959c2 100644 --- a/src/common/features/chats/chat-popup/chat-popup-header.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-header.tsx @@ -1,5 +1,5 @@ -import Tooltip from "../../../components/tooltip"; -import { _t } from "../../../i18n"; +import Tooltip from "../../../../components/tooltip"; +import { _t } from "../../../../i18n"; import { Button } from "@ui/button"; import { addMessageSvg, @@ -7,15 +7,15 @@ import { duotoneRefreshSvg, expandArrow, extendedView -} from "../../../img/svg"; +} from "../../../../img/svg"; import ChatsCommunityDropdownMenu from "../chats-community-dropdown-menu"; -import { history } from "../../../store"; +import { history } from "../../../../store"; import ChatsDropdownMenu from "../chats-dropdown-menu"; -import { classNameObject } from "../../../helper/class-name-object"; +import { classNameObject } from "../../../../helper/class-name-object"; import React, { useContext } from "react"; -import UserAvatar from "../../../components/user-avatar"; -import { Community } from "../../../store/communities"; -import { ChatContext } from "../chat-context-provider"; +import UserAvatar from "../../../../components/user-avatar"; +import { Community } from "../../../../store/communities"; +import { ChatContext } from "../../chat-context-provider"; interface Props { currentUser: string; diff --git a/src/common/features/chats/chat-popup/chat-popup-messages-list.tsx b/src/common/features/chats/components/chat-popup/chat-popup-messages-list.tsx similarity index 86% rename from src/common/features/chats/chat-popup/chat-popup-messages-list.tsx rename to src/common/features/chats/components/chat-popup/chat-popup-messages-list.tsx index 2e5430f5754..840913851c6 100644 --- a/src/common/features/chats/chat-popup/chat-popup-messages-list.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-messages-list.tsx @@ -2,11 +2,11 @@ import { Link } from "react-router-dom"; import ChatsProfileBox from "../chats-profile-box"; import ChatsDirectMessages from "../chats-direct-messages"; import ChatsChannelMessages from "../chats-channel-messages"; -import { history } from "../../../store"; +import { history } from "../../../../store"; import React, { useContext } from "react"; -import { ChatContext } from "../chat-context-provider"; -import { Community } from "../../../store/communities"; -import { DirectMessage, PublicMessage } from "../../../../managers/message-manager-types"; +import { ChatContext } from "../../chat-context-provider"; +import { Community } from "../../../../store/communities"; +import { DirectMessage, PublicMessage } from "../../../../../managers/message-manager-types"; interface Props { isCurrentUser: boolean; diff --git a/src/common/features/chats/chat-popup/chat-popup-search-user.tsx b/src/common/features/chats/components/chat-popup/chat-popup-search-user.tsx similarity index 89% rename from src/common/features/chats/chat-popup/chat-popup-search-user.tsx rename to src/common/features/chats/components/chat-popup/chat-popup-search-user.tsx index df7a16050f9..d4653c183aa 100644 --- a/src/common/features/chats/chat-popup/chat-popup-search-user.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-search-user.tsx @@ -1,10 +1,10 @@ import { FormControl, InputGroup } from "@ui/input"; -import { _t } from "../../../i18n"; -import accountReputation from "../../../helper/account-reputation"; +import { _t } from "../../../../i18n"; +import accountReputation from "../../../../helper/account-reputation"; import React, { useState } from "react"; -import UserAvatar from "../../../components/user-avatar"; +import UserAvatar from "../../../../components/user-avatar"; import useDebounce from "react-use/lib/useDebounce"; -import { useSearchUsersQuery } from "../queries"; +import { useSearchUsersQuery } from "../../queries"; import { Spinner } from "@ui/spinner"; interface Props { diff --git a/src/common/features/chats/chat-popup/index.scss b/src/common/features/chats/components/chat-popup/index.scss similarity index 100% rename from src/common/features/chats/chat-popup/index.scss rename to src/common/features/chats/components/chat-popup/index.scss diff --git a/src/common/features/chats/chat-popup/index.tsx b/src/common/features/chats/components/chat-popup/index.tsx similarity index 95% rename from src/common/features/chats/chat-popup/index.tsx rename to src/common/features/chats/components/chat-popup/index.tsx index b9581fe24ae..b65e6b1f94d 100644 --- a/src/common/features/chats/chat-popup/index.tsx +++ b/src/common/features/chats/components/chat-popup/index.tsx @@ -1,30 +1,30 @@ import React, { useContext, useEffect, useMemo, useRef, useState } from "react"; import { useLocation } from "react-router"; -import { history } from "../../../store"; -import { Community } from "../../../store/communities"; +import { history } from "../../../../store"; +import { Community } from "../../../../store/communities"; import { ChannelUpdate, DirectMessage, PublicMessage -} from "../../../../managers/message-manager-types"; -import Tooltip from "../../../components/tooltip"; -import LinearProgress from "../../../components/linear-progress"; -import { setNostrkeys } from "../../../../managers/message-manager"; +} from "../../../../../managers/message-manager-types"; +import Tooltip from "../../../../components/tooltip"; +import LinearProgress from "../../../../components/linear-progress"; +import { setNostrkeys } from "../../../../../managers/message-manager"; import ManageChatKey from "../manage-chat-key"; import ChatInput from "../chat-input"; -import { chevronDownSvgForSlider, chevronUpSvg } from "../../../img/svg"; +import { chevronDownSvgForSlider, chevronUpSvg } from "../../../../img/svg"; import { EmojiPickerStyle, GifPickerStyle } from "./chat-constants"; -import { _t } from "../../../i18n"; -import { usePrevious } from "../../../util/use-previous"; -import { getCommunity } from "../../../api/bridge"; +import { _t } from "../../../../i18n"; +import { usePrevious } from "../../../../util/use-previous"; +import { getCommunity } from "../../../../api/bridge"; import "./index.scss"; -import { fetchCommunityMessages, getPrivateKey, getUserChatPublicKey } from "../utils"; -import { useMappedStore } from "../../../store/use-mapped-store"; -import { ChatContext } from "../chat-context-provider"; +import { fetchCommunityMessages, getPrivateKey, getUserChatPublicKey } from "../../utils"; +import { useMappedStore } from "../../../../store/use-mapped-store"; +import { ChatContext } from "../../chat-context-provider"; import { useMount } from "react-use"; import { Spinner } from "@ui/spinner"; import { Button } from "@ui/button"; -import { classNameObject } from "../../../helper/class-name-object"; +import { classNameObject } from "../../../../helper/class-name-object"; import { ChatPopupHeader } from "./chat-popup-header"; import { ChatPopupMessagesList } from "./chat-popup-messages-list"; import { ChatPopupSearchUser } from "./chat-popup-search-user"; diff --git a/src/common/features/chats/chats-channel-messages/index.scss b/src/common/features/chats/components/chats-channel-messages/index.scss similarity index 99% rename from src/common/features/chats/chats-channel-messages/index.scss rename to src/common/features/chats/components/chats-channel-messages/index.scss index b5d08a1da9a..2f792bbba79 100644 --- a/src/common/features/chats/chats-channel-messages/index.scss +++ b/src/common/features/chats/components/chats-channel-messages/index.scss @@ -2,6 +2,7 @@ .channel-messages { padding-bottom: 15px; + .custom-divider { .custom-divider-text { margin-bottom: 1rem; @@ -11,18 +12,23 @@ .receiver { display: flex; + .user-img { padding: 18px 8px 8px 16px; } + .community-user-img { padding: 8px 8px 8px 16px; + .user-avatar.medium { cursor: pointer; } } + .user-info { padding-top: 8px; width: 100%; + .user-msg-time { margin: 0; color: rgb(138, 141, 145); @@ -30,10 +36,12 @@ font-size: 12px; margin-left: 4px; margin-bottom: 2px; + .username-community { margin-right: 8px; } } + .receiver-message { display: flex; @@ -74,6 +82,7 @@ &.gif { background: none; padding: 0; + img { max-width: 100%; } @@ -82,6 +91,7 @@ &.chat-image { background: none; padding: 0; + img { max-width: 100%; } @@ -92,12 +102,15 @@ .receiver-message-wrapper { background: none; } + .receiver-message-content { padding: 0; + img { padding-bottom: 10px; } } + .receiver-msg-time { @include themify(day) { color: rgb(138, 141, 145); @@ -109,13 +122,16 @@ } } } + &.same-user-msg { margin-left: 4rem; padding-top: 1px; + .receiver-message-wrapper { border-radius: 10px; } } + &.date-changed { margin-left: 0; border-radius: 0px 10px 10px 0px; @@ -136,6 +152,7 @@ .resend-svg { margin: 5px 15px 0 0; cursor: pointer; + svg { @include themify(day) { @apply fill-gray-600; @@ -148,6 +165,7 @@ .failed-svg { margin: 8px 0 0 5px; + svg { width: 16px; height: 16px; @@ -158,6 +176,7 @@ &.sending { margin-right: 5px; } + &.failed { margin-right: 7px; } @@ -180,6 +199,7 @@ background: none; padding: 0; + img { max-width: 100%; } @@ -188,6 +208,7 @@ &.chat-image { background: none; padding: 0; + img { max-width: 100%; } @@ -198,8 +219,10 @@ .sender-message-time { color: rgb(138, 141, 145); } + .sender-message-content { padding: 10px 0 0 5px; + img { padding-bottom: 10px; } @@ -237,17 +260,21 @@ } } } + .hide-msg { margin-right: 15px; margin-top: 5px; + svg { height: 14px; width: 14px; } + .hide-msg-svg { cursor: pointer; margin-bottom: 0; } + &.receiver { margin-top: 5px; margin-left: 10px; @@ -257,20 +284,24 @@ .profile-box { padding: 15px; + .profile-box-content { .user-avatar.large { margin-bottom: 20px; } + .profile-name { margin: 5px 0 10px 0; font-size: 18px; font-weight: 800; } + .profile-box-buttons { padding: 0 5px; } } } + .medium-zoom-overlay, .medium-zoom-image--opened { z-index: 999; diff --git a/src/common/features/chats/chats-channel-messages/index.tsx b/src/common/features/chats/components/chats-channel-messages/index.tsx similarity index 97% rename from src/common/features/chats/chats-channel-messages/index.tsx rename to src/common/features/chats/components/chats-channel-messages/index.tsx index 2879349f771..936bb00f821 100644 --- a/src/common/features/chats/chats-channel-messages/index.tsx +++ b/src/common/features/chats/components/chats-channel-messages/index.tsx @@ -6,20 +6,20 @@ import { communityModerator, Profile, PublicMessage -} from "../../../../managers/message-manager-types"; +} from "../../../../../managers/message-manager-types"; import { History } from "history"; import { renderPostBody } from "@ecency/render-helper"; -import { useMappedStore } from "../../../store/use-mapped-store"; -import UserAvatar from "../../../components/user-avatar"; -import FollowControls from "../../../components/follow-controls"; +import { useMappedStore } from "../../../../store/use-mapped-store"; +import UserAvatar from "../../../../components/user-avatar"; +import FollowControls from "../../../../components/follow-controls"; import usePrevious from "react-use/lib/usePrevious"; -import { _t } from "../../../i18n"; -import Tooltip from "../../../components/tooltip"; -import { failedMessageSvg, hideSvg, resendMessageSvg } from "../../../img/svg"; +import { _t } from "../../../../i18n"; +import Tooltip from "../../../../components/tooltip"; +import { failedMessageSvg, hideSvg, resendMessageSvg } from "../../../../img/svg"; import ChatsConfirmationModal from "../chats-confirmation-modal"; -import { error } from "../../../components/feedback"; +import { error } from "../../../../components/feedback"; import { CHATPAGE, COMMUNITYADMINROLES, PRIVILEGEDROLES } from "../chat-popup/chat-constants"; -import { Theme } from "../../../store/global/types"; +import { Theme } from "../../../../store/global/types"; import { checkContiguousMessage, formatMessageDate, @@ -27,8 +27,8 @@ import { formatMessageTime, isMessageGif, isMessageImage -} from "../utils"; -import { ChatContext } from "../chat-context-provider"; +} from "../../utils"; +import { ChatContext } from "../../chat-context-provider"; import "./index.scss"; import { Popover, PopoverContent } from "@ui/popover"; diff --git a/src/common/features/chats/chats-community-dropdown-menu/index.scss b/src/common/features/chats/components/chats-community-dropdown-menu/index.scss similarity index 99% rename from src/common/features/chats/chats-community-dropdown-menu/index.scss rename to src/common/features/chats/components/chats-community-dropdown-menu/index.scss index ec44864509c..fd409af6790 100644 --- a/src/common/features/chats/chats-community-dropdown-menu/index.scss +++ b/src/common/features/chats/components/chats-community-dropdown-menu/index.scss @@ -5,11 +5,13 @@ .community-chat-role-edit-dialog-content { margin-top: 2rem; } + .success-dialog-header { .success-dialog-titles { margin-top: 10px; } } + .success-dialog-body { padding: 10px 20px 20px 10px; } diff --git a/src/common/features/chats/chats-community-dropdown-menu/index.tsx b/src/common/features/chats/components/chats-community-dropdown-menu/index.tsx similarity index 95% rename from src/common/features/chats/chats-community-dropdown-menu/index.tsx rename to src/common/features/chats/components/chats-community-dropdown-menu/index.tsx index 07d42ceb806..d0c7125e271 100644 --- a/src/common/features/chats/chats-community-dropdown-menu/index.tsx +++ b/src/common/features/chats/components/chats-community-dropdown-menu/index.tsx @@ -1,10 +1,10 @@ import React, { useContext, useEffect, useState } from "react"; import { History } from "history"; -import DropDown, { MenuItem } from "../../../components/dropdown"; +import DropDown, { MenuItem } from "../../../../components/dropdown"; import useDebounce from "react-use/lib/useDebounce"; -import { chatLeaveSvg, editSVG, kebabMenuSvg, linkSvg, removeUserSvg } from "../../../img/svg"; -import { _t } from "../../../i18n"; +import { chatLeaveSvg, editSVG, kebabMenuSvg, linkSvg, removeUserSvg } from "../../../../img/svg"; +import { _t } from "../../../../i18n"; import { ADDROLE, CHATPAGE, @@ -13,17 +13,17 @@ import { NOSTRKEY, UNBLOCKUSER } from "../chat-popup/chat-constants"; -import { useMappedStore } from "../../../store/use-mapped-store"; -import { Channel, communityModerator } from "../../../../managers/message-manager-types"; -import { error, success } from "../../../components/feedback"; -import LinearProgress from "../../../components/linear-progress"; -import { ROLES } from "../../../store/communities"; -import UserAvatar from "../../../components/user-avatar"; -import { copyToClipboard } from "../utils"; -import { ChatContext } from "../chat-context-provider"; +import { useMappedStore } from "../../../../store/use-mapped-store"; +import { Channel, communityModerator } from "../../../../../managers/message-manager-types"; +import { error, success } from "../../../../components/feedback"; +import LinearProgress from "../../../../components/linear-progress"; +import { ROLES } from "../../../../store/communities"; +import UserAvatar from "../../../../components/user-avatar"; +import { copyToClipboard } from "../../utils"; +import { ChatContext } from "../../chat-context-provider"; import "./index.scss"; -import { getAccountFull } from "../../../api/hive"; +import { getAccountFull } from "../../../../api/hive"; import { Button } from "@ui/button"; import { FormControl, InputGroup } from "@ui/input"; import { Modal, ModalBody, ModalHeader } from "@ui/modal"; diff --git a/src/common/features/chats/chats-confirmation-modal/index.scss b/src/common/features/chats/components/chats-confirmation-modal/index.scss similarity index 99% rename from src/common/features/chats/chats-confirmation-modal/index.scss rename to src/common/features/chats/components/chats-confirmation-modal/index.scss index 167d69933b0..55c7e9b13c0 100644 --- a/src/common/features/chats/chats-confirmation-modal/index.scss +++ b/src/common/features/chats/components/chats-confirmation-modal/index.scss @@ -4,6 +4,7 @@ font-size: 18px; margin-top: 12px; } + .join-community-confirm-buttons { text-align: right; margin-top: 2rem; diff --git a/src/common/features/chats/chats-confirmation-modal/index.tsx b/src/common/features/chats/components/chats-confirmation-modal/index.tsx similarity index 91% rename from src/common/features/chats/chats-confirmation-modal/index.tsx rename to src/common/features/chats/components/chats-confirmation-modal/index.tsx index 115c5ef99b3..a8bdd7306da 100644 --- a/src/common/features/chats/chats-confirmation-modal/index.tsx +++ b/src/common/features/chats/components/chats-confirmation-modal/index.tsx @@ -1,7 +1,7 @@ import React, { useContext, useEffect, useState } from "react"; -import { _t } from "../../../i18n"; -import LinearProgress from "../../../components/linear-progress"; -import { ChatContext } from "../chat-context-provider"; +import { _t } from "../../../../i18n"; +import LinearProgress from "../../../../components/linear-progress"; +import { ChatContext } from "../../chat-context-provider"; import "./index.scss"; import { Button } from "@ui/button"; @@ -13,6 +13,7 @@ interface Props { onClose: () => void; onConfirm: () => void; } + const ChatsConfirmationModal = (props: Props) => { const { onClose, onConfirm, content, actionType } = props; const { hasUserJoinedChat } = useContext(ChatContext); diff --git a/src/common/features/chats/chats-direct-messages/index.scss b/src/common/features/chats/components/chats-direct-messages/index.scss similarity index 99% rename from src/common/features/chats/chats-direct-messages/index.scss rename to src/common/features/chats/components/chats-direct-messages/index.scss index 6a9451effbf..9a4d2fa0182 100644 --- a/src/common/features/chats/chats-direct-messages/index.scss +++ b/src/common/features/chats/components/chats-direct-messages/index.scss @@ -2,26 +2,33 @@ .direct-messages { padding-bottom: 15px; + .custom-divider { .custom-divider-text { margin-bottom: 1rem; font-size: 14px; } } + .receiver { display: flex; + .user-img { padding: 8px 8px 8px 16px; } + .community-user-img { padding: 8px 8px 0px 16px; + .user-avatar.medium { cursor: pointer; } } + .user-info { padding-top: 20px; width: 100%; + .user-msg-time { margin: 0; color: rgb(138, 141, 145); @@ -29,10 +36,12 @@ font-size: 12px; margin-left: 4px; margin-bottom: 2px; + .username-community { margin-right: 8px; } } + .receiver-messag { display: flex; @@ -73,6 +82,7 @@ &.gif { background: none; padding: 0; + img { max-width: 100%; } @@ -81,6 +91,7 @@ &.chat-image { background: none; padding: 0; + img { max-width: 100%; } @@ -91,12 +102,15 @@ .receiver-message-wrapper { background: none; } + .receiver-message-content { padding: 0; + img { padding-bottom: 10px; } } + .receiver-msg-time { @include themify(day) { color: rgb(138, 141, 145); @@ -108,9 +122,11 @@ } } } + &.same-user-msg { margin-left: 4rem; padding-top: 1px; + .receiver-message-wrapper { border-radius: 10px; } @@ -136,6 +152,7 @@ .resend-svg { margin: 5px 15px 0 0; cursor: pointer; + svg { @include themify(day) { @apply fill-gray-600; @@ -148,6 +165,7 @@ .failed-svg { margin: 8px 0 0 5px; + svg { width: 16px; height: 16px; @@ -158,6 +176,7 @@ &.sending { margin-right: 5px; } + &.failed { margin-right: 7px; } @@ -180,6 +199,7 @@ background: none; padding: 0; + img { max-width: 100%; } @@ -188,6 +208,7 @@ &.chat-image { background: none; padding: 0; + img { max-width: 100%; } @@ -198,8 +219,10 @@ .sender-message-time { color: rgb(138, 141, 145); } + .sender-message-content { padding: 10px 0 0 5px; + img { padding-bottom: 10px; } @@ -213,6 +236,7 @@ font-size: 16px; font-weight: 400; padding: 8px 8px 8px 12px; + a { text-decoration: underline; color: white; diff --git a/src/common/features/chats/chats-direct-messages/index.tsx b/src/common/features/chats/components/chats-direct-messages/index.tsx similarity index 94% rename from src/common/features/chats/chats-direct-messages/index.tsx rename to src/common/features/chats/components/chats-direct-messages/index.tsx index 2406948cae1..7f5cb845319 100644 --- a/src/common/features/chats/chats-direct-messages/index.tsx +++ b/src/common/features/chats/components/chats-direct-messages/index.tsx @@ -9,21 +9,21 @@ import { formatMessageTime, isMessageGif, isMessageImage -} from "../utils"; +} from "../../utils"; -import { Theme } from "../../../store/global/types"; -import { DirectMessage } from "../../../../managers/message-manager-types"; +import { Theme } from "../../../../store/global/types"; +import { DirectMessage } from "../../../../../managers/message-manager-types"; -import { useMappedStore } from "../../../store/use-mapped-store"; +import { useMappedStore } from "../../../../store/use-mapped-store"; -import UserAvatar from "../../../components/user-avatar"; -import Tooltip from "../../../components/tooltip"; -import { ChatContext } from "../chat-context-provider"; +import UserAvatar from "../../../../components/user-avatar"; +import Tooltip from "../../../../components/tooltip"; +import { ChatContext } from "../../chat-context-provider"; -import { failedMessageSvg, resendMessageSvg } from "../../../img/svg"; +import { failedMessageSvg, resendMessageSvg } from "../../../../img/svg"; import { renderPostBody } from "@ecency/render-helper"; -import { _t } from "../../../i18n"; +import { _t } from "../../../../i18n"; import "./index.scss"; import ChatsConfirmationModal from "../chats-confirmation-modal"; diff --git a/src/common/features/chats/chats-dropdown-menu/index.tsx b/src/common/features/chats/components/chats-dropdown-menu/index.tsx similarity index 82% rename from src/common/features/chats/chats-dropdown-menu/index.tsx rename to src/common/features/chats/components/chats-dropdown-menu/index.tsx index 5a1ed185dd1..685008f4f5e 100644 --- a/src/common/features/chats/chats-dropdown-menu/index.tsx +++ b/src/common/features/chats/components/chats-dropdown-menu/index.tsx @@ -1,8 +1,8 @@ import React, { useMemo } from "react"; import { History } from "history"; -import DropDown, { MenuItem } from "../../../components/dropdown"; -import { chatKeySvg, kebabMenuSvg } from "../../../img/svg"; -import { _t } from "../../../i18n"; +import DropDown, { MenuItem } from "../../../../components/dropdown"; +import { chatKeySvg, kebabMenuSvg } from "../../../../img/svg"; +import { _t } from "../../../../i18n"; interface Props { history: History | null; diff --git a/src/common/features/chats/chats-messages-box/index.scss b/src/common/features/chats/components/chats-messages-box/index.scss similarity index 99% rename from src/common/features/chats/chats-messages-box/index.scss rename to src/common/features/chats/components/chats-messages-box/index.scss index 7897210be42..1a40327eea0 100644 --- a/src/common/features/chats/chats-messages-box/index.scss +++ b/src/common/features/chats/components/chats-messages-box/index.scss @@ -23,6 +23,7 @@ display: flex; align-items: center; justify-content: center; + .start-chat-wrapper { .start-chat { @include themify(day) { diff --git a/src/common/features/chats/chats-messages-box/index.tsx b/src/common/features/chats/components/chats-messages-box/index.tsx similarity index 92% rename from src/common/features/chats/chats-messages-box/index.tsx rename to src/common/features/chats/components/chats-messages-box/index.tsx index 40491912b07..689429cab48 100644 --- a/src/common/features/chats/chats-messages-box/index.tsx +++ b/src/common/features/chats/components/chats-messages-box/index.tsx @@ -1,17 +1,17 @@ import React, { useContext, useEffect, useState } from "react"; import { match } from "react-router"; import { History } from "history"; -import { Account } from "../../../store/accounts/types"; -import { ToggleType, UI } from "../../../store/ui/types"; -import { User } from "../../../store/users/types"; +import { Account } from "../../../../store/accounts/types"; +import { ToggleType, UI } from "../../../../store/ui/types"; +import { User } from "../../../../store/users/types"; import ChatsMessagesHeader from "../chats-messages-header"; import ChatsMessagesView from "../chats-messages-view"; -import { Channel, ChannelUpdate } from "../../../../managers/message-manager-types"; -import LinearProgress from "../../../components/linear-progress"; -import { formattedUserName, getJoinedCommunities, getProfileMetaData } from "../utils"; -import { useMappedStore } from "../../../store/use-mapped-store"; +import { Channel, ChannelUpdate } from "../../../../../managers/message-manager-types"; +import LinearProgress from "../../../../components/linear-progress"; +import { formattedUserName, getJoinedCommunities, getProfileMetaData } from "../../utils"; +import { useMappedStore } from "../../../../store/use-mapped-store"; import { CHANNEL } from "../chat-popup/chat-constants"; -import { ChatContext } from "../chat-context-provider"; +import { ChatContext } from "../../chat-context-provider"; import { useMount } from "react-use"; import "./index.scss"; diff --git a/src/common/features/chats/chats-messages-header/index.scss b/src/common/features/chats/components/chats-messages-header/index.scss similarity index 99% rename from src/common/features/chats/chats-messages-header/index.scss rename to src/common/features/chats/components/chats-messages-header/index.scss index 90f45cd07d8..e21f0ecc3d6 100644 --- a/src/common/features/chats/chats-messages-header/index.scss +++ b/src/common/features/chats/components/chats-messages-header/index.scss @@ -31,6 +31,7 @@ .user-info-wrapper { .expand-icon { margin-top: 15px; + .expand-svg { height: 35px; width: 35px; @@ -67,6 +68,7 @@ font-weight: 700; margin: 7px 0 0 12px; } + &:hover { border-radius: 10px; @include themify(day) { @@ -90,6 +92,7 @@ margin-top: 14px; cursor: pointer; border-radius: 50%; + svg { @include themify(day) { @apply text-gray-charcoal; @@ -99,6 +102,7 @@ @apply text-light-200; } } + &:hover { @include themify(day) { background: rgba(29, 155, 240, 0.1); diff --git a/src/common/features/chats/chats-messages-header/index.tsx b/src/common/features/chats/components/chats-messages-header/index.tsx similarity index 83% rename from src/common/features/chats/chats-messages-header/index.tsx rename to src/common/features/chats/components/chats-messages-header/index.tsx index 3822892eb5c..76d466c49c5 100644 --- a/src/common/features/chats/chats-messages-header/index.tsx +++ b/src/common/features/chats/components/chats-messages-header/index.tsx @@ -1,14 +1,14 @@ import React, { useContext } from "react"; import { History } from "history"; -import { useMappedStore } from "../../../store/use-mapped-store"; +import { useMappedStore } from "../../../../store/use-mapped-store"; import ChatsCommunityDropdownMenu from "../chats-community-dropdown-menu"; -import UserAvatar from "../../../components/user-avatar"; +import UserAvatar from "../../../../components/user-avatar"; import { CHATPAGE } from "../chat-popup/chat-constants"; -import { Chat } from "../../../store/chat/types"; -import { formattedUserName } from "../utils"; -import Link from "../../../components/alink"; -import { expandSideBar } from "../../../img/svg"; -import { ChatContext } from "../chat-context-provider"; +import { Chat } from "../../../../store/chat/types"; +import { formattedUserName } from "../../utils"; +import Link from "../../../../components/alink"; +import { expandSideBar } from "../../../../img/svg"; +import { ChatContext } from "../../chat-context-provider"; import "./index.scss"; diff --git a/src/common/features/chats/chats-messages-view/index.scss b/src/common/features/chats/components/chats-messages-view/index.scss similarity index 100% rename from src/common/features/chats/chats-messages-view/index.scss rename to src/common/features/chats/components/chats-messages-view/index.scss diff --git a/src/common/features/chats/chats-messages-view/index.tsx b/src/common/features/chats/components/chats-messages-view/index.tsx similarity index 92% rename from src/common/features/chats/chats-messages-view/index.tsx rename to src/common/features/chats/components/chats-messages-view/index.tsx index 4084dca2f87..1eec8163a84 100644 --- a/src/common/features/chats/chats-messages-view/index.tsx +++ b/src/common/features/chats/components/chats-messages-view/index.tsx @@ -1,22 +1,26 @@ import React, { useContext, useEffect, useRef, useState } from "react"; import { Link } from "react-router-dom"; -import { Channel, DirectMessage, PublicMessage } from "../../../../managers/message-manager-types"; -import { fetchCommunityMessages, fetchDirectMessages } from "../utils"; +import { + Channel, + DirectMessage, + PublicMessage +} from "../../../../../managers/message-manager-types"; +import { fetchCommunityMessages, fetchDirectMessages } from "../../utils"; import { History } from "history"; -import { useMappedStore } from "../../../store/use-mapped-store"; +import { useMappedStore } from "../../../../store/use-mapped-store"; import ChatsProfileBox from "../chats-profile-box"; import "./index.scss"; import ChatsChannelMessages from "../chats-channel-messages"; import ChatsDirectMessages from "../chats-direct-messages"; -import { Account } from "../../../store/accounts/types"; -import { ToggleType, UI } from "../../../store/ui/types"; -import { User } from "../../../store/users/types"; +import { Account } from "../../../../store/accounts/types"; +import { ToggleType, UI } from "../../../../store/ui/types"; +import { User } from "../../../../store/users/types"; import ChatInput from "../chat-input"; import ChatsScroller from "../chats-scroller"; import { CHATPAGE } from "../chat-popup/chat-constants"; -import { EmojiPickerStyleProps } from "../types"; -import { ChatContext } from "../chat-context-provider"; -import { classNameObject } from "../../../helper/class-name-object"; +import { EmojiPickerStyleProps } from "../../types"; +import { ChatContext } from "../../chat-context-provider"; +import { classNameObject } from "../../../../helper/class-name-object"; const EmojiPickerStyle: EmojiPickerStyleProps = { width: "56.5%", @@ -27,6 +31,7 @@ const EmojiPickerStyle: EmojiPickerStyleProps = { borderTopRightRadius: "8px", borderBottomLeftRadius: "0px" }; + interface Props { username: string; users: User[]; diff --git a/src/common/features/chats/chats-profile-box/index.scss b/src/common/features/chats/components/chats-profile-box/index.scss similarity index 99% rename from src/common/features/chats/chats-profile-box/index.scss rename to src/common/features/chats/components/chats-profile-box/index.scss index 3fed697a18d..54574a03468 100644 --- a/src/common/features/chats/chats-profile-box/index.scss +++ b/src/common/features/chats/components/chats-profile-box/index.scss @@ -23,6 +23,7 @@ padding-top: 16px; cursor: pointer; } + .user-name { font-size: 20px; font-weight: 700; @@ -33,6 +34,7 @@ color: $white; } } + .about { text-align: center; padding: 4px 0; @@ -43,9 +45,11 @@ @apply text-white; } } + .joining-info { justify-content: space-evenly; } + .created-date { padding-top: 8px; font-size: 14px; @@ -56,6 +60,7 @@ @apply text-white; } } + .followers { font-size: 14px; padding-top: 4px !important; diff --git a/src/common/features/chats/chats-profile-box/index.tsx b/src/common/features/chats/components/chats-profile-box/index.tsx similarity index 91% rename from src/common/features/chats/chats-profile-box/index.tsx rename to src/common/features/chats/components/chats-profile-box/index.tsx index f537a0399a8..0d9f4020944 100644 --- a/src/common/features/chats/chats-profile-box/index.tsx +++ b/src/common/features/chats/components/chats-profile-box/index.tsx @@ -1,10 +1,10 @@ import React, { useEffect, useState } from "react"; -import UserAvatar from "../../../components/user-avatar"; -import { dateToFormatted } from "../../../helper/parse-date"; -import { _t } from "../../../i18n"; -import { getAccountFull } from "../../../api/hive"; -import { formattedUserName } from "../utils"; -import { useCommunityCache } from "../../../core"; +import UserAvatar from "../../../../components/user-avatar"; +import { dateToFormatted } from "../../../../helper/parse-date"; +import { _t } from "../../../../i18n"; +import { getAccountFull } from "../../../../api/hive"; +import { formattedUserName } from "../../utils"; +import { useCommunityCache } from "../../../../core"; import "./index.scss"; diff --git a/src/common/features/chats/chats-scroller/index.scss b/src/common/features/chats/components/chats-scroller/index.scss similarity index 100% rename from src/common/features/chats/chats-scroller/index.scss rename to src/common/features/chats/components/chats-scroller/index.scss diff --git a/src/common/features/chats/chats-scroller/index.tsx b/src/common/features/chats/components/chats-scroller/index.tsx similarity index 89% rename from src/common/features/chats/chats-scroller/index.tsx rename to src/common/features/chats/components/chats-scroller/index.tsx index 8ca89454d4e..8cec4b490d5 100644 --- a/src/common/features/chats/chats-scroller/index.tsx +++ b/src/common/features/chats/components/chats-scroller/index.tsx @@ -1,8 +1,8 @@ import React from "react"; -import Tooltip from "../../../components/tooltip"; +import Tooltip from "../../../../components/tooltip"; -import { _t } from "../../../i18n"; -import { chevronDownSvgForSlider, chevronUpSvg } from "../../../img/svg"; +import { _t } from "../../../../i18n"; +import { chevronDownSvgForSlider, chevronUpSvg } from "../../../../img/svg"; import "./index.scss"; @@ -12,6 +12,7 @@ interface Props { isScrollToBottom: boolean; marginRight: string; } + export default function ChatsScroller(props: Props) { const { bodyRef, isScrollToTop, isScrollToBottom, marginRight } = props; diff --git a/src/common/features/chats/chats-sidebar/indes.tsx b/src/common/features/chats/components/chats-sidebar/indes.tsx similarity index 92% rename from src/common/features/chats/chats-sidebar/indes.tsx rename to src/common/features/chats/components/chats-sidebar/indes.tsx index 450fdae899a..49eea945e66 100644 --- a/src/common/features/chats/chats-sidebar/indes.tsx +++ b/src/common/features/chats/components/chats-sidebar/indes.tsx @@ -2,28 +2,28 @@ import React, { useContext, useEffect, useState } from "react"; import { Link } from "react-router-dom"; import { History } from "history"; import useDebounce from "react-use/lib/useDebounce"; -import { setNostrkeys } from "../../../../managers/message-manager"; -import { Channel } from "../../../../managers/message-manager-types"; -import { getAccountReputations } from "../../../api/hive"; -import accountReputation from "../../../helper/account-reputation"; +import { setNostrkeys } from "../../../../../managers/message-manager"; +import { Channel } from "../../../../../managers/message-manager-types"; +import { getAccountReputations } from "../../../../api/hive"; +import accountReputation from "../../../../helper/account-reputation"; import { formattedUserName, getCommunityLastMessage, getDirectLastMessage, getJoinedCommunities, getUserChatPublicKey -} from "../utils"; -import { _t } from "../../../i18n"; -import { arrowBackSvg, closeSvg, syncSvg } from "../../../img/svg"; +} from "../../utils"; +import { _t } from "../../../../i18n"; +import { arrowBackSvg, closeSvg, syncSvg } from "../../../../img/svg"; import ChatsScroller from "../chats-scroller"; -import LinearProgress from "../../../components/linear-progress"; -import Tooltip from "../../../components/tooltip"; -import UserAvatar from "../../../components/user-avatar"; +import LinearProgress from "../../../../components/linear-progress"; +import Tooltip from "../../../../components/tooltip"; +import UserAvatar from "../../../../components/user-avatar"; import ChatsDropdownMenu from "../chats-dropdown-menu"; -import { AccountWithReputation } from "../types"; -import { useMappedStore } from "../../../store/use-mapped-store"; -import { ChatContext } from "../chat-context-provider"; +import { AccountWithReputation } from "../../types"; +import { useMappedStore } from "../../../../store/use-mapped-store"; +import { ChatContext } from "../../chat-context-provider"; import "./index.scss"; import { FormControl } from "@ui/input"; @@ -32,6 +32,7 @@ interface Props { username: string; history: History; } + export default function ChatsSideBar(props: Props) { const { username } = props; const { chat, resetChat } = useMappedStore(); diff --git a/src/common/features/chats/chats-sidebar/index.scss b/src/common/features/chats/components/chats-sidebar/index.scss similarity index 99% rename from src/common/features/chats/chats-sidebar/index.scss rename to src/common/features/chats/components/chats-sidebar/index.scss index 9f214296045..3be4333425e 100644 --- a/src/common/features/chats/chats-sidebar/index.scss +++ b/src/common/features/chats/components/chats-sidebar/index.scss @@ -16,6 +16,7 @@ .close-sidebar { margin: 10px 5px 0 0; + svg { width: 30px; height: 30px; @@ -64,6 +65,7 @@ .chat-actions { .refresh-button { margin-top: 2px; + .refresh-svg { width: 40px; height: 40px; @@ -78,6 +80,7 @@ height: 20px; width: 20px; } + &:hover { @include themify(day) { background: rgba(29, 155, 240, 0.1); @@ -88,6 +91,7 @@ } } } + .chat-menu { border: none; width: 40px; @@ -98,6 +102,7 @@ margin-top: 3px; cursor: pointer; border-radius: 50%; + svg { @include themify(day) { @apply text-gray-charcoal; @@ -107,6 +112,7 @@ @apply text-light-200; } } + &:hover { @include themify(day) { background: rgba(29, 155, 240, 0.1); @@ -138,11 +144,13 @@ cursor: pointer; padding: 15px 3px 15px 25px; } + .user-name { font-size: 20px; font-weight: 700; margin: 7px 5px 0 15px; } + .user-reputation { margin: 9px 5px 0 5px; } @@ -186,6 +194,7 @@ font-size: 18px; font-weight: 700; } + .dm-last-message, .community-last-message { max-width: 255px; @@ -197,6 +206,7 @@ font-size: 13px; } } + &.selected { @include themify(day) { background: #ecf3ff; @@ -221,6 +231,7 @@ .chats-list { height: calc(100vh - 220px); overflow: auto; + a { color: inherit; } @@ -234,27 +245,33 @@ .chats-title { margin: 15px 0px 5px 5px; } + .chats-search { padding: 0 5px 0 5px; margin-bottom: 0; } + .chats-list { overflow-x: hidden; + .community-title, .dm-title { font-size: 1rem; padding: 15px 0 10px 8px; } + .community, .dm { margin: 0; padding: 9.5px 0px 9.5px 5px; + .community-info, .dm-info { .community-name, .dm-name { padding: 3px 0 0 10px; } + .community-last-message, .dm-last-message { padding: 4px 0 0 10px; diff --git a/src/common/features/chats/import-chats/index.scss b/src/common/features/chats/components/import-chats/index.scss similarity index 100% rename from src/common/features/chats/import-chats/index.scss rename to src/common/features/chats/components/import-chats/index.scss diff --git a/src/common/features/chats/import-chats/index.tsx b/src/common/features/chats/components/import-chats/index.tsx similarity index 85% rename from src/common/features/chats/import-chats/index.tsx rename to src/common/features/chats/components/import-chats/index.tsx index 8cf512de746..31e1e174e52 100644 --- a/src/common/features/chats/import-chats/index.tsx +++ b/src/common/features/chats/components/import-chats/index.tsx @@ -1,15 +1,15 @@ import React, { useContext, useEffect, useState } from "react"; -import { getPublicKey } from "../../../../lib/nostr-tools/keys"; -import { _t } from "../../../i18n"; -import { keySvg } from "../../../img/svg"; -import { useMappedStore } from "../../../store/use-mapped-store"; -import OrDivider from "../../../components/or-divider"; -import * as ls from "../../../util/local-storage"; -import { ChatContext } from "../chat-context-provider"; -import { setNostrkeys } from "../../../../managers/message-manager"; +import { getPublicKey } from "../../../../../lib/nostr-tools/keys"; +import { _t } from "../../../../i18n"; +import { keySvg } from "../../../../img/svg"; +import { useMappedStore } from "../../../../store/use-mapped-store"; +import OrDivider from "../../../../components/or-divider"; +import * as ls from "../../../../util/local-storage"; +import { ChatContext } from "../../chat-context-provider"; +import { setNostrkeys } from "../../../../../managers/message-manager"; import "./index.scss"; -import LinearProgress from "../../../components/linear-progress"; +import LinearProgress from "../../../../components/linear-progress"; import ChatsConfirmationModal from "../chats-confirmation-modal"; import { Button } from "@ui/button"; import { Form } from "@ui/form"; diff --git a/src/common/features/chats/join-chat/index.tsx b/src/common/features/chats/components/join-chat/index.tsx similarity index 89% rename from src/common/features/chats/join-chat/index.tsx rename to src/common/features/chats/components/join-chat/index.tsx index 47ba2890b8d..2c066698a72 100644 --- a/src/common/features/chats/join-chat/index.tsx +++ b/src/common/features/chats/components/join-chat/index.tsx @@ -1,8 +1,8 @@ import React, { useContext, useEffect, useState } from "react"; -import { ChatContext } from "../chat-context-provider"; +import { ChatContext } from "../../chat-context-provider"; import { Button } from "@ui/button"; import { Spinner } from "@ui/spinner"; -import { _t } from "../../../i18n"; +import { _t } from "../../../../i18n"; export default function JoinChat() { const { messageServiceInstance, joinChat } = useContext(ChatContext); diff --git a/src/common/features/chats/join-community-chat-btn/index.tsx b/src/common/features/chats/components/join-community-chat-btn/index.tsx similarity index 94% rename from src/common/features/chats/join-community-chat-btn/index.tsx rename to src/common/features/chats/components/join-community-chat-btn/index.tsx index 34342e34718..4299a3954d1 100644 --- a/src/common/features/chats/join-community-chat-btn/index.tsx +++ b/src/common/features/chats/components/join-community-chat-btn/index.tsx @@ -1,12 +1,12 @@ import React, { useContext, useEffect, useState } from "react"; import { History } from "history"; -import { Community, ROLES } from "../../../store/communities"; -import { _t } from "../../../i18n"; -import { useMappedStore } from "../../../store/use-mapped-store"; -import { Channel, communityModerator } from "../../../../managers/message-manager-types"; -import { ChatContext } from "../chat-context-provider"; +import { Community, ROLES } from "../../../../store/communities"; +import { _t } from "../../../../i18n"; +import { useMappedStore } from "../../../../store/use-mapped-store"; +import { Channel, communityModerator } from "../../../../../managers/message-manager-types"; +import { ChatContext } from "../../chat-context-provider"; import { CHANNEL, NOSTRKEY } from "../chat-popup/chat-constants"; -import { getProfileMetaData, setChannelMetaData } from "../utils"; +import { getProfileMetaData, setChannelMetaData } from "../../utils"; import { Spinner } from "@ui/spinner"; import { Button } from "@ui/button"; diff --git a/src/common/features/chats/manage-chat-key/index.scss b/src/common/features/chats/components/manage-chat-key/index.scss similarity index 99% rename from src/common/features/chats/manage-chat-key/index.scss rename to src/common/features/chats/components/manage-chat-key/index.scss index cea50e61b95..43861fbf647 100644 --- a/src/common/features/chats/manage-chat-key/index.scss +++ b/src/common/features/chats/components/manage-chat-key/index.scss @@ -8,6 +8,7 @@ .expand-icon { margin: 13px 15px 0 0; + .expand-svg { height: 35px; width: 35px; @@ -27,12 +28,14 @@ } } } + .chat-priv-key { width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + .copy-svg { margin-top: 3px; margin-left: 2px; diff --git a/src/common/features/chats/manage-chat-key/index.tsx b/src/common/features/chats/components/manage-chat-key/index.tsx similarity index 88% rename from src/common/features/chats/manage-chat-key/index.tsx rename to src/common/features/chats/components/manage-chat-key/index.tsx index aa8cc86e9fa..3078b25dac9 100644 --- a/src/common/features/chats/manage-chat-key/index.tsx +++ b/src/common/features/chats/components/manage-chat-key/index.tsx @@ -1,12 +1,12 @@ import React, { useContext, useEffect, useRef, useState } from "react"; -import { _t } from "../../../i18n"; +import { _t } from "../../../../i18n"; import { History } from "history"; -import { expandSideBar } from "../../../img/svg"; -import { ChatContext } from "../chat-context-provider"; +import { expandSideBar } from "../../../../img/svg"; +import { ChatContext } from "../../chat-context-provider"; import "./index.scss"; import { InputGroupCopyClipboard } from "@ui/input"; import qrcode from "qrcode"; -import { classNameObject } from "../../../helper/class-name-object"; +import { classNameObject } from "../../../../helper/class-name-object"; interface Props { history: History; diff --git a/src/common/features/chats/mutations/index.ts b/src/common/features/chats/mutations/index.ts new file mode 100644 index 00000000000..ab6125af0b0 --- /dev/null +++ b/src/common/features/chats/mutations/index.ts @@ -0,0 +1 @@ +export * from "./upload"; diff --git a/src/common/features/chats/mutations/upload.ts b/src/common/features/chats/mutations/upload.ts new file mode 100644 index 00000000000..23cbe8e3e31 --- /dev/null +++ b/src/common/features/chats/mutations/upload.ts @@ -0,0 +1,66 @@ +import { useMutation } from "@tanstack/react-query"; +import { getAccessToken } from "../../../helper/user-token"; +import { uploadImage } from "../../../api/misc"; +import { addImage } from "../../../api/private-api"; +import { error } from "../../../components/feedback"; +import { _t } from "../../../i18n"; +import axios from "axios"; +import { useMappedStore } from "../../../store/use-mapped-store"; + +class FileUploadingError { + constructor(public code: number, public message: string) {} +} + +export function useChatFileUpload( + setMessage: (v: string) => void, + setIsMessageText: (v: boolean) => void +) { + const { activeUser, global } = useMappedStore(); + + return useMutation( + ["chat-file-upload"], + async (file: File) => { + const username = activeUser?.username; + + if (!username) { + throw new FileUploadingError(0, "[Chat][File uploading] No user"); + } + + const token = getAccessToken(username); + + if (!token) { + throw new FileUploadingError(1, "[Chat][File uploading] No token"); + } + + const tempImgTag = `![Uploading ${file.name} #${Math.floor(Math.random() * 99)}]()\n\n`; + setMessage(tempImgTag); + + let imageUrl: string; + const resp = await uploadImage(file, token); + imageUrl = resp.url; + + if (global.usePrivate && imageUrl.length > 0) { + await addImage(username, imageUrl); + } + + const imgTag = imageUrl.length > 0 && `![](${imageUrl})\n\n`; + if (imgTag) { + setMessage(imgTag); + } + + setIsMessageText(true); + }, + { + onError: (e) => { + if (axios.isAxiosError(e) && e.response?.status === 413) { + error(_t("editor-toolbar.image-error-size")); + } else if (e instanceof FileUploadingError) { + error(_t("editor-toolbar.image-error-cache")); + throw new Error(e.message); + } else { + error(_t("editor-toolbar.image-error")); + } + } + } + ); +} diff --git a/src/common/features/chats/utils/is-message-gif.ts b/src/common/features/chats/utils/is-message-gif.ts index e3e2ee5b823..77d8fda9817 100644 --- a/src/common/features/chats/utils/is-message-gif.ts +++ b/src/common/features/chats/utils/is-message-gif.ts @@ -1,4 +1,4 @@ -import { GIPHGY } from "../chat-popup/chat-constants"; +import { GIPHGY } from "../components/chat-popup/chat-constants"; export const isMessageGif = (content: string) => { return content.includes(GIPHGY); diff --git a/src/common/pages/chats/index.tsx b/src/common/pages/chats/index.tsx index 53f729c429e..58a8608571f 100644 --- a/src/common/pages/chats/index.tsx +++ b/src/common/pages/chats/index.tsx @@ -4,15 +4,15 @@ import { match } from "react-router"; import NavBar from "../../components/navbar"; import NavBarElectron from "../../../desktop/app/components/navbar"; import { pageMapDispatchToProps, pageMapStateToProps, PageProps } from "../common"; -import ChatsSideBar from "../../features/chats/chats-sidebar/indes"; -import ChatsMessagesBox from "../../features/chats/chats-messages-box"; +import ChatsSideBar from "../../features/chats/components/chats-sidebar/indes"; +import ChatsMessagesBox from "../../features/chats/components/chats-messages-box"; -import ManageChatKey from "../../features/chats/manage-chat-key"; +import ManageChatKey from "../../features/chats/components/manage-chat-key"; import Feedback from "../../components/feedback"; import { useMappedStore } from "../../store/use-mapped-store"; import { ChatContext } from "../../features/chats/chat-context-provider"; -import ImportChats from "../../features/chats/import-chats"; -import JoinChat from "../../features/chats/join-chat"; +import ImportChats from "../../features/chats/components/import-chats"; +import JoinChat from "../../features/chats/components/join-chat"; import "./index.scss"; import { Spinner } from "@ui/spinner"; From 9b2fc5f57ef04801b1a1d916285c07e14812acf8 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Fri, 20 Oct 2023 23:17:36 +0600 Subject: [PATCH 099/179] Chat: improved sidebar UI --- package.json | 3 +- src/common/app.tsx | 1 + .../__snapshots__/index.spec.tsx.snap | 2 +- .../components/community-settings/index.tsx | 2 +- .../components/emoji-picker/_index.scss | 1 - src/common/components/emoji-picker/index.tsx | 17 +- .../components/entry-list-item/index.tsx | 2 +- src/common/components/gif-picker/index.tsx | 13 +- .../video-upload-threespeak/index.tsx | 2 +- .../components/wallet-hive-engine/index.tsx | 6 +- .../chats/components/chat-input/index.scss | 70 ---- .../chats/components/chat-input/index.tsx | 275 +++++++--------- .../chats/components/chat-popup/index.tsx | 25 +- .../chats-channel-messages/index.tsx | 14 +- .../chats-community-dropdown-menu/index.tsx | 10 +- .../chats-direct-messages/index.tsx | 2 +- .../chats-messages-header/index.tsx | 4 +- .../components/chats-messages-view/index.tsx | 1 - .../chat-sidebar-direct-contact.tsx | 56 ++++ .../chats-sidebar/chat-sidebar-header.tsx | 67 ++++ .../chat-sidebar-search-item.tsx | 24 ++ .../chats-sidebar/chat-sidebar-search.tsx | 51 +++ .../chats/components/chats-sidebar/indes.tsx | 304 ------------------ .../chats/components/chats-sidebar/index.scss | 284 ---------------- .../chats/components/chats-sidebar/index.tsx | 180 +++++++++++ .../chats/components/import-chats/index.tsx | 2 +- .../features/chats/utils/get-relative-date.ts | 20 ++ src/common/features/chats/utils/index.ts | 1 + .../utils/use-get-direct-last-message.ts | 2 +- src/common/features/ui/button/props.ts | 1 + src/common/features/ui/button/styles.ts | 6 +- .../features/ui/dropdown/dropdown-item.tsx | 15 +- .../features/ui/dropdown/dropdown-menu.tsx | 6 +- src/common/features/ui/input/input-group.tsx | 16 +- src/common/i18n/locales/en-US.json | 2 + src/common/img/svg.tsx | 53 ++- src/common/pages/chats.tsx | 111 +++++++ src/common/pages/chats/index.scss | 27 -- src/common/pages/chats/index.tsx | 127 -------- src/common/pages/onboard.tsx | 2 +- yarn.lock | 19 ++ 41 files changed, 784 insertions(+), 1042 deletions(-) delete mode 100644 src/common/features/chats/components/chat-input/index.scss create mode 100644 src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx create mode 100644 src/common/features/chats/components/chats-sidebar/chat-sidebar-header.tsx create mode 100644 src/common/features/chats/components/chats-sidebar/chat-sidebar-search-item.tsx create mode 100644 src/common/features/chats/components/chats-sidebar/chat-sidebar-search.tsx delete mode 100644 src/common/features/chats/components/chats-sidebar/indes.tsx delete mode 100644 src/common/features/chats/components/chats-sidebar/index.scss create mode 100644 src/common/features/chats/components/chats-sidebar/index.tsx create mode 100644 src/common/features/chats/utils/get-relative-date.ts create mode 100644 src/common/pages/chats.tsx delete mode 100644 src/common/pages/chats/index.scss delete mode 100644 src/common/pages/chats/index.tsx diff --git a/package.json b/package.json index 2b44729d520..9deee56246d 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,8 @@ "@hiveio/hivescript": "^1.2.7", "@loadable/component": "^5.15.2", "@loadable/server": "^5.15.2", - "@popperjs/core": "^2.11.8", "@noble/secp256k1": "^1.7.1", + "@popperjs/core": "^2.11.8", "@tanstack/react-query": "^4.29.7", "@tanstack/react-query-devtools": "^4.29.7", "@types/tus-js-client": "^2.1.0", @@ -36,6 +36,7 @@ "connected-react-router": "^6.8.0", "cookie-parser": "^1.4.5", "currency-symbol-map": "^4.0.4", + "date-fns": "^2.30.0", "debounce": "^1.2.1", "diff-match-patch": "^1.0.5", "emoji-mart": "^5.5.2", diff --git a/src/common/app.tsx b/src/common/app.tsx index cd2686c37d6..0af333a3be2 100644 --- a/src/common/app.tsx +++ b/src/common/app.tsx @@ -202,6 +202,7 @@ const App = (props: any) => { -
+
diff --git a/src/common/components/emoji-picker/_index.scss b/src/common/components/emoji-picker/_index.scss index fddfd5d5a44..9978e348bdd 100644 --- a/src/common/components/emoji-picker/_index.scss +++ b/src/common/components/emoji-picker/_index.scss @@ -3,7 +3,6 @@ .emoji-picker-dialog { position: absolute; z-index: 201; - top: 100%; em-emoji-picker { width: 300px; diff --git a/src/common/components/emoji-picker/index.tsx b/src/common/components/emoji-picker/index.tsx index ae2895eaa87..cbba7cf3f0f 100644 --- a/src/common/components/emoji-picker/index.tsx +++ b/src/common/components/emoji-picker/index.tsx @@ -5,10 +5,12 @@ import { v4 } from "uuid"; import Picker from "@emoji-mart/react"; import { useMappedStore } from "../../store/use-mapped-store"; import useClickAway from "react-use/lib/useClickAway"; +import { classNameObject } from "../../helper/class-name-object"; interface Props { - anchor: HTMLElement | null; + anchor: Element | null; onSelect: (e: string) => void; + position?: "top" | "bottom"; } /** @@ -16,15 +18,16 @@ interface Props { * * @param {Props} anchor - The anchor element to position the picker relative to. * @param {function} onSelect - The callback function to be called when an emoji is selected. + * @param position * @return The rendered emoji picker dialog. */ -export function EmojiPicker({ anchor, onSelect }: Props) { +export function EmojiPicker({ anchor, onSelect, position }: Props) { const ref = useRef(null); const { global } = useMappedStore(); const [show, setShow] = useState(false); - const [position, setPosition] = useState({ x: 0, y: 0 }); + // Due to ability to hold multiple dialogs we have to identify them const dialogId = useMemo(() => v4(), []); @@ -37,7 +40,7 @@ export function EmojiPicker({ anchor, onSelect }: Props) { useEffect(() => { if (anchor) { anchor.addEventListener("click", () => { - anchor.style.position = "relative !important"; + (anchor as HTMLElement).style.position = "relative !important"; setShow(true); }); } @@ -48,7 +51,11 @@ export function EmojiPicker({ anchor, onSelect }: Props) { ref={ref} id={dialogId} key={dialogId} - className="emoji-picker-dialog" + className={classNameObject({ + "emoji-picker-dialog": true, + "top-[100%]": (position ?? "bottom") === "bottom", + "bottom-[100%] right-0": position === "top" + })} style={{ display: show ? "block" : "none" }} diff --git a/src/common/components/entry-list-item/index.tsx b/src/common/components/entry-list-item/index.tsx index 24b3d911cb5..ffa0bbc94ef 100644 --- a/src/common/components/entry-list-item/index.tsx +++ b/src/common/components/entry-list-item/index.tsx @@ -288,7 +288,7 @@ export function EntryListItem({
- +
{volumeOffSvg}
{_t("g.muted")}
diff --git a/src/common/components/gif-picker/index.tsx b/src/common/components/gif-picker/index.tsx index 7d6b6fd7cdb..f590be2b771 100644 --- a/src/common/components/gif-picker/index.tsx +++ b/src/common/components/gif-picker/index.tsx @@ -6,6 +6,7 @@ import { insertOrReplace } from "../../util/input-util"; import _ from "lodash"; import { fetchGif } from "../../api/misc"; import "./_index.scss"; + interface Props { fallback?: (e: string) => void; shGif: boolean; @@ -60,6 +61,7 @@ export default class GifPicker extends BaseComponent { this.delayedSearchOnScroll(filter, limit, offset + 50); } }; + componentDidMount() { const gifWrapper = document.querySelector(".emoji-picker"); gifWrapper?.addEventListener("scroll", this.handleScroll); @@ -111,6 +113,9 @@ export default class GifPicker extends BaseComponent { }; this.stateSet(_data); }; + + delayedSearch = _.debounce(this.getSearchedData, 2000); + getSearchedDataOnScroll = async (_filter: string | null, limit: string, offset: string) => { const { data } = await fetchGif(_filter, limit, offset); if (_filter?.length) { @@ -126,6 +131,8 @@ export default class GifPicker extends BaseComponent { } }; + delayedSearchOnScroll = _.debounce(this.getSearchedDataOnScroll, 2000); + getGifsData = async (_filter: string | null, limit: string, offset: string) => { const { data } = await fetchGif(_filter, limit, offset); let _data: State = { @@ -138,6 +145,7 @@ export default class GifPicker extends BaseComponent { }; this.stateSet(_data); }; + getGifsDataOnScroll = async (_filter: string | null, limit: string, offset: string) => { const { data } = await fetchGif(_filter, limit, offset); let _data: State = { @@ -174,9 +182,6 @@ export default class GifPicker extends BaseComponent { this.props.changeState(!this.props.shGif); }; - delayedSearch = _.debounce(this.getSearchedData, 2000); - delayedSearchOnScroll = _.debounce(this.getSearchedDataOnScroll, 2000); - filterChanged = (e: React.ChangeEvent) => { this.setState({ filter: e.target.value }); if (e.target.value === "") { @@ -248,7 +253,7 @@ export default class GifPicker extends BaseComponent { ); } })()} - {_t("gif-picker.credits")} + {_t("gif-picker.credits")}
); } diff --git a/src/common/components/video-upload-threespeak/index.tsx b/src/common/components/video-upload-threespeak/index.tsx index 30758c074ae..a273692f906 100644 --- a/src/common/components/video-upload-threespeak/index.tsx +++ b/src/common/components/video-upload-threespeak/index.tsx @@ -186,7 +186,7 @@ export const VideoUpload = (props: Props & React.HTMLAttributes)
-
+
- + setMessage((prevMessage) => prevMessage + e)} + /> +
+
+ + + {showGifPicker && ( + setShowGifPicker(gifState!)} + fallback={(e) => + isCurrentUser + ? messageServiceInstance?.sendDirectMessage(receiverPubKey!, e) + : messageServiceInstance?.sendPublicMessage(currentChannel, e, [], "") + } + /> + )} +
); } diff --git a/src/common/features/chats/components/chat-popup/index.tsx b/src/common/features/chats/components/chat-popup/index.tsx index b65e6b1f94d..808146eb91b 100644 --- a/src/common/features/chats/components/chat-popup/index.tsx +++ b/src/common/features/chats/components/chat-popup/index.tsx @@ -13,7 +13,7 @@ import { setNostrkeys } from "../../../../../managers/message-manager"; import ManageChatKey from "../manage-chat-key"; import ChatInput from "../chat-input"; import { chevronDownSvgForSlider, chevronUpSvg } from "../../../../img/svg"; -import { EmojiPickerStyle, GifPickerStyle } from "./chat-constants"; +import { GifPickerStyle } from "./chat-constants"; import { _t } from "../../../../i18n"; import { usePrevious } from "../../../../util/use-previous"; import { getCommunity } from "../../../../api/bridge"; @@ -472,17 +472,18 @@ export const ChatPopUp = () => { )}
- {(isCurrentUser || isCommunity) && ( - - )} +
+ {(isCurrentUser || isCommunity) && ( + + )} +
)} diff --git a/src/common/features/chats/components/chats-channel-messages/index.tsx b/src/common/features/chats/components/chats-channel-messages/index.tsx index 936bb00f821..ff7803cf804 100644 --- a/src/common/features/chats/components/chats-channel-messages/index.tsx +++ b/src/common/features/chats/components/chats-channel-messages/index.tsx @@ -312,17 +312,17 @@ export default function ChatsChannelMessages(props: Props) {
}>
-
+
-

{`@${name!}`}

+

{`@${name!}`}

{dayAndMonth && (
- + {dayAndMonth}
@@ -570,7 +570,7 @@ export default function ChatsChannelMessages(props: Props) { ); })} {isActveUserRemoved && ( - + You have been blocked from this community )} diff --git a/src/common/features/chats/components/chats-community-dropdown-menu/index.tsx b/src/common/features/chats/components/chats-community-dropdown-menu/index.tsx index d0c7125e271..522be38a54b 100644 --- a/src/common/features/chats/components/chats-community-dropdown-menu/index.tsx +++ b/src/common/features/chats/components/chats-community-dropdown-menu/index.tsx @@ -293,7 +293,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => {
-
+
- + {" "} @{moderator.name} @@ -380,7 +380,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => { return (
- + {" "} @{user.name} @@ -417,7 +417,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => { const successModal = (message: string) => { return ( <> -
+
2
{_t("manage-authorities.success-title")}
@@ -428,7 +428,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => {
{message === UNBLOCKUSER ? "User unblock successfully" : ""}
-
+
diff --git a/src/common/features/chats/components/chats-direct-messages/index.tsx b/src/common/features/chats/components/chats-direct-messages/index.tsx index 7f5cb845319..ef56e9e9c02 100644 --- a/src/common/features/chats/components/chats-direct-messages/index.tsx +++ b/src/common/features/chats/components/chats-direct-messages/index.tsx @@ -116,7 +116,7 @@ export default function ChatsDirectMessages(props: Props) { {dayAndMonth && (
- + {dayAndMonth}
diff --git a/src/common/features/chats/components/chats-messages-header/index.tsx b/src/common/features/chats/components/chats-messages-header/index.tsx index 76d466c49c5..f75b021b311 100644 --- a/src/common/features/chats/components/chats-messages-header/index.tsx +++ b/src/common/features/chats/components/chats-messages-header/index.tsx @@ -41,8 +41,8 @@ export default function ChatsMessagesHeader(props: Props) { return (
-
-
+
+

setShowSideBar(true)}> {expandSideBar} diff --git a/src/common/features/chats/components/chats-messages-view/index.tsx b/src/common/features/chats/components/chats-messages-view/index.tsx index 1eec8163a84..8cee37ae185 100644 --- a/src/common/features/chats/components/chats-messages-view/index.tsx +++ b/src/common/features/chats/components/chats-messages-view/index.tsx @@ -221,7 +221,6 @@ export default function ChatsMessagesView(props: Props) { )}

void; + handleSideBar: () => void; +} + +export function ChatSidebarDirectContact({ + contact, + username, + handleRevealPrivKey, + handleSideBar +}: Props) { + const { setReceiverPubKey } = useContext(ChatContext); + const { chat } = useMappedStore(); + + const lastMessage = useMemo( + () => getDirectLastMessage(contact.pubkey, chat.directMessages), + [chat, contact] + ); + const lastMessageDate = useMemo(() => getRelativeDate(lastMessage?.created), [lastMessage]); + + return ( + { + setReceiverPubKey(contact.pubkey); + handleSideBar(); + handleRevealPrivKey(); + }} + > + +
+
+
{contact.name}
+
{lastMessageDate}
+
+
{lastMessage?.content}
+
+ + ); +} diff --git a/src/common/features/chats/components/chats-sidebar/chat-sidebar-header.tsx b/src/common/features/chats/components/chats-sidebar/chat-sidebar-header.tsx new file mode 100644 index 00000000000..ea55f820405 --- /dev/null +++ b/src/common/features/chats/components/chats-sidebar/chat-sidebar-header.tsx @@ -0,0 +1,67 @@ +import { Button } from "@ui/button"; +import { arrowBackSvg, closeSvg, syncSvg } from "../../../../img/svg"; +import Tooltip from "../../../../components/tooltip"; +import { _t } from "../../../../i18n"; +import ChatsDropdownMenu from "../chats-dropdown-menu"; +import React, { useContext } from "react"; +import { ChatContext } from "../../chat-context-provider"; +import { setNostrkeys } from "../../../../../managers/message-manager"; +import { useMappedStore } from "../../../../store/use-mapped-store"; +import { History } from "history"; + +interface Props { + history: History; +} + +export function ChatSidebarHeader({ history }: Props) { + const { resetChat } = useMappedStore(); + const { + activeUserKeys, + setShowSpinner, + revealPrivKey, + chatPrivKey, + windowWidth, + setShowSideBar, + setRevealPrivKey + } = useContext(ChatContext); + + const handleRefreshChat = () => { + resetChat(); + setNostrkeys(activeUserKeys!); + setShowSpinner(true); + }; + + return ( +
+
+ {windowWidth < 768 && ( +
+
+ +
+
+ ); +} diff --git a/src/common/features/chats/components/chats-sidebar/chat-sidebar-search-item.tsx b/src/common/features/chats/components/chats-sidebar/chat-sidebar-search-item.tsx new file mode 100644 index 00000000000..70a9181f14e --- /dev/null +++ b/src/common/features/chats/components/chats-sidebar/chat-sidebar-search-item.tsx @@ -0,0 +1,24 @@ +import accountReputation from "../../../../helper/account-reputation"; +import { Link } from "react-router-dom"; +import React from "react"; +import { AccountWithReputation } from "../../types"; +import UserAvatar from "../../../../components/user-avatar"; + +interface Props { + user: AccountWithReputation; + onClick: () => void; +} + +export function ChatSidebarSearchItem({ user, onClick }: Props) { + return ( + + + {user.account} + ({accountReputation(user.reputation)}) + + ); +} diff --git a/src/common/features/chats/components/chats-sidebar/chat-sidebar-search.tsx b/src/common/features/chats/components/chats-sidebar/chat-sidebar-search.tsx new file mode 100644 index 00000000000..428339c0d1f --- /dev/null +++ b/src/common/features/chats/components/chats-sidebar/chat-sidebar-search.tsx @@ -0,0 +1,51 @@ +import { FormControl } from "@ui/input"; +import { _t } from "../../../../i18n"; +import React, { useState } from "react"; +import useDebounce from "react-use/lib/useDebounce"; +import { getAccountReputations } from "../../../../api/hive"; +import { AccountWithReputation } from "../../types"; +import LinearProgress from "../../../../components/linear-progress"; + +interface Props { + searchQuery: string; + setSearchQuery: (v: string) => void; + setUserList: (v: AccountWithReputation[]) => void; +} + +export function ChatSidebarSearch({ setUserList, searchQuery, setSearchQuery }: Props) { + const [searchInProgress, setSearchInProgress] = useState(false); + + useDebounce( + async () => { + if (searchQuery.length !== 0) { + const resp = await getAccountReputations(searchQuery, 30); + const sortedByReputation = resp.sort((a, b) => (a.reputation > b.reputation ? -1 : 1)); + setUserList(sortedByReputation); + setSearchInProgress(false); + } + }, + 500, + [searchQuery] + ); + + return ( + <> +
+ { + setSearchQuery(e.target.value); + setSearchInProgress(true); + if (e.target.value.length === 0) { + setSearchInProgress(false); + setUserList([]); + } + }} + /> +
+ {searchInProgress && } + + ); +} diff --git a/src/common/features/chats/components/chats-sidebar/indes.tsx b/src/common/features/chats/components/chats-sidebar/indes.tsx deleted file mode 100644 index 49eea945e66..00000000000 --- a/src/common/features/chats/components/chats-sidebar/indes.tsx +++ /dev/null @@ -1,304 +0,0 @@ -import React, { useContext, useEffect, useState } from "react"; -import { Link } from "react-router-dom"; -import { History } from "history"; -import useDebounce from "react-use/lib/useDebounce"; -import { setNostrkeys } from "../../../../../managers/message-manager"; -import { Channel } from "../../../../../managers/message-manager-types"; -import { getAccountReputations } from "../../../../api/hive"; -import accountReputation from "../../../../helper/account-reputation"; -import { - formattedUserName, - getCommunityLastMessage, - getDirectLastMessage, - getJoinedCommunities, - getUserChatPublicKey -} from "../../utils"; -import { _t } from "../../../../i18n"; -import { arrowBackSvg, closeSvg, syncSvg } from "../../../../img/svg"; -import ChatsScroller from "../chats-scroller"; -import LinearProgress from "../../../../components/linear-progress"; -import Tooltip from "../../../../components/tooltip"; -import UserAvatar from "../../../../components/user-avatar"; - -import ChatsDropdownMenu from "../chats-dropdown-menu"; -import { AccountWithReputation } from "../../types"; -import { useMappedStore } from "../../../../store/use-mapped-store"; -import { ChatContext } from "../../chat-context-provider"; - -import "./index.scss"; -import { FormControl } from "@ui/input"; - -interface Props { - username: string; - history: History; -} - -export default function ChatsSideBar(props: Props) { - const { username } = props; - const { chat, resetChat } = useMappedStore(); - const chatContext = useContext(ChatContext); - const { channels, directContacts, leftChannelsList } = chat; - - const { - activeUserKeys, - revealPrivKey, - chatPrivKey, - showSideBar, - windowWidth, - setShowSideBar, - setShowSpinner, - setRevealPrivKey, - setReceiverPubKey - } = chatContext; - - const chatsSideBarRef = React.createRef(); - - const [showDivider, setShowDivider] = useState(false); - const [searchText, setSearchText] = useState(""); - const [searchInProgress, setSearchInProgress] = useState(false); - const [userList, setUserList] = useState([]); - const [isScrollToTop, setIsScrollToTop] = useState(false); - const [communities, setCommunities] = useState([]); - - useDebounce( - async () => { - if (searchText.length !== 0) { - const resp = await getAccountReputations(searchText, 30); - const sortedByReputation = resp.sort((a, b) => (a.reputation > b.reputation ? -1 : 1)); - setUserList(sortedByReputation); - setSearchInProgress(false); - } - }, - 500, - [searchText] - ); - - useEffect(() => { - if (username) { - if (username.startsWith("@")) { - getReceiverPubKey(formattedUserName(username)); - } - } - }, [username]); - - useEffect(() => { - const communities = getJoinedCommunities(channels, leftChannelsList); - setCommunities(communities); - }, [channels, leftChannelsList]); - - const handleScroll = (event: React.UIEvent) => { - var element = event.currentTarget; - if (element.scrollTop > 2) { - setShowDivider(true); - } else { - if (showDivider) { - setShowDivider(false); - } - } - let srollHeight: number = (element.scrollHeight / 100) * 25; - const isScrollToTop = element.scrollTop >= srollHeight; - setIsScrollToTop(isScrollToTop); - }; - - const handleRefreshChat = () => { - resetChat(); - setNostrkeys(activeUserKeys!); - setShowSpinner(true); - }; - - const handleRevealPrivKey = () => { - if (revealPrivKey) { - setRevealPrivKey(false); - } - }; - - const getReceiverPubKey = async (username: string) => { - const peer = directContacts.find((x) => x.name === username)?.pubkey ?? ""; - if (peer) { - setReceiverPubKey(peer); - } else { - const pubkey = await getUserChatPublicKey(username); - if (pubkey === undefined) { - setReceiverPubKey(""); - } else { - setReceiverPubKey(pubkey); - } - } - }; - - const handleSideBar = () => { - if (windowWidth < 768 && showSideBar) { - setShowSideBar(false); - } - }; - - return ( - <> - {showSideBar && ( -
- {windowWidth < 768 && ( -
setShowSideBar(false)} - > - {closeSvg} -
- )} - -
-
- {revealPrivKey && windowWidth > 768 && ( - -
setRevealPrivKey(false)} - > - {arrowBackSvg} -
-
- )} - -

Chats

-
- -
-
- -

- {syncSvg} -

-
-
- {chatPrivKey && ( -
- { - setRevealPrivKey(!revealPrivKey); - if (windowWidth < 768) { - setShowSideBar(false); - } - }} - {...props} - /> -
- )} -
-
-
-
- { - setSearchText(e.target.value); - setSearchInProgress(true); - if (e.target.value.length === 0) { - setSearchInProgress(false); - setUserList([]); - } - }} - /> -
-
- {showDivider &&
} - {searchInProgress && } -
- {searchText ? ( -
- {userList.map((user) => ( - { - setSearchText(""); - setSearchInProgress(false); - }} - key={user.account} - > -
{ - setRevealPrivKey(false); - getReceiverPubKey(user.account); - handleSideBar(); - }} - > - - - - {user.account} - - ({accountReputation(user.reputation)}) - -
- - ))} -
- ) : ( - <> - {communities.length !== 0 &&

Communities

} - {communities.map((channel) => ( - -
{ - setRevealPrivKey(false); - handleSideBar(); - }} - > - -
-

{channel.name}

-

- {getCommunityLastMessage(channel.id, chat.publicMessages)} -

-
-
- - ))} - {directContacts.length !== 0 &&

DMs

} - {directContacts.map((contact) => ( - { - setReceiverPubKey(contact.pubkey); - handleSideBar(); - }} - > -
- -
-

{contact.name}

-

- {getDirectLastMessage(contact.pubkey, chat.directMessages)} -

-
-
- - ))} - - )} -
- {isScrollToTop && ( - - )} -
- )} - - ); -} diff --git a/src/common/features/chats/components/chats-sidebar/index.scss b/src/common/features/chats/components/chats-sidebar/index.scss deleted file mode 100644 index 3be4333425e..00000000000 --- a/src/common/features/chats/components/chats-sidebar/index.scss +++ /dev/null @@ -1,284 +0,0 @@ -@import "src/style/vars_mixins"; - -.chats-sidebar { - @include themify(day) { - @apply bg-light-200; - box-shadow: 0px -5px 5px rgba(0, 0, 0, 0.1); - } - @include themify(night) { - @apply bg-dark-200; - box-shadow: 0px -5px 5px rgb(60, 68, 58); - } - z-index: 10; - min-width: 330px; - height: 100%; - margin-top: 10px; - - .close-sidebar { - margin: 10px 5px 0 0; - - svg { - width: 30px; - height: 30px; - @apply text-gray-steel; - cursor: pointer; - } - } - - .chats-title { - margin: 30px 16px 12px 16px; - - .chats-content { - .back-arrow-image { - cursor: pointer; - width: 40px; - height: 40px; - border-radius: 50%; - margin-top: 1px; - - &:hover { - @include themify(day) { - background: rgba(29, 155, 240, 0.1); - } - @include themify(night) { - @apply bg-blue-dark-grey; - } - } - } - } - - .chats { - @include themify(day) { - color: #050505; - } - @include themify(night) { - @apply text-white; - } - - color: #050505; - font-weight: 700; - font-size: 24px; - padding-top: 8px; - margin-left: 6px; - } - - .chat-actions { - .refresh-button { - margin-top: 2px; - - .refresh-svg { - width: 40px; - height: 40px; - justify-content: center; - text-align: center; - border-radius: 50%; - padding-top: 10px; - display: flex; - cursor: pointer; - - svg { - height: 20px; - width: 20px; - } - - &:hover { - @include themify(day) { - background: rgba(29, 155, 240, 0.1); - } - @include themify(night) { - @apply bg-blue-dark-grey - } - } - } - } - - .chat-menu { - border: none; - width: 40px; - height: 40px; - justify-content: center; - text-align: center; - display: flex; - margin-top: 3px; - cursor: pointer; - border-radius: 50%; - - svg { - @include themify(day) { - @apply text-gray-charcoal; - } - - @include themify(night) { - @apply text-light-200; - } - } - - &:hover { - @include themify(day) { - background: rgba(29, 155, 240, 0.1); - } - @include themify(night) { - @apply bg-blue-dark-grey; - } - } - } - } - } - - .chats-search { - padding: 0 8px 0 15px; - margin-bottom: 20px; - } - - .divider { - @include themify(day) { - border-bottom: 1px solid #ced0d4; - } - @include themify(night) { - @apply border-b border-gray-charcoal - } - } - - .searched-users { - .user-info { - cursor: pointer; - padding: 15px 3px 15px 25px; - } - - .user-name { - font-size: 20px; - font-weight: 700; - margin: 7px 5px 0 15px; - } - - .user-reputation { - margin: 9px 5px 0 5px; - } - } - - .community-title, - .dm-title { - font-size: 1.3rem; - font-weight: 700; - font-family: Faktum, sans-serif; - margin-bottom: 0; - padding: 15px 0 10px 15px; - } - - .community:hover, - .dm:hover, - .user-info:hover { - @include themify(day) { - background: #eeeeee; - } - - @include themify(night) { - @apply bg-blue-dusky; - } - } - - .community, - .dm { - display: flex; - padding: 12.5px 5px 12.5px 13px; - cursor: pointer; - border-radius: 8px; - margin: 0px 10px; - - .community-info, - .dm-info { - .community-name, - .dm-name { - padding: 3px 0 0 25px; - margin-bottom: 0; - font-size: 18px; - font-weight: 700; - } - - .dm-last-message, - .community-last-message { - max-width: 255px; - padding: 4px 0 0 25px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - margin-bottom: 0; - font-size: 13px; - } - } - - &.selected { - @include themify(day) { - background: #ecf3ff; - } - - @include themify(night) { - @apply border-gray-charcoal; - } - - &:hover { - @include themify(day) { - background: #ecf3ff; - } - - @include themify(night) { - @apply bg-gray-charcoal - } - } - } - } - - .chats-list { - height: calc(100vh - 220px); - overflow: auto; - - a { - color: inherit; - } - } -} - -@media (max-width: 320px) { - .chats-sidebar { - min-width: 260px; - - .chats-title { - margin: 15px 0px 5px 5px; - } - - .chats-search { - padding: 0 5px 0 5px; - margin-bottom: 0; - } - - .chats-list { - overflow-x: hidden; - - .community-title, - .dm-title { - font-size: 1rem; - padding: 15px 0 10px 8px; - } - - .community, - .dm { - margin: 0; - padding: 9.5px 0px 9.5px 5px; - - .community-info, - .dm-info { - .community-name, - .dm-name { - padding: 3px 0 0 10px; - } - - .community-last-message, - .dm-last-message { - padding: 4px 0 0 10px; - max-width: 35vw; - } - } - } - } - } -} diff --git a/src/common/features/chats/components/chats-sidebar/index.tsx b/src/common/features/chats/components/chats-sidebar/index.tsx new file mode 100644 index 00000000000..e1825dc4fd6 --- /dev/null +++ b/src/common/features/chats/components/chats-sidebar/index.tsx @@ -0,0 +1,180 @@ +import React, { useContext, useEffect, useState } from "react"; +import { Link } from "react-router-dom"; +import { History } from "history"; +import { Channel } from "../../../../../managers/message-manager-types"; +import { + formattedUserName, + getCommunityLastMessage, + getJoinedCommunities, + getUserChatPublicKey +} from "../../utils"; +import ChatsScroller from "../chats-scroller"; +import UserAvatar from "../../../../components/user-avatar"; +import { AccountWithReputation } from "../../types"; +import { useMappedStore } from "../../../../store/use-mapped-store"; +import { ChatContext } from "../../chat-context-provider"; +import { ChatSidebarHeader } from "./chat-sidebar-header"; +import { ChatSidebarSearch } from "./chat-sidebar-search"; +import { ChatSidebarSearchItem } from "./chat-sidebar-search-item"; +import { ChatSidebarDirectContact } from "./chat-sidebar-direct-contact"; +import { _t } from "../../../../i18n"; + +interface Props { + username: string; + history: History; +} + +export default function ChatsSideBar(props: Props) { + const { username } = props; + const { chat } = useMappedStore(); + const { + revealPrivKey, + showSideBar, + windowWidth, + setShowSideBar, + setRevealPrivKey, + setReceiverPubKey + } = useContext(ChatContext); + const { channels, directContacts, leftChannelsList } = chat; + + const chatsSideBarRef = React.createRef(); + + const [searchQuery, setSearchQuery] = useState(""); + const [showDivider, setShowDivider] = useState(false); + const [userList, setUserList] = useState([]); + const [isScrollToTop, setIsScrollToTop] = useState(false); + const [communities, setCommunities] = useState([]); + + useEffect(() => { + if (username) { + if (username.startsWith("@")) { + getReceiverPubKey(formattedUserName(username)); + } + } + }, [username]); + + useEffect(() => { + const communities = getJoinedCommunities(channels, leftChannelsList); + setCommunities(communities); + }, [channels, leftChannelsList]); + + const handleScroll = (event: React.UIEvent) => { + var element = event.currentTarget; + if (element.scrollTop > 2) { + setShowDivider(true); + } else { + if (showDivider) { + setShowDivider(false); + } + } + let srollHeight: number = (element.scrollHeight / 100) * 25; + const isScrollToTop = element.scrollTop >= srollHeight; + setIsScrollToTop(isScrollToTop); + }; + + const handleRevealPrivKey = () => { + if (revealPrivKey) { + setRevealPrivKey(false); + } + }; + + const getReceiverPubKey = async (username: string) => { + const peer = directContacts.find((x) => x.name === username)?.pubkey ?? ""; + if (peer) { + setReceiverPubKey(peer); + } else { + const pubkey = await getUserChatPublicKey(username); + if (pubkey === undefined) { + setReceiverPubKey(""); + } else { + setReceiverPubKey(pubkey); + } + } + }; + + const handleSideBar = () => { + if (windowWidth < 768 && showSideBar) { + setShowSideBar(false); + } + }; + + return ( + <> + {showSideBar && ( +
+ + + {showDivider &&
} +
+ {searchQuery ? ( + userList.map((user) => ( + { + setSearchQuery(""); + setRevealPrivKey(false); + getReceiverPubKey(user.account); + handleSideBar(); + }} + key={user.account} + /> + )) + ) : ( + <> + {communities.length !== 0 &&

Communities

} + {communities.map((channel) => ( + +
{ + setRevealPrivKey(false); + handleSideBar(); + }} + > + +
+

{channel.name}

+

+ {getCommunityLastMessage(channel.id, chat.publicMessages)} +

+
+
+ + ))} + {directContacts.length !== 0 && ( +
+ {_t("chat.direct-messages")} +
+ )} + {directContacts.map((contact) => ( + + ))} + + )} +
+ {isScrollToTop && ( + + )} +
+ )} + + ); +} diff --git a/src/common/features/chats/components/import-chats/index.tsx b/src/common/features/chats/components/import-chats/index.tsx index 31e1e174e52..924a91cd144 100644 --- a/src/common/features/chats/components/import-chats/index.tsx +++ b/src/common/features/chats/components/import-chats/index.tsx @@ -62,7 +62,7 @@ export default function ImportChats() { <>
-
+
diff --git a/src/common/features/chats/utils/get-relative-date.ts b/src/common/features/chats/utils/get-relative-date.ts new file mode 100644 index 00000000000..aff85dce892 --- /dev/null +++ b/src/common/features/chats/utils/get-relative-date.ts @@ -0,0 +1,20 @@ +import { differenceInDays, differenceInHours, differenceInYears, format, getDay } from "date-fns"; + +export function getRelativeDate(timestamp?: number) { + if (!timestamp) { + return ""; + } + + const date = new Date(timestamp * 1000); + const now = new Date(); + + if (getDay(now) === getDay(date) && differenceInHours(now, date) <= 24) { + return format(date, "HH:mm"); + } else if (differenceInDays(now, date) <= 7) { + return format(date, "EEE"); + } else if (differenceInYears(now, date) === 0) { + return format(date, "dd.MM"); + } else { + return format(date, "dd.MM.yyyy"); + } +} diff --git a/src/common/features/chats/utils/index.ts b/src/common/features/chats/utils/index.ts index f24962a55f2..84476493cac 100644 --- a/src/common/features/chats/utils/index.ts +++ b/src/common/features/chats/utils/index.ts @@ -17,3 +17,4 @@ export * from "./copy-to-clipboard"; export * from "./use-fetch-direct-messages"; export * from "./check-contiguous-message"; export * from "./format-message-date-and-day"; +export * from "./get-relative-date"; diff --git a/src/common/features/chats/utils/use-get-direct-last-message.ts b/src/common/features/chats/utils/use-get-direct-last-message.ts index 0dc34b20514..26cbbde59a8 100644 --- a/src/common/features/chats/utils/use-get-direct-last-message.ts +++ b/src/common/features/chats/utils/use-get-direct-last-message.ts @@ -4,7 +4,7 @@ export const getDirectLastMessage = (pubkey: string, directMessages: directMessa const msgsList = fetchDirectMessages(pubkey, directMessages); const messages = msgsList.sort((a, b) => a.created - b.created); const lastMessage = messages.slice(-1); - return lastMessage[0]?.content; + return lastMessage[0]; }; const fetchDirectMessages = (peer: string, directMessages: directMessagesList[]) => { diff --git a/src/common/features/ui/button/props.ts b/src/common/features/ui/button/props.ts index ee2ddeb616a..c0574cc24be 100644 --- a/src/common/features/ui/button/props.ts +++ b/src/common/features/ui/button/props.ts @@ -3,6 +3,7 @@ import { AnchorHTMLAttributes, ButtonHTMLAttributes, ReactNode } from "react"; export type ButtonAppearance = | "primary" | "secondary" + | "gray-link" | "link" | "danger" | "success" diff --git a/src/common/features/ui/button/styles.ts b/src/common/features/ui/button/styles.ts index 8b48accace2..ea9dece6776 100644 --- a/src/common/features/ui/button/styles.ts +++ b/src/common/features/ui/button/styles.ts @@ -8,7 +8,8 @@ export const BUTTON_STYLES: Record = { danger: "bg-red hover:bg-red-020 focus:bg-red-030 text-white", success: "", warning: "", - info: "bg-info-default hover:info-hover focus:bg-info-focus text-white disabled:opacity-50 disabled:hover:bg-info-default disabled:focus:bg-info-default" + info: "bg-info-default hover:info-hover focus:bg-info-focus text-white disabled:opacity-50 disabled:hover:bg-info-default disabled:focus:bg-info-default", + "gray-link": "text-gray-500 hover:text-blue-dark-sky focus:text-blue-dark-sky-active" }; export const BUTTON_OUTLINE_STYLES: Record = { @@ -21,7 +22,8 @@ export const BUTTON_OUTLINE_STYLES: Record = { "border-red hover:border-red-020 focus:border-red-030 text-red hover:text-red-020 focus:text-red-030", success: "", warning: "", - info: "border-info-default hover:border-info-hover focus:border-info-focus text-info-default hover:text-info-hover focus:text-info-focus" + info: "border-info-default hover:border-info-hover focus:border-info-focus text-info-default hover:text-info-hover focus:text-info-focus", + "gray-link": "" }; export const BUTTON_SIZES: Record = { diff --git a/src/common/features/ui/dropdown/dropdown-item.tsx b/src/common/features/ui/dropdown/dropdown-item.tsx index b806d9b4c72..4860a353299 100644 --- a/src/common/features/ui/dropdown/dropdown-item.tsx +++ b/src/common/features/ui/dropdown/dropdown-item.tsx @@ -1,4 +1,4 @@ -import React, { HTMLProps, useContext } from "react"; +import React, { HTMLProps, ReactNode, useContext } from "react"; import { classNameObject } from "../../../helper/class-name-object"; import { DropdownContext } from "@ui/dropdown/dropdown-context"; @@ -20,3 +20,16 @@ export function DropdownItem(props: HTMLProps) { /> ); } + +export function DropdownItemWithIcon( + props: HTMLProps & { icon: ReactNode; label: ReactNode } +) { + return ( + +
+
{props.icon}
+
{props.label}
+
+
+ ); +} diff --git a/src/common/features/ui/dropdown/dropdown-menu.tsx b/src/common/features/ui/dropdown/dropdown-menu.tsx index 2bdf5242e5f..36fb331b0f1 100644 --- a/src/common/features/ui/dropdown/dropdown-menu.tsx +++ b/src/common/features/ui/dropdown/dropdown-menu.tsx @@ -4,7 +4,7 @@ import { DropdownContext } from "@ui/dropdown/dropdown-context"; import { useFilteredProps } from "../../../util/props-filter"; interface Props { - align?: "left" | "right"; + align?: "left" | "right" | "top" | "bottom"; } export function DropdownMenu(props: HTMLProps & Props) { @@ -16,9 +16,11 @@ export function DropdownMenu(props: HTMLProps & Props) {
diff --git a/src/common/features/ui/input/input-group.tsx b/src/common/features/ui/input/input-group.tsx index 968dbea2db6..e6d0dccec22 100644 --- a/src/common/features/ui/input/input-group.tsx +++ b/src/common/features/ui/input/input-group.tsx @@ -11,6 +11,8 @@ interface Props { prepend?: ReactNode; onPrependClick?: () => void; className?: string; + transparentPrepend?: boolean; + transparentAppend?: boolean; } // TODO: Make styles for childrens: buttons @@ -22,7 +24,9 @@ export function InputGroup({ className, onPrependClick, onAppendClick, - onClick + onClick, + transparentPrepend, + transparentAppend }: PropsWithChildren & HTMLAttributes) { return (
.ecency-spinner]:w-3.5 [&>.ecency-spinner]:h-3.5": (prepend as ReactElement)?.type === Spinner, "[&>svg]:w-4 [&>svg]:h-4 px-2": true, - "border-2": (prepend as ReactElement)?.type !== Button + "border-2": (prepend as ReactElement)?.type !== Button, + "bg-gray-200 dark:bg-gray-600": !transparentPrepend ?? true })} onClick={() => onPrependClick?.()} > @@ -56,13 +61,14 @@ export function InputGroup({ {append ? (
.ecency-spinner]:w-3.5 [&>.ecency-spinner]:h-3.5": (prepend as ReactElement)?.type === Spinner, - "border-2": (append as ReactElement)?.type !== Button + "border-2": (append as ReactElement)?.type !== Button, + "bg-gray-200 dark:bg-gray-600": !transparentAppend ?? true })} onClick={() => onAppendClick?.()} > diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 291d0c587d7..07aa032b40d 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1827,8 +1827,10 @@ "update-success-message": "Your Recovery email has been updated successfully" }, "chat": { + "title": "Chats", "messages": "Messages", "new-message": "New Message", + "direct-messages": "Direct messages", "collapse": "Collapse", "expand": "Expand", "scroll-to-bottom": "scroll to Bottom", diff --git a/src/common/img/svg.tsx b/src/common/img/svg.tsx index 2d4806f80c6..35dd141f34a 100644 --- a/src/common/img/svg.tsx +++ b/src/common/img/svg.tsx @@ -2084,12 +2084,11 @@ export const arrowBackSvg = ( export const messageSendSvg = ( - - + + + ); @@ -2405,3 +2411,28 @@ export const duotoneRefreshSvg = ( /> ); + +export const attachFileSvg = ( + + + + +); diff --git a/src/common/pages/chats.tsx b/src/common/pages/chats.tsx new file mode 100644 index 00000000000..0c070062ab4 --- /dev/null +++ b/src/common/pages/chats.tsx @@ -0,0 +1,111 @@ +import React, { useContext, useEffect, useMemo } from "react"; +import { connect } from "react-redux"; +import { match } from "react-router"; +import NavBar from "../components/navbar"; +import { pageMapDispatchToProps, pageMapStateToProps, PageProps } from "./common"; +import ChatsSideBar from "../features/chats/components/chats-sidebar"; +import ChatsMessagesBox from "../features/chats/components/chats-messages-box"; +import ManageChatKey from "../features/chats/components/manage-chat-key"; +import Feedback from "../components/feedback"; +import { useMappedStore } from "../store/use-mapped-store"; +import { ChatContext } from "../features/chats/chat-context-provider"; +import ImportChats from "../features/chats/components/import-chats"; +import { Spinner } from "@ui/spinner"; + +interface Props extends PageProps { + match: match<{ + filter: string; + name: string; + path: string; + url: string; + username: string; + }>; +} + +export const Chats = (props: Props) => { + const { activeUser, global } = useMappedStore(); + const { match, history } = props; + + const username = match.params.username; + + const { showSpinner, activeUserKeys, revealPrivKey, chatPrivKey, windowWidth } = + useContext(ChatContext); + + const isReady = useMemo( + () => activeUser && activeUserKeys?.pub && chatPrivKey, + [activeUserKeys, activeUserKeys, chatPrivKey] + ); + + useEffect(() => { + document.body.style.overflow = "hidden"; + + return () => { + document.body.style.overflow = "auto"; + }; + }, []); + return ( +
+ + + +
+
+
+ {isReady ? : <>} +
+
+ {isReady ? ( + revealPrivKey ? ( +
+ +
+ ) : ( +
+ +
+ ) + ) : ( + + )} + {showSpinner ? ( +
+ +

+ Loading... +

+
+ ) : ( + <> + )} +
+ {/*{activeUser ? (): (*/} + {/* <>*/} + {/* {activeUserKeys?.pub ? (*/} + {/* chatPrivKey ? (*/} + {/* <>*/} + {/* ) : (*/} + {/* <>*/} + + {/* */} + {/* )*/} + {/* ) : (*/} + {/*
*/} + {/* */} + {/*
*/} + {/* )}*/} + {/* */} + {/* )*/} + {/* ) : (*/} + {/*

*/} + {/* Please login to continue the chat*/} + {/*

*/} + {/* )}*/} +
+
+
+ ); +}; +export default connect(pageMapStateToProps, pageMapDispatchToProps)(Chats); diff --git a/src/common/pages/chats/index.scss b/src/common/pages/chats/index.scss deleted file mode 100644 index 3fdad1cf095..00000000000 --- a/src/common/pages/chats/index.scss +++ /dev/null @@ -1,27 +0,0 @@ -@import "../../../style/vars_mixins"; - -.chats-page { - display: flex; - margin-top: 3.5rem; - - .full-page { - width: 100vw; - height: 100vh; - } - .chats-manage-key { - .private-key { - margin-top: 4.5rem; - @include media-breakpoint-down(md) { - width: 95vw; - } - .form-group { - width: 100% !important; - } - } - } - .import-chat { - width: 100%; - height: 100%; - margin-top: 10%; - } -} diff --git a/src/common/pages/chats/index.tsx b/src/common/pages/chats/index.tsx deleted file mode 100644 index 58a8608571f..00000000000 --- a/src/common/pages/chats/index.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import React, { useContext, useEffect, useState } from "react"; -import { connect } from "react-redux"; -import { match } from "react-router"; -import NavBar from "../../components/navbar"; -import NavBarElectron from "../../../desktop/app/components/navbar"; -import { pageMapDispatchToProps, pageMapStateToProps, PageProps } from "../common"; -import ChatsSideBar from "../../features/chats/components/chats-sidebar/indes"; -import ChatsMessagesBox from "../../features/chats/components/chats-messages-box"; - -import ManageChatKey from "../../features/chats/components/manage-chat-key"; -import Feedback from "../../components/feedback"; -import { useMappedStore } from "../../store/use-mapped-store"; -import { ChatContext } from "../../features/chats/chat-context-provider"; -import ImportChats from "../../features/chats/components/import-chats"; -import JoinChat from "../../features/chats/components/join-chat"; - -import "./index.scss"; -import { Spinner } from "@ui/spinner"; - -interface MatchParams { - filter: string; - name: string; - path: string; - url: string; - username: string; -} - -interface Props extends PageProps { - match: match; -} - -export const Chats = (props: Props) => { - const { activeUser, global } = useMappedStore(); - const { match, history } = props; - const [marginTop, setMarginTop] = useState(0); - - const username = match.params.username; - - const { showSpinner, activeUserKeys, revealPrivKey, chatPrivKey, windowWidth } = - useContext(ChatContext); - - useEffect(() => { - document.body.style.overflow = "hidden"; - - return () => { - document.body.style.overflow = "auto"; - }; - }, []); - - useEffect(() => { - handleResize(); - }, []); - - useEffect(() => { - window.addEventListener("resize", handleResize); - return () => { - window.removeEventListener("resize", handleResize); - }; - }, []); - - const handleResize = () => { - const parentElemet = document.getElementById("sticky-container")?.getBoundingClientRect(); - if (parentElemet) { - setMarginTop(parentElemet?.y + parentElemet?.height - 30); - } - }; - - return ( - <> - - {global.isElectron ? : } - -
- {activeUser ? ( - showSpinner ? ( -
- -

- Loading... -

-
- ) : ( - <> - {activeUserKeys?.pub ? ( - chatPrivKey ? ( - <> - - {revealPrivKey ? ( -
- -
- ) : ( - <> - - - )} - - ) : ( - <> -
- -
- - ) - ) : ( -
- -
- )} - - ) - ) : ( -

- Please login to continue the chat -

- )} -
- - ); -}; -export default connect(pageMapStateToProps, pageMapDispatchToProps)(Chats); diff --git a/src/common/pages/onboard.tsx b/src/common/pages/onboard.tsx index b8144f31c48..27f523e7a97 100644 --- a/src/common/pages/onboard.tsx +++ b/src/common/pages/onboard.tsx @@ -530,7 +530,7 @@ const Onboard = (props: Props) => {
{_t("onboard.copy-key")} -
+
{innerWidth <= 768 ? shortPassword + "..." : masterPassword} diff --git a/yarn.lock b/yarn.lock index b9628cdd3b5..3239b1be276 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1216,6 +1216,13 @@ dependencies: regenerator-runtime "^0.13.11" +"@babel/runtime@^7.21.0": + version "7.23.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885" + integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/runtime@^7.7.2": version "7.20.7" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd" @@ -4790,6 +4797,13 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" +date-fns@^2.30.0: + version "2.30.0" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0" + integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw== + dependencies: + "@babel/runtime" "^7.21.0" + debounce@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" @@ -10920,6 +10934,11 @@ regenerator-runtime@^0.13.4: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== +regenerator-runtime@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" + integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== + regenerator-transform@^0.15.0: version "0.15.0" resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.0.tgz#cbd9ead5d77fae1a48d957cf889ad0586adb6537" From 9d011a318c8e71c73fb80ba26f99f1d6b11f49f9 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Fri, 20 Oct 2023 23:28:03 +0600 Subject: [PATCH 100/179] Chat: improved manage private key UI --- .../chats/components/chat-popup/index.tsx | 4 +- .../chats-sidebar/chat-sidebar-header.tsx | 13 +++-- .../components/manage-chat-key/index.scss | 53 ------------------- .../components/manage-chat-key/index.tsx | 48 +++++------------ src/common/pages/chats.tsx | 9 ++-- 5 files changed, 31 insertions(+), 96 deletions(-) delete mode 100644 src/common/features/chats/components/manage-chat-key/index.scss diff --git a/src/common/features/chats/components/chat-popup/index.tsx b/src/common/features/chats/components/chat-popup/index.tsx index 808146eb91b..7ced4c88993 100644 --- a/src/common/features/chats/components/chat-popup/index.tsx +++ b/src/common/features/chats/components/chat-popup/index.tsx @@ -443,7 +443,9 @@ export const ChatPopUp = () => { )} ) : revealPrivKey ? ( - +
+ +
) : (
diff --git a/src/common/features/chats/components/manage-chat-key/index.scss b/src/common/features/chats/components/manage-chat-key/index.scss deleted file mode 100644 index 43861fbf647..00000000000 --- a/src/common/features/chats/components/manage-chat-key/index.scss +++ /dev/null @@ -1,53 +0,0 @@ -@import "src/style/vars_mixins"; - -.manage-chat-key { - .private-key { - margin-top: 2.5rem; - margin-left: 14px; - margin-right: 7px; - - .expand-icon { - margin: 13px 15px 0 0; - - .expand-svg { - height: 35px; - width: 35px; - background: #e4e6eb; - margin-left: 10px; - border-radius: 10px; - cursor: pointer; - - svg { - width: 35px; - height: 35px; - } - - &:hover { - background: #ecf3ff; - } - } - } - } - - .chat-priv-key { - width: 100%; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - .copy-svg { - margin-top: 3px; - margin-left: 2px; - cursor: pointer; - - svg { - @include themify(day) { - color: #a1a1a1; - } - @include themify(night) { - @apply text-light-200; - } - } - } -} diff --git a/src/common/features/chats/components/manage-chat-key/index.tsx b/src/common/features/chats/components/manage-chat-key/index.tsx index 3078b25dac9..80339807eeb 100644 --- a/src/common/features/chats/components/manage-chat-key/index.tsx +++ b/src/common/features/chats/components/manage-chat-key/index.tsx @@ -1,19 +1,12 @@ import React, { useContext, useEffect, useRef, useState } from "react"; import { _t } from "../../../../i18n"; -import { History } from "history"; -import { expandSideBar } from "../../../../img/svg"; import { ChatContext } from "../../chat-context-provider"; -import "./index.scss"; import { InputGroupCopyClipboard } from "@ui/input"; import qrcode from "qrcode"; import { classNameObject } from "../../../../helper/class-name-object"; -interface Props { - history: History; -} - -export default function ManageChatKey({ history }: Props) { - const { chatPrivKey, windowWidth, setShowSideBar } = useContext(ChatContext); +export default function ManageChatKey() { + const { chatPrivKey } = useContext(ChatContext); const qrImgRef = useRef(null); const [isQrShow, setIsQrShow] = useState(false); @@ -30,30 +23,17 @@ export default function ManageChatKey({ history }: Props) { }; return ( - <> -
-
- {windowWidth < 768 && history.location.pathname.includes("/chats") && ( -
-

setShowSideBar(true)}> - {expandSideBar} -

-
- )} -
-
{_t("chat.chat-priv-key")}
- - -
-
-
- +
+
{_t("chat.chat-priv-key")}
+ + +
); } diff --git a/src/common/pages/chats.tsx b/src/common/pages/chats.tsx index 0c070062ab4..5ac80ab4a4e 100644 --- a/src/common/pages/chats.tsx +++ b/src/common/pages/chats.tsx @@ -56,11 +56,10 @@ export const Chats = (props: Props) => {
{isReady ? ( revealPrivKey ? ( -
- +
+
+ +
) : (
From aa755743a8c846d9a8cd1aa96a514a65bb99eec9 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Mon, 23 Oct 2023 13:40:10 +0600 Subject: [PATCH 101/179] Chat: fixed message box component UI --- src/common/app.tsx | 2 +- .../components/chats-messages-box/index.scss | 43 ------- .../components/chats-messages-box/index.tsx | 21 +--- .../chats-messages-header/index.scss | 116 ------------------ .../chats-messages-header/index.tsx | 53 ++++---- .../components/chats-messages-view/index.scss | 12 -- .../components/chats-messages-view/index.tsx | 66 +++++----- .../components/chats-profile-box/index.scss | 70 ----------- .../components/chats-profile-box/index.tsx | 56 ++++----- .../chats/screens}/chats.tsx | 61 +++++---- src/common/features/chats/screens/index.ts | 1 + 11 files changed, 112 insertions(+), 389 deletions(-) delete mode 100644 src/common/features/chats/components/chats-messages-box/index.scss delete mode 100644 src/common/features/chats/components/chats-messages-header/index.scss delete mode 100644 src/common/features/chats/components/chats-messages-view/index.scss delete mode 100644 src/common/features/chats/components/chats-profile-box/index.scss rename src/common/{pages => features/chats/screens}/chats.tsx (63%) create mode 100644 src/common/features/chats/screens/index.ts diff --git a/src/common/app.tsx b/src/common/app.tsx index 0af333a3be2..98c6fad7254 100644 --- a/src/common/app.tsx +++ b/src/common/app.tsx @@ -51,7 +51,7 @@ const AuthPage = (props: any) => ; const SubmitContainer = loadable(() => import("./pages/submit")); const SubmitPage = (props: any) => ; -const ChatsContainer = loadable(() => import("./pages/chats")); +const ChatsContainer = loadable(() => import("./features/chats/screens/chats")); const ChatsPage = (props: any) => ; const OnboardContainer = loadable(() => import("./pages/onboard")); diff --git a/src/common/features/chats/components/chats-messages-box/index.scss b/src/common/features/chats/components/chats-messages-box/index.scss deleted file mode 100644 index 1a40327eea0..00000000000 --- a/src/common/features/chats/components/chats-messages-box/index.scss +++ /dev/null @@ -1,43 +0,0 @@ -@import "src/style/vars_mixins"; - -.chats-messages-box { - margin-top: 10px; - - display: grid; - grid-template-rows: repeat(18, 1fr); - grid-template-columns: repeat(1, 1fr); - - @include themify(day) { - box-shadow: 0px -5px 5px rgba(0, 0, 0, 0.1); - } - - @include themify(night) { - box-shadow: 0px -5px 5px rgb(60, 68, 73); - } - width: 100%; - - .no-chat-select { - height: calc(100vh - 70px); - font-size: 22px; - font-weight: 700; - display: flex; - align-items: center; - justify-content: center; - - .start-chat-wrapper { - .start-chat { - @include themify(day) { - color: #65676b; - } - @include themify(night) { - @apply text-white; - } - } - } - - .info-message { - font-size: 16px; - // font-weight: 100; - } - } -} diff --git a/src/common/features/chats/components/chats-messages-box/index.tsx b/src/common/features/chats/components/chats-messages-box/index.tsx index 689429cab48..cf2293329e6 100644 --- a/src/common/features/chats/components/chats-messages-box/index.tsx +++ b/src/common/features/chats/components/chats-messages-box/index.tsx @@ -1,9 +1,6 @@ import React, { useContext, useEffect, useState } from "react"; import { match } from "react-router"; import { History } from "history"; -import { Account } from "../../../../store/accounts/types"; -import { ToggleType, UI } from "../../../../store/ui/types"; -import { User } from "../../../../store/users/types"; import ChatsMessagesHeader from "../chats-messages-header"; import ChatsMessagesView from "../chats-messages-view"; import { Channel, ChannelUpdate } from "../../../../../managers/message-manager-types"; @@ -13,8 +10,6 @@ import { useMappedStore } from "../../../../store/use-mapped-store"; import { CHANNEL } from "../chat-popup/chat-constants"; import { ChatContext } from "../../chat-context-provider"; import { useMount } from "react-use"; - -import "./index.scss"; import { Button } from "@ui/button"; interface MatchParams { @@ -27,13 +22,7 @@ interface MatchParams { interface Props { match: match; - users: User[]; history: History; - ui: UI; - setActiveUser: (username: string | null) => void; - updateActiveUser: (data?: Account) => void; - deleteUser: (username: string) => void; - toggleUIProp: (what: ToggleType) => void; } export default function ChatsMessagesBox(props: Props) { @@ -143,13 +132,7 @@ export default function ChatsMessagesBox(props: Props) { }; return ( -
+
{match.url === "/chats" ? (
@@ -164,7 +147,7 @@ export default function ChatsMessagesBox(props: Props) { {inProgress && } -
-
-
-

setShowSideBar(true)}> - {expandSideBar} -

-
- -
- -

{formattedName(username, chat)}

-
- -
- - {isChannel(username) && ( -
- -
- )} +
+
+
+ + {isChannel(username) && ( +
+ +
+ )}
); } diff --git a/src/common/features/chats/components/chats-messages-view/index.scss b/src/common/features/chats/components/chats-messages-view/index.scss deleted file mode 100644 index 0c3fe3489eb..00000000000 --- a/src/common/features/chats/components/chats-messages-view/index.scss +++ /dev/null @@ -1,12 +0,0 @@ -.chats-messages-view { - grid-row: span 16; - overflow: scroll; - overflow-x: hidden; - a::after { - display: none; - } - - &.no-scroll { - overflow: hidden; - } -} diff --git a/src/common/features/chats/components/chats-messages-view/index.tsx b/src/common/features/chats/components/chats-messages-view/index.tsx index 8cee37ae185..fd0e2a613e3 100644 --- a/src/common/features/chats/components/chats-messages-view/index.tsx +++ b/src/common/features/chats/components/chats-messages-view/index.tsx @@ -9,47 +9,31 @@ import { fetchCommunityMessages, fetchDirectMessages } from "../../utils"; import { History } from "history"; import { useMappedStore } from "../../../../store/use-mapped-store"; import ChatsProfileBox from "../chats-profile-box"; -import "./index.scss"; import ChatsChannelMessages from "../chats-channel-messages"; import ChatsDirectMessages from "../chats-direct-messages"; -import { Account } from "../../../../store/accounts/types"; -import { ToggleType, UI } from "../../../../store/ui/types"; -import { User } from "../../../../store/users/types"; import ChatInput from "../chat-input"; import ChatsScroller from "../chats-scroller"; import { CHATPAGE } from "../chat-popup/chat-constants"; -import { EmojiPickerStyleProps } from "../../types"; import { ChatContext } from "../../chat-context-provider"; import { classNameObject } from "../../../../helper/class-name-object"; -const EmojiPickerStyle: EmojiPickerStyleProps = { - width: "56.5%", - bottom: "58px", - left: "340px", - marginLeft: "14px", - borderTopLeftRadius: "8px", - borderTopRightRadius: "8px", - borderBottomLeftRadius: "0px" -}; - interface Props { username: string; - users: User[]; history: History; - ui: UI; currentChannel: Channel; inProgress: boolean; - setActiveUser: (username: string | null) => void; - updateActiveUser: (data?: Account) => void; - deleteUser: (username: string) => void; - toggleUIProp: (what: ToggleType) => void; currentChannelSetter: (channel: Channel) => void; setInProgress: (d: boolean) => void; } -export default function ChatsMessagesView(props: Props) { - const { username, currentChannel, inProgress, currentChannelSetter, setInProgress } = props; - +export default function ChatsMessagesView({ + username, + currentChannel, + inProgress, + currentChannelSetter, + setInProgress, + history +}: Props) { const { messageServiceInstance } = useContext(ChatContext); const messagesBoxRef = useRef(null); @@ -176,13 +160,14 @@ export default function ChatsMessagesView(props: Props) { <>
@@ -191,7 +176,8 @@ export default function ChatsMessagesView(props: Props) { {communityName.length !== 0 ? ( <> ) : ( )}
- + +
+ +
); } diff --git a/src/common/features/chats/components/chats-profile-box/index.scss b/src/common/features/chats/components/chats-profile-box/index.scss deleted file mode 100644 index 54574a03468..00000000000 --- a/src/common/features/chats/components/chats-profile-box/index.scss +++ /dev/null @@ -1,70 +0,0 @@ -@import "src/style/vars_mixins"; - -.chats-profile-box { - .user-profile { - border-bottom-right-radius: 20px; - border-bottom-left-radius: 20px; - min-height: 266px; - - @include themify(day) { - @apply bg-light-300; - } - @include themify(night) { - background: #172b44; - } - - padding: 0 16px 16px 16px; - margin: 0 16px 16px 16px; - - .user-logo { - display: flex; - justify-content: center; - align-items: center; - padding-top: 16px; - cursor: pointer; - } - - .user-name { - font-size: 20px; - font-weight: 700; - @include themify(day) { - @apply text-gray-charcoal; - } - @include themify(night) { - color: $white; - } - } - - .about { - text-align: center; - padding: 4px 0; - @include themify(day) { - @apply text-gray-charcoal; - } - @include themify(night) { - @apply text-white; - } - } - - .joining-info { - justify-content: space-evenly; - } - - .created-date { - padding-top: 8px; - font-size: 14px; - @include themify(day) { - @apply text-gray-charcoal; - } - @include themify(night) { - @apply text-white; - } - } - - .followers { - font-size: 14px; - padding-top: 4px !important; - font-family: New Century Schoolbook; - } - } -} diff --git a/src/common/features/chats/components/chats-profile-box/index.tsx b/src/common/features/chats/components/chats-profile-box/index.tsx index 0d9f4020944..25e5222e956 100644 --- a/src/common/features/chats/components/chats-profile-box/index.tsx +++ b/src/common/features/chats/components/chats-profile-box/index.tsx @@ -6,14 +6,11 @@ import { getAccountFull } from "../../../../api/hive"; import { formattedUserName } from "../../utils"; import { useCommunityCache } from "../../../../core"; -import "./index.scss"; - export interface profileData { joiningData: string; about: string | undefined; followers: number | undefined; name: string; - subscribers: string; username: string; } @@ -25,9 +22,13 @@ interface Props { currentUser?: string; } -export default function ChatsProfileBox(props: Props) { - const { username, isCommunity, isCurrentUser, communityName, currentUser } = props; - +export default function ChatsProfileBox({ + username, + isCommunity, + isCurrentUser, + communityName, + currentUser +}: Props) { const [profileData, setProfileData] = useState(); const { data: community } = useCommunityCache(username ? username! : communityName!); @@ -56,7 +57,6 @@ export default function ChatsProfileBox(props: Props) { about: response.profile?.about, followers: response.follow_stats?.follower_count, name: response.name, - subscribers: _t("chat.followers"), username: response.name }); } else { @@ -65,7 +65,6 @@ export default function ChatsProfileBox(props: Props) { about: community?.about, followers: community?.subscribers, name: community?.title!, - subscribers: _t("chat.subscribers"), username: community?.name! }); } @@ -86,39 +85,34 @@ export default function ChatsProfileBox(props: Props) { about: community?.about, followers: community?.subscribers, name: community?.title!, - subscribers: _t("chat.subscribers"), username: community?.name! }); } } }; - return ( -
-
- {profileData?.joiningData && ( -
- - - -

{profileData.name}

- {profileData.about?.length !== 0 && ( -

{profileData.about}

- )} + return profileData?.joiningData ? ( +
+
+ +
{profileData.name}
+ {profileData.about?.length !== 0 &&
{profileData.about}
} -
-

- {" "} - {_t("chat.joined")} {dateToFormatted(profileData!.joiningData, "LL")} -

-

- {" "} - {formatFollowers(profileData!.followers)} {profileData.subscribers} -

+
+
+
{_t("chat.joined")}
+
+ {dateToFormatted(profileData!.joiningData, "LL")}
- )} +
+
{_t("chat.subscribers")}
+
{formatFollowers(profileData!.followers)}
+
+
+ ) : ( + <> ); } diff --git a/src/common/pages/chats.tsx b/src/common/features/chats/screens/chats.tsx similarity index 63% rename from src/common/pages/chats.tsx rename to src/common/features/chats/screens/chats.tsx index 5ac80ab4a4e..c88ca9c81e4 100644 --- a/src/common/pages/chats.tsx +++ b/src/common/features/chats/screens/chats.tsx @@ -1,16 +1,16 @@ import React, { useContext, useEffect, useMemo } from "react"; import { connect } from "react-redux"; import { match } from "react-router"; -import NavBar from "../components/navbar"; -import { pageMapDispatchToProps, pageMapStateToProps, PageProps } from "./common"; -import ChatsSideBar from "../features/chats/components/chats-sidebar"; -import ChatsMessagesBox from "../features/chats/components/chats-messages-box"; -import ManageChatKey from "../features/chats/components/manage-chat-key"; -import Feedback from "../components/feedback"; -import { useMappedStore } from "../store/use-mapped-store"; -import { ChatContext } from "../features/chats/chat-context-provider"; -import ImportChats from "../features/chats/components/import-chats"; +import NavBar from "../../../components/navbar"; +import { pageMapDispatchToProps, pageMapStateToProps, PageProps } from "../../../pages/common"; +import ChatsSideBar from "../components/chats-sidebar"; +import ManageChatKey from "../components/manage-chat-key"; +import Feedback from "../../../components/feedback"; +import { useMappedStore } from "../../../store/use-mapped-store"; +import { ChatContext } from "../chat-context-provider"; import { Spinner } from "@ui/spinner"; +import ImportChats from "../components/import-chats"; +import ChatsMessagesBox from "../components/chats-messages-box"; interface Props extends PageProps { match: match<{ @@ -28,7 +28,7 @@ export const Chats = (props: Props) => { const username = match.params.username; - const { showSpinner, activeUserKeys, revealPrivKey, chatPrivKey, windowWidth } = + const { receiverPubKey, showSpinner, activeUserKeys, revealPrivKey, chatPrivKey } = useContext(ChatContext); const isReady = useMemo( @@ -54,31 +54,30 @@ export const Chats = (props: Props) => { {isReady ? : <>}
- {isReady ? ( - revealPrivKey ? ( -
-
- -
+ {isReady && revealPrivKey && ( +
+
+
- ) : ( -
- -
- ) - ) : ( - +
)} - {showSpinner ? ( -
- -

- Loading... -

+ {showSpinner && ( +
+
- ) : ( - <> )} + {!isReady && !showSpinner && ( +
+ +
+ )} + {isReady && !showSpinner && receiverPubKey && ( + + )} + {/*{isReady ? (*/} + {/* revealPrivKey ? (*/} + {/* */} + {/*)}*/}
{/*{activeUser ? (): (*/} {/* <>*/} diff --git a/src/common/features/chats/screens/index.ts b/src/common/features/chats/screens/index.ts new file mode 100644 index 00000000000..9a6ff3c6d09 --- /dev/null +++ b/src/common/features/chats/screens/index.ts @@ -0,0 +1 @@ +export * from "./chats"; From 3e6ac6585ca9a927f1a1eaaed41b20040bc3caed Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Wed, 25 Oct 2023 23:02:31 +0600 Subject: [PATCH 102/179] Chat: fixed message box component UI --- .../chat-popup/chat-popup-direct-messages.tsx | 2 +- .../components/chats-profile-box/index.tsx | 1 - src/common/features/chats/screens/chats.tsx | 24 ++++++++++++++----- .../chats/utils/upload-chat-public-key.ts | 1 - 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx b/src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx index df283f2a4d2..2c87ae1fa4d 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx @@ -63,7 +63,7 @@ export function ChatPopupDirectMessages({ userClicked(v); }} key={user.pubkey} - lastMessage={getDirectLastMessage(user.pubkey, chat.directMessages)} + lastMessage={getDirectLastMessage(user.pubkey, chat.directMessages)?.content} /> ))} diff --git a/src/common/features/chats/components/chats-profile-box/index.tsx b/src/common/features/chats/components/chats-profile-box/index.tsx index 25e5222e956..66824a1a34b 100644 --- a/src/common/features/chats/components/chats-profile-box/index.tsx +++ b/src/common/features/chats/components/chats-profile-box/index.tsx @@ -76,7 +76,6 @@ export default function ChatsProfileBox({ about: response.profile?.about, followers: response.follow_stats?.follower_count, name: response.name, - subscribers: _t("chat.followers"), username: response.name }); } else { diff --git a/src/common/features/chats/screens/chats.tsx b/src/common/features/chats/screens/chats.tsx index c88ca9c81e4..a3b05deff6d 100644 --- a/src/common/features/chats/screens/chats.tsx +++ b/src/common/features/chats/screens/chats.tsx @@ -11,6 +11,7 @@ import { ChatContext } from "../chat-context-provider"; import { Spinner } from "@ui/spinner"; import ImportChats from "../components/import-chats"; import ChatsMessagesBox from "../components/chats-messages-box"; +import JoinChat from "../components/join-chat"; interface Props extends PageProps { match: match<{ @@ -32,10 +33,12 @@ export const Chats = (props: Props) => { useContext(ChatContext); const isReady = useMemo( - () => activeUser && activeUserKeys?.pub && chatPrivKey, + () => !!(activeUser && activeUserKeys?.pub && chatPrivKey), [activeUserKeys, activeUserKeys, chatPrivKey] ); + console.log(isReady); + useEffect(() => { document.body.style.overflow = "hidden"; @@ -71,13 +74,22 @@ export const Chats = (props: Props) => {
)} - {isReady && !showSpinner && receiverPubKey && ( + {isReady && !showSpinner && receiverPubKey && !revealPrivKey && ( )} - {/*{isReady ? (*/} - {/* revealPrivKey ? (*/} - {/* */} - {/*)}*/} + {isReady && !receiverPubKey && !revealPrivKey && !showSpinner && ( +
+
+ Hello, @{activeUser?.username} +
+
Search a person or community and start messaging
+
+ )} + {!isReady && ( +
+ +
+ )}
{/*{activeUser ? (): (*/} {/* <>*/} diff --git a/src/common/features/chats/utils/upload-chat-public-key.ts b/src/common/features/chats/utils/upload-chat-public-key.ts index 31154282d9c..c96ae2d1e82 100644 --- a/src/common/features/chats/utils/upload-chat-public-key.ts +++ b/src/common/features/chats/utils/upload-chat-public-key.ts @@ -5,7 +5,6 @@ import { ActiveUser } from "../../../store/active-user/types"; export const uploadChatPublicKey = async (activeUser: ActiveUser | null, noStrPubKey: string) => { const response = await getAccountFull(activeUser?.username!); - debugger; return await updateProfile(response, { ...JSON.parse(response?.posting_json_metadata ? response.posting_json_metadata : "{}"), nsKey: noStrPubKey From afc8b6453f86652655223517cf028668618000e2 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Wed, 25 Oct 2023 23:49:07 +0600 Subject: [PATCH 103/179] Chat: re-arranged components --- .../features/chats/chat-context-provider.tsx | 22 +-- .../{chat-input/index.tsx => chat-input.tsx} | 23 ++- .../index.tsx => chat-message-box.tsx} | 22 +-- .../index.tsx => chat-messages-header.tsx} | 28 ++-- .../index.tsx => chat-messages-view.tsx} | 26 ++-- .../chat-popup/chat-popup-messages-list.tsx | 2 +- .../index.tsx => chat-profile-box.tsx} | 12 +- .../index.tsx => chats-dropdown-menu.tsx} | 6 +- .../chats-sidebar/chat-sidebar-header.tsx | 9 +- .../chats/components/chats-sidebar/index.tsx | 142 +++++++++--------- .../{join-chat/index.tsx => join-chat.tsx} | 4 +- .../index.tsx => join-community-chat-btn.tsx} | 14 +- .../index.tsx => manage-chat-key.tsx} | 6 +- src/common/features/chats/screens/chats.tsx | 50 +++--- 14 files changed, 172 insertions(+), 194 deletions(-) rename src/common/features/chats/components/{chat-input/index.tsx => chat-input.tsx} (91%) rename src/common/features/chats/components/{chats-messages-box/index.tsx => chat-message-box.tsx} (89%) rename src/common/features/chats/components/{chats-messages-header/index.tsx => chat-messages-header.tsx} (66%) rename src/common/features/chats/components/{chats-messages-view/index.tsx => chat-messages-view.tsx} (90%) rename src/common/features/chats/components/{chats-profile-box/index.tsx => chat-profile-box.tsx} (91%) rename src/common/features/chats/components/{chats-dropdown-menu/index.tsx => chats-dropdown-menu.tsx} (82%) rename src/common/features/chats/components/{join-chat/index.tsx => join-chat.tsx} (89%) rename src/common/features/chats/components/{join-community-chat-btn/index.tsx => join-community-chat-btn.tsx} (93%) rename src/common/features/chats/components/{manage-chat-key/index.tsx => manage-chat-key.tsx} (85%) diff --git a/src/common/features/chats/chat-context-provider.tsx b/src/common/features/chats/chat-context-provider.tsx index 90a65488047..1cfa6ad591d 100644 --- a/src/common/features/chats/chat-context-provider.tsx +++ b/src/common/features/chats/chat-context-provider.tsx @@ -23,11 +23,11 @@ interface Context { messageServiceInstance: MessageService | null; hasUserJoinedChat: boolean; currentChannel: Channel | null; - showSideBar: boolean; + showMobileMessageBox: boolean; windowWidth: number; maxHeight: number; isActveUserRemoved: boolean; - setShowSideBar: (d: boolean) => void; + setShowMobileMessageBox: (d: boolean) => void; setCurrentChannel: (channel: Channel) => void; setRevealPrivKey: (d: boolean) => void; setShowSpinner: (d: boolean) => void; @@ -52,11 +52,11 @@ export const ChatContext = React.createContext({ messageServiceInstance: null, hasUserJoinedChat: false, currentChannel: null, - showSideBar: true, + showMobileMessageBox: true, windowWidth: 0, maxHeight: 0, isActveUserRemoved: false, - setShowSideBar: () => {}, + setShowMobileMessageBox: () => {}, setCurrentChannel: () => {}, setRevealPrivKey: () => {}, setShowSpinner: () => {}, @@ -80,7 +80,7 @@ export const ChatContextProvider = (props: Props) => { const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); const [shouldUpdateProfile, setShouldUpdateProfile] = useState(false); const [currentChannel, setCurrentChannel] = useState(null); - const [showSideBar, setShowSideBar] = useState(true); + const [showMobileMessageBox, setShowMobileMessageBox] = useState(true); const [windowWidth, setWindowWidth] = useState(0); const [maxHeight, setMaxHeight] = useState(0); const [isActveUserRemoved, setIsActiveUserRemoved] = useState(false); @@ -140,13 +140,7 @@ export const ChatContextProvider = (props: Props) => { useDebounce(() => setShowSpinner(false), 5000, [showSpinner]); - const handleShowSideBar = () => { - if (window.innerWidth < 768) { - setShowSideBar(false); - } else { - setShowSideBar(true); - } - }; + const handleShowSideBar = () => setShowMobileMessageBox(window.innerWidth < 768); const getActiveUserKeys = async () => { const pubKey = await getUserChatPublicKey(activeUser?.username!); @@ -193,11 +187,11 @@ export const ChatContextProvider = (props: Props) => { messageServiceInstance, hasUserJoinedChat, currentChannel, - showSideBar, + showMobileMessageBox, windowWidth, maxHeight, isActveUserRemoved, - setShowSideBar, + setShowMobileMessageBox, setCurrentChannel, setRevealPrivKey, setShowSpinner, diff --git a/src/common/features/chats/components/chat-input/index.tsx b/src/common/features/chats/components/chat-input.tsx similarity index 91% rename from src/common/features/chats/components/chat-input/index.tsx rename to src/common/features/chats/components/chat-input.tsx index a75a1320ff0..5feb5a39c70 100644 --- a/src/common/features/chats/components/chat-input/index.tsx +++ b/src/common/features/chats/components/chat-input.tsx @@ -1,26 +1,25 @@ import React, { useContext, useEffect, useMemo, useRef, useState } from "react"; -import { Channel } from "../../../../../managers/message-manager-types"; -import { EmojiPickerStyleProps } from "../../types"; -import { EmojiPicker } from "../../../../components/emoji-picker"; -import { error } from "../../../../components/feedback"; - +import { Channel } from "../../../../managers/message-manager-types"; +import { EmojiPickerStyleProps } from "../types"; +import { EmojiPicker } from "../../../components/emoji-picker"; +import { error } from "../../../components/feedback"; import { attachFileSvg, chatBoxImageSvg, emoticonHappyOutlineSvg, gifIcon, messageSendSvg -} from "../../../../img/svg"; -import { CHAT_FILE_CONTENT_TYPES, GifImagesStyle, UPLOADING } from "../chat-popup/chat-constants"; -import { useMappedStore } from "../../../../store/use-mapped-store"; -import { _t } from "../../../../i18n"; -import { ChatContext } from "../../chat-context-provider"; +} from "../../../img/svg"; +import { CHAT_FILE_CONTENT_TYPES, GifImagesStyle, UPLOADING } from "./chat-popup/chat-constants"; +import { useMappedStore } from "../../../store/use-mapped-store"; +import { _t } from "../../../i18n"; +import { ChatContext } from "../chat-context-provider"; import { Form } from "@ui/form"; import { FormControl } from "@ui/input"; import { Button } from "@ui/button"; -import { useChatFileUpload } from "../../mutations"; +import { useChatFileUpload } from "../mutations"; import { Dropdown, DropdownItemWithIcon, DropdownMenu, DropdownToggle } from "@ui/dropdown"; -import GifPicker from "../../../../components/gif-picker"; +import GifPicker from "../../../components/gif-picker"; interface Props { isCurrentUser: boolean; diff --git a/src/common/features/chats/components/chats-messages-box/index.tsx b/src/common/features/chats/components/chat-message-box.tsx similarity index 89% rename from src/common/features/chats/components/chats-messages-box/index.tsx rename to src/common/features/chats/components/chat-message-box.tsx index cf2293329e6..808def62598 100644 --- a/src/common/features/chats/components/chats-messages-box/index.tsx +++ b/src/common/features/chats/components/chat-message-box.tsx @@ -1,14 +1,14 @@ import React, { useContext, useEffect, useState } from "react"; import { match } from "react-router"; import { History } from "history"; -import ChatsMessagesHeader from "../chats-messages-header"; -import ChatsMessagesView from "../chats-messages-view"; -import { Channel, ChannelUpdate } from "../../../../../managers/message-manager-types"; -import LinearProgress from "../../../../components/linear-progress"; -import { formattedUserName, getJoinedCommunities, getProfileMetaData } from "../../utils"; -import { useMappedStore } from "../../../../store/use-mapped-store"; -import { CHANNEL } from "../chat-popup/chat-constants"; -import { ChatContext } from "../../chat-context-provider"; +import ChatsMessagesHeader from "./chat-messages-header"; +import ChatsMessagesView from "./chat-messages-view"; +import { Channel, ChannelUpdate } from "../../../../managers/message-manager-types"; +import LinearProgress from "../../../components/linear-progress"; +import { formattedUserName, getJoinedCommunities, getProfileMetaData } from "../utils"; +import { useMappedStore } from "../../../store/use-mapped-store"; +import { CHANNEL } from "./chat-popup/chat-constants"; +import { ChatContext } from "../chat-context-provider"; import { useMount } from "react-use"; import { Button } from "@ui/button"; @@ -34,7 +34,7 @@ export default function ChatsMessagesBox(props: Props) { windowWidth, maxHeight, setCurrentChannel, - setShowSideBar + setShowMobileMessageBox } = useContext(ChatContext); const { channels, updatedChannel } = chat; @@ -137,7 +137,9 @@ export default function ChatsMessagesBox(props: Props) {

Select a chat or start a new conversation

- {windowWidth < 768 && } + {windowWidth < 768 && ( + + )}
) : ( diff --git a/src/common/features/chats/components/chats-messages-header/index.tsx b/src/common/features/chats/components/chat-messages-header.tsx similarity index 66% rename from src/common/features/chats/components/chats-messages-header/index.tsx rename to src/common/features/chats/components/chat-messages-header.tsx index aabbad330be..72c00b4bd3f 100644 --- a/src/common/features/chats/components/chats-messages-header/index.tsx +++ b/src/common/features/chats/components/chat-messages-header.tsx @@ -1,14 +1,14 @@ import React, { useContext } from "react"; import { History } from "history"; -import { useMappedStore } from "../../../../store/use-mapped-store"; -import ChatsCommunityDropdownMenu from "../chats-community-dropdown-menu"; -import UserAvatar from "../../../../components/user-avatar"; -import { CHATPAGE } from "../chat-popup/chat-constants"; -import { Chat } from "../../../../store/chat/types"; -import { formattedUserName } from "../../utils"; -import Link from "../../../../components/alink"; -import { expandSideBar } from "../../../../img/svg"; -import { ChatContext } from "../../chat-context-provider"; +import { useMappedStore } from "../../../store/use-mapped-store"; +import ChatsCommunityDropdownMenu from "./chats-community-dropdown-menu"; +import UserAvatar from "../../../components/user-avatar"; +import { CHATPAGE } from "./chat-popup/chat-constants"; +import { Chat } from "../../../store/chat/types"; +import { formattedUserName } from "../utils"; +import Link from "../../../components/alink"; +import { expandSideBar } from "../../../img/svg"; +import { ChatContext } from "../chat-context-provider"; import { Button } from "@ui/button"; interface Props { @@ -19,7 +19,7 @@ interface Props { export default function ChatsMessagesHeader(props: Props) { const { username } = props; const { chat } = useMappedStore(); - const { setShowSideBar } = useContext(ChatContext); + const { setShowMobileMessageBox } = useContext(ChatContext); const isChannel = (username: string) => { if (username.startsWith("@")) { @@ -41,7 +41,13 @@ export default function ChatsMessagesHeader(props: Props) { return (
- - )}
) : ( diff --git a/src/common/features/chats/components/chat-messages-header.tsx b/src/common/features/chats/components/chat-messages-header.tsx index 72c00b4bd3f..4ddfcbd1095 100644 --- a/src/common/features/chats/components/chat-messages-header.tsx +++ b/src/common/features/chats/components/chat-messages-header.tsx @@ -8,8 +8,8 @@ import { Chat } from "../../../store/chat/types"; import { formattedUserName } from "../utils"; import Link from "../../../components/alink"; import { expandSideBar } from "../../../img/svg"; -import { ChatContext } from "../chat-context-provider"; import { Button } from "@ui/button"; +import { ChatContext } from "../chat-context-provider"; interface Props { username: string; @@ -18,15 +18,10 @@ interface Props { export default function ChatsMessagesHeader(props: Props) { const { username } = props; + const { setReceiverPubKey } = useContext(ChatContext); const { chat } = useMappedStore(); - const { setShowMobileMessageBox } = useContext(ChatContext); - const isChannel = (username: string) => { - if (username.startsWith("@")) { - return false; - } - return true; - }; + const isChannel = (username: string) => !username.startsWith("@"); const formattedName = (username: string, chat: Chat) => { if (username && !username.startsWith("@")) { @@ -46,7 +41,8 @@ export default function ChatsMessagesHeader(props: Props) { className="md:hidden" noPadding={true} icon={expandSideBar} - onClick={() => setShowMobileMessageBox(!setShowMobileMessageBox)} + to="/chats" + onClick={() => setReceiverPubKey("")} /> +
{revealPrivKey && windowWidth > 768 && ( @@ -55,12 +54,7 @@ export function ChatSidebarHeader({ history }: Props) { {chatPrivKey && (
{ - setRevealPrivKey(!revealPrivKey); - if (windowWidth < 768) { - setShowMobileMessageBox(false); - } - }} + onManageChatKey={() => setRevealPrivKey(!revealPrivKey)} history={history} />
diff --git a/src/common/features/chats/screens/chats.tsx b/src/common/features/chats/screens/chats.tsx index 9e8851ffe55..7930230356a 100644 --- a/src/common/features/chats/screens/chats.tsx +++ b/src/common/features/chats/screens/chats.tsx @@ -30,19 +30,23 @@ export const Chats = (props: Props) => { const username = match.params.username; - const { - receiverPubKey, - showSpinner, - activeUserKeys, - revealPrivKey, - chatPrivKey, - showMobileMessageBox - } = useContext(ChatContext); + const { receiverPubKey, showSpinner, activeUserKeys, revealPrivKey, chatPrivKey } = + useContext(ChatContext); const isReady = useMemo( () => !!(activeUser && activeUserKeys?.pub && chatPrivKey), [activeUserKeys, activeUserKeys, chatPrivKey] ); + const isShowManageKey = useMemo(() => isReady && revealPrivKey, [isReady, revealPrivKey]); + const isShowChatRoom = useMemo( + () => isReady && !showSpinner && !!receiverPubKey && !revealPrivKey, + [isReady, showSpinner, receiverPubKey, revealPrivKey] + ); + const isShowDefaultScreen = useMemo( + () => isReady && !receiverPubKey && !revealPrivKey && !showSpinner, + [isReady, receiverPubKey, revealPrivKey, showSpinner] + ); + const isShowImportChats = useMemo(() => !isReady && !showSpinner, [isReady, showSpinner]); useEffect(() => { document.body.style.overflow = "hidden"; @@ -51,13 +55,14 @@ export const Chats = (props: Props) => { document.body.style.overflow = "auto"; }; }, []); + return ( -
+
-
-
+
+
{isReady ? : <>}
@@ -65,13 +70,13 @@ export const Chats = (props: Props) => { className={classNameObject({ "col-span-12 md:col-span-8 xl:col-span-9 h-[calc(100vh-3rem-69px)] overflow-y-auto absolute w-full bg-white z-10 md:static duration-500": true, - "left-0": showMobileMessageBox, - "left-[100%]": showMobileMessageBox + "left-0": isShowChatRoom || isShowManageKey, + "left-[100%]": !isShowChatRoom && !isShowManageKey })} > - {isReady && revealPrivKey && ( + {isShowManageKey && (
-
+
@@ -81,15 +86,13 @@ export const Chats = (props: Props) => {
)} - {!isReady && !showSpinner && ( + {isShowImportChats && (
)} - {isReady && !showSpinner && receiverPubKey && !revealPrivKey && ( - - )} - {isReady && !receiverPubKey && !revealPrivKey && !showSpinner && ( + {isShowChatRoom && } + {isShowDefaultScreen && (
Hello, @{activeUser?.username} diff --git a/src/common/features/ui/button/index.tsx b/src/common/features/ui/button/index.tsx index d0134ffacaf..7caf859f61e 100644 --- a/src/common/features/ui/button/index.tsx +++ b/src/common/features/ui/button/index.tsx @@ -3,10 +3,11 @@ import { ButtonProps } from "./props"; import { classNameObject } from "../../../helper/class-name-object"; import { BUTTON_OUTLINE_STYLES, BUTTON_SIZES, BUTTON_STYLES } from "./styles"; import { useFilteredProps } from "../../../util/props-filter"; +import { Link, NavLinkProps } from "react-router-dom"; export * from "./props"; -export const Button = forwardRef( +export const Button = forwardRef( (props, ref) => { const nativeProps = useFilteredProps>(props, [ "appearance", @@ -49,6 +50,11 @@ export const Button = forwardRef + ) : "to" in props ? ( + + {children} + {icon} + ) : (
))} - {directContacts.length !== 0 && ( + {directContacts?.length !== 0 && (
{_t("chat.direct-messages")}
)} - {directContacts.map((contact) => ( + {directContacts?.map((contact) => ( { + const { + chat: { channels } + } = useMappedStore(); + const [since, setSince] = useState(0); useEffect(() => { @@ -15,7 +18,7 @@ export const useMessageServiceListener = ( const timer = setTimeout( () => { messageService?.listen( - chatChannels.map((x) => x.id), + channels.map((x) => x.id), Math.floor((since || Date.now()) / 1000) ); setSince(Date.now()); @@ -26,5 +29,5 @@ export const useMessageServiceListener = ( return () => { clearTimeout(timer); }; - }, [since, messageServiceReady, messageService, chatChannels]); + }, [since, messageServiceReady, messageService, channels]); }; diff --git a/src/common/features/chats/managers/message-manager.tsx b/src/common/features/chats/managers/message-manager.tsx index 6b73e7bd18f..e63b6f2db3e 100644 --- a/src/common/features/chats/managers/message-manager.tsx +++ b/src/common/features/chats/managers/message-manager.tsx @@ -4,7 +4,6 @@ import { NostrKeysType } from "../types"; import { Channel, ChannelUpdate, - DirectContact, DirectMessage, Keys, MessagesObject, @@ -17,6 +16,7 @@ import { useMappedStore } from "../../../store/use-mapped-store"; import { ChatContext } from "../chat-context-provider"; import { usePrevious } from "../../../util/use-previous"; import { useMessageServiceListener } from "../hooks/use-message-service-listener"; +import { useDirectContactsQuery } from "../queries"; export const setNostrkeys = (keys: NostrKeysType) => { const detail: NostrKeysType = { @@ -32,7 +32,6 @@ const MessageManager = () => { activeUser, chat, addDirectMessages, - addDirectContacts, addPublicMessage, addChannels, addProfile, @@ -45,6 +44,7 @@ const MessageManager = () => { addPreviousPublicMessages, resetChat } = useMappedStore(); + const { data: directContacts } = useDirectContactsQuery(); const prevActiveUser = usePrevious(activeUser); @@ -116,7 +116,7 @@ const MessageManager = () => { }; //Listen for events in an interval. - useMessageServiceListener(messageServiceReady, messageService, chat.channels); + useMessageServiceListener(messageServiceReady, messageService); // // Ready state handler const handleReadyState = () => { @@ -145,32 +145,7 @@ const MessageManager = () => { }; }, [messageService, chat.profiles]); - //Direct contact handler - const handleDirectContact = (data: DirectContact[]) => { - const result = [...chat.directContacts]; - data.forEach(({ name, pubkey }) => { - const isPresent = chat.directContacts.some( - (obj) => obj.name === name && obj.pubkey === pubkey - ); - if (!isPresent) { - result.push({ name, pubkey }); - } - }); - if (result.length !== 0) { - addDirectContacts(result); - } - }; - - useEffect(() => { - messageService?.removeListener(MessageEvents.DirectContact, handleDirectContact); - messageService?.addListener(MessageEvents.DirectContact, handleDirectContact); - return () => { - messageService?.removeListener(MessageEvents.DirectContact, handleDirectContact); - }; - }, [messageService]); - //Direct message ahandle before sent - const handleDirectMessageBeforeSent = (data: DirectMessage[]) => { data.map((m) => { const { peer, id } = m; @@ -238,10 +213,10 @@ const MessageManager = () => { }; useEffect(() => { - if (chat.directContacts.length !== 0) { + if (directContacts?.length !== 0) { setDirectMessages(); } - }, [chat.directContacts, directMessageBuffer]); + }, [directContacts, directMessageBuffer]); useEffect(() => { messageService?.removeListener( @@ -433,7 +408,6 @@ const MessageManager = () => { messageService?.removeListener(MessageEvents.Ready, handleReadyState); messageService?.removeListener(MessageEvents.ProfileUpdate, handleProfileUpdate); messageService?.removeListener(MessageEvents.ChannelCreation, handleChannelCreation); - messageService?.removeListener(MessageEvents.DirectContact, handleDirectContact); messageService?.removeListener(MessageEvents.LeftChannelList, handleLeftChannelList); messageService?.removeListener(MessageEvents.ChannelUpdate, handleChannelUpdate); messageService?.removeListener( diff --git a/src/common/features/chats/queries/direct-contacts-query.ts b/src/common/features/chats/queries/direct-contacts-query.ts new file mode 100644 index 00000000000..ea88342a7fa --- /dev/null +++ b/src/common/features/chats/queries/direct-contacts-query.ts @@ -0,0 +1,26 @@ +import { ChatQueries } from "./queries"; +import { MessageEvents } from "../../../helper/message-service"; +import { DirectContact } from "../managers/message-manager-types"; +import { useMessageListenerQuery } from "./message-listener-query"; + +export function useDirectContactsQuery() { + return useMessageListenerQuery( + [ChatQueries.DIRECT_CONTACTS], + MessageEvents.DirectContact, + (data, directContacts, resolver) => { + const result = [...data]; + directContacts.forEach(({ name, pubkey }) => { + const isPresent = data.some((obj) => obj.name === name && obj.pubkey === pubkey); + if (!isPresent) { + result.push({ name, pubkey }); + } + }); + if (result.length !== 0) { + resolver(result); + } + }, + { + initialData: [] + } + ); +} diff --git a/src/common/features/chats/queries/index.ts b/src/common/features/chats/queries/index.ts index abb89894ef7..3173dc55d02 100644 --- a/src/common/features/chats/queries/index.ts +++ b/src/common/features/chats/queries/index.ts @@ -1,2 +1,4 @@ export * from "./search-users-query"; export * from "./queries"; +export * from "./direct-contacts-query"; +export * from "./message-listener-query"; diff --git a/src/common/features/chats/queries/keys-query.ts b/src/common/features/chats/queries/keys-query.ts index 67c2bfd1352..90809aabbe7 100644 --- a/src/common/features/chats/queries/keys-query.ts +++ b/src/common/features/chats/queries/keys-query.ts @@ -31,7 +31,6 @@ export function useKeysQuery( queryFn: () => getUserChatPublicKey(activeUser?.username!), onSuccess: (key: string | null) => { if (key) { - console.log("key is : ", key); setActiveUserKeys({ ...activeUserKeys, pub: key @@ -44,7 +43,6 @@ export function useKeysQuery( queryFn: () => getPrivateKey(activeUser?.username!), onSuccess: (key: string | null) => { if (key) { - console.log("private is ", key); setActiveUserKeys({ ...activeUserKeys, priv: key diff --git a/src/common/features/chats/queries/message-listener-query.tsx b/src/common/features/chats/queries/message-listener-query.tsx new file mode 100644 index 00000000000..ac15bfba565 --- /dev/null +++ b/src/common/features/chats/queries/message-listener-query.tsx @@ -0,0 +1,80 @@ +import { + QueryKey, + QueryObserverResult, + QueryOptions, + useQuery, + useQueryClient +} from "@tanstack/react-query"; +import React, { createContext, PropsWithChildren, useContext, useMemo } from "react"; +import { ChatContext } from "../chat-context-provider"; +import { MessageEvents } from "../../../helper/message-service"; +import useMap from "react-use/lib/useMap"; +import useDebounce from "react-use/lib/useDebounce"; + +export const MessageListenerQueriesContext = createContext<{ + subscribers: Record; + set: (key: string, value: boolean) => void; + get: (key: string) => boolean; +}>({ + subscribers: {}, + set: () => {}, + get: () => false +}); + +export function MessageListenerQueriesProvider({ children }: PropsWithChildren) { + const [subscribers, { set, get }] = useMap>(); + + return ( + + ); +} + +/** + * Custom hook for listening to messages and updating a query with new data. + * + * @template TQData - The data type returned by the query. + * @template TQKey - The key used to identify the query. + * @param {TQKey} key - The key that identifies the query. + * @param {MessageEvents} event - The message event to listen for. + * @param {(data: TQData, nextData: TQData, resolver: (data: TQData) => void) => void} queryFn - The function that processes the received data and updates the query. + * @param {QueryOptions} [queryOptions] - Additional query options. + * @returns {QueryObserverResult} - The result of the query, including data and query status. + */ +export function useMessageListenerQuery( + key: TQKey, + event: MessageEvents, + queryFn: (data: TQData, nextData: TQData, resolver: (data: TQData) => void) => void, + queryOptions?: QueryOptions +): QueryObserverResult { + const { messageServiceInstance } = useContext(ChatContext); + const { set, get } = useContext(MessageListenerQueriesContext); + const queryClient = useQueryClient(); + + const query = useQuery(key, () => queryClient.getQueryData(key)!!, queryOptions); + + const joinedKey = useMemo(() => key.join(""), [key]); + + const listener = (nextData: TQData) => { + const resolver = (data: TQData) => queryClient.setQueryData(key, data); + queryFn(query.data!!, nextData, resolver); + }; + + useDebounce( + () => { + if (messageServiceInstance) { + if (get(joinedKey)) { + return; + } + + console.log("fetching"); + + messageServiceInstance.addListener(event, listener as any); // todo fix typings + set(joinedKey, true); + } + }, + 100, + [messageServiceInstance] + ); + + return query; +} diff --git a/src/common/features/chats/queries/queries.ts b/src/common/features/chats/queries/queries.ts index 7ee0eb9b99f..0a3002cccf9 100644 --- a/src/common/features/chats/queries/queries.ts +++ b/src/common/features/chats/queries/queries.ts @@ -1,5 +1,6 @@ export enum ChatQueries { SEARCH_USER = "search-user", PUBLIC_KEY = "public-key", - PRIVATE_KEY = "private-key" + PRIVATE_KEY = "private-key", + DIRECT_CONTACTS = "direct-contacts" } diff --git a/src/common/features/chats/utils/check-contiguous-message.ts b/src/common/features/chats/utils/check-contiguous-message.ts index 95dee746833..ae055d7ca62 100644 --- a/src/common/features/chats/utils/check-contiguous-message.ts +++ b/src/common/features/chats/utils/check-contiguous-message.ts @@ -1,4 +1,4 @@ -import { DirectMessage, PublicMessage } from "../../../../managers/message-manager-types"; +import { DirectMessage, PublicMessage } from "../managers/message-manager-types"; export const checkContiguousMessage = ( msg: PublicMessage | DirectMessage, diff --git a/src/common/features/chats/utils/delete-chat-public-key.ts b/src/common/features/chats/utils/delete-chat-public-key.ts index 66ad862a979..714e21d93af 100644 --- a/src/common/features/chats/utils/delete-chat-public-key.ts +++ b/src/common/features/chats/utils/delete-chat-public-key.ts @@ -13,7 +13,6 @@ export const deleteChatPublicKey = (activeUser: ActiveUser | null) => { ls.remove(`${activeUser?.username}_nsPrivKey`); const response = await getAccountFull(activeUser?.username!); const updatedProfile = await updateProfile(response, { ...profile }); - console.log("Updated profile", updatedProfile); resolve(updatedProfile); } catch (error) { reject(error); diff --git a/src/common/features/chats/utils/format-message-date-and-day.ts b/src/common/features/chats/utils/format-message-date-and-day.ts index d76b1c51152..a02a1e0dbd8 100644 --- a/src/common/features/chats/utils/format-message-date-and-day.ts +++ b/src/common/features/chats/utils/format-message-date-and-day.ts @@ -1,6 +1,5 @@ -import { PublicMessage } from "../../../../managers/message-manager-types"; -import { DirectMessage } from "../../../../managers/message-manager-types"; import { formatMessageDate } from "./format-message-time"; +import { DirectMessage, PublicMessage } from "../managers/message-manager-types"; export const formatMessageDateAndDay = ( msg: DirectMessage | PublicMessage, diff --git a/src/common/features/chats/utils/get-joined-communities.ts b/src/common/features/chats/utils/get-joined-communities.ts index c04b2b20567..d533d7c1491 100644 --- a/src/common/features/chats/utils/get-joined-communities.ts +++ b/src/common/features/chats/utils/get-joined-communities.ts @@ -1,4 +1,4 @@ -import { Channel } from "../../../../managers/message-manager-types"; +import { Channel } from "../managers/message-manager-types"; export const getJoinedCommunities = (channels: Channel[], leftChannels: string[]) => { return channels.filter((item) => !leftChannels.includes(item.id)); diff --git a/src/common/features/chats/utils/upload-channel-data.ts b/src/common/features/chats/utils/upload-channel-data.ts index 0eb3562df2a..45458239bab 100644 --- a/src/common/features/chats/utils/upload-channel-data.ts +++ b/src/common/features/chats/utils/upload-channel-data.ts @@ -1,6 +1,6 @@ -import { Channel } from "../../../../managers/message-manager-types"; import { getAccountFull } from "../../../api/hive"; import { updateProfile } from "../../../api/operations"; +import { Channel } from "../managers/message-manager-types"; export const setChannelMetaData = async (username: string, channel: Channel) => { try { diff --git a/src/common/features/chats/utils/use-fetch-community-messages.ts b/src/common/features/chats/utils/use-fetch-community-messages.ts index 4dae981e7ff..cbfd3f477aa 100644 --- a/src/common/features/chats/utils/use-fetch-community-messages.ts +++ b/src/common/features/chats/utils/use-fetch-community-messages.ts @@ -1,5 +1,5 @@ -import { Channel } from "../../../../managers/message-manager-types"; import { publicMessagesList } from "../../../store/chat/types"; +import { Channel } from "../managers/message-manager-types"; export const fetchCommunityMessages = ( publicMessages: publicMessagesList[], diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts index 10ad7a01976..7f9656b8512 100644 --- a/src/common/helper/message-service.ts +++ b/src/common/helper/message-service.ts @@ -11,7 +11,7 @@ import { Metadata, Profile, PublicMessage -} from "../../managers/message-manager-types"; +} from "../features/chats/managers/message-manager-types"; import { encrypt, decrypt } from "../../lib/nostr-tools/nip04"; import SimplePool from "../../lib/nostr-tools/pool"; import { signEvent, getEventHash, Event } from "../../lib/nostr-tools/event"; diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts index 6bbcb79a15c..3d9b6232c81 100644 --- a/src/common/store/chat/index.ts +++ b/src/common/store/chat/index.ts @@ -1,33 +1,33 @@ import { Channel, + ChannelUpdate, DirectMessage, - PublicMessage, + MessagesObject, Profile, - ChannelUpdate, - MessagesObject -} from "../../../managers/message-manager-types"; + PublicMessage +} from "../../features/chats/managers/message-manager-types"; import { Dispatch } from "redux"; import { - Chat, Actions, ActionTypes, + AddChannelsAction, + AddPreviousPublicMessagesAction, + Chat, + DeleteDirectMessagesAction, + DeletePublicMessagesAction, DirectContactsAction, DirectContactsType, DirectMessagesAction, - ResetChatAction, - AddChannelsAction, - PublicMessagesAction, - ProfilesAction, LeftChannelsAction, - UpdateChannelAction, - ReplacePublicMessagesAction, - VerifyPublicMessageSendingAction, + ProfilesAction, + PublicMessagesAction, ReplaceDirectMessagesAction, + ReplacePublicMessagesAction, + ResetChatAction, + UpdateChannelAction, VerifyDirectMessageSendingAction, - DeletePublicMessagesAction, - DeleteDirectMessagesAction, - AddPreviousPublicMessagesAction + VerifyPublicMessageSendingAction } from "./types"; export const initialState: Chat = { diff --git a/src/common/store/chat/types.ts b/src/common/store/chat/types.ts index 2d9629331ae..a60f15b7f1d 100644 --- a/src/common/store/chat/types.ts +++ b/src/common/store/chat/types.ts @@ -5,7 +5,7 @@ import { MessagesObject, Profile, PublicMessage -} from "../../../managers/message-manager-types"; +} from "../../features/chats/managers/message-manager-types"; export interface DirectContactsType { name: string; @@ -80,6 +80,7 @@ export interface ProfilesAction { type: ActionTypes.PROFILES; data: Profile[]; } + export interface LeftChannelsAction { type: ActionTypes.LEFTCHANNELLIST; data: string[]; @@ -113,6 +114,7 @@ export interface VerifyDirectMessageSendingAction { data: DirectMessage; peer: string; } + export interface DeletePublicMessagesAction { type: ActionTypes.DELETEPUBLICMESSAGE; msgId: string; From 7d9a16c7bb62eac4ad8be0fe259113ef6f9e00da Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Wed, 1 Nov 2023 01:31:20 +0600 Subject: [PATCH 109/179] Chats: Retrieving direct and public messages via query instead of store and manager --- src/common/app.tsx | 3 +- .../chat-sidebar-direct-contact.tsx | 7 +- .../chats/managers/message-manager.tsx | 97 +---------- .../chats/queries/chat-profiles-query.ts | 18 ++ .../chats/queries/direct-messages-query.ts | 164 ++++++++++++++++++ src/common/features/chats/queries/index.ts | 2 + .../chats/queries/message-listener-query.tsx | 15 +- src/common/features/chats/queries/queries.ts | 9 +- .../utils/use-get-direct-last-message.ts | 16 +- src/common/store/actions.ts | 23 ++- src/common/store/chat/index.ts | 31 ---- src/common/store/chat/types.ts | 1 - 12 files changed, 217 insertions(+), 169 deletions(-) create mode 100644 src/common/features/chats/queries/chat-profiles-query.ts create mode 100644 src/common/features/chats/queries/direct-messages-query.ts diff --git a/src/common/app.tsx b/src/common/app.tsx index 3ef80c2f0df..fa301c026fe 100644 --- a/src/common/app.tsx +++ b/src/common/app.tsx @@ -31,6 +31,7 @@ import { EntriesCacheManager } from "./core"; import { UserActivityRecorder } from "./components/user-activity-recorder"; import { ChatContextProvider } from "./features/chats/chat-context-provider"; import { ChatPopUp } from "./features/chats/components/chat-popup"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; // Define lazy pages const ProfileContainer = loadable(() => import("./pages/profile-functional")); @@ -100,7 +101,7 @@ const App = (props: any) => { return ( {/*Excluded from production*/} - {/**/} + diff --git a/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx b/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx index b06fc8bc447..71c0e3301b6 100644 --- a/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx +++ b/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx @@ -6,6 +6,7 @@ import UserAvatar from "../../../../components/user-avatar"; import { useMappedStore } from "../../../../store/use-mapped-store"; import { classNameObject } from "../../../../helper/class-name-object"; import { DirectContact } from "../../managers/message-manager-types"; +import { useDirectMessagesQuery } from "../../queries"; interface Props { contact: DirectContact; @@ -17,9 +18,11 @@ export function ChatSidebarDirectContact({ contact, username, handleRevealPrivKe const { setReceiverPubKey } = useContext(ChatContext); const { chat } = useMappedStore(); + const { data: directMessages } = useDirectMessagesQuery(contact.name); + const lastMessage = useMemo( - () => getDirectLastMessage(contact.pubkey, chat.directMessages), - [chat.directMessages, contact] + () => getDirectLastMessage(directMessages), + [directMessages, contact] ); const lastMessageDate = useMemo(() => getRelativeDate(lastMessage?.created), [lastMessage]); diff --git a/src/common/features/chats/managers/message-manager.tsx b/src/common/features/chats/managers/message-manager.tsx index e63b6f2db3e..2f68df50b83 100644 --- a/src/common/features/chats/managers/message-manager.tsx +++ b/src/common/features/chats/managers/message-manager.tsx @@ -145,94 +145,6 @@ const MessageManager = () => { }; }, [messageService, chat.profiles]); - //Direct message ahandle before sent - const handleDirectMessageBeforeSent = (data: DirectMessage[]) => { - data.map((m) => { - const { peer, id } = m; - setReplacedDirectMessagesBuffer((prevBuffer) => [...prevBuffer, id]); - addDirectMessages(peer, m); - checkDirectMessageSending(peer, m); - }); - }; - - useEffect(() => { - messageService?.removeListener( - MessageEvents.DirectMessageBeforeSent, - handleDirectMessageBeforeSent - ); - messageService?.addListener( - MessageEvents.DirectMessageBeforeSent, - handleDirectMessageBeforeSent - ); - - return () => { - messageService?.removeListener( - MessageEvents.DirectMessageBeforeSent, - handleDirectMessageBeforeSent - ); - }; - }, [messageService, chat.directMessages]); - - const checkDirectMessageSending = (peer: string, data: DirectMessage) => { - setTimeout(() => { - verifyDirectMessageSending(peer, data); - }, 20000); - }; - - // Direct message handler after sent - const handleDirectMessageAfterSent = (data: DirectMessage[]) => { - if (isDirectChatCreated) { - data.forEach((message) => { - const { peer, id } = message; - if (replacedDirectMessagesBuffer.includes(id)) { - setReplacedDirectMessagesBuffer((prevBuffer) => - prevBuffer.filter((messageId) => messageId !== id) - ); - replaceDirectMessage(peer, message); - } else { - addDirectMessages(peer, message); - } - }); - } else { - setDirectMessageBuffer((directMessageBuffer) => [...directMessageBuffer!, ...data]); - } - messageService?.checkProfiles(data.map((x) => x.peer)); - }; - - const setDirectMessages = () => { - directMessageBuffer.forEach((obj) => { - const { peer } = obj; - const matchingStateItem = chat.directMessages.find((stateItem) => stateItem.peer === peer); - if (matchingStateItem) { - addDirectMessages(peer, obj); - setDirectMessageBuffer((prevMessageBuffer) => - prevMessageBuffer.filter((message) => message.id !== obj.id) - ); - } - }); - }; - - useEffect(() => { - if (directContacts?.length !== 0) { - setDirectMessages(); - } - }, [directContacts, directMessageBuffer]); - - useEffect(() => { - messageService?.removeListener( - MessageEvents.DirectMessageAfterSent, - handleDirectMessageAfterSent - ); - messageService?.addListener(MessageEvents.DirectMessageAfterSent, handleDirectMessageAfterSent); - - return () => { - messageService?.removeListener( - MessageEvents.DirectMessageAfterSent, - handleDirectMessageAfterSent - ); - }; - }, [messageService, chat.directMessages]); - // Channel creation handler const handleChannelCreation = (data: Channel[]) => { addChannels(data.filter((x) => !chat.channels.find((y) => y.id === x.id))); @@ -298,6 +210,7 @@ const MessageManager = () => { //Public Message handler after sent const handlePublicMessageAfterSent = (data: PublicMessage[]) => { + console.log("data", data); if (isCommunityCreated) { data.forEach((message) => { const { root, id } = message; @@ -422,14 +335,6 @@ const MessageManager = () => { MessageEvents.PublicMessageAfterSent, handlePublicMessageAfterSent ); - messageService?.removeListener( - MessageEvents.DirectMessageBeforeSent, - handleDirectMessageBeforeSent - ); - messageService?.removeListener( - MessageEvents.DirectMessageAfterSent, - handleDirectMessageAfterSent - ); }; }, [messageService]); diff --git a/src/common/features/chats/queries/chat-profiles-query.ts b/src/common/features/chats/queries/chat-profiles-query.ts new file mode 100644 index 00000000000..ea7fe71cd6a --- /dev/null +++ b/src/common/features/chats/queries/chat-profiles-query.ts @@ -0,0 +1,18 @@ +import { useMessageListenerQuery } from "./message-listener-query"; +import { ChatQueries } from "./queries"; +import { MessageEvents } from "../../../helper/message-service"; +import { Profile } from "../managers/message-manager-types"; + +export function useChatProfilesQuery() { + return useMessageListenerQuery( + [ChatQueries.PROFILES], + MessageEvents.ProfileUpdate, + (_, nextData, resolver) => { + resolver(nextData); + }, + { + initialData: [], + queryFn: () => [] + } + ); +} diff --git a/src/common/features/chats/queries/direct-messages-query.ts b/src/common/features/chats/queries/direct-messages-query.ts new file mode 100644 index 00000000000..13287ea4cc0 --- /dev/null +++ b/src/common/features/chats/queries/direct-messages-query.ts @@ -0,0 +1,164 @@ +import { useContext, useMemo } from "react"; +import { ChatContext } from "../chat-context-provider"; +import { MessageEvents } from "../../../helper/message-service"; +import { useMessageListenerQuery } from "./message-listener-query"; +import { ChatQueries } from "./queries"; +import { useQuery, useQueryClient } from "@tanstack/react-query"; +import { DirectMessage, Message, PublicMessage } from "../managers/message-manager-types"; +import { useDirectContactsQuery } from "./direct-contacts-query"; +import isCommunity from "../../../helper/is-community"; +import { useChatProfilesQuery } from "./chat-profiles-query"; + +export function useDirectMessagesQuery(username?: string) { + const { messageServiceInstance } = useContext(ChatContext); + const queryClient = useQueryClient(); + + const { data: directContacts } = useDirectContactsQuery(); + const { data: profiles } = useChatProfilesQuery(); + + const contact = useMemo( + () => directContacts?.find((i) => i.name === username), + [directContacts, username] + ); + const isPublic = useMemo(() => isCommunity(username ?? ""), [username]); + + // const checkDirectMessageSending = (peer: string, data: DirectMessage) => { + // setTimeout(() => { + // verifyDirectMessageSending(peer, data); + // }, 20000); + // }; + // + // const handleDirectMessageBeforeSent = (data: DirectMessage[]) => { + // data.map((m) => { + // const { peer, id } = m; + // setReplacedDirectMessagesBuffer((prevBuffer) => [...prevBuffer, id]); + // addDirectMessages(peer, m); + // checkDirectMessageSending(peer, m); + // }); + // }; + // }); + // const handlePublicMessageBeforeSent = (data: PublicMessage[]) => { + // data.map((m) => { + // const { id, root } = m; + // setReplacedPublicMessagesBuffer((prevBuffer) => [...prevBuffer, id]); + // addPublicMessage(root, m); + // checkPublicMessageSending(root, m); + // }); + // }; + + // TODO: CONCACT MESSAGES AND CHECK FOR DUPLICATES + const onBeforeSent = (nextData: Message[] | undefined) => { + const nextMessages = [ + ...(queryClient.getQueryData([ChatQueries.DIRECT_MESSAGES, username]) ?? []), + ...(nextData?.filter((i) => + !isPublic && "peer" in i ? i.peer === contact?.pubkey : i.root === contact?.pubkey + ) ?? []) + ]; + queryClient.setQueryData([ChatQueries.DIRECT_MESSAGES, username], nextMessages); + queryClient.invalidateQueries([ChatQueries.DIRECT_MESSAGES, username]); + }; + + const onAfterSent = (nextData: Message[]) => { + const nextMessages = [ + ...(queryClient.getQueryData([ChatQueries.DIRECT_MESSAGES, username]) ?? []), + ...(nextData?.filter((i) => + !isPublic && "peer" in i ? i.peer === contact?.pubkey : i.root === contact?.pubkey + ) ?? []) + ]; + queryClient.setQueryData([ChatQueries.DIRECT_MESSAGES, username], nextMessages); + queryClient.invalidateQueries([ChatQueries.DIRECT_MESSAGES, username]); + }; + + useMessageListenerQuery( + [ChatQueries.BEFORE_DIRECT_MESSAGES_TEMP], + MessageEvents.DirectMessageBeforeSent, + (_, nextData, resolver) => { + onBeforeSent(nextData); + resolver(nextData); + }, + { + queryFn: () => [], + enabled: !!contact, + initialData: [] + } + ); + + useMessageListenerQuery( + [ChatQueries.AFTER_DIRECT_MESSAGES_TEMP], + MessageEvents.DirectMessageAfterSent, + (_, nextData, resolver) => { + onAfterSent(nextData); + resolver(nextData); + + messageServiceInstance?.checkProfiles(nextData.map((x) => x.peer)); + }, + { + queryFn: () => [], + enabled: !!contact, + initialData: [] + } + ); + + useMessageListenerQuery( + [ChatQueries.BEFORE_PUBLIC_MESSAGES_TEMP], + MessageEvents.PublicMessageBeforeSent, + (_, nextData, resolver) => { + onBeforeSent(nextData); + resolver(nextData); + }, + { + queryFn: () => [], + enabled: !!contact, + initialData: [] + } + ); + + useMessageListenerQuery( + [ChatQueries.AFTER_PUBLIC_MESSAGES_TEMP], + MessageEvents.PublicMessageAfterSent, + (_, nextData, resolver) => { + onAfterSent(nextData); + resolver(nextData); + + const uniqueUsers = nextData + .filter((message) => profiles?.find((profile) => profile.creator !== message.creator)) + .reduce>((acc, next) => acc.add(next.creator), new Set()); + messageServiceInstance?.loadProfiles(Array.from(uniqueUsers.values())); + }, + { + queryFn: () => [], + enabled: !!contact, + initialData: [] + } + ); + + useMessageListenerQuery( + [ChatQueries.PREVIOUS_PUBLIC_MESSAGES], + MessageEvents.PreviousPublicMessages, + (_, nextData, resolver) => { + queryClient.setQueryData( + [ChatQueries.DIRECT_MESSAGES, username], + [ + ...(queryClient.getQueryData([ChatQueries.DIRECT_MESSAGES]) ?? []), + ...(nextData ?? []) + ] + ); + + resolver(nextData); + }, + { + queryFn: () => [], + enabled: !!contact, + initialData: [] + } + ); + + return useQuery( + [ChatQueries.DIRECT_MESSAGES, username], + () => queryClient.getQueryData([ChatQueries.DIRECT_MESSAGES, username]) ?? [], + { + enabled: !!contact, + initialData: [] + } + ); +} diff --git a/src/common/features/chats/queries/index.ts b/src/common/features/chats/queries/index.ts index 3173dc55d02..5f8a6323947 100644 --- a/src/common/features/chats/queries/index.ts +++ b/src/common/features/chats/queries/index.ts @@ -2,3 +2,5 @@ export * from "./search-users-query"; export * from "./queries"; export * from "./direct-contacts-query"; export * from "./message-listener-query"; +export * from "./direct-messages-query"; +export * from "./chat-profiles-query"; diff --git a/src/common/features/chats/queries/message-listener-query.tsx b/src/common/features/chats/queries/message-listener-query.tsx index ac15bfba565..2e2d5db22e7 100644 --- a/src/common/features/chats/queries/message-listener-query.tsx +++ b/src/common/features/chats/queries/message-listener-query.tsx @@ -1,15 +1,10 @@ -import { - QueryKey, - QueryObserverResult, - QueryOptions, - useQuery, - useQueryClient -} from "@tanstack/react-query"; +import { QueryKey, QueryObserverResult, useQuery, useQueryClient } from "@tanstack/react-query"; import React, { createContext, PropsWithChildren, useContext, useMemo } from "react"; import { ChatContext } from "../chat-context-provider"; import { MessageEvents } from "../../../helper/message-service"; import useMap from "react-use/lib/useMap"; import useDebounce from "react-use/lib/useDebounce"; +import { UseQueryOptions } from "@tanstack/react-query/src/types"; export const MessageListenerQueriesContext = createContext<{ subscribers: Record; @@ -37,14 +32,14 @@ export function MessageListenerQueriesProvider({ children }: PropsWithChildren void) => void} queryFn - The function that processes the received data and updates the query. - * @param {QueryOptions} [queryOptions] - Additional query options. + * @param {UseQueryOptions} [queryOptions] - Additional query options. * @returns {QueryObserverResult} - The result of the query, including data and query status. */ export function useMessageListenerQuery( key: TQKey, event: MessageEvents, queryFn: (data: TQData, nextData: TQData, resolver: (data: TQData) => void) => void, - queryOptions?: QueryOptions + queryOptions?: UseQueryOptions ): QueryObserverResult { const { messageServiceInstance } = useContext(ChatContext); const { set, get } = useContext(MessageListenerQueriesContext); @@ -66,8 +61,6 @@ export function useMessageListenerQuery( return; } - console.log("fetching"); - messageServiceInstance.addListener(event, listener as any); // todo fix typings set(joinedKey, true); } diff --git a/src/common/features/chats/queries/queries.ts b/src/common/features/chats/queries/queries.ts index 0a3002cccf9..b024aaebc56 100644 --- a/src/common/features/chats/queries/queries.ts +++ b/src/common/features/chats/queries/queries.ts @@ -2,5 +2,12 @@ export enum ChatQueries { SEARCH_USER = "search-user", PUBLIC_KEY = "public-key", PRIVATE_KEY = "private-key", - DIRECT_CONTACTS = "direct-contacts" + DIRECT_CONTACTS = "direct-contacts", + DIRECT_MESSAGES = "direct-messages", + BEFORE_DIRECT_MESSAGES_TEMP = "before-direct-messages-temp", + AFTER_DIRECT_MESSAGES_TEMP = "after-direct-messages-temp", + BEFORE_PUBLIC_MESSAGES_TEMP = "before-public-messages-temp", + AFTER_PUBLIC_MESSAGES_TEMP = "after-public-messages-temp", + PREVIOUS_PUBLIC_MESSAGES = "previous-public-messages", + PROFILES = "profiles" } diff --git a/src/common/features/chats/utils/use-get-direct-last-message.ts b/src/common/features/chats/utils/use-get-direct-last-message.ts index 26cbbde59a8..3b74429c298 100644 --- a/src/common/features/chats/utils/use-get-direct-last-message.ts +++ b/src/common/features/chats/utils/use-get-direct-last-message.ts @@ -1,17 +1,7 @@ -import { directMessagesList } from "../../../store/chat/types"; +import { Message } from "../managers/message-manager-types"; -export const getDirectLastMessage = (pubkey: string, directMessages: directMessagesList[]) => { - const msgsList = fetchDirectMessages(pubkey, directMessages); - const messages = msgsList.sort((a, b) => a.created - b.created); +export const getDirectLastMessage = (directMessages: Message[]) => { + const messages = directMessages.sort((a, b) => a.created - b.created); const lastMessage = messages.slice(-1); return lastMessage[0]; }; - -const fetchDirectMessages = (peer: string, directMessages: directMessagesList[]) => { - for (const item of directMessages) { - if (item.peer === peer) { - return Object.values(item.chat); - } - } - return []; -}; diff --git a/src/common/store/actions.ts b/src/common/store/actions.ts index 01059ac2484..3df5282d855 100644 --- a/src/common/store/actions.ts +++ b/src/common/store/actions.ts @@ -38,23 +38,21 @@ import { updateNotificationsSettings } from "./notifications"; import { setSigningKey } from "./signing-key"; -import { savePageScroll } from "./persistent-page-scroll"; import { - addDirectContacts, - addDirectMessages, - resetChat, addChannels, - addPublicMessage, - addProfile, + addDirectMessages, addleftChannels, - UpdateChannels, - replacePublicMessage, - verifyPublicMessageSending, + addPreviousPublicMessages, + addProfile, + addPublicMessage, + deleteDirectMessage, + deletePublicMessage, replaceDirectMessage, + replacePublicMessage, + resetChat, + UpdateChannels, verifyDirectMessageSending, - deletePublicMessage, - deleteDirectMessage, - addPreviousPublicMessages + verifyPublicMessageSending } from "./chat"; // @note Do not use it directly @@ -101,7 +99,6 @@ export const ACTIONS = { updateNotificationsSettings, fetchNotificationsSettings, setNotificationsSettingsItem, - addDirectContacts, addDirectMessages, resetChat, addChannels, diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts index 3d9b6232c81..3bf18b2a776 100644 --- a/src/common/store/chat/index.ts +++ b/src/common/store/chat/index.ts @@ -16,8 +16,6 @@ import { Chat, DeleteDirectMessagesAction, DeletePublicMessagesAction, - DirectContactsAction, - DirectContactsType, DirectMessagesAction, LeftChannelsAction, ProfilesAction, @@ -31,7 +29,6 @@ import { } from "./types"; export const initialState: Chat = { - directContacts: [], directMessages: [], channels: [], publicMessages: [], @@ -42,23 +39,6 @@ export const initialState: Chat = { export default (state: Chat = initialState, action: Actions): Chat => { switch (action.type) { - case ActionTypes.DIRECTCONTACTS: { - const { data } = action; - const uniqueDirectContacts = data.filter((contact) => { - return !state.directContacts.some((existingContact) => { - return existingContact.name === contact.name && existingContact.pubkey === contact.pubkey; - }); - }); - - return { - ...state, - directContacts: [...state.directContacts, ...uniqueDirectContacts], - directMessages: [ - ...state.directMessages, - ...uniqueDirectContacts.map((contact) => ({ chat: {}, peer: contact.pubkey })) - ] - }; - } case ActionTypes.DIRECTMESSAGES: { const { peer, data } = action; return { @@ -278,10 +258,6 @@ export default (state: Chat = initialState, action: Actions): Chat => { }; /* Actions */ -export const addDirectContacts = (data: DirectContactsType[]) => (dispatch: Dispatch) => { - dispatch(addDirectContactsAct(data)); -}; - export const addDirectMessages = (peer: string, data: DirectMessage) => (dispatch: Dispatch) => { dispatch(addDirectMessagesAct(peer, data)); }; @@ -345,13 +321,6 @@ export const addPreviousPublicMessages = /* Action Creators */ -export const addDirectContactsAct = (data: DirectContactsType[]): DirectContactsAction => { - return { - type: ActionTypes.DIRECTCONTACTS, - data - }; -}; - export const addDirectMessagesAct = (peer: string, data: DirectMessage): DirectMessagesAction => { return { type: ActionTypes.DIRECTMESSAGES, diff --git a/src/common/store/chat/types.ts b/src/common/store/chat/types.ts index a60f15b7f1d..ee30d127a19 100644 --- a/src/common/store/chat/types.ts +++ b/src/common/store/chat/types.ts @@ -23,7 +23,6 @@ export interface publicMessagesList { } export interface Chat { - directContacts: DirectContactsType[]; directMessages: directMessagesList[]; channels: Channel[]; publicMessages: publicMessagesList[]; From 700231303eccaa54c02970d69d089b0001d03788 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Fri, 3 Nov 2023 22:10:07 +0600 Subject: [PATCH 110/179] Chats: updated queries for messages and last messages query --- .../features/chats/components/chat-input.tsx | 1 - .../chats/components/chat-messages-view.tsx | 40 ++++------ .../components/chat-popup/chat-constants.ts | 20 ----- .../chat-popup/chat-popup-direct-messages.tsx | 7 +- .../chat-popup/chat-popup-messages-list.tsx | 12 ++- .../chat-popup/chat-popup-search-user.tsx | 8 +- .../chats/components/chat-popup/index.tsx | 71 ++++------------- .../chat-sidebar-direct-contact.tsx | 23 +++--- src/common/features/chats/queries/Roadmap.md | 23 ++++++ .../chats/queries/direct-contacts-query.ts | 34 +++++++- src/common/features/chats/queries/index.ts | 2 +- .../features/chats/queries/keys-query.ts | 16 ++-- .../chats/queries/message-listener-query.tsx | 1 + ...ct-messages-query.ts => messages-query.ts} | 77 +++++++++---------- src/common/features/chats/queries/queries.ts | 23 +++--- 15 files changed, 167 insertions(+), 191 deletions(-) create mode 100644 src/common/features/chats/queries/Roadmap.md rename src/common/features/chats/queries/{direct-messages-query.ts => messages-query.ts} (66%) diff --git a/src/common/features/chats/components/chat-input.tsx b/src/common/features/chats/components/chat-input.tsx index f4d5b0e00f6..f35a4d1328d 100644 --- a/src/common/features/chats/components/chat-input.tsx +++ b/src/common/features/chats/components/chat-input.tsx @@ -26,7 +26,6 @@ interface Props { isCommunity: boolean; currentChannel: Channel; currentUser: string; - isCurrentUserJoined: boolean; } export default function ChatInput({ diff --git a/src/common/features/chats/components/chat-messages-view.tsx b/src/common/features/chats/components/chat-messages-view.tsx index 3b9e67d3fb3..55fb8f72a19 100644 --- a/src/common/features/chats/components/chat-messages-view.tsx +++ b/src/common/features/chats/components/chat-messages-view.tsx @@ -1,6 +1,6 @@ import React, { useContext, useEffect, useRef, useState } from "react"; import { Link } from "react-router-dom"; -import { fetchCommunityMessages, fetchDirectMessages } from "../utils"; +import { fetchCommunityMessages } from "../utils"; import { History } from "history"; import { useMappedStore } from "../../../store/use-mapped-store"; import ChatsProfileBox from "./chat-profile-box"; @@ -12,7 +12,7 @@ import { CHATPAGE } from "./chat-popup/chat-constants"; import { ChatContext } from "../chat-context-provider"; import { classNameObject } from "../../../helper/class-name-object"; import { Channel, DirectMessage, PublicMessage } from "../managers/message-manager-types"; -import { useDirectContactsQuery } from "../queries"; +import { useMessagesQuery } from "../queries"; interface Props { username: string; @@ -32,14 +32,12 @@ export default function ChatsMessagesView({ history }: Props) { const { messageServiceInstance } = useContext(ChatContext); - const { data: directContacts } = useDirectContactsQuery(); + const { data: messages } = useMessagesQuery(username.replace("@", "")); const messagesBoxRef = useRef(null); const { chat } = useMappedStore(); const [directUser, setDirectUser] = useState(""); - const [publicMessages, setPublicMessages] = useState([]); - const [directMessages, setDirectMessages] = useState([]); const [communityName, setCommunityName] = useState(""); const [isScrollToBottom, setIsScrollToBottom] = useState(false); const [isTop, setIsTop] = useState(false); @@ -51,10 +49,10 @@ export default function ChatsMessagesView({ }, []); useEffect(() => { - if (publicMessages.length < 25) { + if (messages.length < 25) { setHasMore(false); } - }, [publicMessages]); + }, [messages]); useEffect(() => { isDirectUserOrCommunity(); @@ -66,7 +64,7 @@ export default function ChatsMessagesView({ useEffect(() => { if (directUser) { - getDirectMessages(); + // getDirectMessages(); } else if (communityName && currentChannel) { getChannelMessages(); } @@ -90,7 +88,7 @@ export default function ChatsMessagesView({ setInProgress(true); messageServiceInstance - ?.fetchPrevMessages(currentChannel!.id, publicMessages[0].created) + ?.fetchPrevMessages(currentChannel!.id, messages[0].created) .then((num) => { if (num < 25) { setHasMore(false); @@ -120,17 +118,10 @@ export default function ChatsMessagesView({ currentChannel.hiddenMessageIds ); const messages = publicMessages.sort((a, b) => a.created - b.created); - setPublicMessages(messages); + // setPublicMessages(messages); } }; - const getDirectMessages = () => { - const user = directContacts?.find((item) => item.name === directUser); - const messages = user && fetchDirectMessages(user?.pubkey, chat.directMessages); - const directMessages = messages?.sort((a, b) => a.created - b.created); - setDirectMessages(directMessages!); - }; - const scrollToBottom = () => { messagesBoxRef && messagesBoxRef?.current?.scroll({ @@ -140,18 +131,14 @@ export default function ChatsMessagesView({ }; const handleScroll = (event: React.UIEvent) => { - var element = event.currentTarget; + const element = event.currentTarget; const isScrollToBottom = element.scrollTop + messagesBoxRef?.current?.clientHeight! < element.scrollHeight - 200; setIsScrollToBottom(isScrollToBottom); const isScrolled = element.scrollTop + element.clientHeight <= element.scrollHeight - 20; setIsScrolled(isScrolled); - const scrollerTop = element.scrollTop <= 600 && publicMessages.length > 25; - if (communityName && scrollerTop) { - setIsTop(true); - } else { - setIsTop(false); - } + const scrollerTop = element.scrollTop <= 600 && messages.length > 25; + setIsTop(!!communityName && scrollerTop); }; return ( @@ -176,7 +163,7 @@ export default function ChatsMessagesView({ ) : (
diff --git a/src/common/features/chats/components/chat-popup/chat-constants.ts b/src/common/features/chats/components/chat-popup/chat-constants.ts index 2e0ff4d8312..987d0902838 100644 --- a/src/common/features/chats/components/chat-popup/chat-constants.ts +++ b/src/common/features/chats/components/chat-popup/chat-constants.ts @@ -1,23 +1,3 @@ -export const EmojiPickerStyle = { - width: "94%", - bottom: "58px", - left: "0px", - marginLeft: "14px", - borderTopLeftRadius: "8px", - borderTopRightRadius: "8px", - borderBottomLeftRadius: "0px" -}; - -export const GifPickerStyle = { - width: "94%", - bottom: "49px", - left: 0, - marginLeft: "14px", - borderTopLeftRadius: "8px", - borderTopRightRadius: "8px", - borderBottomLeftRadius: "0px" -}; - export const DropDownStyle = { width: "35px", height: "35px" diff --git a/src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx b/src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx index d00a348f9a6..71bfb68c06c 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx @@ -1,13 +1,13 @@ import React, { useContext, useEffect, useState } from "react"; import { _t } from "../../../../i18n"; -import { getCommunityLastMessage, getDirectLastMessage, getJoinedCommunities } from "../../utils"; +import { getCommunityLastMessage, getJoinedCommunities } from "../../utils"; import ImportChats from "../import-chats"; import { Spinner } from "@ui/spinner"; import { Button } from "@ui/button"; import { useMappedStore } from "../../../../store/use-mapped-store"; import { ChatContext } from "../../chat-context-provider"; import { ChatDirectMessage } from "./chat-direct-message"; -import { useDirectContactsQuery } from "../../queries"; +import { useDirectContactsLastMessagesQuery, useDirectContactsQuery } from "../../queries"; import { Channel } from "../../managers/message-manager-types"; interface Props { @@ -27,6 +27,7 @@ export function ChatPopupDirectMessages({ const { activeUserKeys, showSpinner } = useContext(ChatContext); const { data: directContacts } = useDirectContactsQuery(); + const { data: directContactsLastMessages } = useDirectContactsLastMessagesQuery(); const [communities, setCommunities] = useState([]); @@ -63,7 +64,7 @@ export function ChatPopupDirectMessages({ userClicked(v); }} key={user.pubkey} - lastMessage={getDirectLastMessage(user.pubkey, chat.directMessages)?.content} + lastMessage={directContactsLastMessages[user.name]?.content} /> ))} diff --git a/src/common/features/chats/components/chat-popup/chat-popup-messages-list.tsx b/src/common/features/chats/components/chat-popup/chat-popup-messages-list.tsx index f4948ea990a..b6ae2f437b1 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-messages-list.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-messages-list.tsx @@ -7,6 +7,7 @@ import React, { useContext } from "react"; import { ChatContext } from "../../chat-context-provider"; import { Community } from "../../../../store/communities"; import { DirectMessage, PublicMessage } from "../../managers/message-manager-types"; +import { useMessagesQuery } from "../../queries"; interface Props { isCurrentUser: boolean; @@ -14,8 +15,6 @@ interface Props { isCommunity: boolean; communityName: string; currentCommunity?: Community; - directMessagesList: DirectMessage[]; - publicMessages: PublicMessage[]; } export function ChatPopupMessagesList({ @@ -23,11 +22,10 @@ export function ChatPopupMessagesList({ currentUser, isCurrentUser, currentCommunity, - communityName, - directMessagesList, - publicMessages + communityName }: Props) { const { currentChannel, setCurrentChannel } = useContext(ChatContext); + const { data: messages } = useMessagesQuery(currentUser); return (
@@ -50,7 +48,7 @@ export function ChatPopupMessagesList({ {isCurrentUser ? ( @@ -58,7 +56,7 @@ export function ChatPopupMessagesList({ void; - setIsCurrentUser: (v: boolean) => void; } -export function ChatPopupSearchUser({ setCurrentUser, setIsCurrentUser }: Props) { +export function ChatPopupSearchUser({ setCurrentUser }: Props) { const [search, setSearch] = useState(""); const { data, isLoading, refetch } = useSearchUsersQuery(search); @@ -39,10 +38,7 @@ export function ChatPopupSearchUser({ setCurrentUser, setIsCurrentUser }: Props)
{ - setCurrentUser(user.account); - setIsCurrentUser(true); - }} + onClick={() => setCurrentUser(user.account)} >
diff --git a/src/common/features/chats/components/chat-popup/index.tsx b/src/common/features/chats/components/chat-popup/index.tsx index c8cf079f546..8ce72861240 100644 --- a/src/common/features/chats/components/chat-popup/index.tsx +++ b/src/common/features/chats/components/chat-popup/index.tsx @@ -41,7 +41,7 @@ export const ChatPopUp = () => { setRevealPrivKey, setShowSpinner } = useContext(ChatContext); - const { mutateAsync: joinChat } = useJoinChat(); + const { mutateAsync: joinChat, isLoading: isJoinChatLoading } = useJoinChat(); const { data: directContacts } = useDirectContactsQuery(); const routerLocation = useLocation(); @@ -50,25 +50,22 @@ export const ChatPopUp = () => { const [expanded, setExpanded] = useState(false); const [currentUser, setCurrentUser] = useState(""); - const [isCurrentUser, setIsCurrentUser] = useState(false); const [isScrollToTop, setIsScrollToTop] = useState(false); const [isScrollToBottom, setIsScrollToBottom] = useState(false); const [showSearchUser, setShowSearchUser] = useState(false); const [inProgress, setInProgress] = useState(false); const [show, setShow] = useState(false); const [receiverPubKey, setReceiverPubKey] = useState(""); - const [isSpinner, setIsSpinner] = useState(false); const [directMessagesList, setDirectMessagesList] = useState([]); - const [isCurrentUserJoined, setIsCurrentUserJoined] = useState(true); const [isCommunity, setIsCommunity] = useState(false); const [communityName, setCommunityName] = useState(""); const [currentCommunity, setCurrentCommunity] = useState(); const [publicMessages, setPublicMessages] = useState([]); const [isTop, setIsTop] = useState(false); const [hasMore, setHasMore] = useState(true); - const [isChatPage, setIsChatPage] = useState(false); const [isScrolled, setIsScrolled] = useState(false); + const isCurrentUser = useMemo(() => !!currentUser, [currentUser]); const canSendMessage = useMemo( () => !currentUser && hasUserJoinedChat && !!activeUserKeys?.priv && !isCommunity && !revealPrivKey, @@ -76,10 +73,15 @@ export const ChatPopUp = () => { ); useMount(() => { - // deleteChatPublicKey(activeUser); - setShow(!!activeUser?.username && !isChatPage); + setShow(!routerLocation.pathname.match("/chats") && !!activeUser); }); + // Show or hide the popup if current pathname was changed or user changed + useEffect(() => { + setShow(!routerLocation.pathname.match("/chats") && !!activeUser); + }, [routerLocation, activeUser]); + + // todo: ?? useEffect(() => { if (currentChannel && chat.leftChannelsList.includes(currentChannel.id)) { setIsCommunity(false); @@ -87,16 +89,7 @@ export const ChatPopUp = () => { } }, [chat.leftChannelsList]); - useEffect(() => { - handleRouterChange(); - }, [routerLocation]); - - useEffect(() => { - if (isChatPage) { - setShow(false); - } - }, [isChatPage]); - + // todo ??? useEffect(() => { const updated: ChannelUpdate = chat.updatedChannel .filter((x) => x.channelId === currentChannel?.id!) @@ -125,18 +118,13 @@ export const ChatPopUp = () => { } }, [chat.updatedChannel]); + // Fetching previous messages when scrol achieved top useEffect(() => { if (isTop) { fetchPrevMessages(); } }, [isTop]); - useEffect(() => { - if (messageServiceInstance) { - setIsSpinner(false); - } - }, [messageServiceInstance]); - useEffect(() => { const msgsList = fetchDirectMessages(receiverPubKey!); const messages = msgsList.sort((a, b) => a.created - b.created); @@ -146,7 +134,6 @@ export const ChatPopUp = () => { useEffect(() => { if (prevActiveUser?.username !== activeUser?.username) { setIsCommunity(false); - setIsCurrentUser(false); setCurrentUser(""); setCommunityName(""); } @@ -200,7 +187,6 @@ export const ChatPopUp = () => { const isCurrentUserFound = directContacts?.find((contact) => contact.name === currentUser); if (isCurrentUserFound) { setReceiverPubKey(isCurrentUserFound.pubkey); - setIsCurrentUserJoined(true); } else { setInProgress(true); fetchCurrentUserData(); @@ -214,20 +200,10 @@ export const ChatPopUp = () => { setNostrkeys(activeUserKeys!); } } else { - setIsCurrentUserJoined(true); setInProgress(false); } }, [currentUser]); - const handleRouterChange = () => { - if (routerLocation.pathname.match("/chats")) { - setShow(false); - setIsChatPage(true); - } else { - setShow(!!activeUser?.username); - } - }; - const fetchCommunity = async () => { const community = await getCommunity(communityName, activeUser?.username); setCurrentCommunity(community!); @@ -275,19 +251,14 @@ export const ChatPopUp = () => { } else { setReceiverPubKey(""); } - setIsCurrentUserJoined(!!nsKey); setInProgress(false); }; - const userClicked = (username: string) => { - setIsCurrentUser(true); - setCurrentUser(username); - }; - const fetchPrevMessages = () => { if (!hasMore || inProgress) return; setInProgress(true); + // todo: make it infinite query messageServiceInstance ?.fetchPrevMessages(currentChannel!.id, publicMessages[0].created) .then((num) => { @@ -356,7 +327,6 @@ export const ChatPopUp = () => { const handleBackArrowSvg = () => { setCurrentUser(""); - setIsCurrentUser(false); setCommunityName(""); setIsCommunity(false); setShowSearchUser(false); @@ -414,20 +384,15 @@ export const ChatPopUp = () => { currentUser={currentUser} isCommunity={isCommunity} communityName={communityName} - directMessagesList={directMessagesList} - publicMessages={publicMessages} /> ) : showSearchUser ? ( - + ) : ( setCurrentUser(username)} /> )} @@ -438,11 +403,8 @@ export const ChatPopUp = () => { ) : (
diff --git a/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx b/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx index 71c0e3301b6..c8bbcf180b4 100644 --- a/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx +++ b/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx @@ -1,12 +1,11 @@ -import { getDirectLastMessage, getRelativeDate } from "../../utils"; +import { getRelativeDate } from "../../utils"; import { Link } from "react-router-dom"; import React, { useContext, useMemo } from "react"; import { ChatContext } from "../../chat-context-provider"; import UserAvatar from "../../../../components/user-avatar"; -import { useMappedStore } from "../../../../store/use-mapped-store"; import { classNameObject } from "../../../../helper/class-name-object"; import { DirectContact } from "../../managers/message-manager-types"; -import { useDirectMessagesQuery } from "../../queries"; +import { useDirectContactsLastMessagesQuery } from "../../queries"; interface Props { contact: DirectContact; @@ -16,22 +15,20 @@ interface Props { export function ChatSidebarDirectContact({ contact, username, handleRevealPrivKey }: Props) { const { setReceiverPubKey } = useContext(ChatContext); - const { chat } = useMappedStore(); - const { data: directMessages } = useDirectMessagesQuery(contact.name); - - const lastMessage = useMemo( - () => getDirectLastMessage(directMessages), - [directMessages, contact] + const { data: directMessagesLastMessages } = useDirectContactsLastMessagesQuery(); + const rawUsername = useMemo(() => username.replace("@", ""), [username]); + const lastMessageDate = useMemo( + () => getRelativeDate(directMessagesLastMessages[contact.name]?.created), + [directMessagesLastMessages] ); - const lastMessageDate = useMemo(() => getRelativeDate(lastMessage?.created), [lastMessage]); return ( { @@ -45,7 +42,9 @@ export function ChatSidebarDirectContact({ contact, username, handleRevealPrivKe
{contact.name}
{lastMessageDate}
-
{lastMessage?.content}
+
+ {directMessagesLastMessages[contact.name]?.content} +
); diff --git a/src/common/features/chats/queries/Roadmap.md b/src/common/features/chats/queries/Roadmap.md new file mode 100644 index 00000000000..5d7993238f2 --- /dev/null +++ b/src/common/features/chats/queries/Roadmap.md @@ -0,0 +1,23 @@ +# How Chat queries and mutations works + +## Queries + +### General + +- **Fetching the key pairs from stores properly** + + It will allow us to start all queries based on the chat authorization + + This query opens next ones: + + 1. **Fetching direct contacts** + + It will allow us to fetch their last + + 2. **Fetching last messages** + 1. Fetching direct contacts last messages + 2. Aggregate messages by users and fetching them by username properly + +- **Searching users** + + This query doesn't require keys because it will search over Ecency diff --git a/src/common/features/chats/queries/direct-contacts-query.ts b/src/common/features/chats/queries/direct-contacts-query.ts index ea88342a7fa..aeed2244ae7 100644 --- a/src/common/features/chats/queries/direct-contacts-query.ts +++ b/src/common/features/chats/queries/direct-contacts-query.ts @@ -1,9 +1,16 @@ import { ChatQueries } from "./queries"; import { MessageEvents } from "../../../helper/message-service"; -import { DirectContact } from "../managers/message-manager-types"; +import { DirectContact, Message } from "../managers/message-manager-types"; import { useMessageListenerQuery } from "./message-listener-query"; +import { useQuery } from "@tanstack/react-query"; +import isCommunity from "../../../helper/is-community"; +import { useMessagesQuery } from "./messages-query"; +import { getDirectLastMessage } from "../utils"; +import { useKeysQuery } from "./keys-query"; export function useDirectContactsQuery() { + const { hasKeys } = useKeysQuery(); + return useMessageListenerQuery( [ChatQueries.DIRECT_CONTACTS], MessageEvents.DirectContact, @@ -20,7 +27,30 @@ export function useDirectContactsQuery() { } }, { - initialData: [] + initialData: [], + enabled: hasKeys + } + ); +} + +export function useDirectContactsLastMessagesQuery() { + const { data: contacts } = useDirectContactsQuery(); + const { data: messages } = useMessagesQuery("all"); + + return useQuery>( + [ChatQueries.DIRECT_CONTACTS_LAST_MESSAGES], + async () => + (contacts ?? []).reduce>((acc, current) => { + const currentMessages = messages.filter((i) => + !isCommunity(current.name) && "peer" in i + ? i.peer === current?.pubkey + : i.root === current?.pubkey + ); + return { ...acc, [current.name]: getDirectLastMessage(currentMessages) }; + }, {}), + { + initialData: {}, + enabled: (contacts?.length ?? 0) > 0 && messages.length > 0 } ); } diff --git a/src/common/features/chats/queries/index.ts b/src/common/features/chats/queries/index.ts index 5f8a6323947..99b8b717386 100644 --- a/src/common/features/chats/queries/index.ts +++ b/src/common/features/chats/queries/index.ts @@ -2,5 +2,5 @@ export * from "./search-users-query"; export * from "./queries"; export * from "./direct-contacts-query"; export * from "./message-listener-query"; -export * from "./direct-messages-query"; +export * from "./messages-query"; export * from "./chat-profiles-query"; diff --git a/src/common/features/chats/queries/keys-query.ts b/src/common/features/chats/queries/keys-query.ts index 90809aabbe7..5f47708b24d 100644 --- a/src/common/features/chats/queries/keys-query.ts +++ b/src/common/features/chats/queries/keys-query.ts @@ -3,6 +3,7 @@ import { ChatQueries } from "./queries"; import { getPrivateKey, getUserChatPublicKey } from "../utils"; import { useMappedStore } from "../../../store/use-mapped-store"; import { NostrKeysType } from "../types"; +import { useMemo } from "react"; /** * Custom React hook for managing queries related to a user's public and private keys in a chat application. @@ -16,8 +17,8 @@ import { NostrKeysType } from "../types"; * - refetch: A function to manually trigger a refetch of the public and private keys. */ export function useKeysQuery( - activeUserKeys: NostrKeysType, - setActiveUserKeys: (keys: NostrKeysType) => void + activeUserKeys?: NostrKeysType, + setActiveUserKeys?: (keys: Partial) => void ) { const { activeUser } = useMappedStore(); @@ -31,8 +32,8 @@ export function useKeysQuery( queryFn: () => getUserChatPublicKey(activeUser?.username!), onSuccess: (key: string | null) => { if (key) { - setActiveUserKeys({ - ...activeUserKeys, + setActiveUserKeys?.({ + ...(activeUserKeys ?? {}), pub: key }); } @@ -43,8 +44,8 @@ export function useKeysQuery( queryFn: () => getPrivateKey(activeUser?.username!), onSuccess: (key: string | null) => { if (key) { - setActiveUserKeys({ - ...activeUserKeys, + setActiveUserKeys?.({ + ...(activeUserKeys ?? {}), priv: key }); } @@ -53,9 +54,12 @@ export function useKeysQuery( ] }); + const hasKeys = useMemo(() => !!publicKey && !!privateKey, [publicKey, privateKey]); + return { publicKey, privateKey, + hasKeys, refetch: () => { refetchPublicKey(); refetchPrivateKey(); diff --git a/src/common/features/chats/queries/message-listener-query.tsx b/src/common/features/chats/queries/message-listener-query.tsx index 2e2d5db22e7..5712c3205c5 100644 --- a/src/common/features/chats/queries/message-listener-query.tsx +++ b/src/common/features/chats/queries/message-listener-query.tsx @@ -51,6 +51,7 @@ export function useMessageListenerQuery( const listener = (nextData: TQData) => { const resolver = (data: TQData) => queryClient.setQueryData(key, data); + queryClient.invalidateQueries(key); queryFn(query.data!!, nextData, resolver); }; diff --git a/src/common/features/chats/queries/direct-messages-query.ts b/src/common/features/chats/queries/messages-query.ts similarity index 66% rename from src/common/features/chats/queries/direct-messages-query.ts rename to src/common/features/chats/queries/messages-query.ts index 13287ea4cc0..21d3e02a1cc 100644 --- a/src/common/features/chats/queries/direct-messages-query.ts +++ b/src/common/features/chats/queries/messages-query.ts @@ -9,18 +9,14 @@ import { useDirectContactsQuery } from "./direct-contacts-query"; import isCommunity from "../../../helper/is-community"; import { useChatProfilesQuery } from "./chat-profiles-query"; -export function useDirectMessagesQuery(username?: string) { +export function useMessagesQuery(username?: string) { const { messageServiceInstance } = useContext(ChatContext); const queryClient = useQueryClient(); const { data: directContacts } = useDirectContactsQuery(); const { data: profiles } = useChatProfilesQuery(); - const contact = useMemo( - () => directContacts?.find((i) => i.name === username), - [directContacts, username] - ); - const isPublic = useMemo(() => isCommunity(username ?? ""), [username]); + const hasContacts = useMemo(() => (directContacts?.length ?? 0) > 0, [directContacts]); // const checkDirectMessageSending = (peer: string, data: DirectMessage) => { // setTimeout(() => { @@ -46,39 +42,38 @@ export function useDirectMessagesQuery(username?: string) { // }); // }; - // TODO: CONCACT MESSAGES AND CHECK FOR DUPLICATES - const onBeforeSent = (nextData: Message[] | undefined) => { - const nextMessages = [ - ...(queryClient.getQueryData([ChatQueries.DIRECT_MESSAGES, username]) ?? []), - ...(nextData?.filter((i) => - !isPublic && "peer" in i ? i.peer === contact?.pubkey : i.root === contact?.pubkey - ) ?? []) + const updateQueries = (nextData: Message[] | undefined) => { + let nextMessages: Message[] = [ + ...(queryClient.getQueryData([ChatQueries.MESSAGES]) ?? []), + ...(nextData ?? []) ]; - queryClient.setQueryData([ChatQueries.DIRECT_MESSAGES, username], nextMessages); - queryClient.invalidateQueries([ChatQueries.DIRECT_MESSAGES, username]); - }; - const onAfterSent = (nextData: Message[]) => { - const nextMessages = [ - ...(queryClient.getQueryData([ChatQueries.DIRECT_MESSAGES, username]) ?? []), - ...(nextData?.filter((i) => - !isPublic && "peer" in i ? i.peer === contact?.pubkey : i.root === contact?.pubkey - ) ?? []) - ]; - queryClient.setQueryData([ChatQueries.DIRECT_MESSAGES, username], nextMessages); - queryClient.invalidateQueries([ChatQueries.DIRECT_MESSAGES, username]); + directContacts?.forEach((contact) => { + queryClient.setQueryData( + [ChatQueries.MESSAGES, contact.name], + nextMessages.filter((i) => + !isCommunity(contact.name) && "peer" in i + ? i.peer === contact?.pubkey + : i.root === contact?.pubkey + ) ?? [] + ); + queryClient.invalidateQueries([ChatQueries.MESSAGES, contact.name]); + }); + + queryClient.setQueryData([ChatQueries.MESSAGES, "all"], nextMessages); + queryClient.invalidateQueries([ChatQueries.MESSAGES, "all"]); }; useMessageListenerQuery( [ChatQueries.BEFORE_DIRECT_MESSAGES_TEMP], MessageEvents.DirectMessageBeforeSent, (_, nextData, resolver) => { - onBeforeSent(nextData); + updateQueries(nextData); resolver(nextData); }, { queryFn: () => [], - enabled: !!contact, + enabled: hasContacts, initialData: [] } ); @@ -87,14 +82,14 @@ export function useDirectMessagesQuery(username?: string) { [ChatQueries.AFTER_DIRECT_MESSAGES_TEMP], MessageEvents.DirectMessageAfterSent, (_, nextData, resolver) => { - onAfterSent(nextData); + updateQueries(nextData); resolver(nextData); messageServiceInstance?.checkProfiles(nextData.map((x) => x.peer)); }, { queryFn: () => [], - enabled: !!contact, + enabled: hasContacts, initialData: [] } ); @@ -103,12 +98,12 @@ export function useDirectMessagesQuery(username?: string) { [ChatQueries.BEFORE_PUBLIC_MESSAGES_TEMP], MessageEvents.PublicMessageBeforeSent, (_, nextData, resolver) => { - onBeforeSent(nextData); + updateQueries(nextData); resolver(nextData); }, { queryFn: () => [], - enabled: !!contact, + enabled: hasContacts, initialData: [] } ); @@ -117,7 +112,7 @@ export function useDirectMessagesQuery(username?: string) { [ChatQueries.AFTER_PUBLIC_MESSAGES_TEMP], MessageEvents.PublicMessageAfterSent, (_, nextData, resolver) => { - onAfterSent(nextData); + updateQueries(nextData); resolver(nextData); const uniqueUsers = nextData @@ -127,7 +122,7 @@ export function useDirectMessagesQuery(username?: string) { }, { queryFn: () => [], - enabled: !!contact, + enabled: hasContacts, initialData: [] } ); @@ -137,28 +132,30 @@ export function useDirectMessagesQuery(username?: string) { MessageEvents.PreviousPublicMessages, (_, nextData, resolver) => { queryClient.setQueryData( - [ChatQueries.DIRECT_MESSAGES, username], + [ChatQueries.MESSAGES, username], [ - ...(queryClient.getQueryData([ChatQueries.DIRECT_MESSAGES]) ?? []), + ...(queryClient.getQueryData([ChatQueries.MESSAGES, username]) ?? []), ...(nextData ?? []) ] ); + queryClient.invalidateQueries([ChatQueries.MESSAGES, username]); resolver(nextData); }, { queryFn: () => [], - enabled: !!contact, + enabled: hasContacts, initialData: [] } ); return useQuery( - [ChatQueries.DIRECT_MESSAGES, username], - () => queryClient.getQueryData([ChatQueries.DIRECT_MESSAGES, username]) ?? [], + [ChatQueries.MESSAGES, username], + () => queryClient.getQueryData([ChatQueries.MESSAGES, username]) ?? [], { - enabled: !!contact, - initialData: [] + enabled: hasContacts, + initialData: [], + select: (data) => [...data]?.sort((a, b) => a.created - b.created) } ); } diff --git a/src/common/features/chats/queries/queries.ts b/src/common/features/chats/queries/queries.ts index b024aaebc56..9f5460ef791 100644 --- a/src/common/features/chats/queries/queries.ts +++ b/src/common/features/chats/queries/queries.ts @@ -1,13 +1,14 @@ export enum ChatQueries { - SEARCH_USER = "search-user", - PUBLIC_KEY = "public-key", - PRIVATE_KEY = "private-key", - DIRECT_CONTACTS = "direct-contacts", - DIRECT_MESSAGES = "direct-messages", - BEFORE_DIRECT_MESSAGES_TEMP = "before-direct-messages-temp", - AFTER_DIRECT_MESSAGES_TEMP = "after-direct-messages-temp", - BEFORE_PUBLIC_MESSAGES_TEMP = "before-public-messages-temp", - AFTER_PUBLIC_MESSAGES_TEMP = "after-public-messages-temp", - PREVIOUS_PUBLIC_MESSAGES = "previous-public-messages", - PROFILES = "profiles" + SEARCH_USER = "chats/search-user", + PUBLIC_KEY = "chats/public-key", + PRIVATE_KEY = "chats/private-key", + DIRECT_CONTACTS = "chats/direct-contacts", + DIRECT_CONTACTS_LAST_MESSAGES = "chats/direct-contacts-last-messages", + MESSAGES = "chats/messages", + BEFORE_DIRECT_MESSAGES_TEMP = "chats/before-direct-messages-temp", + AFTER_DIRECT_MESSAGES_TEMP = "chats/after-direct-messages-temp", + BEFORE_PUBLIC_MESSAGES_TEMP = "chats/before-public-messages-temp", + AFTER_PUBLIC_MESSAGES_TEMP = "chats/after-public-messages-temp", + PREVIOUS_PUBLIC_MESSAGES = "chats/previous-public-messages", + PROFILES = "chats/profiles" } From 1b83d9280650832dd86bc20211612c2879cd9efd Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Sun, 5 Nov 2023 23:30:54 +0600 Subject: [PATCH 111/179] Chats: added channels query --- .../features/chats/components/chat-input.tsx | 58 ++-- .../chats/components/chat-message-box.tsx | 1 + .../chats/components/chat-message-item.tsx | 87 ++++++ .../chats/components/chat-popup/index.tsx | 4 +- .../chats-channel-messages/index.tsx | 173 +----------- .../chats-direct-messages/index.scss | 253 +----------------- .../chats-direct-messages/index.tsx | 130 +-------- .../chat-sidebar-direct-contact.tsx | 2 +- .../chats/managers/message-manager.tsx | 47 ---- src/common/features/chats/mutations/index.ts | 2 + .../features/chats/mutations/join-chat.ts | 2 +- .../features/chats/mutations/send-message.ts | 48 ++++ src/common/features/chats/mutations/upload.ts | 9 +- .../features/chats/queries/channels-query.ts | 44 +++ src/common/features/chats/queries/index.ts | 1 + src/common/features/chats/queries/queries.ts | 3 +- src/common/i18n/locales/en-US.json | 4 +- 17 files changed, 228 insertions(+), 640 deletions(-) create mode 100644 src/common/features/chats/components/chat-message-item.tsx create mode 100644 src/common/features/chats/mutations/send-message.ts create mode 100644 src/common/features/chats/queries/channels-query.ts diff --git a/src/common/features/chats/components/chat-input.tsx b/src/common/features/chats/components/chat-input.tsx index f35a4d1328d..044d140a7f6 100644 --- a/src/common/features/chats/components/chat-input.tsx +++ b/src/common/features/chats/components/chat-input.tsx @@ -1,6 +1,5 @@ import React, { useContext, useEffect, useMemo, useRef, useState } from "react"; import { EmojiPicker } from "../../../components/emoji-picker"; -import { error } from "../../../components/feedback"; import { attachFileSvg, chatBoxImageSvg, @@ -8,18 +7,19 @@ import { gifIcon, messageSendSvg } from "../../../img/svg"; -import { CHAT_FILE_CONTENT_TYPES, GifImagesStyle, UPLOADING } from "./chat-popup/chat-constants"; +import { CHAT_FILE_CONTENT_TYPES, GifImagesStyle } from "./chat-popup/chat-constants"; import { _t } from "../../../i18n"; import { ChatContext } from "../chat-context-provider"; import { Form } from "@ui/form"; import { FormControl } from "@ui/input"; import { Button } from "@ui/button"; -import { useChatFileUpload } from "../mutations"; +import { useChatFileUpload, useSendMessage } from "../mutations"; import { Dropdown, DropdownItemWithIcon, DropdownMenu, DropdownToggle } from "@ui/dropdown"; import GifPicker from "../../../components/gif-picker"; import useClickAway from "react-use/lib/useClickAway"; -import { useDirectContactsQuery } from "../queries"; import { Channel } from "../managers/message-manager-types"; +import { Spinner } from "@ui/spinner"; +import { useChannelsQuery } from "../queries"; interface Props { isCurrentUser: boolean; @@ -34,18 +34,25 @@ export default function ChatInput({ currentChannel, currentUser }: Props) { + useChannelsQuery(); + const fileInputRef = useRef(null); const emojiButtonRef = useRef(null); const gifPickerRef = useRef(null); - const { data: directContacts } = useDirectContactsQuery(); const { messageServiceInstance, isActiveUserRemoved, receiverPubKey } = useContext(ChatContext); const [message, setMessage] = useState(""); const [showGifPicker, setShowGifPicker] = useState(false); - const [isMessageText, setIsMessageText] = useState(false); - const { mutateAsync } = useChatFileUpload(setMessage, setIsMessageText); + const { mutateAsync: upload } = useChatFileUpload(setMessage); + const { mutateAsync: sendMessage, isLoading: isSendMessageLoading } = useSendMessage( + currentChannel, + currentUser, + () => { + setMessage(""); + } + ); const isDisabled = useMemo( () => (isCurrentUser && !receiverPubKey) || isActiveUserRemoved, @@ -60,30 +67,6 @@ export default function ChatInput({ } }, [isCommunity, isCurrentUser]); - const sendMessage = () => { - if (message.length !== 0 && !message.includes(UPLOADING)) { - if (isCommunity) { - if (!isActiveUserRemoved) { - messageServiceInstance?.sendPublicMessage(currentChannel, message, [], ""); - } else { - error(_t("chat.message-warning")); - } - } - if (isCurrentUser) { - messageServiceInstance?.sendDirectMessage(receiverPubKey!, message); - } - setMessage(""); - setIsMessageText(false); - } - if ( - receiverPubKey && - !directContacts?.some((contact) => contact.name === currentUser) && - isCurrentUser - ) { - messageServiceInstance?.publishContacts(currentUser, receiverPubKey); - } - }; - const checkFile = (filename: string) => { const filenameLow = filename.toLowerCase(); return CHAT_FILE_CONTENT_TYPES.some((el) => filenameLow.endsWith(el)); @@ -97,7 +80,7 @@ export default function ChatInput({ e.preventDefault(); } - files.forEach((file) => mutateAsync(file)); + files.forEach((file) => upload(file)); // reset input e.target.value = ""; @@ -132,7 +115,7 @@ export default function ChatInput({ onSubmit={(e: React.FormEvent) => { e.preventDefault(); e.stopPropagation(); - sendMessage(); + sendMessage(message); }} className="w-full flex items-center gap-2 p-2" > @@ -158,13 +141,12 @@ export default function ChatInput({ autoFocus={true} onChange={(e) => { setMessage(e.target.value); - setIsMessageText(e.target.value.length !== 0); }} required={true} type="text" placeholder={_t("chat.start-chat-placeholder")} autoComplete="off" - disabled={isDisabled} + disabled={isDisabled || isSendMessageLoading} />
@@ -184,9 +166,9 @@ export default function ChatInput({
diff --git a/src/common/features/chats/components/chat-message-box.tsx b/src/common/features/chats/components/chat-message-box.tsx index 13041102606..4243ea8f096 100644 --- a/src/common/features/chats/components/chat-message-box.tsx +++ b/src/common/features/chats/components/chat-message-box.tsx @@ -79,6 +79,7 @@ export default function ChatsMessagesBox(props: Props) { checkUserCommunityMembership(); }, [updatedChannel, username, channels]); + // TODO: MAKE QUERY const fetchCurrentChannel = (communityName: string) => { const channel = channels.find((channel) => channel.communityName === communityName); if (channel) { diff --git a/src/common/features/chats/components/chat-message-item.tsx b/src/common/features/chats/components/chat-message-item.tsx new file mode 100644 index 00000000000..2011d1a5bb6 --- /dev/null +++ b/src/common/features/chats/components/chat-message-item.tsx @@ -0,0 +1,87 @@ +import { Message } from "../managers/message-manager-types"; +import Tooltip from "../../../components/tooltip"; +import { failedMessageSvg, resendMessageSvg } from "../../../img/svg"; +import { formatMessageTime, isMessageGif, isMessageImage } from "../utils"; +import { Spinner } from "@ui/spinner"; +import React, { useMemo } from "react"; +import { classNameObject } from "../../../helper/class-name-object"; +import { renderPostBody } from "@ecency/render-helper"; +import { useMappedStore } from "../../../store/use-mapped-store"; +import { _t } from "../../../i18n"; + +interface Props { + type: "sender" | "receiver"; + message: Message; + isSameUser: boolean; +} + +export function ChatMessageItem({ type, message, isSameUser }: Props) { + const { global } = useMappedStore(); + + const isFailed = useMemo(() => message.sent === 2, [message]); + const isSending = useMemo(() => message.sent === 0, [message]); + const isGif = useMemo(() => isMessageGif(message.content), [message]); + const isImage = useMemo(() => isMessageImage(message.content), [message]); + const renderedPreview = useMemo( + () => + renderPostBody(message.content, false, global.canUseWebp) + .replace(/]*>/g, "") + .replace(/<\/p>/g, ""), + [message] + ); + + return ( +
+
+ {message.sent === 2 && ( + + { + // setStep(1); + // setResendMessage(message); + }} + > + {resendMessageSvg} + + + )} +
+
+
+
+ {formatMessageTime(message.created)} +
+ {message.sent === 0 && ( + + + + )} + {message.sent === 2 && ( + + {failedMessageSvg} + + )} +
+
+ ); +} diff --git a/src/common/features/chats/components/chat-popup/index.tsx b/src/common/features/chats/components/chat-popup/index.tsx index 8ce72861240..1571a073110 100644 --- a/src/common/features/chats/components/chat-popup/index.tsx +++ b/src/common/features/chats/components/chat-popup/index.tsx @@ -419,7 +419,7 @@ export const ChatPopUp = () => {
@@ -428,7 +428,7 @@ export const ChatPopUp = () => { )}
-
+
{(isCurrentUser || isCommunity) && ( )} - {pMsg.creator !== activeUserKeys?.pub ? ( -
!showMessageActions && setHoveredMessageId(pMsg.id)} - onMouseLeave={() => !showMessageActions && setHoveredMessageId("")} - > - {!isSameUserMessage || (isSameUserMessage && dayAndMonth) ? ( -
- {/* handleImageClick(pMsg.id, pMsg.creator)}*/} - {/*>*/} - {/* */} - {/* */} - {/* */} - {/**/} -
- ) : ( - <> - )} - -
- {(!isSameUserMessage || dayAndMonth) && ( -

- {name} -

- )} - -
handelMessageActions(pMsg.id)} - > - -
-
-

{formatMessageTime(pMsg.created)}

-
- - {hoveredMessageId === pMsg.id && - privilegedUsers.includes(activeUser?.username!) && ( - -
-

{ - setClickedMessage(""); - setStep(1); - setHiddenMsgId(pMsg.id); - }} - > - {hideSvg} -

-
-
- )} -
-
-
- ) : ( -
!showMessageActions && setHoveredMessageId(pMsg.id)} - onMouseLeave={() => !showMessageActions && setHoveredMessageId("")} - > -
handelMessageActions(pMsg.id)} - > - {hoveredMessageId === pMsg.id && !isActiveUserRemoved && ( - -
-

{ - setClickedMessage(""); - setStep(1); - setHiddenMsgId(pMsg.id); - }} - > - {hideSvg} -

-
-
- )} - {pMsg.sent === 2 && ( - - { - setStep(4); - setResendMessage(pMsg); - }} - > - {resendMessageSvg} - - - )} - - -
-
-

{formatMessageTime(pMsg.created)}

-
- - - {pMsg.sent === 0 && ( - - - - )} - {pMsg.sent === 2 && ( - - {failedMessageSvg} - - )} -
-
- )} + ); })} diff --git a/src/common/features/chats/components/chats-direct-messages/index.scss b/src/common/features/chats/components/chats-direct-messages/index.scss index 9a4d2fa0182..a0c47aaeb67 100644 --- a/src/common/features/chats/components/chats-direct-messages/index.scss +++ b/src/common/features/chats/components/chats-direct-messages/index.scss @@ -9,258 +9,7 @@ font-size: 14px; } } - - .receiver { - display: flex; - - .user-img { - padding: 8px 8px 8px 16px; - } - - .community-user-img { - padding: 8px 8px 0px 16px; - - .user-avatar.medium { - cursor: pointer; - } - } - - .user-info { - padding-top: 20px; - width: 100%; - - .user-msg-time { - margin: 0; - color: rgb(138, 141, 145); - font-weight: 400; - font-size: 12px; - margin-left: 4px; - margin-bottom: 2px; - - .username-community { - margin-right: 8px; - } - } - - .receiver-messag { - display: flex; - - .receiver-message-wrapper { - box-sizing: content-box; - border-radius: 0 10px 10px 10px; - max-width: 70% !important; - word-wrap: break-word; - @include themify(day) { - background: #e4e6eb; - } - @include themify(night) { - background: #cee2ff; - } - - .receiver-message-content { - max-width: 100%; - color: #050505; - display: inline-block; - padding: 10px; - border-radius: 0px 10px 10px 0px; - } - - .receiver-msg-time { - @include themify(day) { - color: rgb(138, 141, 145); - } - @include themify(night) { - @apply text-gray-charcoal; - } - - font-size: 10px; - display: flex; - justify-content: flex-end; - margin: -5px 7px 3px 45px; - } - - &.gif { - background: none; - padding: 0; - - img { - max-width: 100%; - } - } - - &.chat-image { - background: none; - padding: 0; - - img { - max-width: 100%; - } - } - - &.gif, - &.chat-image { - .receiver-message-wrapper { - background: none; - } - - .receiver-message-content { - padding: 0; - - img { - padding-bottom: 10px; - } - } - - .receiver-msg-time { - @include themify(day) { - color: rgb(138, 141, 145); - } - @include themify(night) { - @apply text-light-300; - } - } - } - } - } - - &.same-user-msg { - margin-left: 4rem; - padding-top: 1px; - - .receiver-message-wrapper { - border-radius: 10px; - } - } - - &.date-changed { - margin-left: 0; - border-radius: 0px 10px 10px 0px; - } - } - } - - .sender { - margin-bottom: 1.5px; - - .sender-message { - margin-left: auto; - - display: flex; - justify-content: flex-end; - margin-right: 17px; - - .resend-svg { - margin: 5px 15px 0 0; - cursor: pointer; - - svg { - @include themify(day) { - @apply fill-gray-600; - } - @include themify(night) { - @apply fill-white; - } - } - } - - .failed-svg { - margin: 8px 0 0 5px; - - svg { - width: 16px; - height: 16px; - @apply fill-red; - } - } - - &.sending { - margin-right: 5px; - } - - &.failed { - margin-right: 7px; - } - - .sender-message-wrapper { - box-sizing: content-box; - border-radius: 10px 0px 10px 10px; - max-width: 70%; - word-wrap: break-word; - - @include themify(day) { - background-color: rgb(0, 132, 255); - @apply text-white; - } - @include themify(night) { - @apply border-gray-charcoal text-white; - } - - &.gif { - background: none; - - padding: 0; - - img { - max-width: 100%; - } - } - - &.chat-image { - background: none; - padding: 0; - - img { - max-width: 100%; - } - } - - &.gif, - &.chat-image { - .sender-message-time { - color: rgb(138, 141, 145); - } - - .sender-message-content { - padding: 10px 0 0 5px; - - img { - padding-bottom: 10px; - } - } - } - - .sender-message-content { - max-width: 100%; - margin-bottom: 0; - @apply text-white; - font-size: 16px; - font-weight: 400; - padding: 8px 8px 8px 12px; - - a { - text-decoration: underline; - color: white; - } - } - - .sender-message-time { - @apply text-white; - margin-bottom: 3px; - font-size: 10px; - display: flex; - justify-content: flex-end; - margin-right: 7px; - margin-left: 45px; - margin-top: -5px; - } - - &.same-user-message { - border-radius: 10px; - } - } - } - } - + .not-joined { display: flex; align-items: center; diff --git a/src/common/features/chats/components/chats-direct-messages/index.tsx b/src/common/features/chats/components/chats-direct-messages/index.tsx index b5f6d29fc94..6e8c746c758 100644 --- a/src/common/features/chats/components/chats-direct-messages/index.tsx +++ b/src/common/features/chats/components/chats-direct-messages/index.tsx @@ -1,27 +1,15 @@ import React, { useContext, useEffect, useState } from "react"; import usePrevious from "react-use/lib/usePrevious"; import mediumZoom, { Zoom } from "medium-zoom"; -import { Link } from "react-router-dom"; -import { - checkContiguousMessage, - formatMessageDate, - formatMessageDateAndDay, - formatMessageTime, - isMessageGif, - isMessageImage -} from "../../utils"; +import { checkContiguousMessage, formatMessageDateAndDay } from "../../utils"; import { Theme } from "../../../../store/global/types"; import { useMappedStore } from "../../../../store/use-mapped-store"; -import UserAvatar from "../../../../components/user-avatar"; -import Tooltip from "../../../../components/tooltip"; import { ChatContext } from "../../chat-context-provider"; -import { failedMessageSvg, resendMessageSvg } from "../../../../img/svg"; -import { renderPostBody } from "@ecency/render-helper"; import { _t } from "../../../../i18n"; import "./index.scss"; import ChatsConfirmationModal from "../chats-confirmation-modal"; -import { Spinner } from "@ui/spinner"; import { DirectMessage } from "../../managers/message-manager-types"; +import { ChatMessageItem } from "../chat-message-item"; interface Props { directMessages: DirectMessage[]; @@ -94,15 +82,6 @@ export default function ChatsDirectMessages(props: Props) { <> {directMessages?.map((msg, i) => { const dayAndMonth = formatMessageDateAndDay(msg, i, directMessages); - let renderedPreview = renderPostBody(msg.content, false, global.canUseWebp); - - renderedPreview = renderedPreview.replace(/]*>/g, ""); - renderedPreview = renderedPreview.replace(/<\/p>/g, ""); - - const isGif = isMessageGif(msg.content); - - const isImage = isMessageImage(msg.content); - const isSameUser = checkContiguousMessage(msg, i, directMessages); return ( @@ -114,106 +93,11 @@ export default function ChatsDirectMessages(props: Props) {
)} - {msg.creator !== activeUserKeys?.pub ? ( -
- {!isSameUser || (isSameUser && dayAndMonth) ? ( -
- - - - - -
- ) : ( - <> - )} - -
-
- -
-
-

{formatMessageTime(msg.created)}

-
- -
-
-
- ) : ( -
-
- {msg.sent === 2 && ( - - { - setStep(1); - setResendMessage(msg); - }} - > - {resendMessageSvg} - - - )} - -
-
-

{formatMessageTime(msg.created)}

-
- - {msg.sent === 0 && ( - - - - )} - {msg.sent === 2 && ( - - {failedMessageSvg} - - )} -
-
- )} + ); })} diff --git a/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx b/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx index c8bbcf180b4..88dde09ebe6 100644 --- a/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx +++ b/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx @@ -17,7 +17,7 @@ export function ChatSidebarDirectContact({ contact, username, handleRevealPrivKe const { setReceiverPubKey } = useContext(ChatContext); const { data: directMessagesLastMessages } = useDirectContactsLastMessagesQuery(); - const rawUsername = useMemo(() => username.replace("@", ""), [username]); + const rawUsername = useMemo(() => username?.replace("@", "") ?? "", [username]); const lastMessageDate = useMemo( () => getRelativeDate(directMessagesLastMessages[contact.name]?.created), [directMessagesLastMessages] diff --git a/src/common/features/chats/managers/message-manager.tsx b/src/common/features/chats/managers/message-manager.tsx index 2f68df50b83..dcecf7e6cdd 100644 --- a/src/common/features/chats/managers/message-manager.tsx +++ b/src/common/features/chats/managers/message-manager.tsx @@ -2,8 +2,6 @@ import React, { useContext, useEffect, useState } from "react"; import { ActiveUser } from "../../../store/active-user/types"; import { NostrKeysType } from "../types"; import { - Channel, - ChannelUpdate, DirectMessage, Keys, MessagesObject, @@ -145,34 +143,6 @@ const MessageManager = () => { }; }, [messageService, chat.profiles]); - // Channel creation handler - const handleChannelCreation = (data: Channel[]) => { - addChannels(data.filter((x) => !chat.channels.find((y) => y.id === x.id))); - }; - - useEffect(() => { - messageService?.removeListener(MessageEvents.ChannelCreation, handleChannelCreation); - messageService?.addListener(MessageEvents.ChannelCreation, handleChannelCreation); - - return () => { - messageService?.removeListener(MessageEvents.ChannelCreation, handleChannelCreation); - }; - }, [messageService]); - - // Channel update handler - const handleChannelUpdate = (data: ChannelUpdate[]) => { - UpdateChannels(data); - }; - - useEffect(() => { - messageService?.removeListener(MessageEvents.ChannelUpdate, handleChannelUpdate); - messageService?.addListener(MessageEvents.ChannelUpdate, handleChannelUpdate); - - return () => { - messageService?.removeListener(MessageEvents.ChannelUpdate, handleChannelUpdate); - }; - }, [messageService]); - const checkPublicMessageSending = (channelId: string, data: PublicMessage) => { setTimeout(() => { verifyPublicMessageSending(channelId, data); @@ -302,27 +272,10 @@ const MessageManager = () => { }; }, [messageService, chat.profiles, chat.publicMessages]); - // Left channel handler - const handleLeftChannelList = (data: string[]) => { - addleftChannels(data); - }; - - useEffect(() => { - messageService?.removeListener(MessageEvents.LeftChannelList, handleLeftChannelList); - messageService?.addListener(MessageEvents.LeftChannelList, handleLeftChannelList); - - return () => { - messageService?.removeListener(MessageEvents.LeftChannelList, handleLeftChannelList); - }; - }, [messageService, chat.leftChannelsList]); - useEffect(() => { return () => { messageService?.removeListener(MessageEvents.Ready, handleReadyState); messageService?.removeListener(MessageEvents.ProfileUpdate, handleProfileUpdate); - messageService?.removeListener(MessageEvents.ChannelCreation, handleChannelCreation); - messageService?.removeListener(MessageEvents.LeftChannelList, handleLeftChannelList); - messageService?.removeListener(MessageEvents.ChannelUpdate, handleChannelUpdate); messageService?.removeListener( MessageEvents.PreviousPublicMessages, handlePreviousPublicMessages diff --git a/src/common/features/chats/mutations/index.ts b/src/common/features/chats/mutations/index.ts index ab6125af0b0..7321c09adb0 100644 --- a/src/common/features/chats/mutations/index.ts +++ b/src/common/features/chats/mutations/index.ts @@ -1 +1,3 @@ export * from "./upload"; +export * from "./send-message"; +export * from "./join-chat"; diff --git a/src/common/features/chats/mutations/join-chat.ts b/src/common/features/chats/mutations/join-chat.ts index 8f9de56e85b..324c4e54499 100644 --- a/src/common/features/chats/mutations/join-chat.ts +++ b/src/common/features/chats/mutations/join-chat.ts @@ -17,7 +17,7 @@ import { ChatQueries } from "../queries"; */ export function useJoinChat(onSuccess?: () => void) { const { activeUser, chat, resetChat } = useMappedStore(); - const { mutateAsync: uploadPublicKey } = useMutation(["chat-upload-public-key"], (key: string) => + const { mutateAsync: uploadPublicKey } = useMutation(["chats/upload-public-key"], (key: string) => uploadChatPublicKey(activeUser, key) ); const queryClient = useQueryClient(); diff --git a/src/common/features/chats/mutations/send-message.ts b/src/common/features/chats/mutations/send-message.ts new file mode 100644 index 00000000000..17254f94493 --- /dev/null +++ b/src/common/features/chats/mutations/send-message.ts @@ -0,0 +1,48 @@ +import { useMutation } from "@tanstack/react-query"; +import { UPLOADING } from "../components/chat-popup/chat-constants"; +import { useContext } from "react"; +import { ChatContext } from "../chat-context-provider"; +import { Channel } from "../managers/message-manager-types"; +import { useDirectContactsQuery } from "../queries"; + +export function useSendMessage( + currentChannel: Channel, + currentUser: string, + onSuccess: () => void +) { + const { messageServiceInstance, isActiveUserRemoved, receiverPubKey } = useContext(ChatContext); + const { data: directContacts } = useDirectContactsQuery(); + + return useMutation( + ["chats/send-message"], + async (message: string) => { + if (!message || message.includes(UPLOADING)) { + throw new Error("[Chat][SendMessage] – empty message or has uploading file"); + } + + if (isActiveUserRemoved) { + throw new Error("[Chat][SendMessage] – no active user"); + } + + // Set the user as direct contact if it isn't there yet + if ( + receiverPubKey && + !directContacts?.some((contact) => contact.name === currentUser) && + !!currentUser + ) { + messageServiceInstance?.publishContacts(currentUser, receiverPubKey); + } + + if (currentChannel) { + return messageServiceInstance?.sendPublicMessage(currentChannel, message, [], ""); + } else if (currentUser) { + return messageServiceInstance?.sendDirectMessage(receiverPubKey!, message); + } else { + throw new Error("[Chat][SendMessage] – no receiver"); + } + }, + { + onSuccess: () => onSuccess() + } + ); +} diff --git a/src/common/features/chats/mutations/upload.ts b/src/common/features/chats/mutations/upload.ts index 23cbe8e3e31..e1ac46cf6de 100644 --- a/src/common/features/chats/mutations/upload.ts +++ b/src/common/features/chats/mutations/upload.ts @@ -11,14 +11,11 @@ class FileUploadingError { constructor(public code: number, public message: string) {} } -export function useChatFileUpload( - setMessage: (v: string) => void, - setIsMessageText: (v: boolean) => void -) { +export function useChatFileUpload(setMessage: (v: string) => void) { const { activeUser, global } = useMappedStore(); return useMutation( - ["chat-file-upload"], + ["chats/file-upload"], async (file: File) => { const username = activeUser?.username; @@ -47,8 +44,6 @@ export function useChatFileUpload( if (imgTag) { setMessage(imgTag); } - - setIsMessageText(true); }, { onError: (e) => { diff --git a/src/common/features/chats/queries/channels-query.ts b/src/common/features/chats/queries/channels-query.ts new file mode 100644 index 00000000000..00403bf993b --- /dev/null +++ b/src/common/features/chats/queries/channels-query.ts @@ -0,0 +1,44 @@ +import { useMessageListenerQuery } from "./message-listener-query"; +import { ChatQueries } from "./queries"; +import { MessageEvents } from "../../../helper/message-service"; +import { Channel } from "../managers/message-manager-types"; + +export function useChannelsQuery() { + console.log("starting"); + + useMessageListenerQuery( + [ChatQueries.CHANNELS], + MessageEvents.ChannelCreation, + (data, nextData, resolver) => { + console.log(nextData); + resolver([...data, ...nextData.filter((ch) => !data.some((dCh) => ch.id === dCh.id))]); + }, + { + initialData: [] + } + ); + + useMessageListenerQuery( + [ChatQueries.CHANNELS], + MessageEvents.ChannelUpdate, + (data, nextData, resolver) => { + console.log(nextData); + resolver([...data, ...nextData.filter((ch) => !data.some((dCh) => ch.id === dCh.id))]); + }, + { + initialData: [] + } + ); + + useMessageListenerQuery( + [ChatQueries.CHANNELS], + MessageEvents.LeftChannelList, + (data, nextData, resolver) => { + console.log(nextData); + resolver([...data, ...nextData.filter((ch) => !data.some((dCh) => ch.id === dCh.id))]); + }, + { + initialData: [] + } + ); +} diff --git a/src/common/features/chats/queries/index.ts b/src/common/features/chats/queries/index.ts index 99b8b717386..f84ca4c3c5e 100644 --- a/src/common/features/chats/queries/index.ts +++ b/src/common/features/chats/queries/index.ts @@ -4,3 +4,4 @@ export * from "./direct-contacts-query"; export * from "./message-listener-query"; export * from "./messages-query"; export * from "./chat-profiles-query"; +export * from "./channels-query"; diff --git a/src/common/features/chats/queries/queries.ts b/src/common/features/chats/queries/queries.ts index 9f5460ef791..9191aeca21e 100644 --- a/src/common/features/chats/queries/queries.ts +++ b/src/common/features/chats/queries/queries.ts @@ -10,5 +10,6 @@ export enum ChatQueries { BEFORE_PUBLIC_MESSAGES_TEMP = "chats/before-public-messages-temp", AFTER_PUBLIC_MESSAGES_TEMP = "chats/after-public-messages-temp", PREVIOUS_PUBLIC_MESSAGES = "chats/previous-public-messages", - PROFILES = "chats/profiles" + PROFILES = "chats/profiles", + CHANNELS = "chats/channels" } diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 07aa032b40d..5a3d24cb06f 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -78,7 +78,9 @@ "first": "First", "last": "Last", "ok": "OK", - "join": "Join" + "join": "Join", + "resend": "Resend", + "failed": "Failed" }, "confirm": { "title": "Are you sure?", From b6d2d423e20998f4479f2319e0e20d4646212696 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Mon, 6 Nov 2023 20:21:29 +0600 Subject: [PATCH 112/179] Chats: made queries and mutations call Nostr directly w/o message service --- .../features/chats/chat-context-provider.tsx | 5 +- .../chats/components/chat-message-box.tsx | 10 +- .../chats/components/chat-messages-header.tsx | 11 +- .../chats/components/chat-messages-view.tsx | 15 +- .../chat-popup/chat-popup-direct-messages.tsx | 7 +- .../chats-channel-messages/index.tsx | 4 +- .../chats-community-dropdown-menu/index.tsx | 6 +- .../components/join-community-chat-btn.tsx | 217 ++------- .../chats/managers/message-manager-types.ts | 4 +- .../chats/mutations/add-community-channel.ts | 44 ++ .../chats/mutations/create-community-chat.ts | 61 +++ src/common/features/chats/mutations/index.ts | 2 + .../features/chats/mutations/send-message.ts | 5 + src/common/features/chats/nostr/index.ts | 4 + .../features/chats/nostr/nostr-context.ts | 12 + .../chats/nostr/nostr-fetch-mutation.ts | 22 + .../chats/nostr/nostr-fetch-query.tsx | 32 ++ .../features/chats/nostr/nostr-filters.ts | 0 .../features/chats/nostr/nostr-provider.tsx | 32 ++ .../chats/nostr/nostr-publish-mutation.ts | 58 +++ .../chats/nostr/utils/event-converter.ts | 39 ++ .../features/chats/nostr/utils/index.ts | 1 + .../chats/nostr/utils/listen-while-finish.ts | 27 ++ .../features/chats/queries/channels-query.ts | 76 ++-- .../chats/queries/community-channel-query.ts | 16 + .../chats/queries/direct-contacts-query.ts | 41 +- src/common/features/chats/queries/index.ts | 2 + .../chats/queries/message-listener-query.tsx | 3 +- .../nostr-joined-community-team-query.ts | 58 +++ src/common/features/chats/queries/queries.ts | 5 +- src/common/helper/message-service.ts | 415 +++++++++--------- 31 files changed, 773 insertions(+), 461 deletions(-) create mode 100644 src/common/features/chats/mutations/add-community-channel.ts create mode 100644 src/common/features/chats/mutations/create-community-chat.ts create mode 100644 src/common/features/chats/nostr/index.ts create mode 100644 src/common/features/chats/nostr/nostr-context.ts create mode 100644 src/common/features/chats/nostr/nostr-fetch-mutation.ts create mode 100644 src/common/features/chats/nostr/nostr-fetch-query.tsx create mode 100644 src/common/features/chats/nostr/nostr-filters.ts create mode 100644 src/common/features/chats/nostr/nostr-provider.tsx create mode 100644 src/common/features/chats/nostr/nostr-publish-mutation.ts create mode 100644 src/common/features/chats/nostr/utils/event-converter.ts create mode 100644 src/common/features/chats/nostr/utils/index.ts create mode 100644 src/common/features/chats/nostr/utils/listen-while-finish.ts create mode 100644 src/common/features/chats/queries/community-channel-query.ts create mode 100644 src/common/features/chats/queries/nostr-joined-community-team-query.ts diff --git a/src/common/features/chats/chat-context-provider.tsx b/src/common/features/chats/chat-context-provider.tsx index 8b601bd7af7..da8fd94633f 100644 --- a/src/common/features/chats/chat-context-provider.tsx +++ b/src/common/features/chats/chat-context-provider.tsx @@ -6,8 +6,9 @@ import { useMappedStore } from "../../store/use-mapped-store"; import { NostrKeysType } from "./types"; import { useMount } from "react-use"; import { useKeysQuery } from "./queries/keys-query"; -import { useJoinChat } from "./mutations/join-chat"; +import { useJoinChat } from "./mutations"; import { MessageListenerQueriesProvider } from "./queries"; +import { NostrProvider } from "./nostr"; interface Context { activeUserKeys: NostrKeysType; @@ -169,7 +170,7 @@ export const ChatContextProvider = (props: Props) => { initMessageServiceInstance }} > - {props.children} + {props.children} ); diff --git a/src/common/features/chats/components/chat-message-box.tsx b/src/common/features/chats/components/chat-message-box.tsx index 4243ea8f096..30f0a06b11c 100644 --- a/src/common/features/chats/components/chat-message-box.tsx +++ b/src/common/features/chats/components/chat-message-box.tsx @@ -11,6 +11,7 @@ import { ChatContext } from "../chat-context-provider"; import { useMount } from "react-use"; import { Button } from "@ui/button"; import { Channel, ChannelUpdate } from "../managers/message-manager-types"; +import { useChannelsQuery } from "../queries"; interface MatchParams { filter: string; @@ -27,10 +28,11 @@ interface Props { export default function ChatsMessagesBox(props: Props) { const { chat } = useMappedStore(); + const { data: channels } = useChannelsQuery(); const { messageServiceInstance, currentChannel, setCurrentChannel } = useContext(ChatContext); - const { channels, updatedChannel } = chat; + const { updatedChannel } = chat; const { match } = props; const username = match.params.username; @@ -61,8 +63,8 @@ export default function ChatsMessagesBox(props: Props) { const checkUserCommunityMembership = () => { getCommunityProfile(); - const communities = getJoinedCommunities(chat.channels, chat.leftChannelsList); - const isCommunity = chat.channels.some((channel) => channel.communityName === username); + const communities = getJoinedCommunities(channels ?? [], chat.leftChannelsList); + const isCommunity = channels?.some((channel) => channel.communityName === username); if (isCommunity) { setIsCommunityChatEnabled(true); const isJoined = communities.some((channel) => channel.communityName === username); @@ -81,7 +83,7 @@ export default function ChatsMessagesBox(props: Props) { // TODO: MAKE QUERY const fetchCurrentChannel = (communityName: string) => { - const channel = channels.find((channel) => channel.communityName === communityName); + const channel = channels?.find((channel) => channel.communityName === communityName); if (channel) { const updated: ChannelUpdate = updatedChannel .filter((x) => x.channelId === channel.id) diff --git a/src/common/features/chats/components/chat-messages-header.tsx b/src/common/features/chats/components/chat-messages-header.tsx index 4ddfcbd1095..95e4f5f72b7 100644 --- a/src/common/features/chats/components/chat-messages-header.tsx +++ b/src/common/features/chats/components/chat-messages-header.tsx @@ -1,15 +1,14 @@ import React, { useContext } from "react"; import { History } from "history"; -import { useMappedStore } from "../../../store/use-mapped-store"; import ChatsCommunityDropdownMenu from "./chats-community-dropdown-menu"; import UserAvatar from "../../../components/user-avatar"; import { CHATPAGE } from "./chat-popup/chat-constants"; -import { Chat } from "../../../store/chat/types"; import { formattedUserName } from "../utils"; import Link from "../../../components/alink"; import { expandSideBar } from "../../../img/svg"; import { Button } from "@ui/button"; import { ChatContext } from "../chat-context-provider"; +import { useChannelsQuery } from "../queries"; interface Props { username: string; @@ -19,13 +18,13 @@ interface Props { export default function ChatsMessagesHeader(props: Props) { const { username } = props; const { setReceiverPubKey } = useContext(ChatContext); - const { chat } = useMappedStore(); + const { data: channels } = useChannelsQuery(); const isChannel = (username: string) => !username.startsWith("@"); - const formattedName = (username: string, chat: Chat) => { + const formattedName = (username: string) => { if (username && !username.startsWith("@")) { - const community = chat.channels.find((channel) => channel.communityName === username); + const community = channels?.find((channel) => channel.communityName === username); if (community) { return community.name; } @@ -50,7 +49,7 @@ export default function ChatsMessagesHeader(props: Props) { target="_blank" > -
{formattedName(username, chat)}
+
{formattedName(username)}
diff --git a/src/common/features/chats/components/chat-messages-view.tsx b/src/common/features/chats/components/chat-messages-view.tsx index 55fb8f72a19..00161a0dd0b 100644 --- a/src/common/features/chats/components/chat-messages-view.tsx +++ b/src/common/features/chats/components/chat-messages-view.tsx @@ -13,6 +13,7 @@ import { ChatContext } from "../chat-context-provider"; import { classNameObject } from "../../../helper/class-name-object"; import { Channel, DirectMessage, PublicMessage } from "../managers/message-manager-types"; import { useMessagesQuery } from "../queries"; +import isCommunity from "../../../helper/is-community"; interface Props { username: string; @@ -54,10 +55,6 @@ export default function ChatsMessagesView({ } }, [messages]); - useEffect(() => { - isDirectUserOrCommunity(); - }, [chat.channels]); - useEffect(() => { getChannelMessages(); }, [chat.publicMessages]); @@ -101,12 +98,10 @@ export default function ChatsMessagesView({ }; const isDirectUserOrCommunity = () => { - if (username) { - if (username && username.startsWith("@")) { - setDirectUser(username.replace("@", "")); - } else { - setCommunityName(username); - } + if (isCommunity(username)) { + setCommunityName(username.replace("@", "")); + } else { + setDirectUser(username.replace("@", "")); } }; diff --git a/src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx b/src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx index 71bfb68c06c..d1f4486e6b3 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx @@ -7,7 +7,11 @@ import { Button } from "@ui/button"; import { useMappedStore } from "../../../../store/use-mapped-store"; import { ChatContext } from "../../chat-context-provider"; import { ChatDirectMessage } from "./chat-direct-message"; -import { useDirectContactsLastMessagesQuery, useDirectContactsQuery } from "../../queries"; +import { + useChannelsQuery, + useDirectContactsLastMessagesQuery, + useDirectContactsQuery +} from "../../queries"; import { Channel } from "../../managers/message-manager-types"; interface Props { @@ -28,6 +32,7 @@ export function ChatPopupDirectMessages({ const { data: directContacts } = useDirectContactsQuery(); const { data: directContactsLastMessages } = useDirectContactsLastMessagesQuery(); + const { data: channels } = useChannelsQuery(); const [communities, setCommunities] = useState([]); diff --git a/src/common/features/chats/components/chats-channel-messages/index.tsx b/src/common/features/chats/components/chats-channel-messages/index.tsx index 359902230b9..5684b26b292 100644 --- a/src/common/features/chats/components/chats-channel-messages/index.tsx +++ b/src/common/features/chats/components/chats-channel-messages/index.tsx @@ -3,7 +3,7 @@ import { useMount } from "react-use"; import mediumZoom, { Zoom } from "medium-zoom"; import { Channel, - communityModerator, + CommunityModerator, Profile, PublicMessage } from "../../managers/message-manager-types"; @@ -192,7 +192,7 @@ export default function ChatsChannelMessages(props: Props) { } }; - const getPrivilegedUsers = (communityModerators: communityModerator[]) => { + const getPrivilegedUsers = (communityModerators: CommunityModerator[]) => { const privilegedUsers = communityModerators.filter((user) => PRIVILEGEDROLES.includes(user.role) ); diff --git a/src/common/features/chats/components/chats-community-dropdown-menu/index.tsx b/src/common/features/chats/components/chats-community-dropdown-menu/index.tsx index 29d32ed9e24..f762c9a38ba 100644 --- a/src/common/features/chats/components/chats-community-dropdown-menu/index.tsx +++ b/src/common/features/chats/components/chats-community-dropdown-menu/index.tsx @@ -26,7 +26,7 @@ import { getAccountFull } from "../../../../api/hive"; import { Button } from "@ui/button"; import { FormControl, InputGroup } from "@ui/input"; import { Modal, ModalBody, ModalHeader } from "@ui/modal"; -import { Channel, communityModerator } from "../../managers/message-manager-types"; +import { Channel, CommunityModerator } from "../../managers/message-manager-types"; interface Props { history: History; @@ -46,7 +46,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => { const [user, setUser] = useState(""); const [addRoleError, setAddRoleError] = useState(""); const [role, setRole] = useState("admin"); - const [moderator, setModerator] = useState(); + const [moderator, setModerator] = useState(); const [communityAdmins, setCommunityAdmins] = useState([]); const [blockedUsers, setBlockedUsers] = useState<{ name: string; pubkey: string }[]>([]); const [removedUserId, setRemovedUserID] = useState(""); @@ -93,7 +93,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => { const updateRole = ( event: React.ChangeEvent, - moderator: communityModerator + moderator: CommunityModerator ) => { const selectedRole = event.target.value; const moderatorIndex = currentChannel?.communityModerators?.findIndex( diff --git a/src/common/features/chats/components/join-community-chat-btn.tsx b/src/common/features/chats/components/join-community-chat-btn.tsx index 3758e4ce6ff..1f74e18d902 100644 --- a/src/common/features/chats/components/join-community-chat-btn.tsx +++ b/src/common/features/chats/components/join-community-chat-btn.tsx @@ -1,15 +1,17 @@ -import React, { useContext, useEffect, useState } from "react"; +import React, { useContext, useMemo } from "react"; import { History } from "history"; -import { Community, ROLES } from "../../../store/communities"; +import { Community } from "../../../store/communities"; import { _t } from "../../../i18n"; import { useMappedStore } from "../../../store/use-mapped-store"; import { ChatContext } from "../chat-context-provider"; -import { CHANNEL, NOSTRKEY } from "./chat-popup/chat-constants"; -import { getProfileMetaData, setChannelMetaData } from "../utils"; import { Spinner } from "@ui/spinner"; import { Button } from "@ui/button"; -import { Channel, communityModerator } from "../managers/message-manager-types"; -import { useJoinChat } from "../mutations/join-chat"; +import { useAddCommunityChannel, useCreateCommunityChat, useJoinChat } from "../mutations"; +import { + useChannelsQuery, + useCommunityChannelQuery, + useNostrJoinedCommunityTeamQuery +} from "../queries"; interface Props { history: History; @@ -18,175 +20,40 @@ interface Props { export default function JoinCommunityChatBtn(props: Props) { const { messageServiceInstance, activeUserKeys, hasUserJoinedChat } = useContext(ChatContext); - const { mutateAsync: joinChat } = useJoinChat(); const { chat, activeUser } = useMappedStore(); - const [inProgress, setInProgress] = useState(false); - const [isCommunityChatJoined, setIsCommunityChatJoined] = useState(false); - const [isChatEnabled, setIsChatEnabled] = useState(false); - const [currentCommunity, setCurrentCommunity] = useState(); - const [communityRoles, setCommunityRoles] = useState([]); - const [loadCommunity, setLoadCommunity] = useState(false); - const [initiateCommunityChat, setInitiateCommunityChat] = useState(false); + const { data: currentChannel } = useCommunityChannelQuery(props.community); + const { data: channels } = useChannelsQuery(); + const { data: communityTeam } = useNostrJoinedCommunityTeamQuery(props.community); - useEffect(() => { - fetchCommunityProfile(); - }, [chat.channels, currentCommunity, chat.leftChannelsList]); + const { mutateAsync: addCommunityChannel, isLoading: isAddCommunityChannelLoading } = + useAddCommunityChannel(currentChannel?.id); + const { mutateAsync: joinChat, isLoading: isJoinChatLoading } = useJoinChat(); + const { mutateAsync: createCommunityChat, isLoading: isCreateCommunityChatLoading } = + useCreateCommunityChat(props.community); - useEffect(() => { - fetchCommunityProfile(); - }, [activeUser]); - - useEffect(() => { - if (activeUserKeys && activeUser?.username === props.community.name) { - getCommunityRoles(); - } - }, [activeUserKeys]); - - useEffect(() => { - fetchCommunityProfile(); - checkIsChatJoined(); - if (messageServiceInstance) { - if (loadCommunity) { - messageServiceInstance?.loadChannel(currentCommunity?.id!); - setInProgress(false); - } - if ( - initiateCommunityChat && - communityRoles.some((role) => role.pubkey === activeUserKeys.pub) - ) { - createCommunityChat(); - } - } - }, [messageServiceInstance, loadCommunity, communityRoles, initiateCommunityChat]); - - useEffect(() => { - checkIsChatJoined(); - - fetchCurrentChannel(); - }, [isCommunityChatJoined, props.community, chat.channels, chat.leftChannelsList]); - - const fetchCommunityProfile = async () => { - const communityProfile = await getProfileMetaData(props.community?.name); - const haschannelMetaData = communityProfile && communityProfile.hasOwnProperty(CHANNEL); - setIsChatEnabled(haschannelMetaData); - - if (!currentCommunity) { - setCurrentCommunity(communityProfile.channel); - } - }; - - const checkIsChatJoined = () => { - setIsCommunityChatJoined( - chat.channels.some( + const isChatEnabled = useMemo(() => !!currentChannel, [currentChannel]); + const isCommunityChatJoined = useMemo( + () => + channels?.some( (item) => item.communityName === props.community.name && - !chat.leftChannelsList.includes(currentCommunity?.id!) - ) - ); - }; - - const getCommunityRoles = async () => { - let communityTeam: communityModerator[] = []; - const { community } = props; - const ownerData = await getProfileMetaData(community.name); - const ownerRole = { - name: activeUser!.username, - pubkey: activeUserKeys?.pub || ownerData.nsKey, - role: "owner" - }; - - communityTeam.push(ownerRole); - - for (let i = 0; i < community.team.length; i++) { - const item = community.team[i]; - if (item[1] === ROLES.ADMIN || item[1] === ROLES.MOD) { - const profileData = await getProfileMetaData(item[0]); - if (profileData && profileData.hasOwnProperty(NOSTRKEY)) { - const roleInfo: communityModerator = { - name: item[0], - pubkey: profileData.nsKey, - role: item[1] - }; - - communityTeam.push(roleInfo); - } - } - } - setCommunityRoles(communityTeam); - }; - - const createCommunityChat = async () => { - const { community } = props; - try { - const data = await messageServiceInstance?.createChannel({ - name: community.title, - about: community.description, - communityName: community.name, - picture: "", - communityModerators: communityRoles, - hiddenMessageIds: [], - removedUserIds: [] - }); - - const content = JSON.parse(data?.content!); - const channelMetaData = { - id: data?.id as string, - creator: data?.pubkey as string, - created: data?.created_at!, - communityName: content.communityName, - name: content.name, - about: content.about, - picture: content.picture - }; - const response = await setChannelMetaData(community.name, channelMetaData); - if (response) { - setCurrentCommunity(channelMetaData); - } - } finally { - setInProgress(false); - setIsCommunityChatJoined(true); - setInitiateCommunityChat(false); - } - }; + !chat.leftChannelsList.includes(currentChannel?.id!) + ), + [channels] + ); - const joinCommunityChat = () => { - setInProgress(true); + const join = async () => { if (!hasUserJoinedChat) { - joinChat(); - setLoadCommunity(true); - return; + await joinChat(); } - if (chat.leftChannelsList.includes(currentCommunity?.id!)) { + // TODO: need to write it as query and mutation that if we left the chat then re-join again + if (chat.leftChannelsList.includes(currentChannel?.id!)) { messageServiceInstance?.updateLeftChannelList( - chat.leftChannelsList.filter((x) => x !== currentCommunity?.id) + chat.leftChannelsList.filter((x) => x !== currentChannel?.id) ); } - messageServiceInstance?.loadChannel(currentCommunity?.id!); - setIsCommunityChatJoined(true); - setInProgress(false); - }; - - const startCommunityChat = () => { - setInProgress(true); - if (!hasUserJoinedChat) { - joinChat(); - setInitiateCommunityChat(true); - - return; - } else { - createCommunityChat(); - } - }; - - const fetchCurrentChannel = () => { - for (const item of chat.channels) { - if (item.communityName === props.community.name) { - setCurrentCommunity(item); - return item; - } - } - return {}; + await addCommunityChannel(); }; return ( @@ -196,16 +63,27 @@ export default function JoinCommunityChatBtn(props: Props) { ) : !isChatEnabled ? ( ) : !isCommunityChatJoined && isChatEnabled && hasUserJoinedChat ? (
-
+
+ ) : (

Community chat not started yet

)} diff --git a/src/common/features/chats/components/chat-messages-header.tsx b/src/common/features/chats/components/chat-messages-header.tsx index 95e4f5f72b7..9baf62fc03e 100644 --- a/src/common/features/chats/components/chat-messages-header.tsx +++ b/src/common/features/chats/components/chat-messages-header.tsx @@ -1,6 +1,6 @@ import React, { useContext } from "react"; import { History } from "history"; -import ChatsCommunityDropdownMenu from "./chats-community-dropdown-menu"; +import ChatsCommunityDropdownMenu from "./chats-community-actions"; import UserAvatar from "../../../components/user-avatar"; import { CHATPAGE } from "./chat-popup/chat-constants"; import { formattedUserName } from "../utils"; @@ -9,6 +9,7 @@ import { expandSideBar } from "../../../img/svg"; import { Button } from "@ui/button"; import { ChatContext } from "../chat-context-provider"; import { useChannelsQuery } from "../queries"; +import isCommunity from "../../../helper/is-community"; interface Props { username: string; @@ -20,8 +21,6 @@ export default function ChatsMessagesHeader(props: Props) { const { setReceiverPubKey } = useContext(ChatContext); const { data: channels } = useChannelsQuery(); - const isChannel = (username: string) => !username.startsWith("@"); - const formattedName = (username: string) => { if (username && !username.startsWith("@")) { const community = channels?.find((channel) => channel.communityName === username); @@ -53,8 +52,8 @@ export default function ChatsMessagesHeader(props: Props) {
- {isChannel(username) && ( -
+ {isCommunity(username) && ( +
void; setInProgress: (d: boolean) => void; } @@ -26,7 +25,6 @@ export default function ChatsMessagesView({ username, currentChannel, inProgress, - currentChannelSetter, setInProgress, history }: Props) { @@ -137,7 +135,6 @@ export default function ChatsMessagesView({ from={CHATPAGE} isScrolled={isScrolled} scrollToBottom={scrollToBottom} - currentChannelSetter={currentChannelSetter} /> ) : ( @@ -160,12 +157,7 @@ export default function ChatsMessagesView({
- +
); diff --git a/src/common/features/chats/components/chat-popup/chat-popup-header.tsx b/src/common/features/chats/components/chat-popup/chat-popup-header.tsx index e1bfdc959c2..38db39c956c 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-header.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-header.tsx @@ -8,7 +8,7 @@ import { expandArrow, extendedView } from "../../../../img/svg"; -import ChatsCommunityDropdownMenu from "../chats-community-dropdown-menu"; +import ChatsCommunityDropdownMenu from "../chats-community-actions"; import { history } from "../../../../store"; import ChatsDropdownMenu from "../chats-dropdown-menu"; import { classNameObject } from "../../../../helper/class-name-object"; diff --git a/src/common/features/chats/components/chat-popup/index.tsx b/src/common/features/chats/components/chat-popup/index.tsx index 4856113cd78..ceb13551777 100644 --- a/src/common/features/chats/components/chat-popup/index.tsx +++ b/src/common/features/chats/components/chat-popup/index.tsx @@ -26,6 +26,7 @@ import { ChatPopupDirectMessages } from "./chat-popup-direct-messages"; import { setNostrkeys } from "../../managers/message-manager"; import { useJoinChat } from "../../mutations/join-chat"; import { useChannelsQuery, useDirectContactsQuery, useMessagesQuery } from "../../queries"; +import { useLeftCommunityChannelsQuery } from "../../queries/left-community-channels-query"; export const ChatPopUp = () => { const { activeUser, global, chat, resetChat } = useMappedStore(); @@ -45,6 +46,7 @@ export const ChatPopUp = () => { const { data: directContacts } = useDirectContactsQuery(); const { data: channels } = useChannelsQuery(); + const { data: leftCommunityChannelsIds } = useLeftCommunityChannelsQuery(); const directContact = useMemo( () => directContacts?.find((contact) => contact.pubkey === receiverPubKey), [directContacts] @@ -88,11 +90,11 @@ export const ChatPopUp = () => { // todo: ?? useEffect(() => { - if (currentChannel && chat.leftChannelsList.includes(currentChannel.id)) { + if (currentChannel && leftCommunityChannelsIds?.includes(currentChannel.id)) { setIsCommunity(false); setCommunityName(""); } - }, [chat.leftChannelsList]); + }, [leftCommunityChannelsIds]); // todo ??? useEffect(() => { @@ -365,12 +367,7 @@ export const ChatPopUp = () => {
{(isCurrentUser || isCommunity) && ( - + )}
diff --git a/src/common/features/chats/components/chats-channel-messages/index.tsx b/src/common/features/chats/components/chats-channel-messages/index.tsx index 5684b26b292..2fe0235d79f 100644 --- a/src/common/features/chats/components/chats-channel-messages/index.tsx +++ b/src/common/features/chats/components/chats-channel-messages/index.tsx @@ -1,5 +1,4 @@ import React, { RefObject, useContext, useEffect, useRef, useState } from "react"; -import { useMount } from "react-use"; import mediumZoom, { Zoom } from "medium-zoom"; import { Channel, @@ -8,7 +7,6 @@ import { PublicMessage } from "../../managers/message-manager-types"; import { History } from "history"; -import { renderPostBody } from "@ecency/render-helper"; import { useMappedStore } from "../../../../store/use-mapped-store"; import UserAvatar from "../../../../components/user-avatar"; import FollowControls from "../../../../components/follow-controls"; @@ -16,22 +14,16 @@ import usePrevious from "react-use/lib/usePrevious"; import { _t } from "../../../../i18n"; import ChatsConfirmationModal from "../chats-confirmation-modal"; import { error } from "../../../../components/feedback"; -import { CHATPAGE, COMMUNITYADMINROLES, PRIVILEGEDROLES } from "../chat-popup/chat-constants"; +import { COMMUNITYADMINROLES, PRIVILEGEDROLES } from "../chat-popup/chat-constants"; import { Theme } from "../../../../store/global/types"; -import { - checkContiguousMessage, - formatMessageDateAndDay, - isMessageGif, - isMessageImage -} from "../../utils"; +import { checkContiguousMessage, formatMessageDateAndDay } from "../../utils"; import { ChatContext } from "../../chat-context-provider"; import "./index.scss"; import { Popover, PopoverContent } from "@ui/popover"; import { Button } from "@ui/button"; -import { Form } from "@ui/form"; -import { FormControl } from "@ui/input"; import { ChatMessageItem } from "../chat-message-item"; +import ChatInput from "../chat-input"; interface Props { publicMessages: PublicMessage[]; @@ -42,7 +34,6 @@ interface Props { isScrollToBottom: boolean; isScrolled?: boolean; scrollToBottom?: () => void; - currentChannelSetter: (channel: Channel) => void; } let zoom: Zoom | null = null; @@ -54,8 +45,7 @@ export default function ChatsChannelMessages(props: Props) { isScrollToBottom, isScrolled, currentChannel, - scrollToBottom, - currentChannelSetter + scrollToBottom } = props; const { chat, @@ -79,23 +69,13 @@ export default function ChatsChannelMessages(props: Props) { const channelMessagesRef = React.createRef(); const [communityAdmins, setCommunityAdmins] = useState([]); - const [hoveredMessageId, setHoveredMessageId] = useState(""); const [step, setStep] = useState(0); - const [dmMessage, setDmMessage] = useState(""); const [clickedMessage, setClickedMessage] = useState(""); const [removedUserId, setRemovedUserID] = useState(""); const [privilegedUsers, setPrivilegedUsers] = useState([]); const [hiddenMsgId, setHiddenMsgId] = useState(""); const [resendMessage, setResendMessage] = useState(); - const [showMessageActions, setShowMessageActions] = useState(false); - - useMount(() => { - if (window.innerWidth <= 768) { - setShowMessageActions(true); - } - }); - useEffect(() => { if (prevGlobal?.theme !== global.theme) { setBackground(); @@ -112,12 +92,6 @@ export default function ChatsChannelMessages(props: Props) { } }, [publicMessages, isScrollToBottom, channelMessagesRef]); - useEffect(() => { - if (windowWidth <= 768) { - setShowMessageActions(true); - } - }, [windowWidth]); - useEffect(() => { if (currentChannel) { zoomInitializer(); @@ -156,17 +130,6 @@ export default function ChatsChannelMessages(props: Props) { }; }, [clickedMessage]); - const sendDM = (name: string, pubkey: string) => { - if (dmMessage) { - messageServiceInstance?.sendDirectMessage(pubkey, dmMessage); - setClickedMessage(""); - setDmMessage(""); - if (from && from === CHATPAGE) { - history?.push(`/chats/@${name}`); - } - } - }; - const zoomInitializer = () => { const elements: HTMLElement[] = [...document.querySelectorAll(".chat-image img")]; zoom = mediumZoom(elements); @@ -181,17 +144,6 @@ export default function ChatsChannelMessages(props: Props) { } }; - const handleImageClick = (msgId: string, pubkey: string) => { - if (clickedMessage === msgId) { - popoverRef.current = null; - setClickedMessage(""); - } else { - popoverRef.current = null; - setClickedMessage(msgId); - setRemovedUserID(pubkey); - } - }; - const getPrivilegedUsers = (communityModerators: CommunityModerator[]) => { const privilegedUsers = communityModerators.filter((user) => PRIVILEGEDROLES.includes(user.role) @@ -267,21 +219,13 @@ export default function ChatsChannelMessages(props: Props) { try { messageServiceInstance?.updateChannel(currentChannel!, updatedMetaData); - currentChannelSetter({ ...currentChannel!, ...updatedMetaData }); + // currentChannelSetter({ ...currentChannel!, ...updatedMetaData }); setStep(0); } catch (err) { error(_t("chat.error-updating-community")); } }; - const handelMessageActions = (msgId: string) => { - if (showMessageActions && hoveredMessageId !== msgId) { - setHoveredMessageId(msgId); - } else { - setHoveredMessageId(""); - } - }; - return ( <>
@@ -291,16 +235,6 @@ export default function ChatsChannelMessages(props: Props) { const dayAndMonth = formatMessageDateAndDay(pMsg, i, publicMessages); const isSameUserMessage = checkContiguousMessage(pMsg, i, publicMessages); - - let renderedPreview = renderPostBody(pMsg.content, false, global.canUseWebp); - - renderedPreview = renderedPreview.replace(/]*>/g, ""); - renderedPreview = renderedPreview.replace(/<\/p>/g, ""); - - const isGif = isMessageGif(pMsg.content); - - const isImage = isMessageImage(pMsg.content); - const name = getProfileName(pMsg.creator, chat.profiles); const popover = ( @@ -363,23 +297,7 @@ export default function ChatsChannelMessages(props: Props) { )}
-
{ - e.preventDefault(); - e.stopPropagation(); - sendDM(name!, pMsg.creator); - }} - > - setDmMessage(e.target.value)} - required={true} - type="text" - placeholder={"Send direct message"} - autoComplete="off" - /> - +
diff --git a/src/common/features/chats/components/chats-community-actions/edit-roles-modal.tsx b/src/common/features/chats/components/chats-community-actions/edit-roles-modal.tsx new file mode 100644 index 00000000000..cbb0613ccad --- /dev/null +++ b/src/common/features/chats/components/chats-community-actions/edit-roles-modal.tsx @@ -0,0 +1,211 @@ +import { _t } from "../../../../i18n"; +import LinearProgress from "../../../../components/linear-progress"; +import { FormControl, InputGroup } from "@ui/input"; +import React, { useMemo, useState } from "react"; +import { Button } from "@ui/button"; +import { NOSTRKEY } from "../chat-popup/chat-constants"; +import { useChannelsQuery } from "../../queries"; +import UserAvatar from "../../../../components/user-avatar"; +import { ROLES } from "../../../../store/communities"; +import { CommunityModerator } from "../../managers/message-manager-types"; +import { error } from "../../../../components/feedback"; +import useDebounce from "react-use/lib/useDebounce"; +import { getAccountFull } from "../../../../api/hive"; +import { useMappedStore } from "../../../../store/use-mapped-store"; +import { useUpdateChannelModerator } from "../../mutations"; +import { Table, Td, Th, Tr } from "@ui/table"; +import { Spinner } from "@ui/spinner"; + +interface Props { + username: string; +} + +const roles = [ROLES.ADMIN, ROLES.MOD, ROLES.GUEST]; + +export function EditRolesModal({ username }: Props) { + const { activeUser } = useMappedStore(); + const { data: channels } = useChannelsQuery(); + + const [inProgress, setInProgress] = useState(false); + const [user, setUser] = useState(""); + const [moderator, setModerator] = useState(); + const [role, setRole] = useState("admin"); + const [addRoleError, setAddRoleError] = useState(""); + + const currentChannel = useMemo( + () => channels?.find((c) => c.communityName === username), + [channels] + ); + + const { mutateAsync: updateModerator, isLoading: isUpdateModeratorLoading } = + useUpdateChannelModerator(currentChannel); + + useDebounce( + async () => { + if (user.length === 0) { + setAddRoleError(""); + setInProgress(false); + return; + } + try { + const response = await getAccountFull(user); + if (!response) { + setAddRoleError("Account does not exist"); + return; + } + + if (!response.posting_json_metadata) { + setAddRoleError("This user hasn't joined the chat yet."); + return; + } + + const { posting_json_metadata } = response; + const profile = JSON.parse(posting_json_metadata).profile; + + if (!profile || !profile.hasOwnProperty(NOSTRKEY)) { + setAddRoleError("You cannot set this user because this user hasn't joined the chat yet."); + return; + } + + const alreadyExists = currentChannel?.communityModerators?.some( + (moderator) => moderator.name === response.name + ); + + if (alreadyExists) { + setAddRoleError("You have already assigned some rule to this user."); + setInProgress(false); + } else { + const moderator = { + name: user, + pubkey: profile.nsKey, + role: role + }; + setModerator(moderator); + setAddRoleError(""); + } + } catch (err) { + error(err as string); + } finally { + setInProgress(false); + } + }, + 200, + [user, role] + ); + + return ( + <> +

{_t("chat.edit-community-roles")}

+ {inProgress && } +
+
+
+
{_t("community-role-edit.username")}
+
+ + { + setUser(e.target.value); + setInProgress(true); + }} + className={addRoleError ? "is-invalid" : ""} + /> + + {addRoleError &&
{addRoleError}
} +
+
+
+
{_t("community-role-edit.role")}
+
+ ) => setRole(e.target.value)} + > + {roles.map((r, i) => ( + + ))} + +
+
+
+ +
+
+ {currentChannel?.communityModerators?.length !== 0 ? ( + <> + + + + + + + + + {currentChannel?.communityModerators && + currentChannel?.communityModerators!.map((moderator, i) => { + return ( + + + + + ); + })} + +
{_t("community.roles-account")}{_t("community.roles-role")}
+
+ {" "} + @{moderator.name} +
+
+ {moderator.name === activeUser?.username ? ( +
{moderator.role}
+ ) : ( + + } + > + ) => + updateModerator({ ...moderator, role: e.target.value }) + } + disabled={isUpdateModeratorLoading} + > + {roles.map((r, i) => ( + + ))} + + + )} +
+ + ) : ( +
+

{_t("chat.no-admin")}

+
+ )} +
+ + ); +} diff --git a/src/common/features/chats/components/chats-community-dropdown-menu/index.scss b/src/common/features/chats/components/chats-community-actions/index.scss similarity index 100% rename from src/common/features/chats/components/chats-community-dropdown-menu/index.scss rename to src/common/features/chats/components/chats-community-actions/index.scss diff --git a/src/common/features/chats/components/chats-community-dropdown-menu/index.tsx b/src/common/features/chats/components/chats-community-actions/index.tsx similarity index 55% rename from src/common/features/chats/components/chats-community-dropdown-menu/index.tsx rename to src/common/features/chats/components/chats-community-actions/index.tsx index f762c9a38ba..450156b6cc5 100644 --- a/src/common/features/chats/components/chats-community-dropdown-menu/index.tsx +++ b/src/common/features/chats/components/chats-community-actions/index.tsx @@ -1,32 +1,23 @@ import React, { useContext, useEffect, useState } from "react"; import { History } from "history"; import DropDown, { MenuItem } from "../../../../components/dropdown"; -import useDebounce from "react-use/lib/useDebounce"; import { chatLeaveSvg, editSVG, kebabMenuSvg, linkSvg, removeUserSvg } from "../../../../img/svg"; import { _t } from "../../../../i18n"; -import { - ADDROLE, - CHATPAGE, - DropDownStyle, - LEAVECOMMUNITY, - NOSTRKEY, - UNBLOCKUSER -} from "../chat-popup/chat-constants"; +import { ADDROLE, DropDownStyle, LEAVECOMMUNITY, UNBLOCKUSER } from "../chat-popup/chat-constants"; import { useMappedStore } from "../../../../store/use-mapped-store"; import { error, success } from "../../../../components/feedback"; -import LinearProgress from "../../../../components/linear-progress"; import { ROLES } from "../../../../store/communities"; import UserAvatar from "../../../../components/user-avatar"; import { copyToClipboard } from "../../utils"; import { ChatContext } from "../../chat-context-provider"; import "./index.scss"; -import { getAccountFull } from "../../../../api/hive"; import { Button } from "@ui/button"; -import { FormControl, InputGroup } from "@ui/input"; import { Modal, ModalBody, ModalHeader } from "@ui/modal"; -import { Channel, CommunityModerator } from "../../managers/message-manager-types"; +import { CommunityModerator } from "../../managers/message-manager-types"; +import { useLeaveCommunityChannel } from "../../mutations"; +import { EditRolesModal } from "./edit-roles-modal"; interface Props { history: History; @@ -42,15 +33,17 @@ const ChatsCommunityDropdownMenu = (props: Props) => { const { history, from } = props; const [step, setStep] = useState(0); const [keyDialog, setKeyDialog] = useState(false); - const [inProgress, setInProgress] = useState(false); - const [user, setUser] = useState(""); - const [addRoleError, setAddRoleError] = useState(""); - const [role, setRole] = useState("admin"); const [moderator, setModerator] = useState(); const [communityAdmins, setCommunityAdmins] = useState([]); const [blockedUsers, setBlockedUsers] = useState<{ name: string; pubkey: string }[]>([]); const [removedUserId, setRemovedUserID] = useState(""); + const { mutateAsync: leaveChannel } = useLeaveCommunityChannel(() => { + setKeyDialog(false); + setStep(0); + history?.push("/chats"); + }); + const { messageServiceInstance } = useContext(ChatContext); useEffect(() => { @@ -91,83 +84,11 @@ const ChatsCommunityDropdownMenu = (props: Props) => { setStep(3); }; - const updateRole = ( - event: React.ChangeEvent, - moderator: CommunityModerator - ) => { - const selectedRole = event.target.value; - const moderatorIndex = currentChannel?.communityModerators?.findIndex( - (mod) => mod.name === moderator.name - ); - if (moderatorIndex !== -1 && currentChannel) { - const newUpdatedChannel: Channel = { ...currentChannel }; - const newUpdatedModerator = { ...newUpdatedChannel?.communityModerators![moderatorIndex!] }; - newUpdatedModerator.role = selectedRole; - newUpdatedChannel!.communityModerators![moderatorIndex!] = newUpdatedModerator; - setCurrentChannel(newUpdatedChannel); - messageServiceInstance?.updateChannel(currentChannel, newUpdatedChannel); - success("Roles updated succesfully"); - } - }; - const finish = () => { setStep(0); setKeyDialog(false); }; - useDebounce( - async () => { - if (user.length === 0) { - setAddRoleError(""); - setInProgress(false); - return; - } - try { - const response = await getAccountFull(user); - if (!response) { - setAddRoleError("Account does not exist"); - return; - } - - if (!response.posting_json_metadata) { - setAddRoleError("This user hasn't joined the chat yet."); - return; - } - - const { posting_json_metadata } = response; - const profile = JSON.parse(posting_json_metadata).profile; - - if (!profile || !profile.hasOwnProperty(NOSTRKEY)) { - setAddRoleError("You cannot set this user because this user hasn't joined the chat yet."); - return; - } - - const alreadyExists = currentChannel?.communityModerators?.some( - (moderator) => moderator.name === response.name - ); - - if (alreadyExists) { - setAddRoleError("You have already assigned some rule to this user."); - setInProgress(false); - } else { - const moderator = { - name: user, - pubkey: profile.nsKey, - role: role - }; - setModerator(moderator); - setAddRoleError(""); - } - } catch (err) { - error(err as string); - } finally { - setInProgress(false); - } - }, - 200, - [user, role] - ); - const communityMenuItems: MenuItem[] = [ { label: _t("chat.invite"), @@ -247,117 +168,6 @@ const ChatsCommunityDropdownMenu = (props: Props) => { ); }; - const EditRolesModal = () => { - return ( - <> -
-
-

{_t("chat.edit-community-roles")}

- {inProgress && } -
-
-
-
-
-
{_t("community-role-edit.username")}
-
- - { - setUser(e.target.value); - setInProgress(true); - }} - className={addRoleError ? "is-invalid" : ""} - /> - - {addRoleError &&
{addRoleError}
} -
-
-
-
{_t("community-role-edit.role")}
-
- ) => setRole(e.target.value)} - > - {roles.map((r, i) => ( - - ))} - -
-
-
- -
-
- {currentChannel?.communityModerators?.length !== 0 ? ( - <> - - - - - - - - - {currentChannel?.communityModerators && - currentChannel?.communityModerators!.map((moderator, i) => { - return ( - - - - - ); - })} - -
{_t("community.roles-account")}{_t("community.roles-role")}
- - {" "} - @{moderator.name} - - - {moderator.name === activeUser?.username ? ( -

{moderator.role}

- ) : ( - ) => - updateRole(e, moderator) - } - > - {roles.map((r, i) => ( - - ))} - - )} -
- - ) : ( -
-

{_t("chat.no-admin")}

-
- )} -
- - ); - }; - const blockedUsersModal = () => { return ( <> @@ -440,16 +250,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => { const handleConfirmButton = (actionType: string) => { switch (actionType) { case LEAVECOMMUNITY: - messageServiceInstance - ?.updateLeftChannelList([...chat.leftChannelsList!, currentChannel?.id!]) - .then(() => {}) - .finally(() => { - setKeyDialog(false); - setStep(0); - if (from && from === CHATPAGE) { - history?.push("/chats"); - } - }); + leaveChannel(currentChannel?.id!!); break; case UNBLOCKUSER: handleChannelUpdate(UNBLOCKUSER); @@ -490,9 +291,6 @@ const ChatsCommunityDropdownMenu = (props: Props) => { setKeyDialog(true); setRemovedUserID(""); } - if (operationType === ADDROLE) { - setUser(""); - } } catch (err) { error(_t("chat.error-updating-community")); } @@ -520,7 +318,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => { {step === 1 && confirmationModal(LEAVECOMMUNITY)} - {step === 2 && EditRolesModal()} + {step === 2 && } {step === 3 && blockedUsersModal()} {step === 4 && confirmationModal(UNBLOCKUSER)} {step === 5 && successModal(UNBLOCKUSER)} diff --git a/src/common/features/chats/components/chats-sidebar/chat-sidebar-channel.tsx b/src/common/features/chats/components/chats-sidebar/chat-sidebar-channel.tsx index 60490c7c9f5..9a2772d5970 100644 --- a/src/common/features/chats/components/chats-sidebar/chat-sidebar-channel.tsx +++ b/src/common/features/chats/components/chats-sidebar/chat-sidebar-channel.tsx @@ -1,9 +1,11 @@ import { Link } from "react-router-dom"; -import React, { useContext } from "react"; +import React, { useContext, useMemo } from "react"; import { Channel } from "../../managers/message-manager-types"; -import { ChatContext } from "../../chat-context-provider"; import UserAvatar from "../../../../components/user-avatar"; import { useLastMessagesQuery } from "../../queries"; +import { classNameObject } from "../../../../helper/class-name-object"; +import { getRelativeDate } from "../../utils"; +import { ChatContext } from "../../chat-context-provider"; interface Props { username: string; @@ -11,22 +13,37 @@ interface Props { } export function ChatSidebarChannel({ channel, username }: Props) { - const { setRevealPrivKey } = useContext(ChatContext); + const { revealPrivKey, setRevealPrivKey, setReceiverPubKey } = useContext(ChatContext); const { data: lastMessages } = useLastMessagesQuery(); + const rawUsername = useMemo(() => username?.replace("@", "") ?? "", [username]); + const lastMessageDate = useMemo( + () => getRelativeDate(lastMessages[channel.name]?.created), + [lastMessages] + ); + return ( - -
setRevealPrivKey(false)} - > - -
-

{channel.name}

-

{lastMessages[channel.name].content}

+ { + if (revealPrivKey) { + setRevealPrivKey(false); + } + }} + > + +
+
+
{channel.name}
+
{lastMessageDate}
+
{lastMessages[channel.name]?.content}
); diff --git a/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx b/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx index 502b0bdde2c..f841b050ba2 100644 --- a/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx +++ b/src/common/features/chats/components/chats-sidebar/chat-sidebar-direct-contact.tsx @@ -15,11 +15,11 @@ interface Props { export function ChatSidebarDirectContact({ contact, username }: Props) { const { setReceiverPubKey, revealPrivKey, setRevealPrivKey } = useContext(ChatContext); - const { data: directMessagesLastMessages } = useLastMessagesQuery(); + const { data: lastMessages } = useLastMessagesQuery(); const rawUsername = useMemo(() => username?.replace("@", "") ?? "", [username]); const lastMessageDate = useMemo( - () => getRelativeDate(directMessagesLastMessages[contact.name]?.created), - [directMessagesLastMessages] + () => getRelativeDate(lastMessages[contact.name]?.created), + [lastMessages] ); return ( @@ -43,9 +43,7 @@ export function ChatSidebarDirectContact({ contact, username }: Props) {
{contact.name}
{lastMessageDate}
-
- {directMessagesLastMessages[contact.name]?.content} -
+
{lastMessages[contact.name]?.content}
); diff --git a/src/common/features/chats/components/chats-sidebar/index.tsx b/src/common/features/chats/components/chats-sidebar/index.tsx index 3ea54341daf..2708c83f7ef 100644 --- a/src/common/features/chats/components/chats-sidebar/index.tsx +++ b/src/common/features/chats/components/chats-sidebar/index.tsx @@ -98,7 +98,11 @@ export default function ChatsSideBar(props: Props) { )) ) : ( <> - {communities.length !== 0 &&

Communities

} + {communities.length !== 0 && ( +
+ {_t("chat.communities")} +
+ )} {communities.map((channel) => ( ))} diff --git a/src/common/features/chats/components/join-community-chat-btn.tsx b/src/common/features/chats/components/join-community-chat-btn.tsx index c3704d297fe..4b8260ce2fc 100644 --- a/src/common/features/chats/components/join-community-chat-btn.tsx +++ b/src/common/features/chats/components/join-community-chat-btn.tsx @@ -20,8 +20,8 @@ interface Props { } export default function JoinCommunityChatBtn(props: Props) { - const { messageServiceInstance, activeUserKeys, hasUserJoinedChat } = useContext(ChatContext); - const { chat, activeUser } = useMappedStore(); + const { activeUserKeys, hasUserJoinedChat } = useContext(ChatContext); + const { activeUser } = useMappedStore(); const { data: currentChannel } = useCommunityChannelQuery(props.community); const { data: channels } = useChannelsQuery(); @@ -49,12 +49,6 @@ export default function JoinCommunityChatBtn(props: Props) { if (!hasUserJoinedChat) { await joinChat(); } - // TODO: need to write it as query and mutation that if we left the chat then re-join again - if (chat.leftChannelsList.includes(currentChannel?.id!)) { - messageServiceInstance?.updateLeftChannelList( - chat.leftChannelsList.filter((x) => x !== currentChannel?.id) - ); - } await addCommunityChannel(); }; diff --git a/src/common/features/chats/mutations/add-community-channel.ts b/src/common/features/chats/mutations/add-community-channel.ts index b042916d9e9..96d38331cac 100644 --- a/src/common/features/chats/mutations/add-community-channel.ts +++ b/src/common/features/chats/mutations/add-community-channel.ts @@ -1,13 +1,17 @@ -import { useNostrFetchMutation } from "../nostr"; +import { useNostrFetchMutation, useUpdateLeftChannels } from "../nostr"; import { Kind } from "../../../../lib/nostr-tools/event"; import { convertEvent } from "../nostr/utils/event-converter"; import { ChatQueries, useChannelsQuery } from "../queries"; import { useQueryClient } from "@tanstack/react-query"; +import { useLeftCommunityChannelsQuery } from "../queries/left-community-channels-query"; export function useAddCommunityChannel(name: string | undefined) { const { data: channels } = useChannelsQuery(); const queryClient = useQueryClient(); + const { data: leftCommunityChannelsIds } = useLeftCommunityChannelsQuery(); + const { mutateAsync: updateLeftChannels } = useUpdateLeftChannels(); + return useNostrFetchMutation( ["chats/add-community-channel"], [ @@ -36,6 +40,14 @@ export function useAddCommunityChannel(name: string | undefined) { queryClient.setQueryData([ChatQueries.CHANNELS], [...(channels ?? []), channel]); queryClient.invalidateQueries([ChatQueries.CHANNELS]); } + + // Remove the community from left list + updateLeftChannels({ + tags: [["d", "left-channel-list"]], + eventMetadata: JSON.stringify( + leftCommunityChannelsIds?.filter((id) => name !== id) ?? [] + ) + }); } }); } diff --git a/src/common/features/chats/mutations/index.ts b/src/common/features/chats/mutations/index.ts index 998202e57fa..ea2befcd2de 100644 --- a/src/common/features/chats/mutations/index.ts +++ b/src/common/features/chats/mutations/index.ts @@ -4,3 +4,6 @@ export * from "./join-chat"; export * from "./create-community-chat"; export * from "./add-community-channel"; export * from "./add-direct-contact"; +export * from "./leave-community-channel"; +export * from "./update-community-channel"; +export * from "./update-channel-moderator"; diff --git a/src/common/features/chats/mutations/leave-community-channel.ts b/src/common/features/chats/mutations/leave-community-channel.ts index fbd5d113b30..90446ef53df 100644 --- a/src/common/features/chats/mutations/leave-community-channel.ts +++ b/src/common/features/chats/mutations/leave-community-channel.ts @@ -1 +1,20 @@ -export function useLeaveCommunityChannel() {} +import { useUpdateLeftChannels } from "../nostr"; +import { useMutation } from "@tanstack/react-query"; +import { useLeftCommunityChannelsQuery } from "../queries/left-community-channels-query"; + +export function useLeaveCommunityChannel(onSuccess?: () => void) { + const { data: leftCommunityChannelsIds } = useLeftCommunityChannelsQuery(); + const { mutateAsync: updateLeftChannels } = useUpdateLeftChannels(); + + return useMutation(["chats/leave-community-channels"], async (name: string) => + updateLeftChannels( + { + tags: [["d", "left-channel-list"]], + eventMetadata: JSON.stringify([...(leftCommunityChannelsIds ?? []), name]) + }, + { + onSuccess + } + ) + ); +} diff --git a/src/common/features/chats/mutations/send-message.ts b/src/common/features/chats/mutations/send-message.ts index 3fc9b362c6b..6d884a53f58 100644 --- a/src/common/features/chats/mutations/send-message.ts +++ b/src/common/features/chats/mutations/send-message.ts @@ -8,9 +8,9 @@ import { useNostrSendDirectMessage, useNostrSendPublicMessage } from "../nostr"; import { useAddDirectContact } from "./add-direct-contact"; export function useSendMessage( - currentChannel: Channel, - currentUser: string, - onSuccess: () => void + currentChannel?: Channel, + currentUser?: string, + onSuccess?: () => void ) { const { activeUserKeys, isActiveUserRemoved, receiverPubKey } = useContext(ChatContext); const { mutateAsync: sendDirectMessage } = useNostrSendDirectMessage( @@ -31,14 +31,16 @@ export function useSendMessage( throw new Error("[Chat][SendMessage] – no active user"); } - // Add user to direct contacts if its not there yet - // E.g. if user opened chat room directly from the address bar - addDirectContact({ pubkey: receiverPubKey, name: currentUser }); - if (!currentChannel && isCommunity(currentUser)) { throw new Error("[Chat][SendMessage] – provided user is community but channel not found"); } + // Add user to direct contacts if it's not there yet + // E.g. if user opened chat room directly from the address bar + if (currentUser) { + addDirectContact({ pubkey: receiverPubKey, name: currentUser }); + } + if (currentChannel) { return sendPublicMessage({ message }); } else if (currentUser) { @@ -48,7 +50,7 @@ export function useSendMessage( } }, { - onSuccess: () => onSuccess() + onSuccess: () => onSuccess?.() } ); } diff --git a/src/common/features/chats/mutations/update-channel-moderator.ts b/src/common/features/chats/mutations/update-channel-moderator.ts new file mode 100644 index 00000000000..57cfc8b183b --- /dev/null +++ b/src/common/features/chats/mutations/update-channel-moderator.ts @@ -0,0 +1,29 @@ +import { useMutation } from "@tanstack/react-query"; +import { Channel, CommunityModerator } from "../managers/message-manager-types"; +import { useUpdateCommunityChannel } from "./update-community-channel"; + +export function useUpdateChannelModerator(channel?: Channel) { + const { mutateAsync: updateChannel } = useUpdateCommunityChannel(channel); + + return useMutation( + ["chats/update-channel-moderator", channel?.communityName], + async (moderator: CommunityModerator) => { + const moderatorIndex = channel?.communityModerators?.findIndex( + (mod) => mod.name === moderator.name + ); + if (!channel) { + console.error("[Chat][Nostr] – trying to update not existing channel"); + return; + } + + const newUpdatedChannel: Channel = { ...channel }; + if (moderatorIndex === -1) { + newUpdatedChannel!.communityModerators?.push(moderator); + } else { + newUpdatedChannel!.communityModerators![moderatorIndex!] = moderator; + } + + return updateChannel(newUpdatedChannel); + } + ); +} diff --git a/src/common/features/chats/mutations/update-community-channel.ts b/src/common/features/chats/mutations/update-community-channel.ts new file mode 100644 index 00000000000..ca4d8ccf439 --- /dev/null +++ b/src/common/features/chats/mutations/update-community-channel.ts @@ -0,0 +1,50 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { Channel } from "../managers/message-manager-types"; +import { ChatQueries, useChannelsQuery } from "../queries"; +import { useNostrPublishMutation } from "../nostr"; +import { Kind } from "../../../../lib/nostr-tools/event"; +import { useFindHealthyRelayQuery } from "../nostr/mutations/find-healthy-relay"; + +export function useUpdateCommunityChannel(channel?: Channel) { + const queryClient = useQueryClient(); + const { data: channels } = useChannelsQuery(); + + const { mutateAsync: updateChannel } = useNostrPublishMutation( + ["chats/nostr-update-channel", channel?.communityName], + Kind.ChannelMetadata, + () => {} + ); + const { mutateAsync: findHealthyRelay } = useFindHealthyRelayQuery(); + + return useMutation( + ["chats/update-community-channel", channel?.communityName], + async (newUpdatedChannel: Channel) => { + if (!channel) { + return; + } + + const relay = await findHealthyRelay(channel.id); + + await updateChannel({ + tags: [["e", channel.id, ...(relay ? [relay] : [])]], + eventMetadata: JSON.stringify(newUpdatedChannel) + }); + + return newUpdatedChannel; + }, + { + onSuccess: (updatedChannel) => { + if (!updatedChannel) { + return; + } + + const tempChannels = [...(channels ?? [])]; + const index = tempChannels.findIndex((ch) => ch.id === updatedChannel?.id); + tempChannels[index] = updatedChannel; + + queryClient.setQueryData([ChatQueries.CHANNELS], tempChannels); + queryClient.invalidateQueries([ChatQueries.CHANNELS]); + } + } + ); +} diff --git a/src/common/features/chats/nostr/mutations/index.ts b/src/common/features/chats/nostr/mutations/index.ts index 92e687d702a..56960208a26 100644 --- a/src/common/features/chats/nostr/mutations/index.ts +++ b/src/common/features/chats/nostr/mutations/index.ts @@ -1,2 +1,3 @@ export * from "./send-direct-message"; export * from "./send-public-message"; +export * from "./update-left-channels"; diff --git a/src/common/features/chats/nostr/mutations/send-public-message.ts b/src/common/features/chats/nostr/mutations/send-public-message.ts index bc78cfd46b9..656bce36603 100644 --- a/src/common/features/chats/nostr/mutations/send-public-message.ts +++ b/src/common/features/chats/nostr/mutations/send-public-message.ts @@ -8,8 +8,8 @@ interface Payload { mentions?: string[]; } -export function useNostrSendPublicMessage(channelId: string, parent?: string) { - const { mutateAsync: publishEncryptedMessage } = useNostrPublishMutation( +export function useNostrSendPublicMessage(channelId?: string, parent?: string) { + const { mutateAsync: publishChannelMessage } = useNostrPublishMutation( ["chats/nostr-publish-channel-message"], Kind.ChannelMessage, () => {} @@ -18,6 +18,12 @@ export function useNostrSendPublicMessage(channelId: string, parent?: string) { return useMutation(["chats/send-public-message"], async ({ message, mentions }: Payload) => { const root = parent || channelId; + + if (!root) { + console.error("[Chat][Nostr] – trying to send public message to not existing channel"); + return; + } + const relay = await findHealthyRelay(root); const tags: string[][] = []; @@ -29,7 +35,7 @@ export function useNostrSendPublicMessage(channelId: string, parent?: string) { mentions.forEach((m) => tags.push(["p", m])); } - return publishEncryptedMessage({ + return publishChannelMessage({ tags, eventMetadata: message }); diff --git a/src/common/features/chats/nostr/mutations/update-left-channels.ts b/src/common/features/chats/nostr/mutations/update-left-channels.ts new file mode 100644 index 00000000000..aa994ceece1 --- /dev/null +++ b/src/common/features/chats/nostr/mutations/update-left-channels.ts @@ -0,0 +1,5 @@ +import { useNostrPublishMutation } from "../core"; + +export function useUpdateLeftChannels() { + return useNostrPublishMutation(["chats/nostr-update-left-channels-list"], 30078, () => {}); +} diff --git a/src/common/features/chats/nostr/utils/listen-while-finish.ts b/src/common/features/chats/nostr/utils/listen-while-finish.ts index cfd3db1d621..57b3d9399ac 100644 --- a/src/common/features/chats/nostr/utils/listen-while-finish.ts +++ b/src/common/features/chats/nostr/utils/listen-while-finish.ts @@ -20,7 +20,14 @@ export async function listenWhileFinish( const events: Event[] = []; subInfo?.on("event", (event: Event) => events.push(event)); subInfo?.on("eose", () => { - resolve(events.sort((a, b) => b.created_at - a.created_at)); + resolve( + events + .reduce( + (acc, event) => [...acc, ...(acc.some((e) => e.id === event.id) ? [] : [event])], + [] + ) + .sort((a, b) => b.created_at - a.created_at) + ); subInfo?.unsub(); }); }); diff --git a/src/common/features/chats/queries/community-channel-query.ts b/src/common/features/chats/queries/community-channel-query.ts index 3ca5946a2a5..1ea4ad8dac4 100644 --- a/src/common/features/chats/queries/community-channel-query.ts +++ b/src/common/features/chats/queries/community-channel-query.ts @@ -8,8 +8,12 @@ import { Channel } from "../managers/message-manager-types"; * Get the community's channel information * @see {@link ../mutations/create-community-chat.ts} */ -export function useCommunityChannelQuery(community: Community) { - return useQuery([ChatQueries.COMMUNITY_CHANNEL, community.name], async () => { +export function useCommunityChannelQuery(community?: Community) { + return useQuery([ChatQueries.COMMUNITY_CHANNEL, community?.name], async () => { + if (!community) { + return undefined; + } + const communityProfile = await getProfileMetaData(community.name); return communityProfile.channel; }); diff --git a/src/common/features/chats/queries/last-messages-query.ts b/src/common/features/chats/queries/last-messages-query.ts index 443091d17c4..c6bc5eecb89 100644 --- a/src/common/features/chats/queries/last-messages-query.ts +++ b/src/common/features/chats/queries/last-messages-query.ts @@ -1,36 +1,45 @@ -import { useMessagesQuery } from "./messages-query"; import { useQuery } from "@tanstack/react-query"; import { Message } from "../managers/message-manager-types"; import { ChatQueries } from "./queries"; import { getDirectLastMessage } from "../utils"; import { useDirectContactsQuery } from "./direct-contacts-query"; import { useChannelsQuery } from "./channels-query"; +import { useDirectMessagesQuery, usePublicMessagesQuery } from "../nostr/queries"; +import { useContext } from "react"; +import { ChatContext } from "../chat-context-provider"; export function useLastMessagesQuery() { + const { activeUserKeys } = useContext(ChatContext); + const { data: contacts } = useDirectContactsQuery(); const { data: channels } = useChannelsQuery(); - const { data: messages } = useMessagesQuery("all"); + const { data: initialDirectMessages } = useDirectMessagesQuery( + contacts ?? [], + activeUserKeys.pub, + activeUserKeys.priv + ); + const { data: initialPublicMessages } = usePublicMessagesQuery(channels ?? []); const getContactsLastMessages = () => - (contacts ?? []).reduce>((acc, current) => { - const currentMessages = messages.filter((i) => + (contacts ?? []).reduce>((acc, current) => { + const currentMessages = initialDirectMessages?.filter((i) => "peer" in i ? i.peer === current?.pubkey : i.root === current?.pubkey ); - return { ...acc, [current.name]: getDirectLastMessage(currentMessages) }; + return { ...acc, [current.name]: getDirectLastMessage(currentMessages ?? []) }; }, {}); const getChannelsLastMessages = () => - (channels ?? []).reduce>((acc, current) => { - const currentMessages = messages.filter((i) => i.root === current.id); - return { ...acc, [current.name]: getDirectLastMessage(currentMessages) }; + (channels ?? []).reduce>((acc, current) => { + const currentMessages = initialPublicMessages?.filter((i) => i.root === current.id); + return { ...acc, [current.name]: getDirectLastMessage(currentMessages ?? []) }; }, {}); - return useQuery>( + return useQuery>( [ChatQueries.LAST_MESSAGES], async () => ({ ...getContactsLastMessages(), ...getChannelsLastMessages() }), { initialData: {}, - enabled: messages.length > 0 + enabled: !!initialDirectMessages?.length && !!initialPublicMessages?.length } ); } diff --git a/src/common/features/chats/queries/messages-query.ts b/src/common/features/chats/queries/messages-query.ts index a8b78bc5719..49243a4b85d 100644 --- a/src/common/features/chats/queries/messages-query.ts +++ b/src/common/features/chats/queries/messages-query.ts @@ -21,8 +21,8 @@ export function useMessagesQuery(username?: string) { const { data: initialPublicMessages } = usePublicMessagesQuery(channels ?? []); const currentChannel = useMemo( - () => channels?.find((channel) => channel.name === username), - [channels] + () => channels?.find((channel) => channel.communityName === username), + [channels, username] ); return useQuery( @@ -49,11 +49,11 @@ export function useMessagesQuery(username?: string) { initialData: [], select: (messages) => { if (currentChannel) { - return messages.filter( - (message) => !currentChannel.hiddenMessageIds?.includes(message.id) - ); + return messages + .filter((message) => !currentChannel.hiddenMessageIds?.includes(message.id)) + .sort((a, b) => a.created - b.created); } - return messages; + return messages.sort((a, b) => a.created - b.created); } } ); diff --git a/src/common/features/chats/screens/chats.tsx b/src/common/features/chats/screens/chats.tsx index ba9308e82f6..b92dcbe285b 100644 --- a/src/common/features/chats/screens/chats.tsx +++ b/src/common/features/chats/screens/chats.tsx @@ -14,6 +14,8 @@ import ChatsMessagesBox from "../components/chat-message-box"; import JoinChat from "../components/join-chat"; import { classNameObject } from "../../../helper/class-name-object"; import "./_chats.scss"; +import { useChannelsQuery } from "../queries"; +import { useLeftCommunityChannelsQuery } from "../queries/left-community-channels-query"; interface Props extends PageProps { match: match<{ @@ -27,28 +29,39 @@ interface Props extends PageProps { export const Chats = (props: Props) => { const { activeUser, global } = useMappedStore(); - const { match, history } = props; - - const username = match.params.username; - const { receiverPubKey, showSpinner, activeUserKeys, revealPrivKey, chatPrivKey } = useContext(ChatContext); + const { data: channels } = useChannelsQuery(); + const { data: leftCommunityChannelsIds } = useLeftCommunityChannelsQuery(); + + const isChannel = useMemo( + () => + channels?.some( + (channel) => + channel.communityName === props.match.params.username && + !leftCommunityChannelsIds?.includes(channel.name) + ), + [channels, leftCommunityChannelsIds] + ); + const isReady = useMemo( () => !!(activeUser && activeUserKeys?.pub && chatPrivKey), [activeUserKeys, activeUserKeys, chatPrivKey] ); const isShowManageKey = useMemo(() => isReady && revealPrivKey, [isReady, revealPrivKey]); const isShowChatRoom = useMemo( - () => isReady && !showSpinner && !!receiverPubKey && !revealPrivKey, + () => isReady && !showSpinner && (!!receiverPubKey || isChannel) && !revealPrivKey, [isReady, showSpinner, receiverPubKey, revealPrivKey] ); const isShowDefaultScreen = useMemo( - () => isReady && !receiverPubKey && !revealPrivKey && !showSpinner, + () => isReady && !receiverPubKey && !isChannel && !revealPrivKey && !showSpinner, [isReady, receiverPubKey, revealPrivKey, showSpinner] ); const isShowImportChats = useMemo(() => !isReady && !showSpinner, [isReady, showSpinner]); + const { match, history } = props; + useEffect(() => { document.body.style.overflow = "hidden"; @@ -65,7 +78,7 @@ export const Chats = (props: Props) => {
- {isReady ? : <>} + {isReady ? : <>}
{ + if (!directMessages.length) { + return undefined; + } + const messages = directMessages.sort((a, b) => a.created - b.created); const lastMessage = messages.slice(-1); return lastMessage[0]; diff --git a/src/common/helper/is-community.ts b/src/common/helper/is-community.ts index b5b5aae16c4..0c233af2fb0 100644 --- a/src/common/helper/is-community.ts +++ b/src/common/helper/is-community.ts @@ -1 +1 @@ -export default (s: string) => s.match(/^hive-\d+/) !== null; +export default (s?: string) => s?.match(/^hive-\d+/) !== null; diff --git a/src/common/pages/community-functional.tsx b/src/common/pages/community-functional.tsx index fbe3631bfe7..e8499eb4d8b 100644 --- a/src/common/pages/community-functional.tsx +++ b/src/common/pages/community-functional.tsx @@ -37,15 +37,14 @@ import { connect } from "react-redux"; import { withPersistentScroll } from "../components/with-persistent-scroll"; import "./community.scss"; import LoginRequired from "../components/login-required"; -import { useMappedStore } from "../store/use-mapped-store"; import { QueryIdentifiers, useCommunityCache } from "../core"; import { useQueryClient } from "@tanstack/react-query"; import { ChatContext } from "../features/chats/chat-context-provider"; import { Button } from "@ui/button"; import { Modal, ModalBody, ModalHeader } from "@ui/modal"; -import { useJoinChat } from "../features/chats/mutations/join-chat"; import { useChannelsQuery } from "../features/chats/queries"; import { useLeftCommunityChannelsQuery } from "../features/chats/queries/left-community-channels-query"; +import { useAddCommunityChannel, useJoinChat } from "../features/chats/mutations"; interface MatchParams { filter: string; @@ -61,13 +60,16 @@ export const CommunityPage = (props: Props) => { const updatedLocation = props.location.search.replace(/communityid=.*?(?=&|$)/, ""); return updatedLocation.replace("?", "").replace("q", "").replace("=", "").replace("&", ""); }; - const { chat } = useMappedStore(); - const queryClient = useQueryClient(); const { data: community } = useCommunityCache(props.match.params.name); const { data: channels } = useChannelsQuery(); const { data: leftChannelsIds } = useLeftCommunityChannelsQuery(); + const currentChannel = useMemo( + () => channels?.find((channel) => channel.communityName === community?.name), + [channels, community] + ); + const [account, setAccount] = useState( props.accounts.find(({ name }) => [props.match.params.name]) ); @@ -77,32 +79,23 @@ export const CommunityPage = (props: Props) => { const [searchData, setSearchData] = useState([]); const [isJoinCommunity, setIsJoinCommunity] = useState(false); const [inProgress, setInProgress] = useState(false); - const [channelId, setChannelId] = useState(""); const [isLoading, setIsLoading] = useState(false); - const [loadCommunity, setLoadCommunity] = useState(false); const prevMatch = usePrevious(props.match); const prevActiveUser = usePrevious(props.activeUser); - const { messageServiceInstance, hasUserJoinedChat } = useContext(ChatContext); + const { hasUserJoinedChat } = useContext(ChatContext); const { mutateAsync: joinChat } = useJoinChat(); + const { mutateAsync: addCommunityChannel } = useAddCommunityChannel(community?.name); const isCommunityAlreadyJoined = useMemo( () => getJoinedCommunities(channels ?? [], leftChannelsIds ?? []).some( - (community) => community.id === channelId + (community) => community.name === currentChannel?.communityName ), [channels, leftChannelsIds] ); - useEffect(() => { - if (messageServiceInstance && loadCommunity) { - messageServiceInstance?.loadChannel(channelId); - setInProgress(false); - setIsJoinCommunity(false); - } - }, [loadCommunity, messageServiceInstance]); - useEffect(() => { setIsLoading(true); @@ -115,7 +108,6 @@ export const CommunityPage = (props: Props) => { const urlParams = new URLSearchParams(location.search); const communityId = urlParams.get("communityid"); if (communityId) { - setChannelId(communityId); setIsJoinCommunity(true); } @@ -233,20 +225,12 @@ export const CommunityPage = (props: Props) => { ); - const joinCommunityChat = () => { + const joinCommunityChat = async () => { if (!hasUserJoinedChat) { setInProgress(true); - joinChat(); - setLoadCommunity(true); - return; - } - if (chat.leftChannelsList.includes(channelId)) { - messageServiceInstance?.updateLeftChannelList( - chat.leftChannelsList.filter((x) => x !== channelId) - ); + await joinChat(); } - messageServiceInstance?.loadChannel(channelId); - setIsJoinCommunity(false); + await addCommunityChannel(); }; const joinCommunityModal = () => { diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts index f08d52f622f..f7f0a934ed5 100644 --- a/src/common/store/chat/index.ts +++ b/src/common/store/chat/index.ts @@ -27,50 +27,15 @@ import { } from "./types"; export const initialState: Chat = { - directMessages: [], - channels: [], - publicMessages: [], profiles: [], - leftChannelsList: [], updatedChannel: [] }; export default (state: Chat = initialState, action: Actions): Chat => { switch (action.type) { - case ActionTypes.DIRECTMESSAGES: { - const { peer, data } = action; - return { - ...state, - directMessages: state.directMessages.map((contact) => - contact.peer === peer - ? { peer: peer, chat: { ...contact.chat, [data.id]: data } } - : contact - ) - }; - } - case ActionTypes.RESET: return initialState; - case ActionTypes.PUBLICMESSAGES: { - const { channelId, data } = action; - - return { - ...state, - publicMessages: state.publicMessages.map((obj) => - obj.channelId === channelId - ? { - channelId: channelId, - PublicMessage: { - ...obj.PublicMessage, - [data.id]: data // Add the new message object with its ID as the key - } - } - : obj - ) - }; - } - case ActionTypes.PROFILES: { const { data } = action; @@ -83,15 +48,6 @@ export default (state: Chat = initialState, action: Actions): Chat => { profiles: [...state.profiles, ...filteredProfiles] }; } - - case ActionTypes.LEFTCHANNELLIST: { - const { data } = action; - return { - ...state, - leftChannelsList: [...data] - }; - } - case ActionTypes.UPDATEDCHANNEL: { const { data } = action; return { @@ -112,14 +68,7 @@ export default (state: Chat = initialState, action: Actions): Chat => { } return { - ...state, - publicMessages: [ - ...state.publicMessages.map((obj) => - obj.channelId === channelId - ? { channelId: channelId, PublicMessage: { ...publicChat } } - : obj - ) - ] + ...state }; } diff --git a/src/common/store/chat/types.ts b/src/common/store/chat/types.ts index 46d3fe1ff12..2e54cfd2e74 100644 --- a/src/common/store/chat/types.ts +++ b/src/common/store/chat/types.ts @@ -1,5 +1,4 @@ import { - Channel, ChannelUpdate, DirectMessage, MessagesObject, @@ -24,15 +23,12 @@ export interface publicMessagesList { export interface Chat { profiles: Profile[]; - leftChannelsList: string[]; updatedChannel: ChannelUpdate[]; } export enum ActionTypes { RESET = "@chat/RESET", - PUBLICMESSAGES = "@chat/PUBLICMESSAGES", PROFILES = "@chat/PROFILES", - LEFTCHANNELLIST = "@chat/LEFTCHANNELLIST", UPDATEDCHANNEL = "@chat/UPDATEDCHANNEL", REPLACEPUBLICMESSAGE = "@chat/REPLACEPUBLICMESSAGE", VERIFYPUBLICMESSAGESENDING = "@chat/VERIFYMESSAGESENDING", @@ -43,37 +39,15 @@ export enum ActionTypes { ADDPREVIOUSPUBLICMESSAGES = "@chat/ADDPREVIOUSPUBLICMESSAGES" } -export interface DirectContactsAction { - type: ActionTypes.DIRECTCONTACTS; - data: DirectContactsType[]; -} - export interface ResetChatAction { type: ActionTypes.RESET; } -export interface DirectMessagesAction { - type: ActionTypes.DIRECTMESSAGES; - data: DirectMessage; - peer: string; -} - -export interface PublicMessagesAction { - type: ActionTypes.PUBLICMESSAGES; - data: PublicMessage; - channelId: string; -} - export interface ProfilesAction { type: ActionTypes.PROFILES; data: Profile[]; } -export interface LeftChannelsAction { - type: ActionTypes.LEFTCHANNELLIST; - data: string[]; -} - export interface UpdateChannelAction { type: ActionTypes.UPDATEDCHANNEL; data: ChannelUpdate[]; @@ -122,12 +96,8 @@ export interface AddPreviousPublicMessagesAction { } export type Actions = - | DirectContactsAction - | DirectMessagesAction | ResetChatAction - | PublicMessagesAction | ProfilesAction - | LeftChannelsAction | UpdateChannelAction | ReplacePublicMessagesAction | VerifyPublicMessageSendingAction From 02bfaa7b7a9bf6cf6c217b19ae941ab32d4e8461 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Wed, 8 Nov 2023 18:34:05 +0600 Subject: [PATCH 117/179] Chats: added previous messages fetching mutation --- .../chats/components/chat-message-box.tsx | 3 +- .../chats/components/chat-messages-view.tsx | 30 ++--- .../chat-popup/chat-direct-message.tsx | 2 +- .../chat-popup/chat-popup-header.tsx | 12 +- .../chat-popup/chat-popup-messages-list.tsx | 64 ++++------ .../chats/components/chat-popup/index.tsx | 109 +++--------------- .../chats/components/chat-profile-box.tsx | 75 ++++-------- .../chats-channel-messages/index.tsx | 10 +- .../chats-direct-messages/index.tsx | 7 +- .../mutations/fetch-previous-messages.ts | 36 ++++++ src/common/features/chats/mutations/index.ts | 1 + 11 files changed, 120 insertions(+), 229 deletions(-) create mode 100644 src/common/features/chats/mutations/fetch-previous-messages.ts diff --git a/src/common/features/chats/components/chat-message-box.tsx b/src/common/features/chats/components/chat-message-box.tsx index b8d24de72f8..046efb8dd7d 100644 --- a/src/common/features/chats/components/chat-message-box.tsx +++ b/src/common/features/chats/components/chat-message-box.tsx @@ -42,7 +42,7 @@ export default function ChatsMessagesBox(props: Props) { const currentChannel = useMemo( () => channels?.find((c) => c.communityName === community?.name), - [channels] + [channels, community] ); const hasCommunityChat = useMemo(() => !!communityChannel, [communityChannel]); const hasLeftCommunity = useMemo( @@ -80,7 +80,6 @@ export default function ChatsMessagesBox(props: Props) { history={props.history} username={username} currentChannel={currentChannel!} - inProgress={inProgress} setInProgress={setInProgress} /> diff --git a/src/common/features/chats/components/chat-messages-view.tsx b/src/common/features/chats/components/chat-messages-view.tsx index f5223526fa5..893ef51cc8f 100644 --- a/src/common/features/chats/components/chat-messages-view.tsx +++ b/src/common/features/chats/components/chat-messages-view.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useRef, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { Link } from "react-router-dom"; import { History } from "history"; import ChatsProfileBox from "./chat-profile-box"; @@ -7,28 +7,25 @@ import ChatsDirectMessages from "./chats-direct-messages"; import ChatInput from "./chat-input"; import ChatsScroller from "./chats-scroller"; import { CHATPAGE } from "./chat-popup/chat-constants"; -import { ChatContext } from "../chat-context-provider"; import { classNameObject } from "../../../helper/class-name-object"; import { Channel, DirectMessage, PublicMessage } from "../managers/message-manager-types"; import { useMessagesQuery } from "../queries"; import isCommunity from "../../../helper/is-community"; +import { useFetchPreviousMessages } from "../mutations"; interface Props { username: string; history: History; currentChannel: Channel; - inProgress: boolean; setInProgress: (d: boolean) => void; } export default function ChatsMessagesView({ username, currentChannel, - inProgress, setInProgress, history }: Props) { - const { messageServiceInstance } = useContext(ChatContext); const { data: messages } = useMessagesQuery(username.replace("@", "")); const messagesBoxRef = useRef(null); @@ -40,10 +37,17 @@ export default function ChatsMessagesView({ const [hasMore, setHasMore] = useState(true); const [isScrolled, setIsScrolled] = useState(false); + const { mutateAsync: fetchPreviousMessages, isLoading: isFetchingPreviousMessages } = + useFetchPreviousMessages(currentChannel, () => {}); + useEffect(() => { isDirectUserOrCommunity(); }, []); + useEffect(() => { + setInProgress(isFetchingPreviousMessages); + }, [isFetchingPreviousMessages]); + useEffect(() => { if (messages.length < 45) { setHasMore(false); @@ -64,20 +68,16 @@ export default function ChatsMessagesView({ }, [isTop]); const fetchPrevMessages = () => { - if (!hasMore || inProgress) return; + if (!hasMore) return; setInProgress(true); - messageServiceInstance - ?.fetchPrevMessages(currentChannel!.id, messages[0].created) - .then((num) => { - if (num < 25) { + fetchPreviousMessages() + .then((events) => { + if (events.length < 25) { setHasMore(false); } }) - .finally(() => { - setInProgress(false); - setIsTop(false); - }); + .finally(() => setIsTop(false)); }; const isDirectUserOrCommunity = () => { @@ -122,7 +122,7 @@ export default function ChatsMessagesView({ to={username.startsWith("@") ? `/${username}` : `/created/${username}`} target="_blank" > - + {communityName.length !== 0 ? ( <> diff --git a/src/common/features/chats/components/chat-popup/chat-direct-message.tsx b/src/common/features/chats/components/chat-popup/chat-direct-message.tsx index 931cfc66c19..909edcba206 100644 --- a/src/common/features/chats/components/chat-popup/chat-direct-message.tsx +++ b/src/common/features/chats/components/chat-popup/chat-direct-message.tsx @@ -4,7 +4,7 @@ import UserAvatar from "../../../../components/user-avatar"; interface Props { username: string; - lastMessage: string; + lastMessage?: string; userClicked: (username: string) => void; } diff --git a/src/common/features/chats/components/chat-popup/chat-popup-header.tsx b/src/common/features/chats/components/chat-popup/chat-popup-header.tsx index 38db39c956c..a89fe706dd7 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-header.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-header.tsx @@ -14,7 +14,6 @@ import ChatsDropdownMenu from "../chats-dropdown-menu"; import { classNameObject } from "../../../../helper/class-name-object"; import React, { useContext } from "react"; import UserAvatar from "../../../../components/user-avatar"; -import { Community } from "../../../../store/communities"; import { ChatContext } from "../../chat-context-provider"; interface Props { @@ -30,7 +29,6 @@ interface Props { handleRefreshSvgClick: () => void; handleExtendedView: () => void; setExpanded: (v: boolean) => void; - currentCommunity?: Community; } export function ChatPopupHeader({ @@ -45,8 +43,7 @@ export function ChatPopupHeader({ handleMessageSvgClick, handleRefreshSvgClick, handleExtendedView, - setExpanded, - currentCommunity + setExpanded }: Props) { const { revealPrivKey, activeUserKeys, hasUserJoinedChat, setRevealPrivKey } = useContext(ChatContext); @@ -67,17 +64,14 @@ export function ChatPopupHeader({ )}
setExpanded(!expanded)}> {(currentUser || isCommunity) && ( - + )}
{currentUser ? currentUser : isCommunity - ? currentCommunity?.title + ? communityName : showSearchUser ? _t("chat.new-message") : revealPrivKey diff --git a/src/common/features/chats/components/chat-popup/chat-popup-messages-list.tsx b/src/common/features/chats/components/chat-popup/chat-popup-messages-list.tsx index b6ae2f437b1..c79f8f4ba7d 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-messages-list.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-messages-list.tsx @@ -3,63 +3,43 @@ import ChatsProfileBox from "../chat-profile-box"; import ChatsDirectMessages from "../chats-direct-messages"; import ChatsChannelMessages from "../chats-channel-messages"; import { history } from "../../../../store"; -import React, { useContext } from "react"; -import { ChatContext } from "../../chat-context-provider"; -import { Community } from "../../../../store/communities"; +import React, { useMemo } from "react"; import { DirectMessage, PublicMessage } from "../../managers/message-manager-types"; -import { useMessagesQuery } from "../../queries"; +import { useChannelsQuery, useMessagesQuery } from "../../queries"; +import { useCommunityCache } from "../../../../core"; interface Props { - isCurrentUser: boolean; - currentUser: string; - isCommunity: boolean; - communityName: string; - currentCommunity?: Community; + username: string; } -export function ChatPopupMessagesList({ - isCommunity, - currentUser, - isCurrentUser, - currentCommunity, - communityName -}: Props) { - const { currentChannel, setCurrentChannel } = useContext(ChatContext); - const { data: messages } = useMessagesQuery(currentUser); +export function ChatPopupMessagesList({ username }: Props) { + const { data: currentCommunity } = useCommunityCache(username); + const { data: messages } = useMessagesQuery(username); + const { data: channels } = useChannelsQuery(); + const currentChannel = useMemo( + () => channels?.find((channel) => channel.communityName === currentCommunity?.name), + [channels, currentCommunity] + ); return (
{" "} - - + + - {isCurrentUser ? ( - - ) : ( + {!!currentChannel ? ( + ) : ( + )}
diff --git a/src/common/features/chats/components/chat-popup/index.tsx b/src/common/features/chats/components/chat-popup/index.tsx index ceb13551777..713b33980ae 100644 --- a/src/common/features/chats/components/chat-popup/index.tsx +++ b/src/common/features/chats/components/chat-popup/index.tsx @@ -1,8 +1,6 @@ import React, { useContext, useEffect, useMemo, useRef, useState } from "react"; import { useLocation } from "react-router"; import { history } from "../../../../store"; -import { Community } from "../../../../store/communities"; -import { ChannelUpdate } from "../../managers/message-manager-types"; import Tooltip from "../../../../components/tooltip"; import LinearProgress from "../../../../components/linear-progress"; import ManageChatKey from "../manage-chat-key"; @@ -10,9 +8,8 @@ import ChatInput from "../chat-input"; import { chevronDownSvgForSlider, chevronUpSvg } from "../../../../img/svg"; import { _t } from "../../../../i18n"; import { usePrevious } from "../../../../util/use-previous"; -import { getCommunity } from "../../../../api/bridge"; import "./index.scss"; -import { getPrivateKey, getUserChatPublicKey } from "../../utils"; +import { getPrivateKey } from "../../utils"; import { useMappedStore } from "../../../../store/use-mapped-store"; import { ChatContext } from "../../chat-context-provider"; import { useMount } from "react-use"; @@ -24,9 +21,9 @@ import { ChatPopupMessagesList } from "./chat-popup-messages-list"; import { ChatPopupSearchUser } from "./chat-popup-search-user"; import { ChatPopupDirectMessages } from "./chat-popup-direct-messages"; import { setNostrkeys } from "../../managers/message-manager"; -import { useJoinChat } from "../../mutations/join-chat"; import { useChannelsQuery, useDirectContactsQuery, useMessagesQuery } from "../../queries"; import { useLeftCommunityChannelsQuery } from "../../queries/left-community-channels-query"; +import { useFetchPreviousMessages, useJoinChat } from "../../mutations"; export const ChatPopUp = () => { const { activeUser, global, chat, resetChat } = useMappedStore(); @@ -35,10 +32,7 @@ export const ChatPopUp = () => { messageServiceInstance, revealPrivKey, activeUserKeys, - showSpinner, hasUserJoinedChat, - currentChannel, - setCurrentChannel, setRevealPrivKey, setShowSpinner } = useContext(ChatContext); @@ -67,11 +61,14 @@ export const ChatPopUp = () => { const [receiverPubKey, setReceiverPubKey] = useState(""); const [isCommunity, setIsCommunity] = useState(false); const [communityName, setCommunityName] = useState(""); - const [currentCommunity, setCurrentCommunity] = useState(); const [isTop, setIsTop] = useState(false); const [hasMore, setHasMore] = useState(true); const [isScrolled, setIsScrolled] = useState(false); + const currentChannel = useMemo( + () => channels?.find((channel) => channel.communityName === communityName), + [communityName, channels] + ); const isCurrentUser = useMemo(() => !!currentUser, [currentUser]); const canSendMessage = useMemo( () => @@ -79,6 +76,9 @@ export const ChatPopUp = () => { [currentUser, hasUserJoinedChat, activeUserKeys, isCommunity, revealPrivKey] ); + const { mutateAsync: fetchPreviousMessages, isLoading: isFetchingMore } = + useFetchPreviousMessages(currentChannel); + useMount(() => { setShow(!routerLocation.pathname.match("/chats") && !!activeUser); }); @@ -96,36 +96,6 @@ export const ChatPopUp = () => { } }, [leftCommunityChannelsIds]); - // todo ??? - useEffect(() => { - const updated: ChannelUpdate = chat.updatedChannel - .filter((x) => x.channelId === currentChannel?.id!) - .sort((a, b) => b.created - a.created)[0]; - if (currentChannel && updated) { - // TODO: check that updated channel affecting messagse - // const publicMessages: PublicMessage[] = fetchCommunityMessages( - // chat.publicMessages, - // currentChannel, - // updated?.hiddenMessageIds - // ); - // const messages = publicMessages.sort((a, b) => a.created - b.created); - // setPublicMessages(messages); - const channel = { - name: updated.name, - about: updated.about, - picture: updated.picture, - communityName: updated.communityName, - communityModerators: updated.communityModerators, - id: updated.channelId, - creator: updated.creator, - created: currentChannel?.created!, - hiddenMessageIds: updated.hiddenMessageIds, - removedUserIds: updated.removedUserIds - }; - setCurrentChannel(channel); - } - }, [chat.updatedChannel]); - // Fetching previous messages when scrol achieved top useEffect(() => { if (isTop) { @@ -155,61 +125,20 @@ export const ChatPopUp = () => { useEffect(() => { if (isCommunity && show) { - fetchCommunity(); - scrollerClicked(); } }, [isCommunity, communityName, show]); - useEffect(() => { - if (currentUser) { - const isCurrentUserFound = directContacts?.find((contact) => contact.name === currentUser); - if (isCurrentUserFound) { - setReceiverPubKey(isCurrentUserFound.pubkey); - } else { - setInProgress(true); - fetchCurrentUserData(); - } - - if (!messageServiceInstance) { - setNostrkeys(activeUserKeys!); - } - } else { - setInProgress(false); - } - }, [currentUser]); - - const fetchCommunity = async () => { - const community = await getCommunity(communityName, activeUser?.username); - setCurrentCommunity(community!); - }; - - const fetchCurrentUserData = async () => { - const nsKey = await getUserChatPublicKey(currentUser); - if (nsKey) { - setReceiverPubKey(nsKey); - } else { - setReceiverPubKey(""); - } - setInProgress(false); - }; - const fetchPrevMessages = () => { - if (!hasMore || inProgress) return; + if (!hasMore) return; - setInProgress(true); - // todo: make it infinite query - messageServiceInstance - ?.fetchPrevMessages(currentChannel!.id, messages[0].created) - .then((num) => { - if (num < 25) { + fetchPreviousMessages() + .then((events) => { + if (events.length < 25) { setHasMore(false); } }) - .finally(() => { - setInProgress(false); - setIsTop(false); - }); + .finally(() => setIsTop(false)); }; const handleScroll = (event: React.UIEvent) => { @@ -303,9 +232,8 @@ export const ChatPopUp = () => { handleMessageSvgClick={handleMessageSvgClick} handleRefreshSvgClick={handleRefreshSvgClick} showSearchUser={showSearchUser} - currentCommunity={currentCommunity} /> - {inProgress && } + {(inProgress || isFetchingMore) && }
{ {hasUserJoinedChat && !revealPrivKey ? ( <> {currentUser.length !== 0 || communityName.length !== 0 ? ( - + ) : showSearchUser ? ( ) : ( diff --git a/src/common/features/chats/components/chat-profile-box.tsx b/src/common/features/chats/components/chat-profile-box.tsx index ac052ed8162..d7f1c3520b4 100644 --- a/src/common/features/chats/components/chat-profile-box.tsx +++ b/src/common/features/chats/components/chat-profile-box.tsx @@ -3,10 +3,9 @@ import UserAvatar from "../../../components/user-avatar"; import { dateToFormatted } from "../../../helper/parse-date"; import { _t } from "../../../i18n"; import { getAccountFull } from "../../../api/hive"; -import { formattedUserName } from "../utils"; import { useCommunityCache } from "../../../core"; -export interface profileData { +export interface ProfileData { joiningData: string; about: string | undefined; followers: number | undefined; @@ -15,27 +14,18 @@ export interface profileData { } interface Props { - username?: string; - isCommunity?: boolean; - isCurrentUser?: boolean; communityName?: string; currentUser?: string; } -export default function ChatsProfileBox({ - username, - isCommunity, - isCurrentUser, - communityName, - currentUser -}: Props) { - const [profileData, setProfileData] = useState(); +export default function ChatsProfileBox({ communityName, currentUser }: Props) { + const [profileData, setProfileData] = useState(); - const { data: community } = useCommunityCache(username ? username! : communityName!); + const { data: community } = useCommunityCache(communityName!); useEffect(() => { fetchProfileData(); - }, [username, isCommunity, isCurrentUser, communityName, currentUser]); + }, [communityName, currentUser]); const formatFollowers = (count: number | undefined) => { if (count) { @@ -49,44 +39,23 @@ export default function ChatsProfileBox({ }; const fetchProfileData = async () => { - if (username && username?.length !== 0) { - if (username?.startsWith("@")) { - const response = await getAccountFull(formattedUserName(username)); - setProfileData({ - joiningData: response.created, - about: response.profile?.about, - followers: response.follow_stats?.follower_count, - name: response.name, - username: response.name - }); - } else { - setProfileData({ - joiningData: community?.created_at!, - about: community?.about, - followers: community?.subscribers, - name: community?.title!, - username: community?.name! - }); - } - } else { - if (isCurrentUser && currentUser?.length !== 0) { - const response = await getAccountFull(currentUser!); - setProfileData({ - joiningData: response.created, - about: response.profile?.about, - followers: response.follow_stats?.follower_count, - name: response.name, - username: response.name - }); - } else { - setProfileData({ - joiningData: community?.created_at!, - about: community?.about, - followers: community?.subscribers, - name: community?.title!, - username: community?.name! - }); - } + if (community) { + setProfileData({ + joiningData: community?.created_at!, + about: community?.about, + followers: community?.subscribers, + name: community?.title!, + username: community?.name! + }); + } else if (currentUser) { + const response = await getAccountFull(currentUser.replace("@", "")); + setProfileData({ + joiningData: response.created, + about: response.profile?.about, + followers: response.follow_stats?.follower_count, + name: response.name, + username: response.name + }); } }; diff --git a/src/common/features/chats/components/chats-channel-messages/index.tsx b/src/common/features/chats/components/chats-channel-messages/index.tsx index 2fe0235d79f..925afe5abbe 100644 --- a/src/common/features/chats/components/chats-channel-messages/index.tsx +++ b/src/common/features/chats/components/chats-channel-messages/index.tsx @@ -38,15 +38,7 @@ interface Props { let zoom: Zoom | null = null; export default function ChatsChannelMessages(props: Props) { - const { - publicMessages, - history, - from, - isScrollToBottom, - isScrolled, - currentChannel, - scrollToBottom - } = props; + const { publicMessages, isScrollToBottom, isScrolled, currentChannel, scrollToBottom } = props; const { chat, global, diff --git a/src/common/features/chats/components/chats-direct-messages/index.tsx b/src/common/features/chats/components/chats-direct-messages/index.tsx index 6e8c746c758..8ca339fda73 100644 --- a/src/common/features/chats/components/chats-direct-messages/index.tsx +++ b/src/common/features/chats/components/chats-direct-messages/index.tsx @@ -24,8 +24,7 @@ export default function ChatsDirectMessages(props: Props) { const { directMessages, currentUser, isScrolled, isScrollToBottom, scrollToBottom } = props; const { global, activeUser, deleteDirectMessage } = useMappedStore(); - const { activeUserKeys, messageServiceInstance, windowWidth, receiverPubKey } = - useContext(ChatContext); + const { activeUserKeys, messageServiceInstance, receiverPubKey } = useContext(ChatContext); let prevGlobal = usePrevious(global); const [step, setStep] = useState(0); @@ -82,8 +81,6 @@ export default function ChatsDirectMessages(props: Props) { <> {directMessages?.map((msg, i) => { const dayAndMonth = formatMessageDateAndDay(msg, i, directMessages); - const isSameUser = checkContiguousMessage(msg, i, directMessages); - return ( {dayAndMonth && ( @@ -96,7 +93,7 @@ export default function ChatsDirectMessages(props: Props) { ); diff --git a/src/common/features/chats/mutations/fetch-previous-messages.ts b/src/common/features/chats/mutations/fetch-previous-messages.ts new file mode 100644 index 00000000000..cc1621ff52c --- /dev/null +++ b/src/common/features/chats/mutations/fetch-previous-messages.ts @@ -0,0 +1,36 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useNostrFetchMutation } from "../nostr"; +import { Event, Kind } from "../../../../lib/nostr-tools/event"; +import { Channel } from "../managers/message-manager-types"; +import { ChatQueries, useMessagesQuery } from "../queries"; +import { convertEvent } from "../nostr/utils/event-converter"; + +export function useFetchPreviousMessages(channel?: Channel, onSuccess?: (events: Event[]) => void) { + const queryClient = useQueryClient(); + + const { data: messages } = useMessagesQuery(channel?.communityName); + const { mutateAsync: fetchPreviousChannels } = useNostrFetchMutation( + ["chats/nostr-fetch-previous-messages"], + [ + { + kinds: [Kind.ChannelMessage], + "#e": [channel?.name!!], + until: messages[0]?.created, + limit: 50 + } + ] + ); + + return useMutation(["chats/fetch-previous-messages"], async () => fetchPreviousChannels(), { + onSuccess: (events) => { + const previousMessages = events.map((event) => convertEvent(event)); + queryClient.setQueryData( + [ChatQueries.MESSAGES, channel?.name], + [...messages, ...previousMessages] + ); + queryClient.invalidateQueries([ChatQueries.MESSAGES, channel?.name]); + + onSuccess?.(events); + } + }); +} diff --git a/src/common/features/chats/mutations/index.ts b/src/common/features/chats/mutations/index.ts index ea2befcd2de..d10daddfb42 100644 --- a/src/common/features/chats/mutations/index.ts +++ b/src/common/features/chats/mutations/index.ts @@ -7,3 +7,4 @@ export * from "./add-direct-contact"; export * from "./leave-community-channel"; export * from "./update-community-channel"; export * from "./update-channel-moderator"; +export * from "./fetch-previous-messages"; From a9fc9e3922d494c35934b2bccb1bbb771f7be6cc Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Wed, 8 Nov 2023 20:16:25 +0600 Subject: [PATCH 118/179] Chats: adding message to list after sending --- .../chats/components/chat-message-box.tsx | 2 +- ...x => chat-popup-contacts-and-channels.tsx} | 14 +++---- .../chats/components/chat-popup/index.tsx | 37 ++++++------------- .../features/chats/components/join-chat.tsx | 24 +++--------- .../features/chats/mutations/send-message.ts | 28 ++++++++++++-- .../nostr/mutations/send-direct-message.ts | 12 +++++- .../nostr/mutations/send-public-message.ts | 7 ++-- .../nostr/queries/direct-messages-query.ts | 3 +- .../features/chats/nostr/queries/index.ts | 1 + .../nostr/queries/public-messages-query.ts | 3 +- .../features/chats/nostr/queries/queries.ts | 4 ++ .../features/chats/queries/messages-query.ts | 4 +- src/common/features/chats/queries/queries.ts | 5 --- 13 files changed, 74 insertions(+), 70 deletions(-) rename src/common/features/chats/components/chat-popup/{chat-popup-direct-messages.tsx => chat-popup-contacts-and-channels.tsx} (88%) create mode 100644 src/common/features/chats/nostr/queries/queries.ts diff --git a/src/common/features/chats/components/chat-message-box.tsx b/src/common/features/chats/components/chat-message-box.tsx index 046efb8dd7d..fa8d2ddfef4 100644 --- a/src/common/features/chats/components/chat-message-box.tsx +++ b/src/common/features/chats/components/chat-message-box.tsx @@ -54,7 +54,7 @@ export default function ChatsMessagesBox(props: Props) { getJoinedCommunities(channels ?? [], leftChannelsIds ?? []).some( (channel) => channel.id === currentChannel?.id ), - [channels, leftChannelsIds] + [channels, currentChannel, leftChannelsIds] ); return ( diff --git a/src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx b/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx similarity index 88% rename from src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx rename to src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx index 44fb1673d30..282a74424ba 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-direct-messages.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx @@ -12,38 +12,36 @@ import { useLeftCommunityChannelsQuery } from "../../queries/left-community-chan interface Props { communityClicked: (v: string) => void; userClicked: (v: string) => void; - setReceiverPubKey: (v: string) => void; setShowSearchUser: (v: boolean) => void; } -export function ChatPopupDirectMessages({ +export function ChatPopupContactsAndChannels({ communityClicked, userClicked, - setReceiverPubKey, setShowSearchUser }: Props) { - const { activeUserKeys, showSpinner } = useContext(ChatContext); + const { activeUserKeys, showSpinner, setReceiverPubKey } = useContext(ChatContext); const { data: directContacts } = useDirectContactsQuery(); const { data: directContactsLastMessages } = useLastMessagesQuery(); const { data: channels } = useChannelsQuery(); const { data: leftChannelsIds } = useLeftCommunityChannelsQuery(); - const communities = useMemo( + const joinedChannels = useMemo( () => getJoinedCommunities(channels ?? [], leftChannelsIds ?? []), [channels, leftChannelsIds] ); return ( <> - {(directContacts?.length !== 0 || (channels?.length !== 0 && communities.length !== 0)) && + {(directContacts?.length !== 0 || (channels?.length !== 0 && joinedChannels.length !== 0)) && !showSpinner && activeUserKeys?.priv ? ( <> - {channels?.length !== 0 && communities.length !== 0 && ( + {joinedChannels.length !== 0 && ( <>
{_t("chat.communities")}
- {communities.map((channel) => ( + {joinedChannels.map((channel) => ( { const { activeUser, global, chat, resetChat } = useMappedStore(); const { - messageServiceInstance, + receiverPubKey, revealPrivKey, activeUserKeys, hasUserJoinedChat, @@ -38,14 +37,13 @@ export const ChatPopUp = () => { } = useContext(ChatContext); const { mutateAsync: joinChat, isLoading: isJoinChatLoading } = useJoinChat(); - const { data: directContacts } = useDirectContactsQuery(); - const { data: channels } = useChannelsQuery(); - const { data: leftCommunityChannelsIds } = useLeftCommunityChannelsQuery(); + const { data: directContacts, isLoading: isDirectContactsLoading } = useDirectContactsQuery(); const directContact = useMemo( () => directContacts?.find((contact) => contact.pubkey === receiverPubKey), - [directContacts] + [directContacts, receiverPubKey] ); const { data: messages } = useMessagesQuery(directContact?.name); + const { data: channels, isLoading: isChannelsLoading } = useChannelsQuery(); const routerLocation = useLocation(); const prevActiveUser = usePrevious(activeUser); @@ -56,9 +54,7 @@ export const ChatPopUp = () => { const [isScrollToTop, setIsScrollToTop] = useState(false); const [isScrollToBottom, setIsScrollToBottom] = useState(false); const [showSearchUser, setShowSearchUser] = useState(false); - const [inProgress, setInProgress] = useState(false); const [show, setShow] = useState(false); - const [receiverPubKey, setReceiverPubKey] = useState(""); const [isCommunity, setIsCommunity] = useState(false); const [communityName, setCommunityName] = useState(""); const [isTop, setIsTop] = useState(false); @@ -88,14 +84,6 @@ export const ChatPopUp = () => { setShow(!routerLocation.pathname.match("/chats") && !!activeUser); }, [routerLocation, activeUser]); - // todo: ?? - useEffect(() => { - if (currentChannel && leftCommunityChannelsIds?.includes(currentChannel.id)) { - setIsCommunity(false); - setCommunityName(""); - } - }, [leftCommunityChannelsIds]); - // Fetching previous messages when scrol achieved top useEffect(() => { if (isTop) { @@ -186,11 +174,6 @@ export const ChatPopUp = () => { } }; - const communityClicked = (community: string) => { - setIsCommunity(true); - setCommunityName(community); - }; - const handleBackArrowSvg = () => { setCurrentUser(""); setCommunityName(""); @@ -233,7 +216,7 @@ export const ChatPopUp = () => { handleRefreshSvgClick={handleRefreshSvgClick} showSearchUser={showSearchUser} /> - {(inProgress || isFetchingMore) && } + {(isJoinChatLoading || isChannelsLoading || isFetchingMore) && }
{ ) : showSearchUser ? ( ) : ( - { + setIsCommunity(true); + setCommunityName(community); + }} setShowSearchUser={setShowSearchUser} userClicked={(username) => setCurrentUser(username)} /> diff --git a/src/common/features/chats/components/join-chat.tsx b/src/common/features/chats/components/join-chat.tsx index 182665021a0..5f075e6d127 100644 --- a/src/common/features/chats/components/join-chat.tsx +++ b/src/common/features/chats/components/join-chat.tsx @@ -1,31 +1,19 @@ -import React, { useContext, useEffect, useState } from "react"; -import { ChatContext } from "../chat-context-provider"; +import React from "react"; import { Button } from "@ui/button"; import { Spinner } from "@ui/spinner"; import { _t } from "../../../i18n"; -import { useJoinChat } from "../mutations/join-chat"; +import { useJoinChat } from "../mutations"; export default function JoinChat() { - const { messageServiceInstance } = useContext(ChatContext); - const { mutateAsync: joinChat } = useJoinChat(); - - const [showSpinner, setShowSpinner] = useState(false); - - useEffect(() => { - if (messageServiceInstance) { - setShowSpinner(false); - } - }, [messageServiceInstance]); + const { mutateAsync: joinChat, isLoading } = useJoinChat(); return (

{_t("chat.you-haven-t-joined")}

+ + ) : ( + <> + + + )} + + )} +
+ + +
+
+ + + ); +} diff --git a/src/common/features/chats/components/chat-message-item.tsx b/src/common/features/chats/components/chat-message-item.tsx index 2011d1a5bb6..2b49988b70c 100644 --- a/src/common/features/chats/components/chat-message-item.tsx +++ b/src/common/features/chats/components/chat-message-item.tsx @@ -1,4 +1,4 @@ -import { Message } from "../managers/message-manager-types"; +import { Channel, Message } from "../managers/message-manager-types"; import Tooltip from "../../../components/tooltip"; import { failedMessageSvg, resendMessageSvg } from "../../../img/svg"; import { formatMessageTime, isMessageGif, isMessageImage } from "../utils"; @@ -8,14 +8,16 @@ import { classNameObject } from "../../../helper/class-name-object"; import { renderPostBody } from "@ecency/render-helper"; import { useMappedStore } from "../../../store/use-mapped-store"; import { _t } from "../../../i18n"; +import { ChatMessageChannelItemExtension } from "./chat-message-channel-item-extension"; interface Props { type: "sender" | "receiver"; message: Message; isSameUser: boolean; + currentChannel?: Channel; } -export function ChatMessageItem({ type, message, isSameUser }: Props) { +export function ChatMessageItem({ type, message, isSameUser, currentChannel }: Props) { const { global } = useMappedStore(); const isFailed = useMemo(() => message.sent === 2, [message]); @@ -63,6 +65,12 @@ export function ChatMessageItem({ type, message, isSameUser }: Props) { "same-user-message": isSameUser })} > + {currentChannel && ( + + )}
(null); const channelMessagesRef = React.createRef(); - const [communityAdmins, setCommunityAdmins] = useState([]); const [step, setStep] = useState(0); const [clickedMessage, setClickedMessage] = useState(""); const [removedUserId, setRemovedUserID] = useState(""); - const [privilegedUsers, setPrivilegedUsers] = useState([]); const [hiddenMsgId, setHiddenMsgId] = useState(""); const [resendMessage, setResendMessage] = useState(); @@ -87,41 +73,9 @@ export default function ChatsChannelMessages(props: Props) { useEffect(() => { if (currentChannel) { zoomInitializer(); - getPrivilegedUsers(currentChannel?.communityModerators!); } }, [currentChannel]); - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - const clickedElement = event.target as HTMLElement; - - if (!popoverRef.current) { - return; - } - - const isAvatarClicked = - clickedElement.classList.contains("user-avatar") && - clickedElement.classList.contains("medium"); - if ( - popoverRef.current && - !popoverRef.current?.contains(event.target as Node) && - !isAvatarClicked - ) { - setClickedMessage(""); - } - }; - - if (channelMessagesRef.current) { - channelMessagesRef.current.addEventListener("mousedown", handleClickOutside); - } - - return () => { - if (channelMessagesRef.current) { - channelMessagesRef.current.removeEventListener("mousedown", handleClickOutside); - } - }; - }, [clickedMessage]); - const zoomInitializer = () => { const elements: HTMLElement[] = [...document.querySelectorAll(".chat-image img")]; zoom = mediumZoom(elements); @@ -136,26 +90,6 @@ export default function ChatsChannelMessages(props: Props) { } }; - const getPrivilegedUsers = (communityModerators: CommunityModerator[]) => { - const privilegedUsers = communityModerators.filter((user) => - PRIVILEGEDROLES.includes(user.role) - ); - const communityAdmins = communityModerators.filter((user) => - COMMUNITYADMINROLES.includes(user.role) - ); - - const privilegedUserNames = privilegedUsers.map((user) => user.name); - const communityAdminNames = communityAdmins.map((user) => user.name); - - setPrivilegedUsers(privilegedUserNames); - setCommunityAdmins(communityAdminNames); - }; - - const getProfileName = (creator: string, profiles: Profile[]) => { - const profile = profiles.find((x) => x.creator === creator); - return profile?.name; - }; - const handleConfirm = () => { let updatedMetaData = { name: currentChannel?.name!, @@ -227,74 +161,6 @@ export default function ChatsChannelMessages(props: Props) { const dayAndMonth = formatMessageDateAndDay(pMsg, i, publicMessages); const isSameUserMessage = checkContiguousMessage(pMsg, i, publicMessages); - const name = getProfileName(pMsg.creator, chat.profiles); - - const popover = ( - - -
}> -
-
- -
- -

{`@${name!}`}

-
- - - {communityAdmins.includes(activeUser?.username!) && - name !== currentChannel.communityName && ( - <> - {currentChannel?.removedUserIds?.includes(pMsg.creator) ? ( - <> - - - ) : ( - <> - - - )} - - )} -
- - -
-
-
-
- ); return ( @@ -307,6 +173,7 @@ export default function ChatsChannelMessages(props: Props) { )} { + if (!channel) { + console.error("[Chat][Nostr] – trying to update not existing channel"); + return; + } + + const newUpdatedChannel: Channel = { ...channel }; + + newUpdatedChannel.removedUserIds = users; + + return updateChannel(newUpdatedChannel); + } + ); +} diff --git a/src/common/features/chats/nostr/queries/get-user-profile-query.ts b/src/common/features/chats/nostr/queries/get-user-profile-query.ts new file mode 100644 index 00000000000..70c2fa931da --- /dev/null +++ b/src/common/features/chats/nostr/queries/get-user-profile-query.ts @@ -0,0 +1,17 @@ +import { useNostrFetchQuery } from "../core"; +import { Kind } from "../../../../../lib/nostr-tools/event"; +import { convertEvent } from "../utils/event-converter"; + +export function useNostrGetUserProfileQuery(user: string) { + return useNostrFetchQuery( + ["chats/nostr-get-user-profile", user], + [ + { + kinds: [Kind.Metadata], + authors: [user] + } + ], + (events) => + events.map((event) => convertEvent(event)!!).filter((profile) => profile!!) + ); +} diff --git a/src/common/features/chats/nostr/queries/index.ts b/src/common/features/chats/nostr/queries/index.ts index 064d39a50be..958dfc95a67 100644 --- a/src/common/features/chats/nostr/queries/index.ts +++ b/src/common/features/chats/nostr/queries/index.ts @@ -1,3 +1,4 @@ export * from "./direct-messages-query"; export * from "./public-messages-query"; +export * from "./get-user-profile-query"; export * from "./queries"; diff --git a/src/common/features/chats/nostr/utils/event-converter.ts b/src/common/features/chats/nostr/utils/event-converter.ts index 6984b1eb36c..3090f041014 100644 --- a/src/common/features/chats/nostr/utils/event-converter.ts +++ b/src/common/features/chats/nostr/utils/event-converter.ts @@ -1,5 +1,5 @@ import { Event, Kind } from "../../../../../lib/nostr-tools/event"; -import { Channel, Message } from "../../managers/message-manager-types"; +import { Channel, Message, Profile } from "../../managers/message-manager-types"; import { findTagValue } from "./find-tag-value"; import { filterTagValue } from "./filter-tag-value"; import { decrypt } from "../../../../../lib/nostr-tools/nip04"; @@ -8,6 +8,7 @@ export interface EventConverterResult { [Kind.ChannelCreation]: Channel; [Kind.EncryptedDirectMessage]: Promise; [Kind.ChannelMessage]: Message; + [Kind.Metadata]: Profile; 30078: string[]; } @@ -86,6 +87,19 @@ export function convertEvent( sent: 1 } : (null as any); + case Kind.Metadata: + if (!content) { + return null; + } + + return { + id: event.id, + creator: event.pubkey, + created: event.created_at, + name: content.name || "", + about: content.about || "", + picture: content.picture || "" + } as any; case "30078": return content; default: From f22bd2eaf97987d7c08170a9d6a8afa5a945a465 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Wed, 8 Nov 2023 21:53:46 +0600 Subject: [PATCH 120/179] Chats: code cleanup --- .../features/chats/chat-context-provider.tsx | 34 +---------------- .../chats/components/chat-message-item.tsx | 1 + .../chat-popup-contacts-and-channels.tsx | 8 ++-- .../chat-popup/chat-popup-header.tsx | 10 +++-- .../chats/components/chat-popup/index.tsx | 23 ++++------- .../chats-channel-messages/index.tsx | 25 ++++-------- .../chats-community-actions/index.tsx | 34 ++++++++--------- .../chats-direct-messages/index.tsx | 38 ++++--------------- .../chats-sidebar/chat-sidebar-header.tsx | 6 +-- .../chats/components/import-chats/index.tsx | 15 ++++---- .../components/join-community-chat-btn.tsx | 6 ++- .../chats/managers/message-manager.tsx | 23 +---------- .../features/chats/mutations/join-chat.ts | 19 +++++++++- .../features/chats/mutations/send-message.ts | 6 ++- .../chats/nostr/core/nostr-fetch-mutation.ts | 6 +-- .../chats/nostr/core/nostr-fetch-query.tsx | 9 +++-- .../nostr/core/nostr-publish-mutation.ts | 8 ++-- .../nostr/mutations/send-direct-message.ts | 17 +++++---- .../chats/queries/last-messages-query.ts | 10 ++--- .../features/chats/queries/messages-query.ts | 10 ++--- .../nostr-joined-community-team-query.ts | 10 +---- src/common/features/chats/screens/chats.tsx | 10 ++--- 22 files changed, 123 insertions(+), 205 deletions(-) diff --git a/src/common/features/chats/chat-context-provider.tsx b/src/common/features/chats/chat-context-provider.tsx index 6dbeb0e261d..4a2b9b55515 100644 --- a/src/common/features/chats/chat-context-provider.tsx +++ b/src/common/features/chats/chat-context-provider.tsx @@ -2,7 +2,6 @@ import React, { useEffect, useState } from "react"; import { Channel, Keys } from "./managers/message-manager-types"; import useDebounce from "react-use/lib/useDebounce"; import MessageService from "../../helper/message-service"; -import { useMappedStore } from "../../store/use-mapped-store"; import { NostrKeysType } from "./types"; import { useMount } from "react-use"; import { useKeysQuery } from "./queries/keys-query"; @@ -10,7 +9,6 @@ import { useJoinChat } from "./mutations"; import { NostrListenerQueriesProvider, NostrProvider } from "./nostr"; interface Context { - activeUserKeys: NostrKeysType; showSpinner: boolean; revealPrivKey: boolean; chatPrivKey: string; @@ -24,7 +22,6 @@ interface Context { setRevealPrivKey: (d: boolean) => void; setShowSpinner: (d: boolean) => void; setChatPrivKey: (key: string) => void; - setActiveUserKeys: (keys: NostrKeysType) => void; setReceiverPubKey: (key: string) => void; setMessageServiceInstance: (instance: MessageService | null) => void; initMessageServiceInstance: (keys: Keys) => MessageService | null; @@ -35,7 +32,6 @@ interface Props { } export const ChatContext = React.createContext({ - activeUserKeys: { pub: " ", priv: "" }, showSpinner: false, revealPrivKey: false, chatPrivKey: "", @@ -49,15 +45,13 @@ export const ChatContext = React.createContext({ setRevealPrivKey: () => {}, setShowSpinner: () => {}, setChatPrivKey: () => {}, - setActiveUserKeys: () => {}, setReceiverPubKey: () => {}, setMessageServiceInstance: () => {}, initMessageServiceInstance: () => (({} as MessageService) || null) }); export const ChatContextProvider = (props: Props) => { - const { activeUser, chat, resetChat } = useMappedStore(); - + // TODO: USE QUERY INTEAD const [activeUserKeys, setActiveUserKeys] = useState({ pub: "", priv: "" }); const [showSpinner, setShowSpinner] = useState(true); const [chatPrivKey, setChatPrivKey] = useState(""); @@ -65,16 +59,14 @@ export const ChatContextProvider = (props: Props) => { const [receiverPubKey, setReceiverPubKey] = useState(""); const [messageServiceInstance, setMessageServiceInstance] = useState(null); const [hasUserJoinedChat, setHasUserJoinedChat] = useState(false); - const [shouldUpdateProfile, setShouldUpdateProfile] = useState(false); const [currentChannel, setCurrentChannel] = useState(null); const [windowWidth, setWindowWidth] = useState(0); const [isActiveUserRemoved, setIsActiveUserRemoved] = useState(false); - const { privateKey, publicKey, refetch } = useKeysQuery(activeUserKeys, setActiveUserKeys); + const { privateKey, publicKey, refetch } = useKeysQuery(); useJoinChat(() => { setHasUserJoinedChat(true); - setShouldUpdateProfile(true); }); useDebounce(() => setShowSpinner(false), 5000, [showSpinner]); @@ -111,26 +103,6 @@ export const ChatContextProvider = (props: Props) => { }; }, []); - useEffect(() => { - refetch(); - if ( - messageServiceInstance && - chat.profiles.some((profile) => profile.name === activeUser?.username) - ) { - setShouldUpdateProfile(false); - } - }, [messageServiceInstance, chat.profiles]); - - useEffect(() => { - if (shouldUpdateProfile && messageServiceInstance) { - messageServiceInstance.updateProfile({ - name: activeUser?.username!, - about: "", - picture: "" - }); - } - }, [shouldUpdateProfile, messageServiceInstance]); - const initMessageServiceInstance = (keys: Keys) => { if (messageServiceInstance) { messageServiceInstance.close(); @@ -149,7 +121,6 @@ export const ChatContextProvider = (props: Props) => { { setRevealPrivKey, setShowSpinner, setChatPrivKey, - setActiveUserKeys, setReceiverPubKey, setMessageServiceInstance, initMessageServiceInstance diff --git a/src/common/features/chats/components/chat-message-item.tsx b/src/common/features/chats/components/chat-message-item.tsx index 2b49988b70c..a0c49b1545a 100644 --- a/src/common/features/chats/components/chat-message-item.tsx +++ b/src/common/features/chats/components/chat-message-item.tsx @@ -17,6 +17,7 @@ interface Props { currentChannel?: Channel; } +// TODO: Add resend export function ChatMessageItem({ type, message, isSameUser, currentChannel }: Props) { const { global } = useMappedStore(); diff --git a/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx b/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx index 282a74424ba..d014a0fc7c4 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx @@ -8,6 +8,7 @@ import { ChatContext } from "../../chat-context-provider"; import { ChatDirectMessage } from "./chat-direct-message"; import { useChannelsQuery, useDirectContactsQuery, useLastMessagesQuery } from "../../queries"; import { useLeftCommunityChannelsQuery } from "../../queries/left-community-channels-query"; +import { useKeysQuery } from "../../queries/keys-query"; interface Props { communityClicked: (v: string) => void; @@ -20,8 +21,9 @@ export function ChatPopupContactsAndChannels({ userClicked, setShowSearchUser }: Props) { - const { activeUserKeys, showSpinner, setReceiverPubKey } = useContext(ChatContext); + const { showSpinner, setReceiverPubKey } = useContext(ChatContext); + const { privateKey } = useKeysQuery(); const { data: directContacts } = useDirectContactsQuery(); const { data: directContactsLastMessages } = useLastMessagesQuery(); const { data: channels } = useChannelsQuery(); @@ -36,7 +38,7 @@ export function ChatPopupContactsAndChannels({ <> {(directContacts?.length !== 0 || (channels?.length !== 0 && joinedChannels.length !== 0)) && !showSpinner && - activeUserKeys?.priv ? ( + privateKey ? ( <> {joinedChannels.length !== 0 && ( <> @@ -64,7 +66,7 @@ export function ChatPopupContactsAndChannels({ /> ))} - ) : !activeUserKeys?.priv ? ( + ) : !privateKey ? ( ) : showSpinner ? (
diff --git a/src/common/features/chats/components/chat-popup/chat-popup-header.tsx b/src/common/features/chats/components/chat-popup/chat-popup-header.tsx index a89fe706dd7..422a0e55356 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-header.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-header.tsx @@ -15,6 +15,7 @@ import { classNameObject } from "../../../../helper/class-name-object"; import React, { useContext } from "react"; import UserAvatar from "../../../../components/user-avatar"; import { ChatContext } from "../../chat-context-provider"; +import { useKeysQuery } from "../../queries/keys-query"; interface Props { currentUser: string; @@ -45,8 +46,9 @@ export function ChatPopupHeader({ handleExtendedView, setExpanded }: Props) { - const { revealPrivKey, activeUserKeys, hasUserJoinedChat, setRevealPrivKey } = - useContext(ChatContext); + const { revealPrivKey, hasUserJoinedChat, setRevealPrivKey } = useContext(ChatContext); + + const { privateKey } = useKeysQuery(); return (
@@ -101,7 +103,7 @@ export function ChatPopupHeader({ /> )} - {hasUserJoinedChat && activeUserKeys?.priv && !revealPrivKey && ( + {hasUserJoinedChat && privateKey && !revealPrivKey && (
)} - {!isCommunity && !isCurrentUser && activeUserKeys?.priv && ( + {!isCommunity && !isCurrentUser && privateKey && (
setExpanded(true)}> { const { activeUser, global, chat, resetChat } = useMappedStore(); - const { - receiverPubKey, - revealPrivKey, - activeUserKeys, - hasUserJoinedChat, - setRevealPrivKey, - setShowSpinner - } = useContext(ChatContext); + const { receiverPubKey, revealPrivKey, hasUserJoinedChat, setRevealPrivKey, setShowSpinner } = + useContext(ChatContext); const { mutateAsync: joinChat, isLoading: isJoinChatLoading } = useJoinChat(); + const { privateKey, publicKey } = useKeysQuery(); const { data: directContacts, isLoading: isDirectContactsLoading } = useDirectContactsQuery(); const directContact = useMemo( () => directContacts?.find((contact) => contact.pubkey === receiverPubKey), @@ -67,9 +62,8 @@ export const ChatPopUp = () => { ); const isCurrentUser = useMemo(() => !!currentUser, [currentUser]); const canSendMessage = useMemo( - () => - !currentUser && hasUserJoinedChat && !!activeUserKeys?.priv && !isCommunity && !revealPrivKey, - [currentUser, hasUserJoinedChat, activeUserKeys, isCommunity, revealPrivKey] + () => !currentUser && hasUserJoinedChat && !!privateKey && !isCommunity && !revealPrivKey, + [currentUser, hasUserJoinedChat, privateKey, isCommunity, revealPrivKey] ); const { mutateAsync: fetchPreviousMessages, isLoading: isFetchingMore } = @@ -167,10 +161,9 @@ export const ChatPopUp = () => { if (getPrivateKey(activeUser?.username!)) { setShowSpinner(true); const keys = { - pub: activeUserKeys?.pub!, - priv: getPrivateKey(activeUser?.username!)!! + pub: publicKey!, + priv: privateKey }; - setNostrkeys(keys); } }; diff --git a/src/common/features/chats/components/chats-channel-messages/index.tsx b/src/common/features/chats/components/chats-channel-messages/index.tsx index b9b31b9ba02..9d73ddf8cb9 100644 --- a/src/common/features/chats/components/chats-channel-messages/index.tsx +++ b/src/common/features/chats/components/chats-channel-messages/index.tsx @@ -13,6 +13,7 @@ import { ChatContext } from "../../chat-context-provider"; import "./index.scss"; import { ChatMessageItem } from "../chat-message-item"; +import { useKeysQuery } from "../../queries/keys-query"; interface Props { publicMessages: PublicMessage[]; @@ -28,32 +29,21 @@ interface Props { let zoom: Zoom | null = null; export default function ChatsChannelMessages(props: Props) { const { publicMessages, isScrollToBottom, isScrolled, currentChannel, scrollToBottom } = props; - const { - chat, - global, - activeUser, - ui, - users, - deletePublicMessage, - setActiveUser, - updateActiveUser, - deleteUser, - toggleUIProp - } = useMappedStore(); - - const { messageServiceInstance, activeUserKeys, windowWidth, isActiveUserRemoved } = - useContext(ChatContext); + const { global, activeUser, deletePublicMessage } = useMappedStore(); + + const { messageServiceInstance, isActiveUserRemoved } = useContext(ChatContext); let prevGlobal = usePrevious(global); const channelMessagesRef = React.createRef(); const [step, setStep] = useState(0); - const [clickedMessage, setClickedMessage] = useState(""); const [removedUserId, setRemovedUserID] = useState(""); const [hiddenMsgId, setHiddenMsgId] = useState(""); const [resendMessage, setResendMessage] = useState(); + const { publicKey } = useKeysQuery(); + useEffect(() => { if (prevGlobal?.theme !== global.theme) { setBackground(); @@ -156,7 +146,6 @@ export default function ChatsChannelMessages(props: Props) { <>
{publicMessages.length !== 0 && - activeUserKeys && publicMessages.map((pMsg, i) => { const dayAndMonth = formatMessageDateAndDay(pMsg, i, publicMessages); @@ -174,7 +163,7 @@ export default function ChatsChannelMessages(props: Props) { diff --git a/src/common/features/chats/components/chats-community-actions/index.tsx b/src/common/features/chats/components/chats-community-actions/index.tsx index 450156b6cc5..13f8d53e0e9 100644 --- a/src/common/features/chats/components/chats-community-actions/index.tsx +++ b/src/common/features/chats/components/chats-community-actions/index.tsx @@ -1,13 +1,12 @@ -import React, { useContext, useEffect, useState } from "react"; +import React, { useContext, useEffect, useMemo, useState } from "react"; import { History } from "history"; import DropDown, { MenuItem } from "../../../../components/dropdown"; import { chatLeaveSvg, editSVG, kebabMenuSvg, linkSvg, removeUserSvg } from "../../../../img/svg"; import { _t } from "../../../../i18n"; -import { ADDROLE, DropDownStyle, LEAVECOMMUNITY, UNBLOCKUSER } from "../chat-popup/chat-constants"; +import { DropDownStyle, LEAVECOMMUNITY, UNBLOCKUSER } from "../chat-popup/chat-constants"; import { useMappedStore } from "../../../../store/use-mapped-store"; import { error, success } from "../../../../components/feedback"; -import { ROLES } from "../../../../store/communities"; import UserAvatar from "../../../../components/user-avatar"; import { copyToClipboard } from "../../utils"; import { ChatContext } from "../../chat-context-provider"; @@ -18,6 +17,7 @@ import { Modal, ModalBody, ModalHeader } from "@ui/modal"; import { CommunityModerator } from "../../managers/message-manager-types"; import { useLeaveCommunityChannel } from "../../mutations"; import { EditRolesModal } from "./edit-roles-modal"; +import { useChannelsQuery } from "../../queries"; interface Props { history: History; @@ -25,11 +25,8 @@ interface Props { username: string; } -const roles = [ROLES.ADMIN, ROLES.MOD, ROLES.GUEST]; - const ChatsCommunityDropdownMenu = (props: Props) => { const { activeUser, chat } = useMappedStore(); - const { currentChannel, setCurrentChannel } = useContext(ChatContext); const { history, from } = props; const [step, setStep] = useState(0); const [keyDialog, setKeyDialog] = useState(false); @@ -38,6 +35,12 @@ const ChatsCommunityDropdownMenu = (props: Props) => { const [blockedUsers, setBlockedUsers] = useState<{ name: string; pubkey: string }[]>([]); const [removedUserId, setRemovedUserID] = useState(""); + const { data: channels } = useChannelsQuery(); + const currentChannel = useMemo( + () => channels?.find((channel) => channel.communityName === props.username), + [channels, props.username] + ); + const { mutateAsync: leaveChannel } = useLeaveCommunityChannel(() => { setKeyDialog(false); setStep(0); @@ -50,7 +53,7 @@ const ChatsCommunityDropdownMenu = (props: Props) => { getCommunityAdmins(); if (currentChannel && currentChannel?.removedUserIds) { - getBlockedUsers(currentChannel?.removedUserIds!); + // getBlockedUsers(currentChannel?.removedUserIds!); } }, [currentChannel, removedUserId]); @@ -72,12 +75,12 @@ const ChatsCommunityDropdownMenu = (props: Props) => { setCommunityAdmins(communityAdminNames!); }; - const getBlockedUsers = (blockedUser: string[]) => { - const blockedUsers = chat.profiles - .filter((item) => blockedUser.includes(item.creator)) - .map((item) => ({ name: item.name, pubkey: item.creator })); - setBlockedUsers(blockedUsers); - }; + // const getBlockedUsers = (blockedUser: string[]) => { + // const blockedUsers = profiles + // .filter((item) => blockedUser.includes(item.creator)) + // .map((item) => ({ name: item.name, pubkey: item.creator })); + // setBlockedUsers(blockedUsers); + // }; const handleBlockedUsers = () => { setKeyDialog(true); @@ -269,10 +272,6 @@ const ChatsCommunityDropdownMenu = (props: Props) => { removedUserIds: currentChannel?.removedUserIds }; switch (operationType) { - case ADDROLE: - const updatedRoles = [...(currentChannel?.communityModerators || []), moderator!]; - updatedMetaData.communityModerators = updatedRoles; - break; case UNBLOCKUSER: const NewUpdatedRemovedUsers = currentChannel?.removedUserIds?.filter( (item) => item !== removedUserId @@ -284,7 +283,6 @@ const ChatsCommunityDropdownMenu = (props: Props) => { } try { messageServiceInstance?.updateChannel(currentChannel!, updatedMetaData); - setCurrentChannel({ ...currentChannel!, ...updatedMetaData }); if (operationType === UNBLOCKUSER) { setStep(5); diff --git a/src/common/features/chats/components/chats-direct-messages/index.tsx b/src/common/features/chats/components/chats-direct-messages/index.tsx index 8ca339fda73..4a54d9fc9d9 100644 --- a/src/common/features/chats/components/chats-direct-messages/index.tsx +++ b/src/common/features/chats/components/chats-direct-messages/index.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useState } from "react"; +import React, { useContext, useEffect } from "react"; import usePrevious from "react-use/lib/usePrevious"; import mediumZoom, { Zoom } from "medium-zoom"; import { checkContiguousMessage, formatMessageDateAndDay } from "../../utils"; @@ -7,9 +7,9 @@ import { useMappedStore } from "../../../../store/use-mapped-store"; import { ChatContext } from "../../chat-context-provider"; import { _t } from "../../../../i18n"; import "./index.scss"; -import ChatsConfirmationModal from "../chats-confirmation-modal"; import { DirectMessage } from "../../managers/message-manager-types"; import { ChatMessageItem } from "../chat-message-item"; +import { useKeysQuery } from "../../queries/keys-query"; interface Props { directMessages: DirectMessage[]; @@ -21,14 +21,14 @@ interface Props { let zoom: Zoom | null = null; export default function ChatsDirectMessages(props: Props) { - const { directMessages, currentUser, isScrolled, isScrollToBottom, scrollToBottom } = props; + const { directMessages, isScrolled, isScrollToBottom, scrollToBottom } = props; const { global, activeUser, deleteDirectMessage } = useMappedStore(); - const { activeUserKeys, messageServiceInstance, receiverPubKey } = useContext(ChatContext); + const { receiverPubKey } = useContext(ChatContext); let prevGlobal = usePrevious(global); - const [step, setStep] = useState(0); - const [resendMessage, setResendMessage] = useState(); + + const { publicKey } = useKeysQuery(); useEffect(() => { if (prevGlobal?.theme !== global.theme) { @@ -60,20 +60,6 @@ export default function ChatsDirectMessages(props: Props) { } }; - const handleConfirm = () => { - switch (step) { - case 1: - if (resendMessage) { - deleteDirectMessage(receiverPubKey, resendMessage?.id); - messageServiceInstance?.sendDirectMessage(receiverPubKey!, resendMessage.content); - } - break; - default: - break; - } - setStep(0); - }; - return ( <>
@@ -91,7 +77,7 @@ export default function ChatsDirectMessages(props: Props) {
)} @@ -103,16 +89,6 @@ export default function ChatsDirectMessages(props: Props) {

{_t("chat.not-joined")}

)}
- {step !== 0 && ( - { - setStep(0); - }} - onConfirm={handleConfirm} - /> - )} ); } diff --git a/src/common/features/chats/components/chats-sidebar/chat-sidebar-header.tsx b/src/common/features/chats/components/chats-sidebar/chat-sidebar-header.tsx index aa11eceb420..4124b13fd88 100644 --- a/src/common/features/chats/components/chats-sidebar/chat-sidebar-header.tsx +++ b/src/common/features/chats/components/chats-sidebar/chat-sidebar-header.tsx @@ -7,7 +7,6 @@ import React, { useContext } from "react"; import { ChatContext } from "../../chat-context-provider"; import { useMappedStore } from "../../../../store/use-mapped-store"; import { History } from "history"; -import { setNostrkeys } from "../../managers/message-manager"; interface Props { history: History; @@ -15,13 +14,10 @@ interface Props { export function ChatSidebarHeader({ history }: Props) { const { resetChat } = useMappedStore(); - const { activeUserKeys, setShowSpinner, revealPrivKey, chatPrivKey, setRevealPrivKey } = - useContext(ChatContext); + const { revealPrivKey, chatPrivKey, setRevealPrivKey } = useContext(ChatContext); const handleRefreshChat = () => { resetChat(); - setNostrkeys(activeUserKeys!); - setShowSpinner(true); }; return ( diff --git a/src/common/features/chats/components/import-chats/index.tsx b/src/common/features/chats/components/import-chats/index.tsx index b23772231d8..ae73f648355 100644 --- a/src/common/features/chats/components/import-chats/index.tsx +++ b/src/common/features/chats/components/import-chats/index.tsx @@ -4,7 +4,6 @@ import { _t } from "../../../../i18n"; import { keySvg } from "../../../../img/svg"; import { useMappedStore } from "../../../../store/use-mapped-store"; import OrDivider from "../../../../components/or-divider"; -import * as ls from "../../../../util/local-storage"; import { ChatContext } from "../../chat-context-provider"; import "./index.scss"; import LinearProgress from "../../../../components/linear-progress"; @@ -12,8 +11,8 @@ import ChatsConfirmationModal from "../chats-confirmation-modal"; import { Button } from "@ui/button"; import { Form } from "@ui/form"; import { FormControl, InputGroup } from "@ui/input"; -import { setNostrkeys } from "../../managers/message-manager"; import { useJoinChat } from "../../mutations/join-chat"; +import { useKeysQuery } from "../../queries/keys-query"; export default function ImportChats() { const { activeUser } = useMappedStore(); @@ -24,21 +23,21 @@ export default function ImportChats() { const [privKey, setPrivKey] = useState(""); const [step, setStep] = useState(0); - const { activeUserKeys, hasUserJoinedChat, setChatPrivKey } = useContext(ChatContext); + const { hasUserJoinedChat, setChatPrivKey } = useContext(ChatContext); const { mutateAsync: joinChat } = useJoinChat(); + const { publicKey, privateKey } = useKeysQuery(); + const handleImportChatSubmit = () => { try { setInProgress(true); const pubKey = getPublicKey(privKey); - if (pubKey === activeUserKeys?.pub) { + if (pubKey === publicKey) { setChatPrivKey(privKey); - ls.set(`${activeUser?.username}_nsPrivKey`, privKey); const keys = { - pub: activeUserKeys?.pub!, - priv: privKey + pub: publicKey, + priv: privateKey }; - setNostrkeys(keys); setImportPrivKey(false); setChatPrivKey(privKey); } else { diff --git a/src/common/features/chats/components/join-community-chat-btn.tsx b/src/common/features/chats/components/join-community-chat-btn.tsx index 4b8260ce2fc..845b247b78f 100644 --- a/src/common/features/chats/components/join-community-chat-btn.tsx +++ b/src/common/features/chats/components/join-community-chat-btn.tsx @@ -13,6 +13,7 @@ import { useNostrJoinedCommunityTeamQuery } from "../queries"; import { useLeftCommunityChannelsQuery } from "../queries/left-community-channels-query"; +import { useKeysQuery } from "../queries/keys-query"; interface Props { history: History; @@ -20,7 +21,8 @@ interface Props { } export default function JoinCommunityChatBtn(props: Props) { - const { activeUserKeys, hasUserJoinedChat } = useContext(ChatContext); + const { publicKey } = useKeysQuery(); + const { hasUserJoinedChat } = useContext(ChatContext); const { activeUser } = useMappedStore(); const { data: currentChannel } = useCommunityChannelQuery(props.community); @@ -64,7 +66,7 @@ export default function JoinCommunityChatBtn(props: Props) { await joinChat(); } - if (communityTeam.some((role) => role.pubkey === activeUserKeys.pub)) { + if (communityTeam.some((role) => role.pubkey === publicKey)) { await createCommunityChat(); await join(); } diff --git a/src/common/features/chats/managers/message-manager.tsx b/src/common/features/chats/managers/message-manager.tsx index 3ca9e03ca21..5f1e44ab16c 100644 --- a/src/common/features/chats/managers/message-manager.tsx +++ b/src/common/features/chats/managers/message-manager.tsx @@ -1,13 +1,7 @@ import React, { useContext, useEffect, useState } from "react"; import { ActiveUser } from "../../../store/active-user/types"; import { NostrKeysType } from "../types"; -import { - DirectMessage, - Keys, - MessagesObject, - Profile, - PublicMessage -} from "./message-manager-types"; +import { DirectMessage, Keys, PublicMessage } from "./message-manager-types"; import MessageService, { MessageEvents } from "../../../helper/message-service"; import { getPrivateKey, getProfileMetaData } from "../utils"; import { useMappedStore } from "../../../store/use-mapped-store"; @@ -28,7 +22,6 @@ export const setNostrkeys = (keys: NostrKeysType) => { const MessageManager = () => { const { activeUser, - chat, addDirectMessages, addPublicMessage, addProfile, @@ -126,19 +119,6 @@ const MessageManager = () => { }; }, [messageServiceReady, messageService]); - // Profile update handler - const handleProfileUpdate = (data: Profile[]) => { - addProfile(data); - }; - - useEffect(() => { - messageService?.removeListener(MessageEvents.ProfileUpdate, handleProfileUpdate); - messageService?.addListener(MessageEvents.ProfileUpdate, handleProfileUpdate); - return () => { - messageService?.removeListener(MessageEvents.ProfileUpdate, handleProfileUpdate); - }; - }, [messageService, chat.profiles]); - // const checkPublicMessageSending = (channelId: string, data: PublicMessage) => { // setTimeout(() => { // verifyPublicMessageSending(channelId, data); @@ -270,7 +250,6 @@ const MessageManager = () => { useEffect(() => { return () => { messageService?.removeListener(MessageEvents.Ready, handleReadyState); - messageService?.removeListener(MessageEvents.ProfileUpdate, handleProfileUpdate); // messageService?.removeListener( // MessageEvents.PreviousPublicMessages, // handlePreviousPublicMessages diff --git a/src/common/features/chats/mutations/join-chat.ts b/src/common/features/chats/mutations/join-chat.ts index 324c4e54499..47937afc0df 100644 --- a/src/common/features/chats/mutations/join-chat.ts +++ b/src/common/features/chats/mutations/join-chat.ts @@ -4,6 +4,8 @@ import * as ls from "../../../util/local-storage"; import { setNostrkeys } from "../managers/message-manager"; import { useMappedStore } from "../../../store/use-mapped-store"; import { ChatQueries } from "../queries"; +import { useNostrPublishMutation } from "../nostr"; +import { Kind } from "../../../../lib/nostr-tools/event"; /** * Custom React hook for joining a chat with some side effects. @@ -16,11 +18,17 @@ import { ChatQueries } from "../queries"; * @returns A function from the `useMutation` hook, which can be used to initiate the chat join process. */ export function useJoinChat(onSuccess?: () => void) { + const queryClient = useQueryClient(); const { activeUser, chat, resetChat } = useMappedStore(); + const { mutateAsync: uploadPublicKey } = useMutation(["chats/upload-public-key"], (key: string) => uploadChatPublicKey(activeUser, key) ); - const queryClient = useQueryClient(); + const { mutateAsync: updateProfile } = useNostrPublishMutation( + ["chats/update-nostr-profile"], + Kind.Metadata, + () => {} + ); return useMutation( ["chat-join-chat"], @@ -37,6 +45,15 @@ export function useJoinChat(onSuccess?: () => void) { queryClient.setQueryData([ChatQueries.PUBLIC_KEY], keys.pub); queryClient.setQueryData([ChatQueries.PRIVATE_KEY], keys.priv); + await updateProfile({ + tags: [], + eventMetadata: { + name: activeUser?.username!, + about: "", + picture: "" + } + }); + onSuccess?.(); } } diff --git a/src/common/features/chats/mutations/send-message.ts b/src/common/features/chats/mutations/send-message.ts index 86573a92296..5ff65b90356 100644 --- a/src/common/features/chats/mutations/send-message.ts +++ b/src/common/features/chats/mutations/send-message.ts @@ -7,6 +7,7 @@ import isCommunity from "../../../helper/is-community"; import { useNostrSendDirectMessage, useNostrSendPublicMessage } from "../nostr"; import { useAddDirectContact } from "./add-direct-contact"; import { ChatQueries, useMessagesQuery } from "../queries"; +import { useKeysQuery } from "../queries/keys-query"; export function useSendMessage( currentChannel?: Channel, @@ -15,11 +16,12 @@ export function useSendMessage( ) { const queryClient = useQueryClient(); - const { activeUserKeys, isActiveUserRemoved, receiverPubKey } = useContext(ChatContext); + const { isActiveUserRemoved, receiverPubKey } = useContext(ChatContext); + const { privateKey } = useKeysQuery(); const { data: messages } = useMessagesQuery(currentChannel?.communityName ?? currentUser); const { mutateAsync: sendDirectMessage } = useNostrSendDirectMessage( - activeUserKeys.priv, + privateKey!!, receiverPubKey, undefined ); diff --git a/src/common/features/chats/nostr/core/nostr-fetch-mutation.ts b/src/common/features/chats/nostr/core/nostr-fetch-mutation.ts index 6be5c0ae809..f9cfc78ca23 100644 --- a/src/common/features/chats/nostr/core/nostr-fetch-mutation.ts +++ b/src/common/features/chats/nostr/core/nostr-fetch-mutation.ts @@ -1,10 +1,10 @@ import { MutationKey, useMutation, UseMutationOptions } from "@tanstack/react-query"; import { useContext } from "react"; import { NostrContext } from "../nostr-context"; -import { ChatContext } from "../../chat-context-provider"; import { listenWhileFinish } from "../utils"; import { Event } from "../../../../../lib/nostr-tools/event"; import { Filter } from "../../../../../lib/nostr-tools/filter"; +import { useKeysQuery } from "../../queries/keys-query"; export function useNostrFetchMutation( key: MutationKey, @@ -12,11 +12,11 @@ export function useNostrFetchMutation( options?: UseMutationOptions ) { const { pool, readRelays } = useContext(NostrContext); - const { activeUserKeys } = useContext(ChatContext); + const { publicKey } = useKeysQuery(); return useMutation( key, - () => listenWhileFinish(pool, readRelays, [], activeUserKeys.pub, filters), + () => listenWhileFinish(pool, readRelays, [], publicKey!!, filters), options ); } diff --git a/src/common/features/chats/nostr/core/nostr-fetch-query.tsx b/src/common/features/chats/nostr/core/nostr-fetch-query.tsx index 61681146aee..cb045088d0a 100644 --- a/src/common/features/chats/nostr/core/nostr-fetch-query.tsx +++ b/src/common/features/chats/nostr/core/nostr-fetch-query.tsx @@ -2,10 +2,10 @@ import { QueryKey, useQuery, useQueryClient } from "@tanstack/react-query"; import { Event, Kind } from "../../../../../lib/nostr-tools/event"; import { useContext } from "react"; import { NostrContext } from "../nostr-context"; -import { ChatContext } from "../../chat-context-provider"; import { UseQueryOptions } from "@tanstack/react-query/src/types"; import { listenWhileFinish } from "../utils"; import { Filter } from "../../../../../lib/nostr-tools/filter"; +import { useKeysQuery } from "../../queries/keys-query"; export function useNostrFetchQuery( key: QueryKey, @@ -13,10 +13,11 @@ export function useNostrFetchQuery( dataResolver: (events: Event[]) => DATA | Promise, queryOptions?: UseQueryOptions ) { - const { activeUserKeys } = useContext(ChatContext); - const { pool, readRelays } = useContext(NostrContext); const queryClient = useQueryClient(); + const { pool, readRelays } = useContext(NostrContext); + const { publicKey } = useKeysQuery(); + return useQuery( key, async () => { @@ -31,7 +32,7 @@ export function useNostrFetchQuery( const filters = kindsOrFilters.every((item) => typeof item === "object") ? (kindsOrFilters as Filter[]) : undefined; - const events = await listenWhileFinish(pool, readRelays, kinds, activeUserKeys.pub, filters); + const events = await listenWhileFinish(pool, readRelays, kinds, publicKey!!, filters); return dataResolver(events); }, queryOptions diff --git a/src/common/features/chats/nostr/core/nostr-publish-mutation.ts b/src/common/features/chats/nostr/core/nostr-publish-mutation.ts index 4d3b3669424..e4f7b1a64ff 100644 --- a/src/common/features/chats/nostr/core/nostr-publish-mutation.ts +++ b/src/common/features/chats/nostr/core/nostr-publish-mutation.ts @@ -3,7 +3,7 @@ import { useContext } from "react"; import { Metadata } from "../../managers/message-manager-types"; import { Event, getEventHash, Kind, signEvent } from "../../../../../lib/nostr-tools/event"; import { NostrContext } from "../nostr-context"; -import { ChatContext } from "../../chat-context-provider"; +import { useKeysQuery } from "../../queries/keys-query"; type Payload = { eventMetadata: Metadata | string; tags: string[][] }; @@ -14,12 +14,12 @@ export function useNostrPublishMutation( options?: UseMutationOptions ) { const { pool, writeRelays } = useContext(NostrContext); - const { activeUserKeys } = useContext(ChatContext); + const { publicKey, privateKey } = useKeysQuery(); const sign = async (event: Event) => ({ ...event, id: getEventHash(event), - sig: await signEvent(event, activeUserKeys.priv) + sig: await signEvent(event, privateKey!!) }); return useMutation( @@ -34,7 +34,7 @@ export function useNostrPublishMutation( sig: "", content: typeof eventMetadata === "object" ? JSON.stringify(eventMetadata) : eventMetadata, - pubkey: activeUserKeys.pub, + pubkey: publicKey!!, created_at: Math.floor(Date.now() / 1000), tags }); diff --git a/src/common/features/chats/nostr/mutations/send-direct-message.ts b/src/common/features/chats/nostr/mutations/send-direct-message.ts index 139dd3dc201..d84d601eadd 100644 --- a/src/common/features/chats/nostr/mutations/send-direct-message.ts +++ b/src/common/features/chats/nostr/mutations/send-direct-message.ts @@ -4,15 +4,14 @@ import { useMutation } from "@tanstack/react-query"; import { encrypt } from "../../../../../lib/nostr-tools/nip04"; import { useFindHealthyRelayQuery } from "./find-healthy-relay"; import { convertEvent } from "../utils/event-converter"; -import { useContext } from "react"; -import { ChatContext } from "../../chat-context-provider"; +import { useKeysQuery } from "../../queries/keys-query"; export function useNostrSendDirectMessage( ownerPrivateKey: string, destinationPublicKey: string, parent?: string ) { - const { activeUserKeys } = useContext(ChatContext); + const { privateKey, publicKey } = useKeysQuery(); const { mutateAsync: publishEncryptedMessage } = useNostrPublishMutation( ["chats/nostr-publish-encrypted-message"], @@ -22,6 +21,12 @@ export function useNostrSendDirectMessage( const { mutateAsync: findHealthyRelay } = useFindHealthyRelayQuery(); return useMutation(["chats/send-direct-message"], async (message: string) => { + if (!publicKey || !privateKey) { + throw new Error( + "[Chat][Nostr] – attempting to send direct message with no private and public key" + ); + } + const encryptedMessage = await encrypt(ownerPrivateKey, destinationPublicKey, message); const tags = [["p", destinationPublicKey]]; @@ -35,10 +40,6 @@ export function useNostrSendDirectMessage( tags, eventMetadata: encryptedMessage }); - return convertEvent( - event, - activeUserKeys.pub, - activeUserKeys.priv - )!!; + return convertEvent(event, publicKey, privateKey)!!; }); } diff --git a/src/common/features/chats/queries/last-messages-query.ts b/src/common/features/chats/queries/last-messages-query.ts index c6bc5eecb89..e23fcef2bc5 100644 --- a/src/common/features/chats/queries/last-messages-query.ts +++ b/src/common/features/chats/queries/last-messages-query.ts @@ -5,18 +5,16 @@ import { getDirectLastMessage } from "../utils"; import { useDirectContactsQuery } from "./direct-contacts-query"; import { useChannelsQuery } from "./channels-query"; import { useDirectMessagesQuery, usePublicMessagesQuery } from "../nostr/queries"; -import { useContext } from "react"; -import { ChatContext } from "../chat-context-provider"; +import { useKeysQuery } from "./keys-query"; export function useLastMessagesQuery() { - const { activeUserKeys } = useContext(ChatContext); - + const { publicKey, privateKey } = useKeysQuery(); const { data: contacts } = useDirectContactsQuery(); const { data: channels } = useChannelsQuery(); const { data: initialDirectMessages } = useDirectMessagesQuery( contacts ?? [], - activeUserKeys.pub, - activeUserKeys.priv + publicKey!!, + privateKey!! ); const { data: initialPublicMessages } = usePublicMessagesQuery(channels ?? []); diff --git a/src/common/features/chats/queries/messages-query.ts b/src/common/features/chats/queries/messages-query.ts index c688e190949..807c8914e5d 100644 --- a/src/common/features/chats/queries/messages-query.ts +++ b/src/common/features/chats/queries/messages-query.ts @@ -1,22 +1,22 @@ import { useQuery } from "@tanstack/react-query"; import { useDirectContactsQuery } from "./direct-contacts-query"; -import { useContext, useMemo } from "react"; -import { ChatContext } from "../chat-context-provider"; +import { useMemo } from "react"; import { useDirectMessagesQuery, usePublicMessagesQuery } from "../nostr/queries"; import { ChatQueries } from "./queries"; import { Message } from "../managers/message-manager-types"; import { useChannelsQuery } from "./channels-query"; import { queryClient } from "../../../core"; +import { useKeysQuery } from "./keys-query"; export function useMessagesQuery(username?: string) { - const { activeUserKeys } = useContext(ChatContext); + const { privateKey, publicKey } = useKeysQuery(); const { data: directContacts } = useDirectContactsQuery(); const { data: channels } = useChannelsQuery(); const { data: initialDirectMessages } = useDirectMessagesQuery( directContacts ?? [], - activeUserKeys.pub, - activeUserKeys.priv + publicKey!!, + privateKey!! ); const { data: initialPublicMessages } = usePublicMessagesQuery(channels ?? []); diff --git a/src/common/features/chats/queries/nostr-joined-community-team-query.ts b/src/common/features/chats/queries/nostr-joined-community-team-query.ts index 6f1004f4922..1380e0e09f9 100644 --- a/src/common/features/chats/queries/nostr-joined-community-team-query.ts +++ b/src/common/features/chats/queries/nostr-joined-community-team-query.ts @@ -5,8 +5,6 @@ import { getProfileMetaData } from "../utils"; import { Community, ROLES } from "../../../store/communities"; import { NOSTRKEY } from "../components/chat-popup/chat-constants"; import { useMappedStore } from "../../../store/use-mapped-store"; -import { useContext } from "react"; -import { ChatContext } from "../chat-context-provider"; import { useKeysQuery } from "./keys-query"; /** @@ -14,22 +12,18 @@ import { useKeysQuery } from "./keys-query"; */ export function useNostrJoinedCommunityTeamQuery(community: Community) { const { activeUser } = useMappedStore(); - const { activeUserKeys } = useContext(ChatContext); - const { hasKeys } = useKeysQuery(); + const { hasKeys, publicKey } = useKeysQuery(); return useQuery( [ChatQueries.COMMUNITY_ROLES, community.name], async () => { - if (!activeUserKeys || activeUser?.username === community.name) { - } - let communityTeam: CommunityModerator[] = []; const ownerData = await getProfileMetaData(community.name); communityTeam.push({ name: activeUser!.username, - pubkey: activeUserKeys?.pub || ownerData.nsKey, + pubkey: publicKey || ownerData.nsKey, role: "owner" }); diff --git a/src/common/features/chats/screens/chats.tsx b/src/common/features/chats/screens/chats.tsx index b92dcbe285b..b09b08127ea 100644 --- a/src/common/features/chats/screens/chats.tsx +++ b/src/common/features/chats/screens/chats.tsx @@ -16,6 +16,7 @@ import { classNameObject } from "../../../helper/class-name-object"; import "./_chats.scss"; import { useChannelsQuery } from "../queries"; import { useLeftCommunityChannelsQuery } from "../queries/left-community-channels-query"; +import { useKeysQuery } from "../queries/keys-query"; interface Props extends PageProps { match: match<{ @@ -29,9 +30,9 @@ interface Props extends PageProps { export const Chats = (props: Props) => { const { activeUser, global } = useMappedStore(); - const { receiverPubKey, showSpinner, activeUserKeys, revealPrivKey, chatPrivKey } = - useContext(ChatContext); + const { receiverPubKey, showSpinner, revealPrivKey } = useContext(ChatContext); + const { publicKey, privateKey } = useKeysQuery(); const { data: channels } = useChannelsQuery(); const { data: leftCommunityChannelsIds } = useLeftCommunityChannelsQuery(); @@ -45,10 +46,7 @@ export const Chats = (props: Props) => { [channels, leftCommunityChannelsIds] ); - const isReady = useMemo( - () => !!(activeUser && activeUserKeys?.pub && chatPrivKey), - [activeUserKeys, activeUserKeys, chatPrivKey] - ); + const isReady = useMemo(() => !!(activeUser && publicKey && privateKey), [publicKey, privateKey]); const isShowManageKey = useMemo(() => isReady && revealPrivKey, [isReady, revealPrivKey]); const isShowChatRoom = useMemo( () => isReady && !showSpinner && (!!receiverPubKey || isChannel) && !revealPrivKey, From c349318cd5aff2d4c242a193c72afa666329d6c4 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Thu, 9 Nov 2023 00:44:13 +0600 Subject: [PATCH 121/179] Chats: added chat importing mutation --- .../chat-popup-contacts-and-channels.tsx | 4 +- .../chats/components/chats-import.tsx | 88 ++++++++++++++ .../chats/components/import-chats/index.scss | 7 -- .../chats/components/import-chats/index.tsx | 109 ------------------ .../chats/mutations/import-chat-by-key.ts | 39 +++++++ src/common/features/chats/mutations/index.ts | 1 + .../features/chats/mutations/join-chat.ts | 43 +++---- .../chats/queries/chat-profiles-query.ts | 13 --- .../features/chats/queries/keys-query.ts | 40 +------ .../features/chats/queries/messages-query.ts | 3 +- src/common/features/chats/screens/chats.tsx | 8 +- .../chats/utils/get-user-chat-public-key.ts | 4 +- src/common/i18n/locales/en-US.json | 4 +- 13 files changed, 164 insertions(+), 199 deletions(-) create mode 100644 src/common/features/chats/components/chats-import.tsx delete mode 100644 src/common/features/chats/components/import-chats/index.scss delete mode 100644 src/common/features/chats/components/import-chats/index.tsx create mode 100644 src/common/features/chats/mutations/import-chat-by-key.ts delete mode 100644 src/common/features/chats/queries/chat-profiles-query.ts diff --git a/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx b/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx index d014a0fc7c4..ed334f393a4 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx @@ -1,7 +1,7 @@ import React, { useContext, useMemo } from "react"; import { _t } from "../../../../i18n"; import { getJoinedCommunities } from "../../utils"; -import ImportChats from "../import-chats"; +import { ChatsImport } from "../chats-import"; import { Spinner } from "@ui/spinner"; import { Button } from "@ui/button"; import { ChatContext } from "../../chat-context-provider"; @@ -67,7 +67,7 @@ export function ChatPopupContactsAndChannels({ ))} ) : !privateKey ? ( - + ) : showSpinner ? (
diff --git a/src/common/features/chats/components/chats-import.tsx b/src/common/features/chats/components/chats-import.tsx new file mode 100644 index 00000000000..101c39f264c --- /dev/null +++ b/src/common/features/chats/components/chats-import.tsx @@ -0,0 +1,88 @@ +import React, { useContext, useEffect, useState } from "react"; +import { _t } from "../../../i18n"; +import { keySvg } from "../../../img/svg"; +import { ChatContext } from "../chat-context-provider"; +import LinearProgress from "../../../components/linear-progress"; +import ChatsConfirmationModal from "./chats-confirmation-modal"; +import { Button } from "@ui/button"; +import { Form } from "@ui/form"; +import { FormControl, InputGroup } from "@ui/input"; +import { useImportChatByKey, useJoinChat } from "../mutations"; +import OrDivider from "../../../components/or-divider"; + +export function ChatsImport() { + const [showImportChats, setShowImportChats] = useState(false); + const [privateKeyInput, setPrivateKeyInput] = useState(""); + const [step, setStep] = useState(0); + const [error, setError] = useState(""); + + const { hasUserJoinedChat } = useContext(ChatContext); + const { mutateAsync: joinChat } = useJoinChat(); + + const { mutateAsync: importChatByKey, isLoading, error: importError } = useImportChatByKey(); + + useEffect(() => { + if (importError) { + setError(_t("chat.invalid-private-key")); + } + }, [importError]); + + useEffect(() => { + if (hasUserJoinedChat) { + setStep(0); + } + }, [hasUserJoinedChat]); + + return ( + <> +
+ {showImportChats && ( +
+
e.preventDefault()}> + importChatByKey({ key: privateKeyInput })}> + {_t("chat.submit")} + + } + > + { + setPrivateKeyInput(e.target.value); + setError(""); + }} + /> + + {isLoading && } + {error &&
{error}
} + +
+ )} + + +
+ +
+
+ {step !== 0 && ( + { + setStep(0); + }} + onConfirm={() => joinChat()} + /> + )} + + ); +} diff --git a/src/common/features/chats/components/import-chats/index.scss b/src/common/features/chats/components/import-chats/index.scss deleted file mode 100644 index 6a2e3d11b86..00000000000 --- a/src/common/features/chats/components/import-chats/index.scss +++ /dev/null @@ -1,7 +0,0 @@ -@import "src/style/vars_mixins"; - -.import-chats { - display: flex; - justify-content: center; - align-items: center; -} diff --git a/src/common/features/chats/components/import-chats/index.tsx b/src/common/features/chats/components/import-chats/index.tsx deleted file mode 100644 index ae73f648355..00000000000 --- a/src/common/features/chats/components/import-chats/index.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import React, { useContext, useEffect, useState } from "react"; -import { getPublicKey } from "../../../../../lib/nostr-tools/keys"; -import { _t } from "../../../../i18n"; -import { keySvg } from "../../../../img/svg"; -import { useMappedStore } from "../../../../store/use-mapped-store"; -import OrDivider from "../../../../components/or-divider"; -import { ChatContext } from "../../chat-context-provider"; -import "./index.scss"; -import LinearProgress from "../../../../components/linear-progress"; -import ChatsConfirmationModal from "../chats-confirmation-modal"; -import { Button } from "@ui/button"; -import { Form } from "@ui/form"; -import { FormControl, InputGroup } from "@ui/input"; -import { useJoinChat } from "../../mutations/join-chat"; -import { useKeysQuery } from "../../queries/keys-query"; - -export default function ImportChats() { - const { activeUser } = useMappedStore(); - - const [importPrivKey, setImportPrivKey] = useState(false); - const [error, setError] = useState(""); - const [inProgress, setInProgress] = useState(false); - const [privKey, setPrivKey] = useState(""); - const [step, setStep] = useState(0); - - const { hasUserJoinedChat, setChatPrivKey } = useContext(ChatContext); - const { mutateAsync: joinChat } = useJoinChat(); - - const { publicKey, privateKey } = useKeysQuery(); - - const handleImportChatSubmit = () => { - try { - setInProgress(true); - const pubKey = getPublicKey(privKey); - if (pubKey === publicKey) { - setChatPrivKey(privKey); - const keys = { - pub: publicKey, - priv: privateKey - }; - setImportPrivKey(false); - setChatPrivKey(privKey); - } else { - setImportPrivKey(true); - setError("Invalid Private key"); - } - } catch (error) { - setImportPrivKey(true); - setError("Invalid Private key"); - } finally { - setInProgress(false); - } - }; - - useEffect(() => { - if (hasUserJoinedChat) { - setStep(0); - } - }, [hasUserJoinedChat]); - - return ( - <> -
-
-
- -
- {importPrivKey && ( -
-
e.preventDefault()}> - {_t("chat.submit")}} - > - setPrivKey(e.target.value)} - /> - - {inProgress && } - {error &&
{error}
} - -
- )} - {} -
- -
-
-
- {step !== 0 && ( - { - setStep(0); - }} - onConfirm={() => joinChat()} - /> - )} - - ); -} diff --git a/src/common/features/chats/mutations/import-chat-by-key.ts b/src/common/features/chats/mutations/import-chat-by-key.ts new file mode 100644 index 00000000000..82c7237bfe8 --- /dev/null +++ b/src/common/features/chats/mutations/import-chat-by-key.ts @@ -0,0 +1,39 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { getPublicKey } from "../../../../lib/nostr-tools/keys"; +import { ChatQueries } from "../queries"; +import { PREFIX } from "../../../util/local-storage"; +import { useMappedStore } from "../../../store/use-mapped-store"; +import { getUserChatPublicKey } from "../utils"; + +interface Payload { + key: string; +} + +export function useImportChatByKey(onSuccess?: () => void) { + const queryClient = useQueryClient(); + const { activeUser } = useMappedStore(); + + return useMutation( + ["chats/import-chat-by-key"], + async ({ key }: Payload) => { + const publicKey = getPublicKey(key); + + if (activeUser) { + const activeUserExistingPublicKey = await getUserChatPublicKey(activeUser.username); + + if (activeUserExistingPublicKey !== publicKey) { + throw new Error( + "[Chat][Nostr] – account public key doesn't match to generated from private" + ); + } + } + + localStorage.setItem(PREFIX + "_nostr_pr_" + activeUser?.username, key); + queryClient.invalidateQueries([ChatQueries.PUBLIC_KEY]); + queryClient.invalidateQueries([ChatQueries.PRIVATE_KEY]); + }, + { + onSuccess + } + ); +} diff --git a/src/common/features/chats/mutations/index.ts b/src/common/features/chats/mutations/index.ts index 3da1015edb7..d66abbecf55 100644 --- a/src/common/features/chats/mutations/index.ts +++ b/src/common/features/chats/mutations/index.ts @@ -9,3 +9,4 @@ export * from "./update-community-channel"; export * from "./update-channel-moderator"; export * from "./fetch-previous-messages"; export * from "./update-channel-blocked-users"; +export * from "./import-chat-by-key"; diff --git a/src/common/features/chats/mutations/join-chat.ts b/src/common/features/chats/mutations/join-chat.ts index 47937afc0df..216db2bfd62 100644 --- a/src/common/features/chats/mutations/join-chat.ts +++ b/src/common/features/chats/mutations/join-chat.ts @@ -19,7 +19,7 @@ import { Kind } from "../../../../lib/nostr-tools/event"; */ export function useJoinChat(onSuccess?: () => void) { const queryClient = useQueryClient(); - const { activeUser, chat, resetChat } = useMappedStore(); + const { activeUser } = useMappedStore(); const { mutateAsync: uploadPublicKey } = useMutation(["chats/upload-public-key"], (key: string) => uploadChatPublicKey(activeUser, key) @@ -30,32 +30,25 @@ export function useJoinChat(onSuccess?: () => void) { () => {} ); - return useMutation( - ["chat-join-chat"], - async () => { - resetChat(); - return createNoStrAccount(); - }, - { - onSuccess: async (keys: ReturnType) => { - ls.set(`${activeUser?.username}_nsPrivKey`, keys.priv); - await uploadPublicKey(keys.pub); + return useMutation(["chat-join-chat"], async () => createNoStrAccount(), { + onSuccess: async (keys: ReturnType) => { + ls.set(`${activeUser?.username}_nsPrivKey`, keys.priv); + await uploadPublicKey(keys.pub); - setNostrkeys(keys); - queryClient.setQueryData([ChatQueries.PUBLIC_KEY], keys.pub); - queryClient.setQueryData([ChatQueries.PRIVATE_KEY], keys.priv); + setNostrkeys(keys); + queryClient.setQueryData([ChatQueries.PUBLIC_KEY], keys.pub); + queryClient.setQueryData([ChatQueries.PRIVATE_KEY], keys.priv); - await updateProfile({ - tags: [], - eventMetadata: { - name: activeUser?.username!, - about: "", - picture: "" - } - }); + await updateProfile({ + tags: [], + eventMetadata: { + name: activeUser?.username!, + about: "", + picture: "" + } + }); - onSuccess?.(); - } + onSuccess?.(); } - ); + }); } diff --git a/src/common/features/chats/queries/chat-profiles-query.ts b/src/common/features/chats/queries/chat-profiles-query.ts deleted file mode 100644 index 8077d7c1cff..00000000000 --- a/src/common/features/chats/queries/chat-profiles-query.ts +++ /dev/null @@ -1,13 +0,0 @@ -// export function useChatProfilesQuery() { -// return useMessageListenerQuery( -// [ChatQueries.PROFILES], -// MessageEvents.ProfileUpdate, -// (_, nextData, resolver) => { -// resolver(nextData); -// }, -// { -// initialData: [], -// queryFn: () => [] -// } -// ); -// } diff --git a/src/common/features/chats/queries/keys-query.ts b/src/common/features/chats/queries/keys-query.ts index 5f47708b24d..a78d8be93b1 100644 --- a/src/common/features/chats/queries/keys-query.ts +++ b/src/common/features/chats/queries/keys-query.ts @@ -1,25 +1,11 @@ import { useQueries } from "@tanstack/react-query"; import { ChatQueries } from "./queries"; -import { getPrivateKey, getUserChatPublicKey } from "../utils"; +import { getUserChatPublicKey } from "../utils"; import { useMappedStore } from "../../../store/use-mapped-store"; -import { NostrKeysType } from "../types"; import { useMemo } from "react"; +import { PREFIX } from "../../../util/local-storage"; -/** - * Custom React hook for managing queries related to a user's public and private keys in a chat application. - * - * This hook handles querying and managing the user's public and private keys, as well as providing a - * method for manual data refetch. - * - * @returns An object containing the user's public and private keys, along with a refetch method. - * - publicKey: The user's public key for secure communication. - * - privateKey: The user's private key for secure communication. - * - refetch: A function to manually trigger a refetch of the public and private keys. - */ -export function useKeysQuery( - activeUserKeys?: NostrKeysType, - setActiveUserKeys?: (keys: Partial) => void -) { +export function useKeysQuery() { const { activeUser } = useMappedStore(); const [ @@ -29,27 +15,11 @@ export function useKeysQuery( queries: [ { queryKey: [ChatQueries.PUBLIC_KEY], - queryFn: () => getUserChatPublicKey(activeUser?.username!), - onSuccess: (key: string | null) => { - if (key) { - setActiveUserKeys?.({ - ...(activeUserKeys ?? {}), - pub: key - }); - } - } + queryFn: async () => getUserChatPublicKey(activeUser?.username!) }, { queryKey: [ChatQueries.PRIVATE_KEY], - queryFn: () => getPrivateKey(activeUser?.username!), - onSuccess: (key: string | null) => { - if (key) { - setActiveUserKeys?.({ - ...(activeUserKeys ?? {}), - priv: key - }); - } - } + queryFn: () => localStorage.getItem(PREFIX + "_nostr_pr_" + activeUser?.username) } ] }); diff --git a/src/common/features/chats/queries/messages-query.ts b/src/common/features/chats/queries/messages-query.ts index 807c8914e5d..cfba7602779 100644 --- a/src/common/features/chats/queries/messages-query.ts +++ b/src/common/features/chats/queries/messages-query.ts @@ -9,7 +9,7 @@ import { queryClient } from "../../../core"; import { useKeysQuery } from "./keys-query"; export function useMessagesQuery(username?: string) { - const { privateKey, publicKey } = useKeysQuery(); + const { privateKey, publicKey, hasKeys } = useKeysQuery(); const { data: directContacts } = useDirectContactsQuery(); const { data: channels } = useChannelsQuery(); @@ -47,6 +47,7 @@ export function useMessagesQuery(username?: string) { }, { initialData: [], + enabled: hasKeys, select: (messages) => { if (currentChannel) { return messages diff --git a/src/common/features/chats/screens/chats.tsx b/src/common/features/chats/screens/chats.tsx index b09b08127ea..c87717e1e99 100644 --- a/src/common/features/chats/screens/chats.tsx +++ b/src/common/features/chats/screens/chats.tsx @@ -9,7 +9,6 @@ import Feedback from "../../../components/feedback"; import { useMappedStore } from "../../../store/use-mapped-store"; import { ChatContext } from "../chat-context-provider"; import { Spinner } from "@ui/spinner"; -import ImportChats from "../components/import-chats"; import ChatsMessagesBox from "../components/chat-message-box"; import JoinChat from "../components/join-chat"; import { classNameObject } from "../../../helper/class-name-object"; @@ -17,6 +16,7 @@ import "./_chats.scss"; import { useChannelsQuery } from "../queries"; import { useLeftCommunityChannelsQuery } from "../queries/left-community-channels-query"; import { useKeysQuery } from "../queries/keys-query"; +import { ChatsImport } from "../components/chats-import"; interface Props extends PageProps { match: match<{ @@ -98,9 +98,9 @@ export const Chats = (props: Props) => {
)} - {isShowImportChats && ( + {isShowImportChats && activeUser && (
- +
)} {isShowChatRoom && } @@ -112,7 +112,7 @@ export const Chats = (props: Props) => {
Search a person or community and start messaging
)} - {!isReady && ( + {!isReady && !isShowImportChats && !showSpinner && (
diff --git a/src/common/features/chats/utils/get-user-chat-public-key.ts b/src/common/features/chats/utils/get-user-chat-public-key.ts index 3e6aa39eddd..d20427e4fff 100644 --- a/src/common/features/chats/utils/get-user-chat-public-key.ts +++ b/src/common/features/chats/utils/get-user-chat-public-key.ts @@ -1,6 +1,6 @@ import { getAccountFull } from "../../../api/hive"; -export const getUserChatPublicKey = async (username: string): Promise => { +export const getUserChatPublicKey = async (username: string): Promise => { const response = await getAccountFull(username); if (response && response.posting_json_metadata) { const { posting_json_metadata } = response; @@ -10,5 +10,5 @@ export const getUserChatPublicKey = async (username: string): Promise Date: Thu, 9 Nov 2023 00:51:14 +0600 Subject: [PATCH 122/179] Chats: removed store --- src/common/components/login/index.tsx | 10 +- .../chats/components/chat-popup/index.tsx | 3 +- .../chats-channel-messages/index.tsx | 16 +- .../chats-community-actions/index.tsx | 4 +- .../chats-direct-messages/index.tsx | 2 +- .../chats-sidebar/chat-sidebar-header.tsx | 6 +- .../chats/managers/message-manager.tsx | 126 +----- src/common/features/chats/utils/index.ts | 4 - .../chats/utils/upload-channel-data.ts | 17 - .../utils/use-fetch-community-messages.ts | 18 - .../chats/utils/use-fetch-direct-messages.ts | 10 - .../utils/use-get-community-last-message.ts | 20 - src/common/store/actions.ts | 30 +- src/common/store/chat/index.ts | 369 ------------------ src/common/store/chat/types.ts | 108 ----- src/common/store/index.ts | 4 +- src/common/store/initial-state.ts | 4 +- 17 files changed, 29 insertions(+), 722 deletions(-) delete mode 100644 src/common/features/chats/utils/upload-channel-data.ts delete mode 100644 src/common/features/chats/utils/use-fetch-community-messages.ts delete mode 100644 src/common/features/chats/utils/use-fetch-direct-messages.ts delete mode 100644 src/common/features/chats/utils/use-get-community-last-message.ts delete mode 100644 src/common/store/chat/index.ts delete mode 100644 src/common/store/chat/types.ts diff --git a/src/common/components/login/index.tsx b/src/common/components/login/index.tsx index d506b5a4922..77e29db1c01 100644 --- a/src/common/components/login/index.tsx +++ b/src/common/components/login/index.tsx @@ -730,7 +730,6 @@ interface Props { updateActiveUser: (data?: Account) => void; deleteUser: (username: string) => void; toggleUIProp: (what: ToggleType) => void; - resetChat: () => void; } class LoginDialog extends Component { @@ -751,7 +750,7 @@ class LoginDialog extends Component { doLogin = async (hsCode: string, postingKey: null | undefined | string, account: Account) => { const profile = await getProfileMetaData(account.name); - const { global, setActiveUser, updateActiveUser, addUser, resetChat } = this.props; + const { global, setActiveUser, updateActiveUser, addUser } = this.props; // get access token from code return hsTokenRenew(hsCode).then((x) => { @@ -766,9 +765,6 @@ class LoginDialog extends Component { // add / update user data addUser(user); - //reset the already stored chat - resetChat(); - //create new message service instance for newly logged in user if (profile && profile.nsKey && getPrivateKey(account.name)) { const keys = { @@ -830,8 +826,7 @@ export default ({ history }: Pick) => { setActiveUser, updateActiveUser, deleteUser, - toggleUIProp, - resetChat + toggleUIProp } = useMappedStore(); const location = useLocation(); @@ -843,7 +838,6 @@ export default ({ history }: Pick) => { ui={ui} users={users} activeUser={activeUser} - resetChat={resetChat} addUser={addUser} setActiveUser={setActiveUser} updateActiveUser={updateActiveUser} diff --git a/src/common/features/chats/components/chat-popup/index.tsx b/src/common/features/chats/components/chat-popup/index.tsx index d0253a217d3..0b59afed9a9 100644 --- a/src/common/features/chats/components/chat-popup/index.tsx +++ b/src/common/features/chats/components/chat-popup/index.tsx @@ -25,7 +25,7 @@ import { useFetchPreviousMessages, useJoinChat } from "../../mutations"; import { useKeysQuery } from "../../queries/keys-query"; export const ChatPopUp = () => { - const { activeUser, global, chat, resetChat } = useMappedStore(); + const { activeUser, global } = useMappedStore(); const { receiverPubKey, revealPrivKey, hasUserJoinedChat, setRevealPrivKey, setShowSpinner } = useContext(ChatContext); @@ -156,7 +156,6 @@ export const ChatPopUp = () => { const handleRefreshSvgClick = () => { setExpanded(true); - resetChat(); handleBackArrowSvg(); if (getPrivateKey(activeUser?.username!)) { setShowSpinner(true); diff --git a/src/common/features/chats/components/chats-channel-messages/index.tsx b/src/common/features/chats/components/chats-channel-messages/index.tsx index 9d73ddf8cb9..4ab376b2606 100644 --- a/src/common/features/chats/components/chats-channel-messages/index.tsx +++ b/src/common/features/chats/components/chats-channel-messages/index.tsx @@ -29,7 +29,7 @@ interface Props { let zoom: Zoom | null = null; export default function ChatsChannelMessages(props: Props) { const { publicMessages, isScrollToBottom, isScrolled, currentChannel, scrollToBottom } = props; - const { global, activeUser, deletePublicMessage } = useMappedStore(); + const { global, activeUser } = useMappedStore(); const { messageServiceInstance, isActiveUserRemoved } = useContext(ChatContext); @@ -120,13 +120,13 @@ export default function ChatsChannelMessages(props: Props) { break; case 4: if (resendMessage) { - deletePublicMessage(currentChannel.id, resendMessage?.id); - messageServiceInstance?.sendPublicMessage( - currentChannel!, - resendMessage?.content, - [], - "" - ); + // // deletePublicMessage(currentChannel.id, resendMessage?.id); + // messageServiceInstance?.sendPublicMessage( + // currentChannel!, + // resendMessage?.content, + // [], + // "" + // ); } break; default: diff --git a/src/common/features/chats/components/chats-community-actions/index.tsx b/src/common/features/chats/components/chats-community-actions/index.tsx index 13f8d53e0e9..8d9f773e198 100644 --- a/src/common/features/chats/components/chats-community-actions/index.tsx +++ b/src/common/features/chats/components/chats-community-actions/index.tsx @@ -14,7 +14,6 @@ import { ChatContext } from "../../chat-context-provider"; import "./index.scss"; import { Button } from "@ui/button"; import { Modal, ModalBody, ModalHeader } from "@ui/modal"; -import { CommunityModerator } from "../../managers/message-manager-types"; import { useLeaveCommunityChannel } from "../../mutations"; import { EditRolesModal } from "./edit-roles-modal"; import { useChannelsQuery } from "../../queries"; @@ -26,11 +25,10 @@ interface Props { } const ChatsCommunityDropdownMenu = (props: Props) => { - const { activeUser, chat } = useMappedStore(); + const { activeUser } = useMappedStore(); const { history, from } = props; const [step, setStep] = useState(0); const [keyDialog, setKeyDialog] = useState(false); - const [moderator, setModerator] = useState(); const [communityAdmins, setCommunityAdmins] = useState([]); const [blockedUsers, setBlockedUsers] = useState<{ name: string; pubkey: string }[]>([]); const [removedUserId, setRemovedUserID] = useState(""); diff --git a/src/common/features/chats/components/chats-direct-messages/index.tsx b/src/common/features/chats/components/chats-direct-messages/index.tsx index 4a54d9fc9d9..4364b457cae 100644 --- a/src/common/features/chats/components/chats-direct-messages/index.tsx +++ b/src/common/features/chats/components/chats-direct-messages/index.tsx @@ -23,7 +23,7 @@ let zoom: Zoom | null = null; export default function ChatsDirectMessages(props: Props) { const { directMessages, isScrolled, isScrollToBottom, scrollToBottom } = props; - const { global, activeUser, deleteDirectMessage } = useMappedStore(); + const { global, activeUser } = useMappedStore(); const { receiverPubKey } = useContext(ChatContext); let prevGlobal = usePrevious(global); diff --git a/src/common/features/chats/components/chats-sidebar/chat-sidebar-header.tsx b/src/common/features/chats/components/chats-sidebar/chat-sidebar-header.tsx index 4124b13fd88..e701baea700 100644 --- a/src/common/features/chats/components/chats-sidebar/chat-sidebar-header.tsx +++ b/src/common/features/chats/components/chats-sidebar/chat-sidebar-header.tsx @@ -5,7 +5,6 @@ import { _t } from "../../../../i18n"; import ChatsDropdownMenu from "../chats-dropdown-menu"; import React, { useContext } from "react"; import { ChatContext } from "../../chat-context-provider"; -import { useMappedStore } from "../../../../store/use-mapped-store"; import { History } from "history"; interface Props { @@ -13,12 +12,9 @@ interface Props { } export function ChatSidebarHeader({ history }: Props) { - const { resetChat } = useMappedStore(); const { revealPrivKey, chatPrivKey, setRevealPrivKey } = useContext(ChatContext); - const handleRefreshChat = () => { - resetChat(); - }; + const handleRefreshChat = () => {}; return (
diff --git a/src/common/features/chats/managers/message-manager.tsx b/src/common/features/chats/managers/message-manager.tsx index 5f1e44ab16c..2f3d2bd8707 100644 --- a/src/common/features/chats/managers/message-manager.tsx +++ b/src/common/features/chats/managers/message-manager.tsx @@ -1,14 +1,7 @@ -import React, { useContext, useEffect, useState } from "react"; -import { ActiveUser } from "../../../store/active-user/types"; +import React, { useState } from "react"; import { NostrKeysType } from "../types"; -import { DirectMessage, Keys, PublicMessage } from "./message-manager-types"; -import MessageService, { MessageEvents } from "../../../helper/message-service"; -import { getPrivateKey, getProfileMetaData } from "../utils"; import { useMappedStore } from "../../../store/use-mapped-store"; -import { ChatContext } from "../chat-context-provider"; import { usePrevious } from "../../../util/use-previous"; -import { useMessageServiceListener } from "../hooks/use-message-service-listener"; -import { useChannelsQuery, useDirectContactsQuery } from "../queries"; export const setNostrkeys = (keys: NostrKeysType) => { const detail: NostrKeysType = { @@ -20,104 +13,27 @@ export const setNostrkeys = (keys: NostrKeysType) => { }; const MessageManager = () => { - const { - activeUser, - addDirectMessages, - addPublicMessage, - addProfile, - addleftChannels, - UpdateChannels, - replacePublicMessage, - verifyPublicMessageSending, - replaceDirectMessage, - verifyDirectMessageSending, - addPreviousPublicMessages, - resetChat - } = useMappedStore(); - const { data: directContacts } = useDirectContactsQuery(); - const { data: channels } = useChannelsQuery(); - + const { activeUser } = useMappedStore(); const prevActiveUser = usePrevious(activeUser); const [messageServiceReady, setMessageServiceReady] = useState(false); - const [keys, setKeys] = useState(); - const [messageService, setMessageService] = useState(undefined); - const [directMessageBuffer, setDirectMessageBuffer] = useState([]); - const [publicMessageBuffer, setPublicMessageBuffer] = useState([]); - const [isCommunityCreated, setIsCommunityCreated] = useState(false); - const [replacedPublicMessagesBuffer, setReplacedPublicMessagesBuffer] = useState([]); - const [replacedDirectMessagesBuffer, setReplacedDirectMessagesBuffer] = useState([]); - - const { messageServiceInstance, initMessageServiceInstance, setMessageServiceInstance } = - useContext(ChatContext); - - useEffect(() => { - if (channels?.length !== 0) { - setIsCommunityCreated(true); - } - }, [channels]); - - useEffect(() => { - window.addEventListener("createMSInstance", createMSInstance); - - return () => { - window.removeEventListener("createMSInstance", createMSInstance); - }; - }, []); - - useEffect(() => { - if (keys?.priv) { - const messageService = initMessageServiceInstance(keys); - setMessageServiceInstance(messageService); - setMessageService(messageService!); - } else { - setMessageServiceInstance(null); - } - }, [keys]); - - useEffect(() => { - if (activeUser) { - getNostrKeys(activeUser); - } - if (prevActiveUser?.username !== activeUser?.username) { - resetChat(); - getNostrKeys(activeUser!); - } - }, [activeUser]); - - const createMSInstance = (e: Event) => { - const detail = (e as CustomEvent).detail as NostrKeysType; - const messageService = initMessageServiceInstance(detail); - setMessageServiceInstance(messageService); - setMessageService(messageService!); - }; - - const getNostrKeys = async (activeUser: ActiveUser) => { - const profile = await getProfileMetaData(activeUser.username); - const noStrPrivKey = getPrivateKey(activeUser.username); - const keys = { - pub: profile?.nsKey, - priv: noStrPrivKey!! - }; - setKeys(keys); - }; //Listen for events in an interval. - useMessageServiceListener(messageServiceReady, messageService); + // useMessageServiceListener(messageServiceReady, messageService); // // Ready state handler const handleReadyState = () => { setMessageServiceReady(true); }; - - useEffect(() => { - messageService?.removeListener(MessageEvents.Ready, handleReadyState); - messageService?.addListener(MessageEvents.Ready, handleReadyState); - - return () => { - messageService?.removeListener(MessageEvents.Ready, handleReadyState); - }; - }, [messageServiceReady, messageService]); + // + // useEffect(() => { + // messageService?.removeListener(MessageEvents.Ready, handleReadyState); + // messageService?.addListener(MessageEvents.Ready, handleReadyState); + // + // return () => { + // messageService?.removeListener(MessageEvents.Ready, handleReadyState); + // }; + // }, [messageServiceReady, messageService]); // const checkPublicMessageSending = (channelId: string, data: PublicMessage) => { // setTimeout(() => { @@ -247,24 +163,6 @@ const MessageManager = () => { // }; // }, [messageService, chat.profiles, chat.publicMessages]); - useEffect(() => { - return () => { - messageService?.removeListener(MessageEvents.Ready, handleReadyState); - // messageService?.removeListener( - // MessageEvents.PreviousPublicMessages, - // handlePreviousPublicMessages - // ); - // messageService?.removeListener( - // MessageEvents.PublicMessageBeforeSent, - // handlePublicMessageBeforeSent - // ); - // messageService?.removeListener( - // MessageEvents.PublicMessageAfterSent, - // handlePublicMessageAfterSent - // ); - }; - }, [messageService]); - return <>{}; }; diff --git a/src/common/features/chats/utils/index.ts b/src/common/features/chats/utils/index.ts index 84476493cac..30dab8fa017 100644 --- a/src/common/features/chats/utils/index.ts +++ b/src/common/features/chats/utils/index.ts @@ -1,20 +1,16 @@ export * from "./fetch-profile-metadata"; export * from "./delete-chat-public-key"; export * from "./upload-chat-public-key"; -export * from "./upload-channel-data"; export * from "./create-nostr-account"; export * from "./format-message-time"; -export * from "./use-fetch-community-messages"; export * from "./is-message-gif"; export * from "./get-user-chat-public-key"; -export * from "./use-get-community-last-message"; export * from "./use-get-direct-last-message"; export * from "./formatted-user-name"; export * from "./is-message-image"; export * from "./get-chat-private-key"; export * from "./get-joined-communities"; export * from "./copy-to-clipboard"; -export * from "./use-fetch-direct-messages"; export * from "./check-contiguous-message"; export * from "./format-message-date-and-day"; export * from "./get-relative-date"; diff --git a/src/common/features/chats/utils/upload-channel-data.ts b/src/common/features/chats/utils/upload-channel-data.ts deleted file mode 100644 index 45458239bab..00000000000 --- a/src/common/features/chats/utils/upload-channel-data.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { getAccountFull } from "../../../api/hive"; -import { updateProfile } from "../../../api/operations"; -import { Channel } from "../managers/message-manager-types"; - -export const setChannelMetaData = async (username: string, channel: Channel) => { - try { - const response = await getAccountFull(username!); - const { posting_json_metadata } = response; - const profile = JSON.parse(posting_json_metadata!).profile; - const newProfile = { - channel: channel - }; - return await updateProfile(response, { ...profile, ...newProfile }); - } catch (error) { - throw error; - } -}; diff --git a/src/common/features/chats/utils/use-fetch-community-messages.ts b/src/common/features/chats/utils/use-fetch-community-messages.ts deleted file mode 100644 index cbfd3f477aa..00000000000 --- a/src/common/features/chats/utils/use-fetch-community-messages.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { publicMessagesList } from "../../../store/chat/types"; -import { Channel } from "../managers/message-manager-types"; - -export const fetchCommunityMessages = ( - publicMessages: publicMessagesList[], - currentChannel: Channel, - hiddenMessageIds?: string[] -) => { - const hideMessageIds = hiddenMessageIds || currentChannel?.hiddenMessageIds || []; - for (const item of publicMessages) { - if (item.channelId === currentChannel.id) { - return Object.values(item.PublicMessage).filter( - (message) => !hideMessageIds.includes(message.id) - ); - } - } - return []; -}; diff --git a/src/common/features/chats/utils/use-fetch-direct-messages.ts b/src/common/features/chats/utils/use-fetch-direct-messages.ts deleted file mode 100644 index 189d930d98b..00000000000 --- a/src/common/features/chats/utils/use-fetch-direct-messages.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { directMessagesList } from "../../../store/chat/types"; - -export const fetchDirectMessages = (peer: string, directMessages: directMessagesList[]) => { - for (const item of directMessages) { - if (item.peer === peer) { - return Object.values(item.chat); - } - } - return []; -}; diff --git a/src/common/features/chats/utils/use-get-community-last-message.ts b/src/common/features/chats/utils/use-get-community-last-message.ts deleted file mode 100644 index 24fea4b2ba9..00000000000 --- a/src/common/features/chats/utils/use-get-community-last-message.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { publicMessagesList } from "../../../store/chat/types"; - -export const getCommunityLastMessage = ( - channelId: string, - publicMessages: publicMessagesList[] -) => { - const msgsList = fetchChannelMessages(channelId!, publicMessages); - const messages = msgsList.sort((a, b) => a.created - b.created); - const lastMessage = messages.slice(-1); - return lastMessage[0]?.content; -}; - -const fetchChannelMessages = (channelId: string, publicMessages: publicMessagesList[]) => { - for (const item of publicMessages) { - if (item.channelId === channelId) { - return Object.values(item.PublicMessage); - } - } - return []; -}; diff --git a/src/common/store/actions.ts b/src/common/store/actions.ts index b6acdbf8e52..eb7b42bd5ae 100644 --- a/src/common/store/actions.ts +++ b/src/common/store/actions.ts @@ -38,21 +38,6 @@ import { updateNotificationsSettings } from "./notifications"; import { setSigningKey } from "./signing-key"; -import { - addDirectMessages, - addleftChannels, - addPreviousPublicMessages, - addProfile, - addPublicMessage, - deleteDirectMessage, - deletePublicMessage, - replaceDirectMessage, - replacePublicMessage, - resetChat, - UpdateChannels, - verifyDirectMessageSending, - verifyPublicMessageSending -} from "./chat"; // @note Do not use it directly export const ACTIONS = { @@ -97,20 +82,7 @@ export const ACTIONS = { setSigningKey, updateNotificationsSettings, fetchNotificationsSettings, - setNotificationsSettingsItem, - addDirectMessages, - resetChat, - addPublicMessage, - addProfile, - addleftChannels, - UpdateChannels, - replacePublicMessage, - verifyPublicMessageSending, - replaceDirectMessage, - verifyDirectMessageSending, - deletePublicMessage, - deleteDirectMessage, - addPreviousPublicMessages + setNotificationsSettingsItem }; export const getActions = () => ({ diff --git a/src/common/store/chat/index.ts b/src/common/store/chat/index.ts deleted file mode 100644 index f7f0a934ed5..00000000000 --- a/src/common/store/chat/index.ts +++ /dev/null @@ -1,369 +0,0 @@ -import { - ChannelUpdate, - DirectMessage, - MessagesObject, - Profile, - PublicMessage -} from "../../features/chats/managers/message-manager-types"; -import { Dispatch } from "redux"; - -import { - Actions, - ActionTypes, - AddPreviousPublicMessagesAction, - Chat, - DeleteDirectMessagesAction, - DeletePublicMessagesAction, - DirectMessagesAction, - LeftChannelsAction, - ProfilesAction, - PublicMessagesAction, - ReplaceDirectMessagesAction, - ReplacePublicMessagesAction, - ResetChatAction, - UpdateChannelAction, - VerifyDirectMessageSendingAction, - VerifyPublicMessageSendingAction -} from "./types"; - -export const initialState: Chat = { - profiles: [], - updatedChannel: [] -}; - -export default (state: Chat = initialState, action: Actions): Chat => { - switch (action.type) { - case ActionTypes.RESET: - return initialState; - - case ActionTypes.PROFILES: { - const { data } = action; - - const filteredProfiles = data.filter((profile) => { - return !state.profiles.some((existingProfile) => existingProfile.id === profile.id); - }); - - return { - ...state, - profiles: [...state.profiles, ...filteredProfiles] - }; - } - case ActionTypes.UPDATEDCHANNEL: { - const { data } = action; - return { - ...state, - updatedChannel: [...state.updatedChannel, ...data] - }; - } - - case ActionTypes.REPLACEPUBLICMESSAGE: { - const { channelId, data } = action; - const publicChatObject = state.publicMessages.find((item) => item.channelId === channelId); - const publicChat = publicChatObject?.PublicMessage || {}; - - if (publicChat.hasOwnProperty(data.id)) { - publicChat[data.id] = data; - } else { - publicChat[data.id] = data; - } - - return { - ...state - }; - } - - case ActionTypes.VERIFYPUBLICMESSAGESENDING: { - const { channelId, data } = action; - - const publicChatObject = state.publicMessages.find((item) => item.channelId === channelId); - const publicChat = publicChatObject?.PublicMessage || {}; - - if (publicChat.hasOwnProperty(data.id)) { - if (publicChat[data.id].sent === 0) { - publicChat[data.id].sent = 2; - } - } - return { - ...state, - publicMessages: state.publicMessages.map((obj) => - obj.channelId === channelId ? { channelId, PublicMessage: { ...publicChat } } : obj - ) - }; - } - - case ActionTypes.REPLACEDIRECTMESSAGE: { - const { peer, data } = action; - const directMessagesObject = state.directMessages.find((item) => item.peer === peer); - const directMessages = directMessagesObject?.chat || {}; - - if (directMessages.hasOwnProperty(data.id)) { - directMessages[data.id] = data; - } else { - directMessages[data.id] = data; - } - - return { - ...state, - directMessages: [ - ...state.directMessages.map((obj) => - obj.peer === peer ? { peer, chat: { ...directMessages } } : obj - ) - ] - }; - } - - case ActionTypes.VERIFYDIRECTMESSAGESENDING: { - const { peer, data } = action; - - const directMessagesObject = state.directMessages.find((item) => item.peer === peer); - const directMessages = directMessagesObject?.chat || {}; - - if (directMessages.hasOwnProperty(data.id)) { - if (directMessages[data.id].sent === 0) { - directMessages[data.id].sent = 2; - } - } - return { - ...state, - directMessages: state.directMessages.map((obj) => - obj.peer === peer ? { peer, chat: { ...directMessages } } : obj - ) - }; - } - - case ActionTypes.DELETEPUBLICMESSAGE: { - const { channelId, msgId } = action; - - const publicChatObject = state.publicMessages.find((item) => item.channelId === channelId); - const publicChat = publicChatObject?.PublicMessage || {}; - - if (publicChat[msgId]) { - delete publicChat[msgId]; - } - return { - ...state, - publicMessages: state.publicMessages.map((obj) => - obj.channelId === channelId ? { channelId, PublicMessage: { ...publicChat } } : obj - ) - }; - } - - case ActionTypes.DELETEDIRECTMESSAGE: { - const { peer, msgId } = action; - - const directMessagesObject = state.directMessages.find((item) => item.peer === peer); - const directMessages = directMessagesObject?.chat || {}; - - if (directMessages[msgId]) { - delete directMessages[msgId]; - } - - return { - ...state, - directMessages: [ - ...state.directMessages.map((obj) => - obj.peer === peer ? { peer, chat: { ...directMessages } } : obj - ) - ] - }; - } - - case ActionTypes.ADDPREVIOUSPUBLICMESSAGES: { - const { channelId, data } = action; - - const publicChatObject = state.publicMessages.find((item) => item.channelId === channelId); - const publicChat = publicChatObject?.PublicMessage || {}; - - return { - ...state, - publicMessages: state.publicMessages.map((obj) => - obj.channelId === channelId - ? { channelId, PublicMessage: { ...data, ...publicChat } } - : obj - ) - }; - } - - default: - return state; - } -}; - -/* Actions */ -export const addDirectMessages = (peer: string, data: DirectMessage) => (dispatch: Dispatch) => { - dispatch(addDirectMessagesAct(peer, data)); -}; - -export const resetChat = () => (dispatch: Dispatch) => { - dispatch(resetChatAct()); -}; - -export const addPublicMessage = - (channelId: string, data: PublicMessage) => (dispatch: Dispatch) => { - dispatch(addPublicMessagesAct(channelId, data)); - }; - -export const addProfile = (data: Profile[]) => (dispatch: Dispatch) => { - dispatch(addProfilesAct(data)); -}; - -export const addleftChannels = (data: string[]) => (dispatch: Dispatch) => { - dispatch(addleftChannelsAct(data)); -}; - -export const UpdateChannels = (data: ChannelUpdate[]) => (dispatch: Dispatch) => { - dispatch(UpdateChannelsAct(data)); -}; - -export const replacePublicMessage = - (channelId: string, data: PublicMessage) => (dispatch: Dispatch) => { - dispatch(replacePublicMessagesAct(channelId, data)); - }; - -export const verifyPublicMessageSending = - (channelId: string, data: PublicMessage) => (dispatch: Dispatch) => { - dispatch(verifyPublicMessageSendingAct(channelId, data)); - }; - -export const replaceDirectMessage = (peer: string, data: DirectMessage) => (dispatch: Dispatch) => { - dispatch(replaceDirectMessagesAct(peer, data)); -}; - -export const verifyDirectMessageSending = - (peer: string, data: DirectMessage) => (dispatch: Dispatch) => { - dispatch(verifyDirectMessageSendingAct(peer, data)); - }; - -export const deletePublicMessage = (channelId: string, msgId: string) => (dispatch: Dispatch) => { - dispatch(deletePublicMessageAct(channelId, msgId)); -}; - -export const deleteDirectMessage = (peer: string, msgId: string) => (dispatch: Dispatch) => { - dispatch(deleteDirectMessageAct(peer, msgId)); -}; - -export const addPreviousPublicMessages = - (channelId: string, data: MessagesObject) => (dispatch: Dispatch) => { - dispatch(addPreviousPublicMessagesAct(channelId, data)); - }; - -/* Action Creators */ - -export const addDirectMessagesAct = (peer: string, data: DirectMessage): DirectMessagesAction => { - return { - type: ActionTypes.DIRECTMESSAGES, - peer, - data - }; -}; - -export const addPublicMessagesAct = ( - channelId: string, - data: PublicMessage -): PublicMessagesAction => { - return { - type: ActionTypes.PUBLICMESSAGES, - channelId, - data - }; -}; - -export const resetChatAct = (): ResetChatAction => { - return { - type: ActionTypes.RESET - }; -}; -export const addProfilesAct = (data: Profile[]): ProfilesAction => { - return { - type: ActionTypes.PROFILES, - data - }; -}; - -export const addleftChannelsAct = (data: string[]): LeftChannelsAction => { - return { - type: ActionTypes.LEFTCHANNELLIST, - data - }; -}; - -export const UpdateChannelsAct = (data: ChannelUpdate[]): UpdateChannelAction => { - return { - type: ActionTypes.UPDATEDCHANNEL, - data - }; -}; - -export const replacePublicMessagesAct = ( - channelId: string, - data: PublicMessage -): ReplacePublicMessagesAction => { - return { - type: ActionTypes.REPLACEPUBLICMESSAGE, - channelId, - data - }; -}; - -export const verifyPublicMessageSendingAct = ( - channelId: string, - data: PublicMessage -): VerifyPublicMessageSendingAction => { - return { - type: ActionTypes.VERIFYPUBLICMESSAGESENDING, - channelId, - data - }; -}; - -export const replaceDirectMessagesAct = ( - peer: string, - data: DirectMessage -): ReplaceDirectMessagesAction => { - return { - type: ActionTypes.REPLACEDIRECTMESSAGE, - peer, - data - }; -}; - -export const verifyDirectMessageSendingAct = ( - peer: string, - data: DirectMessage -): VerifyDirectMessageSendingAction => { - return { - type: ActionTypes.VERIFYDIRECTMESSAGESENDING, - peer, - data - }; -}; - -export const deletePublicMessageAct = ( - channelId: string, - msgId: string -): DeletePublicMessagesAction => { - return { - type: ActionTypes.DELETEPUBLICMESSAGE, - channelId, - msgId - }; -}; - -export const deleteDirectMessageAct = (peer: string, msgId: string): DeleteDirectMessagesAction => { - return { - type: ActionTypes.DELETEDIRECTMESSAGE, - peer, - msgId - }; -}; - -export const addPreviousPublicMessagesAct = ( - channelId: string, - data: MessagesObject -): AddPreviousPublicMessagesAction => { - return { - type: ActionTypes.ADDPREVIOUSPUBLICMESSAGES, - channelId, - data - }; -}; diff --git a/src/common/store/chat/types.ts b/src/common/store/chat/types.ts deleted file mode 100644 index 2e54cfd2e74..00000000000 --- a/src/common/store/chat/types.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { - ChannelUpdate, - DirectMessage, - MessagesObject, - Profile, - PublicMessage -} from "../../features/chats/managers/message-manager-types"; - -export interface DirectContactsType { - name: string; - pubkey: string; -} - -export interface directMessagesList { - peer: string; - chat: { [messageId: string]: DirectMessage }; -} - -export interface publicMessagesList { - channelId: string; - PublicMessage: { [messageId: string]: PublicMessage }; -} - -export interface Chat { - profiles: Profile[]; - updatedChannel: ChannelUpdate[]; -} - -export enum ActionTypes { - RESET = "@chat/RESET", - PROFILES = "@chat/PROFILES", - UPDATEDCHANNEL = "@chat/UPDATEDCHANNEL", - REPLACEPUBLICMESSAGE = "@chat/REPLACEPUBLICMESSAGE", - VERIFYPUBLICMESSAGESENDING = "@chat/VERIFYMESSAGESENDING", - REPLACEDIRECTMESSAGE = "@chat/REPLACEDIRECTMESSAGE", - VERIFYDIRECTMESSAGESENDING = "@chat/VERIFYDIRECTMESSAGESENDING", - DELETEPUBLICMESSAGE = "@chat/DELETEPUBLICMESSAGE", - DELETEDIRECTMESSAGE = "@chat/DELETEDIRECTMESSAGE", - ADDPREVIOUSPUBLICMESSAGES = "@chat/ADDPREVIOUSPUBLICMESSAGES" -} - -export interface ResetChatAction { - type: ActionTypes.RESET; -} - -export interface ProfilesAction { - type: ActionTypes.PROFILES; - data: Profile[]; -} - -export interface UpdateChannelAction { - type: ActionTypes.UPDATEDCHANNEL; - data: ChannelUpdate[]; -} - -export interface ReplacePublicMessagesAction { - type: ActionTypes.REPLACEPUBLICMESSAGE; - data: PublicMessage; - channelId: string; -} - -export interface ReplaceDirectMessagesAction { - type: ActionTypes.REPLACEDIRECTMESSAGE; - data: DirectMessage; - peer: string; -} - -export interface VerifyPublicMessageSendingAction { - type: ActionTypes.VERIFYPUBLICMESSAGESENDING; - data: PublicMessage; - channelId: string; -} - -export interface VerifyDirectMessageSendingAction { - type: ActionTypes.VERIFYDIRECTMESSAGESENDING; - data: DirectMessage; - peer: string; -} - -export interface DeletePublicMessagesAction { - type: ActionTypes.DELETEPUBLICMESSAGE; - msgId: string; - channelId: string; -} - -export interface DeleteDirectMessagesAction { - type: ActionTypes.DELETEDIRECTMESSAGE; - msgId: string; - peer: string; -} - -export interface AddPreviousPublicMessagesAction { - type: ActionTypes.ADDPREVIOUSPUBLICMESSAGES; - channelId: string; - data: MessagesObject; -} - -export type Actions = - | ResetChatAction - | ProfilesAction - | UpdateChannelAction - | ReplacePublicMessagesAction - | VerifyPublicMessageSendingAction - | ReplaceDirectMessagesAction - | VerifyDirectMessageSendingAction - | DeletePublicMessagesAction - | DeleteDirectMessagesAction - | AddPreviousPublicMessagesAction; diff --git a/src/common/store/index.ts b/src/common/store/index.ts index c22a180a801..770725fc9c7 100644 --- a/src/common/store/index.ts +++ b/src/common/store/index.ts @@ -21,7 +21,6 @@ import signingKey from "./signing-key"; import persistentPageScroll from "./persistent-page-scroll"; import filterTagExtract from "../helper/filter-tag-extract"; -import chat from "./chat"; let reducers = { global, @@ -38,8 +37,7 @@ let reducers = { subscriptions, notifications, signingKey, - persistentPageScroll, - chat + persistentPageScroll }; export let history: History | undefined; diff --git a/src/common/store/initial-state.ts b/src/common/store/initial-state.ts index ad658298a64..f323a59acc8 100644 --- a/src/common/store/initial-state.ts +++ b/src/common/store/initial-state.ts @@ -15,7 +15,6 @@ import { initialState as notificationsInitialState } from "./notifications"; import { initialState as entriesInitialState } from "./entries"; import { initialState as signingKeyInitialState } from "./signing-key"; import { initialState as persistentPageScrollInitialState } from "./persistent-page-scroll"; -import { initialState as chatInitialState } from "./chat"; const initialState: AppState = { global: globalInitialState, @@ -32,8 +31,7 @@ const initialState: AppState = { notifications: notificationsInitialState, entries: entriesInitialState, signingKey: signingKeyInitialState, - persistentPageScroll: persistentPageScrollInitialState, - chat: chatInitialState + persistentPageScroll: persistentPageScrollInitialState }; export default initialState; From d4463f819f2d96069fbc6563e7935fd8eb7469db Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Thu, 9 Nov 2023 14:03:30 +0600 Subject: [PATCH 123/179] save --- .../chats/queries/community-channel-query.ts | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/common/features/chats/queries/community-channel-query.ts b/src/common/features/chats/queries/community-channel-query.ts index 1ea4ad8dac4..10f350540ae 100644 --- a/src/common/features/chats/queries/community-channel-query.ts +++ b/src/common/features/chats/queries/community-channel-query.ts @@ -9,12 +9,18 @@ import { Channel } from "../managers/message-manager-types"; * @see {@link ../mutations/create-community-chat.ts} */ export function useCommunityChannelQuery(community?: Community) { - return useQuery([ChatQueries.COMMUNITY_CHANNEL, community?.name], async () => { - if (!community) { - return undefined; - } + return useQuery( + [ChatQueries.COMMUNITY_CHANNEL, community?.name], + async () => { + if (!community) { + return undefined; + } - const communityProfile = await getProfileMetaData(community.name); - return communityProfile.channel; - }); + const communityProfile = await getProfileMetaData(community.name); + return communityProfile.channel; + }, + { + enabled: !!community + } + ); } From 72c91d1491594260ebce8ce9a901b83ae30ac558 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Thu, 9 Nov 2023 21:19:34 +0600 Subject: [PATCH 124/179] Tailwind migration: fixed message hiding in channel --- .../features/chats/chat-context-provider.tsx | 3 - .../index.tsx => chat-channel-messages.tsx} | 135 +++++--- .../chats/components/chat-message-box.tsx | 1 - .../chats/components/chat-message-item.tsx | 18 +- .../chats/components/chat-messages-header.tsx | 2 +- .../chats/components/chat-messages-view.tsx | 15 +- .../chat-popup/chat-popup-messages-list.tsx | 5 +- .../chats-channel-messages/index.scss | 308 ------------------ .../mutations/hide-message-in-channel.ts | 53 +++ src/common/features/chats/mutations/index.ts | 1 + src/common/features/chats/screens/chats.tsx | 11 +- src/common/features/ui/dropdown/index.tsx | 28 +- src/common/i18n/locales/en-US.json | 4 +- src/common/img/svg.tsx | 2 + yarn.lock | 127 ++++++++ 15 files changed, 325 insertions(+), 388 deletions(-) rename src/common/features/chats/components/{chats-channel-messages/index.tsx => chat-channel-messages.tsx} (53%) delete mode 100644 src/common/features/chats/components/chats-channel-messages/index.scss create mode 100644 src/common/features/chats/mutations/hide-message-in-channel.ts diff --git a/src/common/features/chats/chat-context-provider.tsx b/src/common/features/chats/chat-context-provider.tsx index 4a2b9b55515..d1e5456ac25 100644 --- a/src/common/features/chats/chat-context-provider.tsx +++ b/src/common/features/chats/chat-context-provider.tsx @@ -15,7 +15,6 @@ interface Context { receiverPubKey: string; messageServiceInstance: MessageService | null; hasUserJoinedChat: boolean; - currentChannel: Channel | null; windowWidth: number; isActiveUserRemoved: boolean; setCurrentChannel: (channel: Channel) => void; @@ -38,7 +37,6 @@ export const ChatContext = React.createContext({ receiverPubKey: "", messageServiceInstance: null, hasUserJoinedChat: false, - currentChannel: null, windowWidth: 0, isActiveUserRemoved: false, setCurrentChannel: () => {}, @@ -127,7 +125,6 @@ export const ChatContextProvider = (props: Props) => { chatPrivKey, messageServiceInstance, hasUserJoinedChat, - currentChannel, windowWidth, isActiveUserRemoved, setCurrentChannel, diff --git a/src/common/features/chats/components/chats-channel-messages/index.tsx b/src/common/features/chats/components/chat-channel-messages.tsx similarity index 53% rename from src/common/features/chats/components/chats-channel-messages/index.tsx rename to src/common/features/chats/components/chat-channel-messages.tsx index 4ab376b2606..9ba1f64062f 100644 --- a/src/common/features/chats/components/chats-channel-messages/index.tsx +++ b/src/common/features/chats/components/chat-channel-messages.tsx @@ -1,42 +1,48 @@ -import React, { useContext, useEffect, useState } from "react"; +import React, { useContext, useEffect, useMemo, useState } from "react"; import mediumZoom, { Zoom } from "medium-zoom"; -import { Channel, PublicMessage } from "../../managers/message-manager-types"; -import { History } from "history"; -import { useMappedStore } from "../../../../store/use-mapped-store"; +import { Channel, PublicMessage } from "../managers/message-manager-types"; +import { useMappedStore } from "../../../store/use-mapped-store"; import usePrevious from "react-use/lib/usePrevious"; -import { _t } from "../../../../i18n"; -import ChatsConfirmationModal from "../chats-confirmation-modal"; -import { error } from "../../../../components/feedback"; -import { Theme } from "../../../../store/global/types"; -import { checkContiguousMessage, formatMessageDateAndDay } from "../../utils"; -import { ChatContext } from "../../chat-context-provider"; - -import "./index.scss"; -import { ChatMessageItem } from "../chat-message-item"; -import { useKeysQuery } from "../../queries/keys-query"; +import { _t } from "../../../i18n"; +import ChatsConfirmationModal from "./chats-confirmation-modal"; +import { error } from "../../../components/feedback"; +import { Theme } from "../../../store/global/types"; +import { checkContiguousMessage, formatMessageDateAndDay } from "../utils"; +import { ChatContext } from "../chat-context-provider"; +import { ChatMessageItem } from "./chat-message-item"; +import { useKeysQuery } from "../queries/keys-query"; +import { Dropdown, DropdownItemWithIcon, DropdownMenu } from "@ui/dropdown"; +import { hideSvg, removeUserSvg } from "../../../img/svg"; +import { useHideMessageInChannel } from "../mutations"; +import { Spinner } from "@ui/spinner"; interface Props { publicMessages: PublicMessage[]; currentChannel: Channel; - username: string; - from?: string; - history: History; isScrollToBottom: boolean; isScrolled?: boolean; scrollToBottom?: () => void; } let zoom: Zoom | null = null; -export default function ChatsChannelMessages(props: Props) { - const { publicMessages, isScrollToBottom, isScrolled, currentChannel, scrollToBottom } = props; + +export function ChatsChannelMessages({ + publicMessages, + isScrollToBottom, + isScrolled, + currentChannel, + scrollToBottom +}: Props) { const { global, activeUser } = useMappedStore(); const { messageServiceInstance, isActiveUserRemoved } = useContext(ChatContext); - let prevGlobal = usePrevious(global); const channelMessagesRef = React.createRef(); + // Message where users interacted with context menu + const [currentInteractingMessageId, setCurrentInteractingMessageId] = useState(); + const [step, setStep] = useState(0); const [removedUserId, setRemovedUserID] = useState(""); const [hiddenMsgId, setHiddenMsgId] = useState(""); @@ -44,6 +50,17 @@ export default function ChatsChannelMessages(props: Props) { const { publicKey } = useKeysQuery(); + const { mutateAsync: hideMessage, isLoading: isHideMessageLoading } = + useHideMessageInChannel(currentChannel); + + const messages = useMemo( + () => + publicMessages.filter((m) => + currentChannel.hiddenMessageIds ? !currentChannel.hiddenMessageIds.includes(m.id) : true + ), + [publicMessages, currentChannel] + ); + useEffect(() => { if (prevGlobal?.theme !== global.theme) { setBackground(); @@ -52,13 +69,13 @@ export default function ChatsChannelMessages(props: Props) { }, [global.theme, activeUser]); useEffect(() => { - if (publicMessages.length !== 0) { + if (messages.length !== 0) { zoomInitializer(); } - if (!isScrollToBottom && publicMessages.length !== 0 && !isScrolled) { + if (!isScrollToBottom && messages.length !== 0 && !isScrolled) { scrollToBottom && scrollToBottom(); } - }, [publicMessages, isScrollToBottom, channelMessagesRef]); + }, [messages, isScrollToBottom, channelMessagesRef]); useEffect(() => { if (currentChannel) { @@ -145,31 +162,55 @@ export default function ChatsChannelMessages(props: Props) { return ( <>
- {publicMessages.length !== 0 && - publicMessages.map((pMsg, i) => { - const dayAndMonth = formatMessageDateAndDay(pMsg, i, publicMessages); - - const isSameUserMessage = checkContiguousMessage(pMsg, i, publicMessages); - - return ( - - {dayAndMonth && ( -
- - {dayAndMonth} - -
- )} - - ( + + {formatMessageDateAndDay(message, i, messages) && ( +
+ + {formatMessageDateAndDay(message, i, messages)} + +
+ )} + + + setCurrentInteractingMessageId(v ? currentInteractingMessageId : undefined) + } + closeOnClickOutside={false} + > + { + if (currentChannel.communityName === activeUser?.username) { + setCurrentInteractingMessageId(message.id); + } + }} + /> + + : hideSvg} + label={_t("chat.hide-message")} + onClick={() => + hideMessage({ + hide: + currentChannel.hiddenMessageIds?.some((id) => id === message.id) === false, + messageId: message.id + }) + } /> -
- ); - })} + + + +
+ ))} + {/*TODO: CHeck it in messages query*/} {isActiveUserRemoved && ( You have been blocked from this community diff --git a/src/common/features/chats/components/chat-message-box.tsx b/src/common/features/chats/components/chat-message-box.tsx index fa8d2ddfef4..b97e6acb16f 100644 --- a/src/common/features/chats/components/chat-message-box.tsx +++ b/src/common/features/chats/components/chat-message-box.tsx @@ -77,7 +77,6 @@ export default function ChatsMessagesBox(props: Props) { {inProgress && } void; } // TODO: Add resend -export function ChatMessageItem({ type, message, isSameUser, currentChannel }: Props) { +export function ChatMessageItem({ + type, + message, + isSameUser, + currentChannel, + onContextMenu +}: Props) { const { global } = useMappedStore(); const isFailed = useMemo(() => message.sent === 2, [message]); @@ -43,6 +50,13 @@ export function ChatMessageItem({ type, message, isSameUser, currentChannel }: P failed: isFailed, sending: isSending })} + onContextMenu={(e) => { + if (onContextMenu) { + e.stopPropagation(); + e.preventDefault(); + onContextMenu(); + } + }} > {message.sent === 2 && ( @@ -59,7 +73,7 @@ export function ChatMessageItem({ type, message, isSameUser, currentChannel }: P )}
+
- -

- - ); - }; - - const blockedUsersModal = () => { - return ( - <> -
-

{_t("chat.blocked-users")}

-
- - {blockedUsers.length !== 0 ? ( - <> - - - - - - - - - {blockedUsers && - blockedUsers.map((user, i) => { - return ( - - - - - ); - })} - -
{_t("community.roles-account")}{_t("chat.action")}
- - {" "} - - @{user.name} - - - - -
- - ) : ( -
-

{_t("chat.no-locked-user")}

-
- )} - - ); - }; - - const successModal = (message: string) => { - return ( - <> -
-
2
-
-
{_t("manage-authorities.success-title")}
-
{_t("manage-authorities.success-sub-title")}
-
-
-
-
- {message === UNBLOCKUSER ? "User unblock successfully" : ""} -
-
- - -
-
- - ); - }; - - const handleConfirmButton = (actionType: string) => { - switch (actionType) { - case LEAVECOMMUNITY: - leaveChannel(currentChannel?.id!!); - break; - case UNBLOCKUSER: - handleChannelUpdate(UNBLOCKUSER); - break; - } - }; - - const handleChannelUpdate = (operationType: string) => { - let updatedMetaData = { - name: currentChannel?.name!, - about: currentChannel?.about!, - picture: "", - communityName: currentChannel?.communityName!, - communityModerators: currentChannel?.communityModerators, - hiddenMessageIds: currentChannel?.hiddenMessageIds, - removedUserIds: currentChannel?.removedUserIds - }; - switch (operationType) { - case UNBLOCKUSER: - const NewUpdatedRemovedUsers = currentChannel?.removedUserIds?.filter( - (item) => item !== removedUserId - ); - updatedMetaData.removedUserIds = NewUpdatedRemovedUsers; - break; - default: - break; - } - try { - messageServiceInstance?.updateChannel(currentChannel!, updatedMetaData); - - if (operationType === UNBLOCKUSER) { - setStep(5); - setKeyDialog(true); - setRemovedUserID(""); - } - } catch (err) { - error(_t("chat.error-updating-community")); - } - }; + const { mutateAsync: leaveChannel } = useLeaveCommunityChannel(() => history?.push("/chats")); return ( <> - + +
{inProgress && }
{content}
-

+ + ); + + return ( + + + {confirmationModalContent} + -

- - ); - - return ( - - - {confirmationModalContent} + ); }; diff --git a/src/common/features/chats/components/join-community-chat-btn.tsx b/src/common/features/chats/components/join-community-chat-btn.tsx index 845b247b78f..c953bc689e1 100644 --- a/src/common/features/chats/components/join-community-chat-btn.tsx +++ b/src/common/features/chats/components/join-community-chat-btn.tsx @@ -6,7 +6,12 @@ import { useMappedStore } from "../../../store/use-mapped-store"; import { ChatContext } from "../chat-context-provider"; import { Spinner } from "@ui/spinner"; import { Button } from "@ui/button"; -import { useAddCommunityChannel, useCreateCommunityChat, useJoinChat } from "../mutations"; +import { + useAddCommunityChannel, + useCreateCommunityChat, + useJoinChat, + useLeaveCommunityChannel +} from "../mutations"; import { useChannelsQuery, useCommunityChannelQuery, @@ -28,13 +33,15 @@ export default function JoinCommunityChatBtn(props: Props) { const { data: currentChannel } = useCommunityChannelQuery(props.community); const { data: channels } = useChannelsQuery(); const { data: communityTeam } = useNostrJoinedCommunityTeamQuery(props.community); + const { data: leftChannelsIds } = useLeftCommunityChannelsQuery(); const { mutateAsync: addCommunityChannel, isLoading: isAddCommunityChannelLoading } = useAddCommunityChannel(currentChannel?.id); const { mutateAsync: joinChat, isLoading: isJoinChatLoading } = useJoinChat(); const { mutateAsync: createCommunityChat, isLoading: isCreateCommunityChatLoading } = useCreateCommunityChat(props.community); - const { data: leftChannelsIds } = useLeftCommunityChannelsQuery(); + const { mutateAsync: leaveCommunityChannel, isLoading: isLeavingCommunityChannelLoading } = + useLeaveCommunityChannel(); const isChatEnabled = useMemo(() => !!currentChannel, [currentChannel]); const isCommunityChatJoined = useMemo( @@ -42,9 +49,9 @@ export default function JoinCommunityChatBtn(props: Props) { channels?.some( (item) => item.communityName === props.community.name && - leftChannelsIds?.includes(currentChannel?.id!) + !leftChannelsIds?.includes(currentChannel?.id!) ), - [channels] + [channels, leftChannelsIds] ); const join = async () => { @@ -58,7 +65,7 @@ export default function JoinCommunityChatBtn(props: Props) { <> {props.community.name === activeUser?.username ? ( isCommunityChatJoined ? ( - + ) : !isChatEnabled ? ( ) : isCommunityChatJoined ? ( - + ) : ( <> )} diff --git a/src/common/features/chats/mutations/add-community-channel.ts b/src/common/features/chats/mutations/add-community-channel.ts index 96d38331cac..c7898becae9 100644 --- a/src/common/features/chats/mutations/add-community-channel.ts +++ b/src/common/features/chats/mutations/add-community-channel.ts @@ -20,13 +20,8 @@ export function useAddCommunityChannel(name: string | undefined) { ids: name ? [name] : undefined }, { - kinds: [Kind.ChannelMetadata], + kinds: [Kind.ChannelMetadata, Kind.EventDeletion], "#e": name ? [name] : undefined - }, - { - kinds: [Kind.ChannelMessage], - "#e": name ? [name] : undefined, - limit: 30 } ], { @@ -38,7 +33,6 @@ export function useAddCommunityChannel(name: string | undefined) { const hasChannelAlready = channels?.some(({ id }) => id === channel?.id); if (!hasChannelAlready && channel) { queryClient.setQueryData([ChatQueries.CHANNELS], [...(channels ?? []), channel]); - queryClient.invalidateQueries([ChatQueries.CHANNELS]); } // Remove the community from left list diff --git a/src/common/features/chats/mutations/import-chat-by-key.ts b/src/common/features/chats/mutations/import-chat-by-key.ts index 82c7237bfe8..86a7e903e3c 100644 --- a/src/common/features/chats/mutations/import-chat-by-key.ts +++ b/src/common/features/chats/mutations/import-chat-by-key.ts @@ -29,8 +29,8 @@ export function useImportChatByKey(onSuccess?: () => void) { } localStorage.setItem(PREFIX + "_nostr_pr_" + activeUser?.username, key); - queryClient.invalidateQueries([ChatQueries.PUBLIC_KEY]); - queryClient.invalidateQueries([ChatQueries.PRIVATE_KEY]); + await queryClient.invalidateQueries([ChatQueries.PUBLIC_KEY]); + await queryClient.invalidateQueries([ChatQueries.PRIVATE_KEY]); }, { onSuccess diff --git a/src/common/features/chats/mutations/join-chat.ts b/src/common/features/chats/mutations/join-chat.ts index 216db2bfd62..a87c5396bcb 100644 --- a/src/common/features/chats/mutations/join-chat.ts +++ b/src/common/features/chats/mutations/join-chat.ts @@ -1,7 +1,6 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; import { createNoStrAccount, uploadChatPublicKey } from "../utils"; -import * as ls from "../../../util/local-storage"; -import { setNostrkeys } from "../managers/message-manager"; +import { PREFIX } from "../../../util/local-storage"; import { useMappedStore } from "../../../store/use-mapped-store"; import { ChatQueries } from "../queries"; import { useNostrPublishMutation } from "../nostr"; @@ -32,10 +31,9 @@ export function useJoinChat(onSuccess?: () => void) { return useMutation(["chat-join-chat"], async () => createNoStrAccount(), { onSuccess: async (keys: ReturnType) => { - ls.set(`${activeUser?.username}_nsPrivKey`, keys.priv); + localStorage.setItem(PREFIX + "_nostr_pr_" + activeUser?.username, keys.priv); await uploadPublicKey(keys.pub); - setNostrkeys(keys); queryClient.setQueryData([ChatQueries.PUBLIC_KEY], keys.pub); queryClient.setQueryData([ChatQueries.PRIVATE_KEY], keys.priv); diff --git a/src/common/features/chats/nostr/queries/get-user-profile-query.ts b/src/common/features/chats/nostr/queries/get-user-profile-query.ts index 70c2fa931da..8927ef58db6 100644 --- a/src/common/features/chats/nostr/queries/get-user-profile-query.ts +++ b/src/common/features/chats/nostr/queries/get-user-profile-query.ts @@ -15,3 +15,18 @@ export function useNostrGetUserProfileQuery(user: string) { events.map((event) => convertEvent(event)!!).filter((profile) => profile!!) ); } + +export function useNostrGetUserProfilesQuery(users: string[]) { + return useNostrFetchQuery( + ["chats/nostr-get-user-profile", users.join("")], + users.map((user) => ({ + kinds: [Kind.Metadata], + authors: [user] + })), + (events) => + events.map((event) => convertEvent(event)!!).filter((profile) => profile!!), + { + initialData: [] + } + ); +} diff --git a/src/common/features/chats/queries/messages-query.ts b/src/common/features/chats/queries/messages-query.ts index cfba7602779..083d49f7e68 100644 --- a/src/common/features/chats/queries/messages-query.ts +++ b/src/common/features/chats/queries/messages-query.ts @@ -35,6 +35,8 @@ export function useMessagesQuery(username?: string) { const existingMessages = queryClient.getQueryData([ChatQueries.MESSAGES, username]) ?? []; + await queryClient.invalidateQueries([ChatQueries.LAST_MESSAGES]); + if (existingMessages.length > 0) { return existingMessages; } diff --git a/src/common/features/ui/dropdown/dropdown-item.tsx b/src/common/features/ui/dropdown/dropdown-item.tsx index 69d55e5facf..e23706328b5 100644 --- a/src/common/features/ui/dropdown/dropdown-item.tsx +++ b/src/common/features/ui/dropdown/dropdown-item.tsx @@ -26,7 +26,7 @@ export function DropdownItemWithIcon( ) { return ( -
+
{props.icon}
{props.label}
diff --git a/src/common/features/ui/dropdown/index.tsx b/src/common/features/ui/dropdown/index.tsx index 56a907eac53..92dd813b89f 100644 --- a/src/common/features/ui/dropdown/index.tsx +++ b/src/common/features/ui/dropdown/index.tsx @@ -21,6 +21,7 @@ export function Dropdown(props: HTMLProps & Props) { useClickAway(ref, () => { if (props.closeOnClickOutside ?? true) { setShow(false); + // props.setShow?.(false); } }); const nativeProps = useFilteredProps(props, ["show", "setShow", "closeOnClickOutside"]); diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 6301a3a70fe..5aad30c20b4 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -80,7 +80,8 @@ "ok": "OK", "join": "Join", "resend": "Resend", - "failed": "Failed" + "failed": "Failed", + "status": "Status" }, "confirm": { "title": "Are you sure?", From f0f2480538d99f661aa907ced8473eb2388b7c55 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Sat, 11 Nov 2023 02:44:19 +0600 Subject: [PATCH 127/179] Chats: added infinite message listener --- .../features/chats/chat-context-provider.tsx | 2 + .../chats-confirmation-modal/index.tsx | 2 - .../features/chats/mutations/send-message.ts | 4 -- src/common/features/chats/nostr/core/index.ts | 1 - .../chats/nostr/core/nostr-fetch-query.tsx | 9 +--- .../chats/nostr/core/nostr-listener-query.tsx | 46 ------------------- .../nostr/queries/direct-messages-query.ts | 3 +- .../nostr/queries/public-messages-query.ts | 3 +- .../chats/nostr/utils/event-converter.ts | 5 +- .../chats/queries/direct-contacts-query.ts | 9 +--- .../chats/queries/last-messages-query.ts | 18 +++++--- .../chats/queries/listen-messages-query.ts | 32 +++++++++++++ .../features/chats/queries/messages-query.ts | 44 +++++++++--------- 13 files changed, 78 insertions(+), 100 deletions(-) delete mode 100644 src/common/features/chats/nostr/core/nostr-listener-query.tsx create mode 100644 src/common/features/chats/queries/listen-messages-query.ts diff --git a/src/common/features/chats/chat-context-provider.tsx b/src/common/features/chats/chat-context-provider.tsx index a4d65449815..1310876a89a 100644 --- a/src/common/features/chats/chat-context-provider.tsx +++ b/src/common/features/chats/chat-context-provider.tsx @@ -6,6 +6,7 @@ import { useMount } from "react-use"; import { useKeysQuery } from "./queries/keys-query"; import { useJoinChat } from "./mutations"; import { NostrListenerQueriesProvider, NostrProvider } from "./nostr"; +import { useListenMessagesQuery } from "./queries/listen-messages-query"; interface Context { showSpinner: boolean; @@ -61,6 +62,7 @@ export const ChatContextProvider = (props: Props) => { useJoinChat(() => { setHasUserJoinedChat(true); }); + useListenMessagesQuery(); useDebounce(() => setShowSpinner(false), 5000, [showSpinner]); diff --git a/src/common/features/chats/components/chats-confirmation-modal/index.tsx b/src/common/features/chats/components/chats-confirmation-modal/index.tsx index 373a094c6d5..9dfb84cc85f 100644 --- a/src/common/features/chats/components/chats-confirmation-modal/index.tsx +++ b/src/common/features/chats/components/chats-confirmation-modal/index.tsx @@ -50,7 +50,6 @@ const ChatsConfirmationModal = (props: Props) => {
- {chatPrivKey && ( + {!!privateKey && (
setRevealPrivKey(!revealPrivKey)} + onManageChatKey={() => setRevealPrivateKey(!revealPrivateKey)} history={history} />
diff --git a/src/common/features/chats/components/chats-sidebar/index.tsx b/src/common/features/chats/components/chats-sidebar/index.tsx index 2708c83f7ef..5e3bd1c346e 100644 --- a/src/common/features/chats/components/chats-sidebar/index.tsx +++ b/src/common/features/chats/components/chats-sidebar/index.tsx @@ -20,7 +20,7 @@ interface Props { export default function ChatsSideBar(props: Props) { const { username } = props; - const { setRevealPrivKey, setReceiverPubKey } = useContext(ChatContext); + const { setRevealPrivateKey, setReceiverPubKey } = useContext(ChatContext); const { data: directContacts } = useDirectContactsQuery(); const { data: channels } = useChannelsQuery(); @@ -90,7 +90,7 @@ export default function ChatsSideBar(props: Props) { user={user} onClick={() => { setSearchQuery(""); - setRevealPrivKey(false); + setRevealPrivateKey(false); getReceiverPubKey(user.account); }} key={user.account} diff --git a/src/common/features/chats/components/manage-chat-key.tsx b/src/common/features/chats/components/manage-chat-key.tsx index 30c0e8c37b4..2fa5499762a 100644 --- a/src/common/features/chats/components/manage-chat-key.tsx +++ b/src/common/features/chats/components/manage-chat-key.tsx @@ -1,19 +1,21 @@ -import React, { useContext, useEffect, useRef, useState } from "react"; +import React, { useEffect, useRef, useState } from "react"; import { _t } from "../../../i18n"; -import { ChatContext } from "../chat-context-provider"; import { InputGroupCopyClipboard } from "@ui/input"; import qrcode from "qrcode"; import { classNameObject } from "../../../helper/class-name-object"; +import { useKeysQuery } from "../queries/keys-query"; export default function ManageChatKey() { - const { chatPrivKey } = useContext(ChatContext); + const { privateKey } = useKeysQuery(); const qrImgRef = useRef(null); const [isQrShow, setIsQrShow] = useState(false); useEffect(() => { - compileQR(chatPrivKey); - }, [chatPrivKey]); + if (privateKey) { + compileQR(privateKey); + } + }, [privateKey]); const compileQR = async (key: string) => { if (qrImgRef.current) { @@ -25,7 +27,7 @@ export default function ManageChatKey() { return (
{_t("chat.chat-priv-key")}
- + { - const { data: channels } = useChannelsQuery(); - - const [since, setSince] = useState(0); - - useEffect(() => { - if (!messageServiceReady) return; - - const timer = setTimeout( - () => { - if (!channels || channels.length === 0) { - return; - } - - // TODO THIS THING - messageService?.listen( - channels?.map((x) => x.id), - Math.floor((since || Date.now()) / 1000) - ); - setSince(Date.now()); - }, - since === 0 ? 500 : 10000 - ); - - return () => { - clearTimeout(timer); - }; - }, [since, messageServiceReady, messageService, channels]); -}; diff --git a/src/common/features/chats/mutations/send-message.ts b/src/common/features/chats/mutations/send-message.ts index 376df8e4aa6..3d71b08104d 100644 --- a/src/common/features/chats/mutations/send-message.ts +++ b/src/common/features/chats/mutations/send-message.ts @@ -15,7 +15,7 @@ export function useSendMessage( ) { const queryClient = useQueryClient(); - const { isActiveUserRemoved, receiverPubKey } = useContext(ChatContext); + const { receiverPubKey } = useContext(ChatContext); const { privateKey } = useKeysQuery(); const { data: messages } = useMessagesQuery(currentChannel?.communityName ?? currentUser); @@ -37,10 +37,6 @@ export function useSendMessage( throw new Error("[Chat][SendMessage] – empty message or has uploading file"); } - if (isActiveUserRemoved) { - throw new Error("[Chat][SendMessage] – no active user"); - } - if (!currentChannel && isCommunity(currentUser)) { throw new Error("[Chat][SendMessage] – provided user is community but channel not found"); } diff --git a/src/common/features/chats/screens/chats.tsx b/src/common/features/chats/screens/chats.tsx index 7435ec27f64..1bde943892d 100644 --- a/src/common/features/chats/screens/chats.tsx +++ b/src/common/features/chats/screens/chats.tsx @@ -8,7 +8,6 @@ import ManageChatKey from "../components/manage-chat-key"; import Feedback from "../../../components/feedback"; import { useMappedStore } from "../../../store/use-mapped-store"; import { ChatContext } from "../chat-context-provider"; -import { Spinner } from "@ui/spinner"; import ChatsMessagesBox from "../components/chat-message-box"; import JoinChat from "../components/join-chat"; import { classNameObject } from "../../../helper/class-name-object"; @@ -30,7 +29,7 @@ interface Props extends PageProps { export const Chats = (props: Props) => { const { activeUser, global } = useMappedStore(); - const { receiverPubKey, showSpinner, revealPrivKey } = useContext(ChatContext); + const { receiverPubKey, revealPrivateKey } = useContext(ChatContext); const { publicKey, privateKey } = useKeysQuery(); const { data: channels } = useChannelsQuery(); @@ -50,16 +49,16 @@ export const Chats = (props: Props) => { () => !!(activeUser && publicKey && privateKey), [publicKey, privateKey, activeUser] ); - const isShowManageKey = useMemo(() => isReady && revealPrivKey, [isReady, revealPrivKey]); + const isShowManageKey = useMemo(() => isReady && revealPrivateKey, [isReady, revealPrivateKey]); const isShowChatRoom = useMemo( - () => isReady && !showSpinner && (!!receiverPubKey || isChannel) && !revealPrivKey, - [isReady, showSpinner, receiverPubKey, revealPrivKey, isChannel] + () => isReady && (!!receiverPubKey || isChannel) && !revealPrivateKey, + [isReady, receiverPubKey, revealPrivateKey, isChannel] ); const isShowDefaultScreen = useMemo( - () => isReady && !receiverPubKey && !isChannel && !revealPrivKey && !showSpinner, - [isReady, receiverPubKey, revealPrivKey, showSpinner, isChannel] + () => isReady && !receiverPubKey && !isChannel && !revealPrivateKey, + [isReady, receiverPubKey, revealPrivateKey, isChannel] ); - const isShowImportChats = useMemo(() => !isReady && !showSpinner, [isReady, showSpinner]); + const isShowImportChats = useMemo(() => !isReady, [isReady]); const { match, history } = props; @@ -96,11 +95,6 @@ export const Chats = (props: Props) => {
)} - {showSpinner && ( -
- -
- )} {isShowImportChats && activeUser && (
@@ -115,7 +109,7 @@ export const Chats = (props: Props) => {
Search a person or community and start messaging
)} - {!isReady && !isShowImportChats && !showSpinner && ( + {!isReady && !isShowImportChats && (
diff --git a/src/common/helper/message-service-global.ts b/src/common/helper/message-service-global.ts deleted file mode 100644 index 7d1a6791ac4..00000000000 --- a/src/common/helper/message-service-global.ts +++ /dev/null @@ -1,16 +0,0 @@ -import MessageService from "./message-service"; -import { Event } from "../../lib/nostr-tools/event"; - -declare global { - interface Window { - messageService?: MessageService; - nostr?: { - getPublicKey: () => Promise; - signEvent: (event: Event) => Promise; - nip04: { - encrypt: (pubkey: string, content: string) => Promise; - decrypt: (pubkey: string, content: string) => Promise; - }; - }; - } -} diff --git a/src/common/helper/message-service.ts b/src/common/helper/message-service.ts deleted file mode 100644 index 88cd0e37dfe..00000000000 --- a/src/common/helper/message-service.ts +++ /dev/null @@ -1,833 +0,0 @@ -import { Sub } from "../../lib/nostr-tools/relay"; -import { Event, getEventHash, Kind, signEvent } from "../../lib/nostr-tools/event"; -import { Filter } from "../../lib/nostr-tools/filter"; -import { TypedEventEmitter } from "./message-event-emitter"; -import { - Channel, - ChannelUpdate, - DirectContact, - DirectMessage, - Metadata, - Profile, - PublicMessage -} from "../features/chats/managers/message-manager-types"; -import { decrypt, encrypt } from "../../lib/nostr-tools/nip04"; -import SimplePool from "../../lib/nostr-tools/pool"; - -const relays = { - "wss://relay1.nostrchat.io": { read: true, write: true }, - "wss://relay2.nostrchat.io": { read: true, write: true }, - "wss://relay.damus.io": { read: true, write: true }, - "wss://relay.snort.social": { read: true, write: true }, - "wss://nos.lol": { read: true, write: true } -}; -const MESSAGE_PAGE_SIZE = 30; - -enum NewKinds { - MuteList = 10000, - Arbitrary = 30078 -} - -export enum MessageEvents { - Ready = "ready", - ProfileUpdate = "profile_update", - LeftChannelList = "left_channel_list", - ChannelUpdate = "channel_update", - ChannelCreation = "channel_creation", - DirectMessageBeforeSent = "direct_message_before_sent", - DirectMessageAfterSent = "direct_message_after_sent", - DirectContact = "direct_contact", - PublicMessageBeforeSent = "public_message_before_sent", - PublicMessageAfterSent = "public_message_after_sent", - PreviousPublicMessages = "previous_public_messages" -} - -type EventHandlerMap = { - [MessageEvents.Ready]: () => void; - [MessageEvents.ProfileUpdate]: (data: Profile[]) => void; - [MessageEvents.DirectMessageBeforeSent]: (data: DirectMessage[]) => void; - [MessageEvents.DirectMessageAfterSent]: (data: DirectMessage[]) => void; - [MessageEvents.ChannelCreation]: (data: Channel[]) => void; - [MessageEvents.ChannelUpdate]: (data: ChannelUpdate[]) => void; - [MessageEvents.PublicMessageBeforeSent]: (data: PublicMessage[]) => void; - [MessageEvents.PublicMessageAfterSent]: (data: PublicMessage[]) => void; - [MessageEvents.PreviousPublicMessages]: (data: PublicMessage[]) => void; - [MessageEvents.LeftChannelList]: (data: string[]) => void; - [MessageEvents.DirectContact]: (data: DirectContact[]) => void; -}; - -class MessageService extends TypedEventEmitter { - public directContacts: any = []; - listenerSub: Sub | null = null; - messageListenerSub: Sub | null = null; - private pool: SimplePool; - private poolL: SimplePool; - private readonly priv: string | "nip07"; - private readonly pub: string; - private readonly readRelays = Object.keys(relays).filter((r) => relays[r].read); - private readonly writeRelays = Object.keys(relays).filter((r) => relays[r].write); - private eventQueue: Event[] = []; - private eventQueueTimer: any; - private eventQueueFlag = true; - private eventQueueBuffer: Event[] = []; - private nameCache: Record = {}; - - constructor(priv: string, pub: string) { - super(); - - this.priv = priv; - this.pub = pub; - - this.pool = new SimplePool(); - this.poolL = new SimplePool({ eoseSubTimeout: 10000 }); - - this.init().then(); - } - - static normalizeMetadata(meta: Metadata) { - return { - name: meta.name || "", - about: meta.about || "", - picture: meta.picture || "" - }; - } - - static parseJson(d: string) { - try { - return JSON.parse(d); - } catch (e) { - return null; - } - } - - static isSha256 = (s: string) => /^[a-f0-9]{64}$/gi.test(s); - - static notEmpty(value: TValue | null | undefined): value is TValue { - return value !== null && value !== undefined; - } - - static findTagValue(ev: Event, tag: "e" | "p" | "d") { - return ev.tags.find(([t]) => t === tag)?.[1]; - } - - static filterTagValue(ev: Event, tag: "e" | "p" | "d") { - return ev.tags.filter(([t]) => t === tag); - } - - public fetchPrevMessages(channel: string, until: number) { - return this.fetchP( - [ - { - kinds: [Kind.ChannelMessage], - "#e": [channel], - until, - limit: MESSAGE_PAGE_SIZE - } - ], - false - ).then((events) => { - if (events.length > 0) { - const formattedEvents = events - .map((ev) => { - const eTags = MessageService.filterTagValue(ev, "e"); - const root = eTags.find((x) => x[3] === "root")?.[1]; - const mentions = MessageService.filterTagValue(ev, "p") - .map((x) => x?.[1]) - .filter(MessageService.notEmpty); - if (!root) return null; - return ev.content - ? { - id: ev.id, - root, - content: ev.content, - creator: ev.pubkey, - mentions, - created: ev.created_at, - sent: 1 - } - : null; - }) - .filter(MessageService.notEmpty); - this.emit(MessageEvents.PreviousPublicMessages, formattedEvents); - } - - return events.length; - }); - } - - public fetchChannels(channels: string[]) { - const filters: Filter[] = [ - { - kinds: [Kind.ChannelCreation], - ids: channels - }, - ...channels.map((c) => ({ - kinds: [Kind.ChannelMetadata, Kind.EventDeletion], - "#e": [c] - })), - ...channels.map((c) => ({ - kinds: [Kind.ChannelMessage], - "#e": [c], - limit: MESSAGE_PAGE_SIZE - })) - ]; - - filters.forEach((c) => { - this.fetch([c]); - }); - } - - public fetchChannel(id: string) { - const filters: Filter[] = [ - { - kinds: [Kind.ChannelCreation], - ids: [id] - }, - { - kinds: [Kind.ChannelMetadata, Kind.EventDeletion], - "#e": [id] - } - ]; - - this.fetch(filters); - } - - public async updateLeftChannelList(channelIds: string[]) { - const tags = [["d", "left-channel-list"]]; - return this.publish(NewKinds.Arbitrary, tags, JSON.stringify(channelIds)); - } - - public fetchMessages() { - this.directContacts.map((contact: any) => { - const filters: Filter[] = [ - { - kinds: [Kind.EncryptedDirectMessage], - "#p": [contact[0]], - authors: [this.pub] - }, - { - kinds: [Kind.EncryptedDirectMessage], - "#p": [this.pub], - authors: [contact[0]] - } - ]; - this.fetch(filters); - }); - } - - public publishContacts(username: string, pubkey: string) { - const newUser = [pubkey, username]; - newUser.forEach((element) => { - let nameExist = false; - - // Check if the name exists in the first array - this.directContacts.forEach((existingElement: string[]) => { - if (existingElement[0] === element || existingElement[1] === element) { - nameExist = true; - return; - } - }); - - // If the name doesn't exist, add the element to the direct contacts array - if (!nameExist) { - this.directContacts.push(newUser); - this.publish(Kind.Contacts, this.directContacts, ""); - return; - } - }); - } - - public getContacts() { - const filters: Filter[] = [ - { - kinds: [Kind.Contacts], - authors: [this.pub] - } - ]; - this.fetch(filters); - } - - public loadChannel(id: string) { - const filters: Filter[] = [ - { - kinds: [Kind.ChannelCreation], - ids: [id] - }, - { - kinds: [Kind.ChannelMetadata, Kind.EventDeletion], - "#e": [id] - }, - { - kinds: [Kind.ChannelMessage], - "#e": [id], - limit: MESSAGE_PAGE_SIZE - } - ]; - - this.fetch(filters); - } - - public loadProfiles(pubs: string[]) { - pubs.forEach((p) => { - this.fetch( - [ - { - kinds: [Kind.Metadata], - authors: [p] - } - ], - true, - false - ); - }); - } - - public checkProfiles(pubs: string[]) { - pubs.forEach((p) => { - if (this.directContacts.length !== 0) { - this.directContacts.forEach((c: any) => { - if (p !== c[0]) { - this.fetch( - [ - { - kinds: [Kind.Metadata], - authors: [p] - } - ], - true, - true - ); - } - }); - } else { - this.fetch( - [ - { - kinds: [Kind.Metadata], - authors: [p] - } - ], - true, - true - ); - } - }); - } - - public listen(channels: string[], since: number) { - if (this.listenerSub) { - this.listenerSub.unsub(); - } - - this.listenerSub = this.fetch( - [ - { - authors: [this.pub], - since - }, - { - kinds: [Kind.EventDeletion, Kind.ChannelMetadata, Kind.ChannelMessage], - "#e": channels, - since - }, - { - kinds: [Kind.EncryptedDirectMessage], - "#p": [this.pub], - since - } - ], - false - ); - } - - public async createChannel(meta: Metadata) { - return this.publish(Kind.ChannelCreation, [], JSON.stringify(meta)); - } - - public async updateChannel(channel: Channel, meta: Metadata) { - return this.findHealthyRelay(this.pool.seenOn(channel.id) as string[]).then((relay) => { - return this.publish(Kind.ChannelMetadata, [["e", channel.id, relay]], JSON.stringify(meta)); - }); - } - - public emitPublicMessageBeforeSent(event: Event) { - let publiMessagesEventArray = []; - - const eTags = MessageService.filterTagValue(event, "e"); - const root = eTags.find((x) => x[3] === "root")?.[1]; - const mentions = MessageService.filterTagValue(event, "p") - .map((x) => x?.[1]) - .filter(MessageService.notEmpty); - const formattedEvent = event.content - ? { - id: event.id, - root, - content: event.content, - creator: event.pubkey, - mentions, - created: event.created_at, - sent: 0 - } - : null; - publiMessagesEventArray.push(formattedEvent); - const filteredEventArray = publiMessagesEventArray.filter( - (item) => item !== null - ) as PublicMessage[]; - this.emit(MessageEvents.PublicMessageBeforeSent, filteredEventArray); - } - - public async emitDirectMessageBeforeSent(event: Event) { - const eventArray = []; - const receiver = MessageService.findTagValue(event, "p"); - - if (!receiver) { - return; - } - - const eTags = MessageService.filterTagValue(event, "e"); - const root = eTags.find((x) => x[3] === "root")?.[1]; - - const peer = receiver === this.pub ? event.pubkey : receiver; - const msg = { - id: event.id, - root, - content: event.content, - peer, - creator: event.pubkey, - created: event.created_at, - decrypted: false, - sent: 0 - }; - if (this.priv === "nip07") { - eventArray.push(msg); - } - - try { - const content = await decrypt(this.priv, peer, event.content); - const decrypted = { - ...msg, - content, - decrypted: true - }; - eventArray.push(decrypted); - } catch (error) { - console.error("Error decrypting:", error); - } - this.emit(MessageEvents.DirectMessageBeforeSent, eventArray); - } - - public async updateProfile(profile: Metadata) { - return this.publish(Kind.Metadata, [], JSON.stringify(profile)); - } - - public async sendDirectMessage(toPubkey: string, message: string, parent?: string) { - const encrypted = await (this.priv === "nip07" - ? window.nostr!.nip04.encrypt(toPubkey, message) - : encrypt(this.priv, toPubkey, message)); - const tags = [["p", toPubkey]]; - if (parent) { - const relay = await this.findHealthyRelay(this.pool.seenOn(parent) as string[]); - tags.push(["e", parent, relay, "root"]); - } - return this.publish(Kind.EncryptedDirectMessage, tags, encrypted); - } - - public async sendPublicMessage( - channel: Channel, - message: string, - mentions?: string[], - parent?: string - ) { - const root = parent || channel.id; - const relay = await this.findHealthyRelay(this.pool.seenOn(root) as string[]); - const tags = [["e", root, relay, "root"]]; - if (mentions) { - mentions.forEach((m) => tags.push(["p", m])); - } - return this.publish(Kind.ChannelMessage, tags, message); - } - - pushToEventBuffer(event: Event) { - const cacheKey = `${event.id}_emitted`; - if (this.nameCache[cacheKey] === undefined) { - if (this.eventQueueFlag) { - if (this.eventQueueBuffer.length > 0) { - this.eventQueue.push(...this.eventQueueBuffer); - this.eventQueueBuffer = []; - } - clearTimeout(this.eventQueueTimer); - this.eventQueue.push(event); - this.eventQueueTimer = setTimeout(() => { - this.processEventQueue().then(); - }, 50); - } else { - this.eventQueueBuffer.push(event); - } - - this.nameCache[cacheKey] = 1; - } - } - - async processEventQueue() { - this.eventQueueFlag = false; - const directContacts = this.eventQueue - .filter((x) => x.kind === Kind.Contacts) - .map((e: any) => { - const profiles: Array<[string, string]> = e.tags; - return profiles.map(([pubkey, name]) => ({ pubkey, name })); - }) - .filter(MessageService.notEmpty); - - if (directContacts.length > 0) { - const directContactsProfile: Array<{ pubkey: string; name: string }> = directContacts[0]; - - if (directContactsProfile.length > 0) { - this.emit(MessageEvents.DirectContact, directContactsProfile); - } - } - - const profileUpdates: Profile[] = this.eventQueue - .filter((x) => x.kind === Kind.Metadata) - .map((ev) => { - const content = MessageService.parseJson(ev.content); - return content - ? { - id: ev.id, - creator: ev.pubkey, - created: ev.created_at, - ...MessageService.normalizeMetadata(content) - } - : null; - }) - .filter(MessageService.notEmpty); - if (profileUpdates.length > 0) { - this.emit(MessageEvents.ProfileUpdate, profileUpdates); - } - - const leftChannelListEv = this.eventQueue - .filter( - (x) => - x.kind.toString() === NewKinds.Arbitrary.toString() && - MessageService.findTagValue(x, "d") === "left-channel-list" - ) - .sort((a, b) => b.created_at - a.created_at)[0]; - - if (leftChannelListEv) { - const content = MessageService.parseJson(leftChannelListEv.content); - if (Array.isArray(content) && content.every((x) => MessageService.isSha256(x))) { - this.emit(MessageEvents.LeftChannelList, content); - } - } - - const channelCreations: Channel[] = this.eventQueue - .filter((x) => x.kind === Kind.ChannelCreation) - .map((ev) => { - const content = MessageService.parseJson(ev.content); - return content - ? { - id: ev.id, - creator: ev.pubkey, - created: ev.created_at, - communityName: content.communityName, - communityModerators: content.communityModerators, - hiddenMessageIds: content.hiddenMessageIds, - removedUserIds: content.removedUserIds, - ...MessageService.normalizeMetadata(content) - } - : null; - }) - .filter(MessageService.notEmpty); - if (channelCreations.length > 0) { - this.emit(MessageEvents.ChannelCreation, channelCreations); - } - - const channelUpdates: ChannelUpdate[] = this.eventQueue - .filter((x) => x.kind === Kind.ChannelMetadata) - .map((ev) => { - const content = MessageService.parseJson(ev.content); - const channelId = MessageService.findTagValue(ev, "e"); - if (!channelId) return null; - return content - ? { - id: ev.id, - creator: ev.pubkey, - created: ev.created_at, - communityName: content.communityName, - communityModerators: content.communityModerators, - hiddenMessageIds: content.hiddenMessageIds, - removedUserIds: content.removedUserIds, - channelId, - ...MessageService.normalizeMetadata(content) - } - : null; - }) - .filter(MessageService.notEmpty); - if (channelUpdates.length > 0) { - this.emit(MessageEvents.ChannelUpdate, channelUpdates); - } - - const publicMessages: PublicMessage[] = this.eventQueue - .filter((x) => x.kind === Kind.ChannelMessage) - .map((ev) => { - const eTags = MessageService.filterTagValue(ev, "e"); - const root = eTags.find((x) => x[3] === "root")?.[1]; - const mentions = MessageService.filterTagValue(ev, "p") - .map((x) => x?.[1]) - .filter(MessageService.notEmpty); - if (!root) return null; - return ev.content - ? { - id: ev.id, - root, - content: ev.content, - creator: ev.pubkey, - mentions, - created: ev.created_at, - sent: 1 - } - : null; - }) - .filter(MessageService.notEmpty); - if (publicMessages.length > 0) { - this.emit(MessageEvents.PublicMessageAfterSent, publicMessages); - } - - Promise.all( - this.eventQueue - .filter((x) => x.kind === Kind.EncryptedDirectMessage) - .map((ev) => { - const receiver = MessageService.findTagValue(ev, "p"); - if (!receiver) return null; - const eTags = MessageService.filterTagValue(ev, "e"); - const root = eTags.find((x) => x[3] === "root")?.[1]; - - const peer = receiver === this.pub ? ev.pubkey : receiver; - const msg = { - id: ev.id, - root, - content: ev.content, - peer, - creator: ev.pubkey, - created: ev.created_at, - decrypted: false, - sent: 1 - }; - - if (this.priv === "nip07") { - return msg; - } - - return decrypt(this.priv, peer, ev.content).then((content) => { - return { - ...msg, - content, - decrypted: true - }; - }); - }) - .filter(MessageService.notEmpty) - ).then((directMessages: DirectMessage[]) => { - if (directMessages.length > 0) { - this.emit(MessageEvents.DirectMessageAfterSent, directMessages); - } - }); - - this.eventQueue = []; - this.eventQueueFlag = true; - } - - public close = () => { - this.pool.close(this.readRelays); - this.removeAllListeners(); - }; - - private async init() { - this.eventQueue = []; - this.eventQueueFlag = true; - this.eventQueueBuffer = []; - - const filters: Filter[] = [ - { - kinds: [Kind.Contacts], - authors: [this.pub] - }, - { - kinds: [Kind.ChannelCreation, Kind.EventDeletion], - authors: [this.pub] - }, - { - kinds: [Kind.ChannelMessage], - authors: [this.pub] - }, - { - kinds: [NewKinds.Arbitrary], - authors: [this.pub], - "#d": ["left-channel-list", "read-mark-map"] - } - ]; - this.fetchP(filters).then((resp) => { - const events = resp.sort((a, b) => b.created_at - a.created_at); - const deletions = resp - .filter((x) => x.kind === Kind.EventDeletion) - .map((x) => MessageService.findTagValue(x, "e")); - const profile = events.find((x) => x.kind === Kind.Contacts); - if (profile && profile?.tags.length !== 0) { - this.directContacts = profile?.tags; - this.pushToEventBuffer(profile!); - } - - const leftChannelList = events.find( - (x) => - x.kind.toString() === NewKinds.Arbitrary.toString() && - MessageService.findTagValue(x, "d") === "left-channel-list" - ); - - if (leftChannelList) { - this.pushToEventBuffer(leftChannelList); - } - - const channels = Array.from( - new Set( - events - .map((x) => { - if (x.kind === Kind.ChannelCreation) { - return x.id; - } - - if (x.kind === Kind.ChannelMessage) { - return MessageService.findTagValue(x, "e"); - } - - return undefined; - }) - .filter((x) => !deletions.includes(x)) - .filter(MessageService.notEmpty) - ) - ); - if (channels.length !== 0) { - this.fetchChannels(channels); - } - - this.fetchMessages(); - this.emit(MessageEvents.Ready); - }); - } - - private addDirectContact(event: Event) { - let isAdded = false; - const user = { - id: event.id, - creator: event.pubkey, - created: event.created_at, - name: JSON.parse(event.content).name, - about: JSON.parse(event.content).about || "", - picture: JSON.parse(event.content).picture || "" - }; - const { creator, name } = user; - if (!isAdded) { - this.publishContacts(name, creator); - isAdded = true; - } - } - - private fetch(filters: Filter[], unsub: boolean = true, isDirectContact: boolean = false) { - const sub = this.pool.sub(this.readRelays, filters); - - sub.on("event", (event: Event) => { - event.kind === Kind.Metadata && isDirectContact - ? this.addDirectContact(event) - : this.pushToEventBuffer(event); - }); - - sub.on("eose", () => { - if (unsub) { - sub.unsub(); - } - }); - - return sub; - } - - private fetchP(filters: Filter[], lowLatencyPool: boolean = false): Promise { - return new Promise((resolve) => { - const sub = (lowLatencyPool ? this.poolL : this.pool).sub(this.readRelays, filters); - const events: Event[] = []; - - sub.on("event", (event: Event) => { - events.push(event); - }); - - sub.on("eose", () => { - sub.unsub(); - resolve(events); - }); - }); - } - - private async findHealthyRelay(relays: string[]) { - for (const relay of relays) { - try { - await this.pool.ensureRelay(relay); - return relay; - } catch (e) {} - } - - throw new Error("Couldn't find a working relay"); - } - - private publish(kind: number, tags: Array[], content: string): Promise { - return new Promise((resolve, reject) => { - this.signEvent({ - kind, - tags, - pubkey: this.pub, - content, - created_at: Math.floor(Date.now() / 1000), - id: "", - sig: "" - }) - .then((event) => { - if (!event) { - reject("Couldn't sign event!"); - return; - } - if (event.kind === Kind.ChannelMessage) { - this.emitPublicMessageBeforeSent(event); - } else if (event.kind === Kind.EncryptedDirectMessage) { - this.emitDirectMessageBeforeSent(event); - } - const pub = this.pool.publish(this.writeRelays, event); - pub.on("ok", () => { - resolve(event); - if (event.kind === Kind.Contacts) { - this.getContacts(); - } - if (event.kind === Kind.Metadata) { - this.pushToEventBuffer(event); - } - }); - - pub.on("failed", () => { - reject("Couldn't sign event!"); - }); - }) - .catch(() => { - reject("Couldn't sign event!"); - }); - }); - } - - private async signEvent(event: Event): Promise { - if (this.priv === "nip07") { - return window.nostr?.signEvent(event); - } else { - return { - ...event, - id: getEventHash(event), - sig: await signEvent(event, this.priv) - }; - } - } -} - -export default MessageService; From 18fd051b43df1545aacc9976ee3acf6ba37f696c Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Sun, 12 Nov 2023 20:17:19 +0600 Subject: [PATCH 130/179] Chats: cleaned UI --- .../chats/components/chat-message-item.tsx | 2 +- .../chats/components/chat-messages-header.tsx | 2 +- .../chats/components/chat-messages-view.tsx | 2 +- .../chat-popup/chat-popup-header.tsx | 8 +-- .../chats/components/chat-profile-box.tsx | 2 +- .../chats-community-actions/index.tsx | 2 +- .../chats/components/chats-dropdown-menu.tsx | 49 +++++++------------ .../chats-sidebar/chat-sidebar-search.tsx | 2 +- 8 files changed, 27 insertions(+), 42 deletions(-) diff --git a/src/common/features/chats/components/chat-message-item.tsx b/src/common/features/chats/components/chat-message-item.tsx index 40a596e1bc8..a308af1eeb6 100644 --- a/src/common/features/chats/components/chat-message-item.tsx +++ b/src/common/features/chats/components/chat-message-item.tsx @@ -75,7 +75,7 @@ export function ChatMessageItem({ className={classNameObject({ "text-sm p-2.5 rounded-b-2xl": !isGif && !isImage, "bg-blue-dark-sky text-white rounded-tl-2xl": type === "sender", - "bg-gray-200 rounded-tr-2xl": type === "receiver", + "bg-gray-200 dark:bg-gray-800 rounded-tr-2xl": type === "receiver", "max-w-[300px] rounded-2xl overflow-hidden": isGif || isImage, "same-user-message": isSameUser })} diff --git a/src/common/features/chats/components/chat-messages-header.tsx b/src/common/features/chats/components/chat-messages-header.tsx index 4d9212bfb4b..ab000e23810 100644 --- a/src/common/features/chats/components/chat-messages-header.tsx +++ b/src/common/features/chats/components/chat-messages-header.tsx @@ -43,7 +43,7 @@ export default function ChatsMessagesHeader(props: Props) { onClick={() => setReceiverPubKey("")} /> diff --git a/src/common/features/chats/components/chat-messages-view.tsx b/src/common/features/chats/components/chat-messages-view.tsx index c6eba1aee46..d2b62575141 100644 --- a/src/common/features/chats/components/chat-messages-view.tsx +++ b/src/common/features/chats/components/chat-messages-view.tsx @@ -145,7 +145,7 @@ export default function ChatsMessagesView({ username, currentChannel, setInProgr )}
-
+
diff --git a/src/common/features/chats/components/chat-popup/chat-popup-header.tsx b/src/common/features/chats/components/chat-popup/chat-popup-header.tsx index d69556bbad1..b3c18fd0463 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-header.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-header.tsx @@ -79,7 +79,7 @@ export function ChatPopupHeader({ + + props.onManageChatKey?.()} + /> + + + ); }; diff --git a/src/common/features/chats/components/chats-sidebar/chat-sidebar-search.tsx b/src/common/features/chats/components/chats-sidebar/chat-sidebar-search.tsx index e525f3c8b10..183f04d45f2 100644 --- a/src/common/features/chats/components/chats-sidebar/chat-sidebar-search.tsx +++ b/src/common/features/chats/components/chats-sidebar/chat-sidebar-search.tsx @@ -30,7 +30,7 @@ export function ChatSidebarSearch({ setUserList, searchQuery, setSearchQuery }: return ( <> -
+
Date: Sun, 12 Nov 2023 22:28:31 +0600 Subject: [PATCH 131/179] Chats: cleanup code, added user switching in chats --- .../features/chats/chat-context-provider.tsx | 2 ++ .../chats/components/chat-message-box.tsx | 15 ++++++++----- .../chats/components/chat-messages-header.tsx | 2 +- src/common/features/chats/hooks/index.ts | 1 + .../chats/hooks/use-active-user-switching.ts | 22 +++++++++++++++++++ .../chats/mutations/add-community-channel.ts | 15 ++++++++----- .../features/chats/mutations/join-chat.ts | 4 ++-- .../mutations/update-community-channel.ts | 5 +++-- .../features/chats/queries/channels-query.ts | 5 ++++- .../chats/queries/direct-contacts-query.ts | 4 +++- .../features/chats/queries/keys-query.ts | 4 ++-- src/common/features/chats/queries/queries.ts | 1 - src/common/features/chats/screens/chats.tsx | 9 +++++--- .../chats/utils/delete-chat-public-key.ts | 21 ------------------ .../chats/utils/get-chat-private-key.ts | 5 ----- src/common/features/chats/utils/index.ts | 2 -- 16 files changed, 66 insertions(+), 51 deletions(-) create mode 100644 src/common/features/chats/hooks/index.ts create mode 100644 src/common/features/chats/hooks/use-active-user-switching.ts delete mode 100644 src/common/features/chats/utils/delete-chat-public-key.ts delete mode 100644 src/common/features/chats/utils/get-chat-private-key.ts diff --git a/src/common/features/chats/chat-context-provider.tsx b/src/common/features/chats/chat-context-provider.tsx index 3e54bcf5501..02998c42929 100644 --- a/src/common/features/chats/chat-context-provider.tsx +++ b/src/common/features/chats/chat-context-provider.tsx @@ -2,6 +2,7 @@ import React, { useState } from "react"; import { useKeysQuery } from "./queries/keys-query"; import { NostrListenerQueriesProvider, NostrProvider } from "./nostr"; import { useListenMessagesQuery } from "./queries/listen-messages-query"; +import { useActiveUserSwitching } from "./hooks"; interface Context { revealPrivateKey: boolean; @@ -30,6 +31,7 @@ export const ChatContextProvider = (props: Props) => { const { hasKeys } = useKeysQuery(); useListenMessagesQuery(); + useActiveUserSwitching(); return ( diff --git a/src/common/features/chats/components/chat-message-box.tsx b/src/common/features/chats/components/chat-message-box.tsx index b97e6acb16f..f088e58407b 100644 --- a/src/common/features/chats/components/chat-message-box.tsx +++ b/src/common/features/chats/components/chat-message-box.tsx @@ -10,6 +10,7 @@ import { useChannelsQuery, useCommunityChannelQuery } from "../queries"; import { useLeftCommunityChannelsQuery } from "../queries/left-community-channels-query"; import { useCommunityCache } from "../../../core"; import { useAddCommunityChannel } from "../mutations"; +import ChatsProfileBox from "./chat-profile-box"; interface MatchParams { filter: string; @@ -33,7 +34,7 @@ export default function ChatsMessagesBox(props: Props) { const { data: leftCommunityChannelsIds } = useLeftCommunityChannelsQuery(); const { mutateAsync: addCommunityChannel, isLoading: isAddCommunityChannelLoading } = - useAddCommunityChannel(props.match.params.name); + useAddCommunityChannel(communityChannel?.id); const { match } = props; const username = match.params.username; @@ -41,10 +42,13 @@ export default function ChatsMessagesBox(props: Props) { const [inProgress, setInProgress] = useState(false); const currentChannel = useMemo( - () => channels?.find((c) => c.communityName === community?.name), + () => + [...(channels ?? []), ...(communityChannel ? [communityChannel] : [])]?.find( + (c) => c.communityName === community?.name + ), [channels, community] ); - const hasCommunityChat = useMemo(() => !!communityChannel, [communityChannel]); + const hasCommunityChat = useMemo(() => !!currentChannel, [currentChannel]); const hasLeftCommunity = useMemo( () => leftCommunityChannelsIds?.includes(currentChannel?.id ?? ""), [currentChannel] @@ -85,8 +89,9 @@ export default function ChatsMessagesBox(props: Props) { ) : hasCommunityChat && !isCommunityJoined ? ( <>
-
-

+

+ +

{hasLeftCommunity ? "You have left this community chat. Rejoin the chat now!" : " You are not part of this community. Join the community chat now!"} diff --git a/src/common/features/chats/components/chat-messages-header.tsx b/src/common/features/chats/components/chat-messages-header.tsx index ab000e23810..4c5ffbd89a5 100644 --- a/src/common/features/chats/components/chat-messages-header.tsx +++ b/src/common/features/chats/components/chat-messages-header.tsx @@ -32,7 +32,7 @@ export default function ChatsMessagesHeader(props: Props) { }; return ( -

+
-
- +
+

{_t("chat.no-chat")}

+ +
)} ); diff --git a/src/common/features/chats/components/chat-popup/index.scss b/src/common/features/chats/components/chat-popup/index.scss index 39514dff971..62ea031fcf0 100644 --- a/src/common/features/chats/components/chat-popup/index.scss +++ b/src/common/features/chats/components/chat-popup/index.scss @@ -14,13 +14,11 @@ transition: height 0.5s ease; @include themify(day) { - box-shadow: rgb(101 119 134 / 20%) 0px 0px 15px, rgb(101 119 134 / 15%) 0px 0px 3px 1px; - @apply bg-white; + @apply bg-white border-[--border-color] border shadow-2xl; } @include themify(night) { - @apply bg-dark-default; - box-shadow: rgb(255 255 255 / 20%) 0px 0px 15px, rgb(255 255 255 / 15%) 0px 0px 3px 1px; + @apply bg-dark-200 border-[--border-color] border shadow-2xl; } &.expanded { @@ -30,18 +28,9 @@ .chat-body { position: relative; height: 470px; - overflow: scroll; + overflow-y: auto; overflow-x: hidden; - .community-header, - .dm-header { - padding: 10px; - border-bottom: 1px solid #e7e7e7; - padding-left: 14px; - font-family: Faktum, sans-serif; - font-weight: 700; - } - &.current-user { padding-bottom: 10px; height: 412px; @@ -69,29 +58,6 @@ } } - &.no-scroll { - overflow: hidden; - } - - &.join-chat { - display: flex; - justify-content: center; - align-items: center; - } - - .no-chat { - display: flex; - align-items: center; - justify-content: center; - margin-top: 20%; - } - - .start-chat-btn { - display: flex; - align-items: center; - justify-content: center; - } - .user-search-suggestion-list { .search-content { padding: 0.7rem; diff --git a/src/common/features/chats/components/chat-popup/index.tsx b/src/common/features/chats/components/chat-popup/index.tsx index 983cd5a6518..272469da95d 100644 --- a/src/common/features/chats/components/chat-popup/index.tsx +++ b/src/common/features/chats/components/chat-popup/index.tsx @@ -198,7 +198,13 @@ export const ChatPopUp = () => {
diff --git a/src/common/features/chats/mutations/add-community-channel.ts b/src/common/features/chats/mutations/add-community-channel.ts index 24f61d66773..2b80885ff13 100644 --- a/src/common/features/chats/mutations/add-community-channel.ts +++ b/src/common/features/chats/mutations/add-community-channel.ts @@ -20,10 +20,6 @@ export function useAddCommunityChannel(id: string | undefined) { { kinds: [Kind.ChannelCreation], ids: id ? [id] : undefined - }, - { - kinds: [Kind.ChannelMetadata, Kind.EventDeletion], - "#e": id ? [id] : undefined } ], { diff --git a/src/common/features/chats/nostr/Readme.md b/src/common/features/chats/nostr/Readme.md new file mode 100644 index 00000000000..e403e5756c4 --- /dev/null +++ b/src/common/features/chats/nostr/Readme.md @@ -0,0 +1,3 @@ +### Documentation source + +https://github.com/nostr-protocol/nips/blob/master/README.md From 1ec17fb05d4a116d1a2dd64a497d2251528a2905 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Tue, 14 Nov 2023 21:46:17 +0600 Subject: [PATCH 135/179] Chats: added auto-scrolling to latest message in chats page --- .../chats/components/chat-message-box.tsx | 15 ++++++++------- .../chats/components/chat-message-item.tsx | 2 +- .../chats/components/chat-popup/index.tsx | 4 ++-- .../chats/components/chat-profile-box.tsx | 2 +- .../components/chats-community-actions/index.tsx | 2 +- src/common/features/chats/hooks/index.ts | 1 + .../chats/hooks/use-auto-scroll-in-chat-box.ts | 16 ++++++++++++++++ 7 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 src/common/features/chats/hooks/use-auto-scroll-in-chat-box.ts diff --git a/src/common/features/chats/components/chat-message-box.tsx b/src/common/features/chats/components/chat-message-box.tsx index f088e58407b..1ac81d823d8 100644 --- a/src/common/features/chats/components/chat-message-box.tsx +++ b/src/common/features/chats/components/chat-message-box.tsx @@ -11,6 +11,7 @@ import { useLeftCommunityChannelsQuery } from "../queries/left-community-channel import { useCommunityCache } from "../../../core"; import { useAddCommunityChannel } from "../mutations"; import ChatsProfileBox from "./chat-profile-box"; +import { useAutoScrollInChatBox } from "../hooks"; interface MatchParams { filter: string; @@ -26,6 +27,8 @@ interface Props { } export default function ChatsMessagesBox(props: Props) { + useAutoScrollInChatBox(props.match.params.username); + const { data: community } = useCommunityCache(props.match.params.username); const { data: channels } = useChannelsQuery(); @@ -36,9 +39,6 @@ export default function ChatsMessagesBox(props: Props) { const { mutateAsync: addCommunityChannel, isLoading: isAddCommunityChannelLoading } = useAddCommunityChannel(communityChannel?.id); - const { match } = props; - const username = match.params.username; - const [inProgress, setInProgress] = useState(false); const currentChannel = useMemo( @@ -68,7 +68,7 @@ export default function ChatsMessagesBox(props: Props) { gridTemplateRows: "min-content 1fr min-content" }} > - {match.url === "/chats" ? ( + {props.match.url === "/chats" ? (

Select a chat or start a new conversation

@@ -76,12 +76,13 @@ export default function ChatsMessagesBox(props: Props) {
) : ( <> - {username.startsWith("@") || (hasCommunityChat && isCommunityJoined) ? ( + {props.match.params.username.startsWith("@") || + (hasCommunityChat && isCommunityJoined) ? ( <> - + {inProgress && } diff --git a/src/common/features/chats/components/chat-message-item.tsx b/src/common/features/chats/components/chat-message-item.tsx index a308af1eeb6..1f2701f0062 100644 --- a/src/common/features/chats/components/chat-message-item.tsx +++ b/src/common/features/chats/components/chat-message-item.tsx @@ -41,7 +41,7 @@ export function ChatMessageItem({ ); return ( -
+
{ const handleScroll = (event: React.UIEvent) => { var element = event.currentTarget; - let srollHeight: number = (element.scrollHeight / 100) * 25; - const isScrollToTop = !isCurrentUser && !isCommunity && element.scrollTop >= srollHeight; + let scrollHeight = (element.scrollHeight / 100) * 25; + const isScrollToTop = !isCurrentUser && !isCommunity && element.scrollTop >= scrollHeight; const isScrollToBottom = (isCurrentUser || isCommunity) && element.scrollTop + chatBodyDivRef?.current?.clientHeight! < element.scrollHeight - 200; diff --git a/src/common/features/chats/components/chat-profile-box.tsx b/src/common/features/chats/components/chat-profile-box.tsx index 7da13a3da6d..4b3d3e2776e 100644 --- a/src/common/features/chats/components/chat-profile-box.tsx +++ b/src/common/features/chats/components/chat-profile-box.tsx @@ -61,7 +61,7 @@ export default function ChatsProfileBox({ communityName, currentUser }: Props) { return profileData?.joiningData ? (
-
+
{profileData.name}
{profileData.about?.length !== 0 &&
{profileData.about}
} diff --git a/src/common/features/chats/components/chats-community-actions/index.tsx b/src/common/features/chats/components/chats-community-actions/index.tsx index d1d07c5428c..9f9235e0e03 100644 --- a/src/common/features/chats/components/chats-community-actions/index.tsx +++ b/src/common/features/chats/components/chats-community-actions/index.tsx @@ -43,7 +43,7 @@ const ChatsCommunityDropdownMenu = ({ history, username }: Props) => {
{step !== 0 && ( - { - setStep(0); - }} - onConfirm={() => joinChat()} - /> + setStep(0)}> + {_t("chat.create-an-account")} + +
{_t("chat.create-description")}
+ {_t("chat.create-pin-description")} + +
+ + + + +
)} ); diff --git a/src/common/features/chats/mutations/join-chat.ts b/src/common/features/chats/mutations/join-chat.ts index c4736e888a7..977aa8ac9cd 100644 --- a/src/common/features/chats/mutations/join-chat.ts +++ b/src/common/features/chats/mutations/join-chat.ts @@ -1,10 +1,12 @@ import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { createNoStrAccount, uploadChatPublicKey } from "../utils"; +import { createNoStrAccount, EncryptionTools, uploadChatKeys } from "../utils"; import { PREFIX } from "../../../util/local-storage"; import { useMappedStore } from "../../../store/use-mapped-store"; -import { ChatQueries } from "../queries"; import { useNostrPublishMutation } from "../nostr"; import { Kind } from "../../../../lib/nostr-tools/event"; +import { ChatQueries } from "../queries"; + +const crypto = require("crypto"); /** * Custom React hook for joining a chat with some side effects. @@ -20,8 +22,9 @@ export function useJoinChat(onSuccess?: () => void) { const queryClient = useQueryClient(); const { activeUser } = useMappedStore(); - const { mutateAsync: uploadPublicKey } = useMutation(["chats/upload-public-key"], (key: string) => - uploadChatPublicKey(activeUser, key) + const { mutateAsync: uploadKeys } = useMutation( + ["chats/upload-public-key"], + (keys: Parameters[1]) => uploadChatKeys(activeUser, keys) ); const { mutateAsync: updateProfile } = useNostrPublishMutation( ["chats/update-nostr-profile"], @@ -29,10 +32,15 @@ export function useJoinChat(onSuccess?: () => void) { () => {} ); - return useMutation(["chat-join-chat"], async () => createNoStrAccount(), { - onSuccess: async (keys: ReturnType) => { - localStorage.setItem(PREFIX + "_nostr_pr_" + activeUser?.username, keys.priv); - await uploadPublicKey(keys.pub); + return useMutation( + ["chat-join-chat"], + async (pin: string) => { + const keys = createNoStrAccount(); + localStorage.setItem(PREFIX + "_nostr_pr_" + activeUser?.username, pin); + + const initialVector = crypto.randomBytes(16); + const encryptedKey = EncryptionTools.encrypt(keys.priv, pin, initialVector); + await uploadKeys({ pub: keys.pub, priv: encryptedKey, iv: initialVector }); queryClient.setQueryData([ChatQueries.PUBLIC_KEY, activeUser?.username], keys.pub); queryClient.setQueryData([ChatQueries.PRIVATE_KEY, activeUser?.username], keys.priv); @@ -45,8 +53,9 @@ export function useJoinChat(onSuccess?: () => void) { picture: "" } }); - - onSuccess?.(); + }, + { + onSuccess } - }); + ); } diff --git a/src/common/features/chats/queries/keys-query.ts b/src/common/features/chats/queries/keys-query.ts index 18ad43919a7..47190d0f9ee 100644 --- a/src/common/features/chats/queries/keys-query.ts +++ b/src/common/features/chats/queries/keys-query.ts @@ -1,6 +1,6 @@ import { useQueries } from "@tanstack/react-query"; import { ChatQueries } from "./queries"; -import { getUserChatPublicKey } from "../utils"; +import { EncryptionTools, getUserChatPrivateKey, getUserChatPublicKey } from "../utils"; import { useMappedStore } from "../../../store/use-mapped-store"; import { useMemo } from "react"; import { PREFIX } from "../../../util/local-storage"; @@ -19,7 +19,15 @@ export function useKeysQuery() { }, { queryKey: [ChatQueries.PRIVATE_KEY, activeUser?.username], - queryFn: () => localStorage.getItem(PREFIX + "_nostr_pr_" + activeUser?.username) + queryFn: async () => { + const pin = localStorage.getItem(PREFIX + "_nostr_pr_" + activeUser?.username); + const { key, iv } = await getUserChatPrivateKey(activeUser?.username!); + if (key && pin && iv) { + return EncryptionTools.decrypt(key, pin, Buffer.from(iv, "base64")); + } + + return undefined; + } } ] }); diff --git a/src/common/features/chats/utils/encryption-tools.ts b/src/common/features/chats/utils/encryption-tools.ts new file mode 100644 index 00000000000..c246d498239 --- /dev/null +++ b/src/common/features/chats/utils/encryption-tools.ts @@ -0,0 +1,17 @@ +import crypto from "crypto"; + +export namespace EncryptionTools { + export function encrypt(privateKey: string, pin: string, initialVector: Buffer) { + const cipher = crypto.createCipheriv("aes-256-cbc", pin + pin + pin + pin, initialVector); + let encryptedKey = cipher.update(privateKey, "utf8", "hex"); + encryptedKey += cipher.final("hex"); + return encryptedKey; + } + + export function decrypt(encryptedData: string, pin: string, initialVector: Buffer) { + const decipher = crypto.createDecipheriv("aes-256-cbc", pin + pin + pin + pin, initialVector); + let decryptedKey = decipher.update(encryptedData, "hex", "utf8"); + decryptedKey += decipher.final("utf8"); + return decryptedKey; + } +} diff --git a/src/common/features/chats/utils/get-user-chat-private-key.ts b/src/common/features/chats/utils/get-user-chat-private-key.ts new file mode 100644 index 00000000000..8d13dce149f --- /dev/null +++ b/src/common/features/chats/utils/get-user-chat-private-key.ts @@ -0,0 +1,16 @@ +import { getAccountFull } from "../../../api/hive"; + +export const getUserChatPrivateKey = async ( + username: string +): Promise> => { + const response = await getAccountFull(username); + if (response && response.posting_json_metadata) { + const { posting_json_metadata } = response; + const profile = JSON.parse(posting_json_metadata).profile; + if (profile) { + const { nsKey, nsIv } = profile || {}; + return { key: nsKey, iv: nsIv }; + } + } + return {}; +}; diff --git a/src/common/features/chats/utils/get-user-chat-public-key.ts b/src/common/features/chats/utils/get-user-chat-public-key.ts index d20427e4fff..030008b898f 100644 --- a/src/common/features/chats/utils/get-user-chat-public-key.ts +++ b/src/common/features/chats/utils/get-user-chat-public-key.ts @@ -6,8 +6,8 @@ export const getUserChatPublicKey = async (username: string): Promise { +export const uploadChatKeys = async ( + activeUser: ActiveUser | null, + { pub, priv, iv }: ReturnType & { iv: Buffer } +) => { const response = await getAccountFull(activeUser?.username!); return await updateProfile(response, { - ...JSON.parse(response?.posting_json_metadata ? response.posting_json_metadata : "{}"), - nsKey: noStrPubKey + ...JSON.parse(response?.posting_json_metadata ? response.posting_json_metadata : "{}").profile, + nsPubKey: pub, + nsKey: priv, + nsIv: iv.toString("base64") }); }; diff --git a/src/common/features/ui/input/code-input.tsx b/src/common/features/ui/input/code-input.tsx new file mode 100644 index 00000000000..05bed893ff9 --- /dev/null +++ b/src/common/features/ui/input/code-input.tsx @@ -0,0 +1,65 @@ +import React, { useEffect, useState } from "react"; +import { FormControl } from "@ui/input/form-controls"; + +interface Props { + codeSize?: number; + value: string; + setValue: (v: string) => void; +} + +export function CodeInput({ value, setValue, codeSize = 6 }: Props) { + const [code, setCode] = useState(new Array(codeSize).fill("")); + + useEffect(() => { + setValue(code.join("")); + }, [code]); + + useEffect(() => { + const nextCode = value.split(""); + setCode( + new Array(codeSize).fill("").map((_, i) => { + if (nextCode[i]) { + return nextCode[i]; + } + return ""; + }) + ); + }, [value]); + + return ( +
+ {code.map((item, i) => ( + { + if (e.key === "Backspace") { + const nextElement = document.querySelector( + `#code-input-${i - 1}` + ) as HTMLInputElement | null; + if (nextElement) { + nextElement.focus(); + } + } + }} + onChange={(e) => { + const lastCharacter = + e.target.value.length > 0 ? e.target.value[e.target.value.length - 1] : ""; + const tempCode = [...code]; + tempCode[i] = lastCharacter; + setCode(tempCode); + + const nextElement = document.querySelector( + `#code-input-${i + 1}` + ) as HTMLInputElement | null; + if (nextElement && lastCharacter) { + nextElement.focus(); + } + }} + /> + ))} +
+ ); +} diff --git a/src/common/features/ui/input/index.ts b/src/common/features/ui/input/index.ts index a3bd8f6f97f..c55a778e1b0 100644 --- a/src/common/features/ui/input/index.ts +++ b/src/common/features/ui/input/index.ts @@ -1,3 +1,4 @@ export * from "./input-group"; export * from "./input-group-copy-clipboard"; export * from "./form-controls"; +export * from "./code-input"; diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 64cbcdae7e9..9051c88a759 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1886,7 +1886,10 @@ "enter-username": "Enter temporary username", "hide-message": "Hide message", "block-author": "Block author", - "unblock-author": "Unblock author" + "unblock-author": "Unblock author", + "create-an-account": "Create chat account", + "create-description": "Welcome aboard! Your account is almost ready. Dive into conversations and enjoy connecting with others. Need help? We're here for you. Happy chatting!", + "create-pin-description": "Please, set PIN for getting access to your chats from any device" }, "add-image": { "title": "Add Image", From 896367504edf524e3b5324966523cebbdc61494f Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Thu, 16 Nov 2023 01:09:04 +0600 Subject: [PATCH 137/179] Chats: added importing chat account by keys and pin --- src/common/api/queries.ts | 7 ++ src/common/core/react-query.ts | 4 +- .../chat-popup-contacts-and-channels.tsx | 4 +- .../chats/components/chat-popup/index.tsx | 16 +-- .../chats/components/chats-import.tsx | 111 ++++++------------ .../chats/components/chats-sidebar/index.tsx | 6 +- .../chats/components/chats-welcome.tsx | 77 ++++++++++++ .../chats/components/create-an-account.tsx | 38 ++++++ .../features/chats/components/join-chat.tsx | 23 ---- .../components/join-community-chat-btn.tsx | 8 +- .../chats/components/manage-chat-key.tsx | 28 ++++- .../chats/mutations/import-chat-by-key.ts | 39 ------ .../chats/mutations/import-chat-by-keys.ts | 59 ++++++++++ src/common/features/chats/mutations/index.ts | 2 +- .../chats/mutations/restore-chat-by-pin.ts | 52 ++++++++ .../features/chats/queries/keys-query.ts | 11 +- src/common/features/chats/screens/chats.tsx | 87 +++++++------- .../chats/utils/get-user-chat-private-key.ts | 11 +- .../chats/utils/get-user-chat-public-key.ts | 9 +- src/common/features/ui/button/index.tsx | 3 +- src/common/features/ui/button/styles.ts | 2 +- src/common/features/ui/input/code-input.tsx | 6 +- src/common/i18n/locales/en-US.json | 19 ++- src/common/pages/community-functional.tsx | 18 +-- 24 files changed, 399 insertions(+), 241 deletions(-) create mode 100644 src/common/features/chats/components/chats-welcome.tsx create mode 100644 src/common/features/chats/components/create-an-account.tsx delete mode 100644 src/common/features/chats/components/join-chat.tsx delete mode 100644 src/common/features/chats/mutations/import-chat-by-key.ts create mode 100644 src/common/features/chats/mutations/import-chat-by-keys.ts create mode 100644 src/common/features/chats/mutations/restore-chat-by-pin.ts diff --git a/src/common/api/queries.ts b/src/common/api/queries.ts index 0e87bd38c74..49a885eae85 100644 --- a/src/common/api/queries.ts +++ b/src/common/api/queries.ts @@ -5,6 +5,7 @@ import { useMappedStore } from "../store/use-mapped-store"; import axios from "axios"; import { catchPostImage } from "@ecency/render-helper"; import { Entry } from "../store/entries/types"; +import { getAccountFull } from "./hive"; const DEFAULT = { points: "0.000", @@ -88,3 +89,9 @@ export function useImageDownloader( } ); } + +export function useGetAccountFullQuery(username?: string) { + return useQuery([QueryIdentifiers.GET_ACCOUNT_FULL, username], () => getAccountFull(username!), { + enabled: !!username + }); +} diff --git a/src/common/core/react-query.ts b/src/common/core/react-query.ts index 663aff22236..b9cc66c7b69 100644 --- a/src/common/core/react-query.ts +++ b/src/common/core/react-query.ts @@ -26,5 +26,7 @@ export enum QueryIdentifiers { THREE_SPEAK_VIDEO_LIST = "three-speak-video-list", THREE_SPEAK_VIDEO_LIST_FILTERED = "three-speak-video-list-filtered", DRAFTS = "drafts", - BY_DRAFT_ID = "by-draft-id" + BY_DRAFT_ID = "by-draft-id", + + GET_ACCOUNT_FULL = "get-account-full" } diff --git a/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx b/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx index f96d620126b..0bebe1fdaed 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-contacts-and-channels.tsx @@ -1,7 +1,7 @@ import React, { useContext, useMemo } from "react"; import { _t } from "../../../../i18n"; import { getJoinedCommunities } from "../../utils"; -import { ChatsImport } from "../chats-import"; +import { ChatsWelcome } from "../chats-welcome"; import { Button } from "@ui/button"; import { ChatContext } from "../../chat-context-provider"; import { ChatDirectContactOrChannelItem } from "./chat-direct-contact-or-channel-item"; @@ -71,7 +71,7 @@ export function ChatPopupContactsAndChannels({ ))} ) : !privateKey ? ( - + ) : (

{_t("chat.no-chat")}

diff --git a/src/common/features/chats/components/chat-popup/index.tsx b/src/common/features/chats/components/chat-popup/index.tsx index ff9321bc4ef..f2c21900410 100644 --- a/src/common/features/chats/components/chat-popup/index.tsx +++ b/src/common/features/chats/components/chat-popup/index.tsx @@ -12,8 +12,6 @@ import "./index.scss"; import { useMappedStore } from "../../../../store/use-mapped-store"; import { ChatContext } from "../../chat-context-provider"; import { useMount } from "react-use"; -import { Spinner } from "@ui/spinner"; -import { Button } from "@ui/button"; import { classNameObject } from "../../../../helper/class-name-object"; import { ChatPopupHeader } from "./chat-popup-header"; import { ChatPopupMessagesList } from "./chat-popup-messages-list"; @@ -22,16 +20,17 @@ import { ChatPopupContactsAndChannels } from "./chat-popup-contacts-and-channels import { useChannelsQuery, useDirectContactsQuery, useMessagesQuery } from "../../queries"; import { useFetchPreviousMessages, useJoinChat } from "../../mutations"; import { useKeysQuery } from "../../queries/keys-query"; +import { ChatsWelcome } from "../chats-welcome"; export const ChatPopUp = () => { const { activeUser, global } = useMappedStore(); const { receiverPubKey, revealPrivateKey, hasUserJoinedChat, setRevealPrivateKey } = useContext(ChatContext); - const { mutateAsync: joinChat, isLoading: isJoinChatLoading } = useJoinChat(); + const { isLoading: isJoinChatLoading } = useJoinChat(); const { privateKey, publicKey } = useKeysQuery(); - const { data: directContacts, isLoading: isDirectContactsLoading } = useDirectContactsQuery(); + const { data: directContacts } = useDirectContactsQuery(); const directContact = useMemo( () => directContacts?.find((contact) => contact.pubkey === receiverPubKey), [directContacts, receiverPubKey] @@ -230,14 +229,7 @@ export const ChatPopUp = () => {
) : ( - + )} {((isScrollToTop && !isCurrentUser) || diff --git a/src/common/features/chats/components/chats-import.tsx b/src/common/features/chats/components/chats-import.tsx index da98e7f5198..9939473ba12 100644 --- a/src/common/features/chats/components/chats-import.tsx +++ b/src/common/features/chats/components/chats-import.tsx @@ -1,94 +1,61 @@ -import React, { useContext, useEffect, useState } from "react"; -import { _t } from "../../../i18n"; -import { keySvg } from "../../../img/svg"; -import { ChatContext } from "../chat-context-provider"; -import LinearProgress from "../../../components/linear-progress"; import { Button } from "@ui/button"; -import { Form } from "@ui/form"; -import { CodeInput, FormControl, InputGroup } from "@ui/input"; -import { useImportChatByKey, useJoinChat } from "../mutations"; -import OrDivider from "../../../components/or-divider"; +import { _t } from "../../../i18n"; import { Modal, ModalBody, ModalFooter, ModalHeader } from "@ui/modal"; +import { CodeInput, FormControl } from "@ui/input"; +import React, { useState } from "react"; +import { useImportChatByKeys } from "../mutations"; import { Alert } from "@ui/alert"; export function ChatsImport() { - const [showImportChats, setShowImportChats] = useState(false); - const [privateKeyInput, setPrivateKeyInput] = useState(""); const [step, setStep] = useState(0); - const [error, setError] = useState(""); + const [publicKey, setPublicKey] = useState(""); + const [privateKey, setPrivateKey] = useState(""); + const [iv, setIv] = useState(""); const [pin, setPin] = useState(""); - const { hasUserJoinedChat } = useContext(ChatContext); - const { mutateAsync: joinChat } = useJoinChat(); - - const { mutateAsync: importChatByKey, isLoading, error: importError } = useImportChatByKey(); - - useEffect(() => { - if (importError) { - setError(_t("chat.invalid-private-key")); - } - }, [importError]); - - useEffect(() => { - if (hasUserJoinedChat) { - setStep(0); - } - }, [hasUserJoinedChat]); + const { mutateAsync: importChatByKey } = useImportChatByKeys(); return ( <> -
- {showImportChats && ( -
-
e.preventDefault()}> - importChatByKey({ key: privateKeyInput })}> - {_t("chat.submit")} - - } - > - { - setPrivateKeyInput(e.target.value); - setError(""); - }} - /> - - {isLoading && } - {error &&
{error}
} - -
- )} - - -
- -
-
- {step !== 0 && ( + + {step === 1 && ( setStep(0)}> - {_t("chat.create-an-account")} + {_t("chat.import.title")} -
{_t("chat.create-description")}
+
{_t("chat.import.description")}
+
{_t("chat.public-key")}
+ setPublicKey(e.target.value)} + /> +
{_t("chat.private-key")}
+ setPrivateKey(e.target.value)} + /> +
{_t("chat.import.iv")}
+ setIv(e.target.value)} + /> {_t("chat.create-pin-description")} - +
-
diff --git a/src/common/features/chats/components/chats-sidebar/index.tsx b/src/common/features/chats/components/chats-sidebar/index.tsx index 5e3bd1c346e..5936e2bcb5c 100644 --- a/src/common/features/chats/components/chats-sidebar/index.tsx +++ b/src/common/features/chats/components/chats-sidebar/index.tsx @@ -12,6 +12,7 @@ import { _t } from "../../../../i18n"; import { useChannelsQuery, useDirectContactsQuery } from "../../queries"; import { useLeftCommunityChannelsQuery } from "../../queries/left-community-channels-query"; import { ChatSidebarChannel } from "./chat-sidebar-channel"; +import { useGetAccountFullQuery } from "../../../../api/queries"; interface Props { username: string; @@ -22,6 +23,7 @@ export default function ChatsSideBar(props: Props) { const { username } = props; const { setRevealPrivateKey, setReceiverPubKey } = useContext(ChatContext); + const { data: fullAccount } = useGetAccountFullQuery(username); const { data: directContacts } = useDirectContactsQuery(); const { data: channels } = useChannelsQuery(); const { data: leftChannelsIds } = useLeftCommunityChannelsQuery(); @@ -64,8 +66,8 @@ export default function ChatsSideBar(props: Props) { const peer = directContacts?.find((x) => x.name === username)?.pubkey ?? ""; if (peer) { setReceiverPubKey(peer); - } else { - const pubkey = await getUserChatPublicKey(username); + } else if (fullAccount) { + const pubkey = getUserChatPublicKey(fullAccount); if (pubkey) { setReceiverPubKey(pubkey); } else { diff --git a/src/common/features/chats/components/chats-welcome.tsx b/src/common/features/chats/components/chats-welcome.tsx new file mode 100644 index 00000000000..5b76544d2ac --- /dev/null +++ b/src/common/features/chats/components/chats-welcome.tsx @@ -0,0 +1,77 @@ +import React, { useContext, useEffect, useMemo, useState } from "react"; +import { _t } from "../../../i18n"; +import { ChatContext } from "../chat-context-provider"; +import { CodeInput } from "@ui/input"; +import OrDivider from "../../../components/or-divider"; +import { useRestoreChatByPin } from "../mutations/restore-chat-by-pin"; +import { useGetAccountFullQuery } from "../../../api/queries"; +import { useMappedStore } from "../../../store/use-mapped-store"; +import { getUserChatPrivateKey, getUserChatPublicKey } from "../utils"; +import { CreateAnAccount } from "./create-an-account"; +import { ChatsImport } from "./chats-import"; + +export function ChatsWelcome() { + const { activeUser } = useMappedStore(); + + const [step, setStep] = useState(0); + const [pin, setPin] = useState(""); + + const { hasUserJoinedChat } = useContext(ChatContext); + const { data: fullAccount } = useGetAccountFullQuery(activeUser?.username); + + const { + mutateAsync: restoreByPin, + isError: isRestoreFailed, + isLoading: isRestoreLoading + } = useRestoreChatByPin(); + + const isAlreadyRegisteredInChats = useMemo(() => { + if (!fullAccount) { + return false; + } + const publicKey = getUserChatPublicKey(fullAccount); + const { key, iv } = getUserChatPrivateKey(fullAccount); + return !!key && !!iv && !!publicKey; + }, [fullAccount]); + + useEffect(() => { + // Handle PIN on account restoring + if (step === 0 && pin.length === 8) { + restoreByPin(pin); + } + }, [pin]); + + useEffect(() => { + if (hasUserJoinedChat) { + setStep(0); + } + }, [hasUserJoinedChat]); + + return ( + <> +
+
+
{_t("chat.welcome.title")}
+
+ + {isAlreadyRegisteredInChats ? ( +
+
{_t("chat.welcome.already-joined-title")}
+
{_t("chat.welcome.already-joined-hint")}
+ + {isRestoreFailed &&
{_t("chat.welcome.pin-failed")}
} +
+ ) : ( +
+
{_t("chat.welcome.description")}
+
+ )} + +
+ + +
+
+ + ); +} diff --git a/src/common/features/chats/components/create-an-account.tsx b/src/common/features/chats/components/create-an-account.tsx new file mode 100644 index 00000000000..7f8d6af63d7 --- /dev/null +++ b/src/common/features/chats/components/create-an-account.tsx @@ -0,0 +1,38 @@ +import { _t } from "../../../i18n"; +import { Button } from "@ui/button"; +import React, { useState } from "react"; +import { Modal, ModalBody, ModalFooter, ModalHeader } from "@ui/modal"; +import { Alert } from "@ui/alert"; +import { CodeInput } from "@ui/input"; +import { useJoinChat } from "../mutations"; + +export function CreateAnAccount() { + const [step, setStep] = useState(0); + const [pin, setPin] = useState(""); + + const { mutateAsync: joinChat } = useJoinChat(); + + return ( + <> + + {step === 1 && ( + setStep(0)}> + {_t("chat.create-an-account")} + +
{_t("chat.create-description")}
+ {_t("chat.create-pin-description")} + +
+ + + + +
+ )} + + ); +} diff --git a/src/common/features/chats/components/join-chat.tsx b/src/common/features/chats/components/join-chat.tsx deleted file mode 100644 index 5f075e6d127..00000000000 --- a/src/common/features/chats/components/join-chat.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react"; -import { Button } from "@ui/button"; -import { Spinner } from "@ui/spinner"; -import { _t } from "../../../i18n"; -import { useJoinChat } from "../mutations"; - -export default function JoinChat() { - const { mutateAsync: joinChat, isLoading } = useJoinChat(); - - return ( -
-

{_t("chat.you-haven-t-joined")}

- -
- ); -} diff --git a/src/common/features/chats/components/join-community-chat-btn.tsx b/src/common/features/chats/components/join-community-chat-btn.tsx index c953bc689e1..5d828e53de6 100644 --- a/src/common/features/chats/components/join-community-chat-btn.tsx +++ b/src/common/features/chats/components/join-community-chat-btn.tsx @@ -56,7 +56,7 @@ export default function JoinCommunityChatBtn(props: Props) { const join = async () => { if (!hasUserJoinedChat) { - await joinChat(); + return; } await addCommunityChannel(); }; @@ -70,7 +70,7 @@ export default function JoinCommunityChatBtn(props: Props) { -

From 4eaa06f0e163e124431b43afa71562fb082d328b Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Thu, 16 Nov 2023 14:22:09 +0600 Subject: [PATCH 138/179] Chats: added logging out from account in chats --- .../chats/components/chats-dropdown-menu.tsx | 6 +++++- src/common/features/chats/mutations/index.ts | 1 + .../chats/mutations/logout-from-chats.tsx | 17 +++++++++++++++++ src/common/features/chats/queries/keys-query.ts | 17 ++++++++--------- src/common/i18n/locales/en-US.json | 3 ++- 5 files changed, 33 insertions(+), 11 deletions(-) create mode 100644 src/common/features/chats/mutations/logout-from-chats.tsx diff --git a/src/common/features/chats/components/chats-dropdown-menu.tsx b/src/common/features/chats/components/chats-dropdown-menu.tsx index 70581840848..4b78d7d553b 100644 --- a/src/common/features/chats/components/chats-dropdown-menu.tsx +++ b/src/common/features/chats/components/chats-dropdown-menu.tsx @@ -1,9 +1,10 @@ import React from "react"; import { History } from "history"; -import { chatKeySvg, kebabMenuSvg } from "../../../img/svg"; +import { chatKeySvg, kebabMenuSvg, keySvg } from "../../../img/svg"; import { _t } from "../../../i18n"; import { Dropdown, DropdownItemWithIcon, DropdownMenu, DropdownToggle } from "@ui/dropdown"; import { Button } from "@ui/button"; +import { useLogoutFromChats } from "../mutations"; interface Props { history: History | null; @@ -11,6 +12,8 @@ interface Props { } const ChatsDropdownMenu = (props: Props) => { + const { mutateAsync: logout } = useLogoutFromChats(); + return ( @@ -23,6 +26,7 @@ const ChatsDropdownMenu = (props: Props) => { icon={chatKeySvg} onClick={() => props.onManageChatKey?.()} /> + logout()} /> diff --git a/src/common/features/chats/mutations/index.ts b/src/common/features/chats/mutations/index.ts index b81f0f7ceac..66a67e794ef 100644 --- a/src/common/features/chats/mutations/index.ts +++ b/src/common/features/chats/mutations/index.ts @@ -11,3 +11,4 @@ export * from "./fetch-previous-messages"; export * from "./update-channel-blocked-users"; export * from "./import-chat-by-keys"; export * from "./hide-message-in-channel"; +export * from "./logout-from-chats"; diff --git a/src/common/features/chats/mutations/logout-from-chats.tsx b/src/common/features/chats/mutations/logout-from-chats.tsx new file mode 100644 index 00000000000..7ca4e8397fc --- /dev/null +++ b/src/common/features/chats/mutations/logout-from-chats.tsx @@ -0,0 +1,17 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { ChatQueries } from "../queries"; +import { useMappedStore } from "../../../store/use-mapped-store"; +import { PREFIX } from "../../../util/local-storage"; + +export function useLogoutFromChats() { + const queryClient = useQueryClient(); + const { activeUser } = useMappedStore(); + + return useMutation(["chats/logout-from-chats"], async () => { + localStorage.removeItem(PREFIX + "_nostr_pr_" + activeUser?.username); + queryClient.setQueryData([ChatQueries.PUBLIC_KEY, activeUser?.username], ""); + queryClient.setQueryData([ChatQueries.PRIVATE_KEY, activeUser?.username], ""); + queryClient.setQueryData([ChatQueries.CHANNELS, activeUser?.username], []); + queryClient.setQueryData([ChatQueries.DIRECT_CONTACTS, activeUser?.username], []); + }); +} diff --git a/src/common/features/chats/queries/keys-query.ts b/src/common/features/chats/queries/keys-query.ts index 70a76cf4b21..25faa8df11b 100644 --- a/src/common/features/chats/queries/keys-query.ts +++ b/src/common/features/chats/queries/keys-query.ts @@ -39,13 +39,12 @@ export function useKeysQuery() { const hasKeys = useMemo(() => !!publicKey && !!privateKey, [publicKey, privateKey]); - return { - publicKey, - privateKey, - hasKeys, - refetch: () => { - refetchPublicKey(); - refetchPrivateKey(); - } - }; + return useMemo( + () => ({ + publicKey, + privateKey, + hasKeys + }), + [publicKey, privateKey, hasKeys] + ); } diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index e661db431f6..0907b356637 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1892,10 +1892,11 @@ "create-an-account": "Create chat account", "create-description": "Welcome aboard! Your account is almost ready. Dive into conversations and enjoy connecting with others. Need help? We're here for you. Happy chatting!", "create-pin-description": "Please, set PIN for getting access to your chats from any device", + "logout": "Logout", "welcome": { "title": "Welcome to chats", "description": "Create an account or import chats with private key to start using chats", - "already-joined-title": "We found We've joined to chats already", + "already-joined-title": "We found You've joined to chats already", "already-joined-hint": "Please, verify your account and get access by entering PIN", "pin-failed": "Invalid PIN. Failed to verify." }, From d11c44be1a3b58028a3ba5025aa46642256e24a6 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Thu, 16 Nov 2023 15:42:34 +0600 Subject: [PATCH 139/179] Chats: fixed direct contact opening via UR: --- .../chats/mutations/add-direct-contact.ts | 53 +++++++++++++------ src/common/features/chats/screens/chats.tsx | 30 ++++++----- 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/src/common/features/chats/mutations/add-direct-contact.ts b/src/common/features/chats/mutations/add-direct-contact.ts index a85781c8e8c..bfcaf0d4155 100644 --- a/src/common/features/chats/mutations/add-direct-contact.ts +++ b/src/common/features/chats/mutations/add-direct-contact.ts @@ -1,29 +1,48 @@ -import { useMutation } from "@tanstack/react-query"; -import { useDirectContactsQuery } from "../queries"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { ChatQueries, useDirectContactsQuery } from "../queries"; import { DirectContact, useNostrPublishMutation } from "../nostr"; import { Kind } from "../../../../lib/nostr-tools/event"; +import { useMappedStore } from "../../../store/use-mapped-store"; export function useAddDirectContact() { + const queryClient = useQueryClient(); + + const { activeUser } = useMappedStore(); + const { data: directContacts } = useDirectContactsQuery(); const { mutateAsync: publishDirectContact } = useNostrPublishMutation( ["chats/nostr-publish-direct-contact"], Kind.Contacts, () => {} ); - const { data: directContacts } = useDirectContactsQuery(); - return useMutation(["chats/add-direct-contact"], async (contact: DirectContact) => { - const hasInDirectContactsAlready = directContacts?.some( - (c) => c.name === contact.name && c.pubkey === contact.pubkey - ); - if (!hasInDirectContactsAlready) { - return publishDirectContact({ - tags: [ - ...(directContacts ?? []).map((contact) => [contact.pubkey, contact.name]), - [contact.pubkey, contact.name] - ], - eventMetadata: "" - }); + return useMutation( + ["chats/add-direct-contact"], + async (contact: DirectContact) => { + const hasInDirectContactsAlready = directContacts?.some( + (c) => c.name === contact.name && c.pubkey === contact.pubkey + ); + if (!hasInDirectContactsAlready) { + await publishDirectContact({ + tags: [ + ...(directContacts ?? []).map((contact) => [contact.pubkey, contact.name]), + [contact.pubkey, contact.name] + ], + eventMetadata: "" + }); + + return contact; + } + return; + }, + { + onSuccess: (contact) => { + if (contact) { + queryClient.setQueryData( + [ChatQueries.DIRECT_CONTACTS, activeUser?.username], + [...(directContacts ?? []), contact] + ); + } + } } - return; - }); + ); } diff --git a/src/common/features/chats/screens/chats.tsx b/src/common/features/chats/screens/chats.tsx index 214f6968271..be7345db070 100644 --- a/src/common/features/chats/screens/chats.tsx +++ b/src/common/features/chats/screens/chats.tsx @@ -17,6 +17,8 @@ import { useKeysQuery } from "../queries/keys-query"; import { ChatsWelcome } from "../components/chats-welcome"; import { useCommunityCache } from "../../../core"; import SSRSuspense from "../../../components/ssr-suspense"; +import { useGetAccountFullQuery } from "../../../api/queries"; +import { getUserChatPublicKey } from "../utils"; interface Props extends PageProps { match: match<{ @@ -28,12 +30,13 @@ interface Props extends PageProps { }>; } -export const Chats = (props: Props) => { +export const Chats = ({ match, history }: Props) => { const { activeUser, global } = useMappedStore(); - const { receiverPubKey, revealPrivateKey } = useContext(ChatContext); - const { data: community } = useCommunityCache(props.match.params.username); + const { receiverPubKey, revealPrivateKey, setReceiverPubKey } = useContext(ChatContext); + const { data: community } = useCommunityCache(match.params.username); const { publicKey, privateKey } = useKeysQuery(); + const { data: userAccount } = useGetAccountFullQuery(match.params.username.replace("@", "")); const { data: channels } = useChannelsQuery(); const { data: leftCommunityChannelsIds } = useLeftCommunityChannelsQuery(); const { data: communityChannel } = useCommunityChannelQuery(community ?? undefined); @@ -42,10 +45,10 @@ export const Chats = (props: Props) => { () => [...(channels ?? []), ...(communityChannel ? [communityChannel] : [])].some( (channel) => - channel.communityName === props.match.params.username && + channel.communityName === match.params.username && !leftCommunityChannelsIds?.includes(channel.name) ), - [channels, leftCommunityChannelsIds, props.match.params.username, communityChannel] + [channels, leftCommunityChannelsIds, match.params.username, communityChannel] ); const isReady = useMemo( @@ -63,21 +66,20 @@ export const Chats = (props: Props) => { ); const isShowImportChats = useMemo(() => !isReady, [isReady]); - const { match, history } = props; - useEffect(() => { - document.body.style.overflow = "hidden"; - - return () => { - document.body.style.overflow = "auto"; - }; - }, []); + if (userAccount) { + const key = getUserChatPublicKey(userAccount); + if (key) { + setReceiverPubKey(key); + } + } + }, [userAccount]); return ( }>
- +
From adec0e5a950de52cc09acc965223ccda8bc94338 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Thu, 16 Nov 2023 19:34:04 +0600 Subject: [PATCH 140/179] Chats: added fallback screen for user not joined chats --- .../chats/components/chat-message-box.tsx | 66 ++++------- .../chat-message-channel-item-extension.tsx | 106 +++++++++-------- .../chats/components/chat-message-item.tsx | 70 +++++------ .../chats/components/chats-sidebar/index.tsx | 30 +---- src/common/features/chats/screens/chats.tsx | 111 ++++++++++-------- src/common/i18n/locales/en-US.json | 6 +- 6 files changed, 188 insertions(+), 201 deletions(-) diff --git a/src/common/features/chats/components/chat-message-box.tsx b/src/common/features/chats/components/chat-message-box.tsx index 1ac81d823d8..07cd81900b8 100644 --- a/src/common/features/chats/components/chat-message-box.tsx +++ b/src/common/features/chats/components/chat-message-box.tsx @@ -68,48 +68,34 @@ export default function ChatsMessagesBox(props: Props) { gridTemplateRows: "min-content 1fr min-content" }} > - {props.match.url === "/chats" ? ( -
-
-

Select a chat or start a new conversation

-
-
- ) : ( + {props.match.params.username.startsWith("@") || (hasCommunityChat && isCommunityJoined) ? ( + <> + + {inProgress && } + + + ) : hasCommunityChat && !isCommunityJoined ? ( <> - {props.match.params.username.startsWith("@") || - (hasCommunityChat && isCommunityJoined) ? ( - <> - - {inProgress && } - - - ) : hasCommunityChat && !isCommunityJoined ? ( - <> -
-
- -

- {hasLeftCommunity - ? "You have left this community chat. Rejoin the chat now!" - : " You are not part of this community. Join the community chat now!"} -

- -
-
- - ) : ( -

Community chat not started yet

- )} +
+
+ +

+ {hasLeftCommunity + ? "You have left this community chat. Rejoin the chat now!" + : " You are not part of this community. Join the community chat now!"} +

+ +
+
+ ) : ( +

Community chat not started yet

)}
); diff --git a/src/common/features/chats/components/chat-message-channel-item-extension.tsx b/src/common/features/chats/components/chat-message-channel-item-extension.tsx index 8e2275519f3..80dcf5e4e42 100644 --- a/src/common/features/chats/components/chat-message-channel-item-extension.tsx +++ b/src/common/features/chats/components/chat-message-channel-item-extension.tsx @@ -51,61 +51,63 @@ export function ChatMessageChannelItemExtension({ ); return ( - - - {children} - -
}> -
-
- -
+ <> + + + {children} + +
}> +
+
+ +
-

{`@${profile?.name}`}

-
- +

{`@${profile?.name}`}

+
+ - {communityAdmins.includes(activeUser?.username!) && - profile?.name !== currentChannel.communityName && ( - <> - {currentChannel?.removedUserIds?.includes(profile?.creator ?? "") ? ( - <> - - - ) : ( - <> - - - )} - - )} -
+ {communityAdmins.includes(activeUser?.username!) && + profile?.name !== currentChannel.communityName && ( + <> + {currentChannel?.removedUserIds?.includes(profile?.creator ?? "") ? ( + <> + + + ) : ( + <> + + + )} + + )} +
- + +
-
- - + + + ); } diff --git a/src/common/features/chats/components/chat-message-item.tsx b/src/common/features/chats/components/chat-message-item.tsx index 1f2701f0062..8b1be70abe9 100644 --- a/src/common/features/chats/components/chat-message-item.tsx +++ b/src/common/features/chats/components/chat-message-item.tsx @@ -9,6 +9,7 @@ import { useMappedStore } from "../../../store/use-mapped-store"; import { _t } from "../../../i18n"; import { ChatMessageChannelItemExtension } from "./chat-message-channel-item-extension"; import { Channel, Message } from "../nostr"; +import { useKeysQuery } from "../queries/keys-query"; interface Props { type: "sender" | "receiver"; @@ -27,6 +28,7 @@ export function ChatMessageItem({ onContextMenu }: Props) { const { global } = useMappedStore(); + const { publicKey } = useKeysQuery(); const isFailed = useMemo(() => message.sent === 2, [message]); const isSending = useMemo(() => message.sent === 0, [message]); @@ -44,9 +46,9 @@ export function ChatMessageItem({
+ {currentChannel && message.creator !== publicKey && ( + + )} {message.sent === 2 && ( )} -
- {currentChannel && ( - +
+
+
+
+ {formatMessageTime(message.created)} +
+ {message.sent === 0 && ( + + + + )} + {message.sent === 2 && ( + + {failedMessageSvg} + )} -
-
-
- {formatMessageTime(message.created)}
- {message.sent === 0 && ( - - - - )} - {message.sent === 2 && ( - - {failedMessageSvg} - - )}
); diff --git a/src/common/features/chats/components/chats-sidebar/index.tsx b/src/common/features/chats/components/chats-sidebar/index.tsx index 5936e2bcb5c..c4ed92e1519 100644 --- a/src/common/features/chats/components/chats-sidebar/index.tsx +++ b/src/common/features/chats/components/chats-sidebar/index.tsx @@ -1,6 +1,6 @@ -import React, { useContext, useEffect, useMemo, useState } from "react"; +import React, { useContext, useMemo, useState } from "react"; import { History } from "history"; -import { formattedUserName, getJoinedCommunities, getUserChatPublicKey } from "../../utils"; +import { getJoinedCommunities } from "../../utils"; import ChatsScroller from "../chats-scroller"; import { AccountWithReputation } from "../../types"; import { ChatContext } from "../../chat-context-provider"; @@ -12,7 +12,6 @@ import { _t } from "../../../../i18n"; import { useChannelsQuery, useDirectContactsQuery } from "../../queries"; import { useLeftCommunityChannelsQuery } from "../../queries/left-community-channels-query"; import { ChatSidebarChannel } from "./chat-sidebar-channel"; -import { useGetAccountFullQuery } from "../../../../api/queries"; interface Props { username: string; @@ -23,7 +22,6 @@ export default function ChatsSideBar(props: Props) { const { username } = props; const { setRevealPrivateKey, setReceiverPubKey } = useContext(ChatContext); - const { data: fullAccount } = useGetAccountFullQuery(username); const { data: directContacts } = useDirectContactsQuery(); const { data: channels } = useChannelsQuery(); const { data: leftChannelsIds } = useLeftCommunityChannelsQuery(); @@ -40,14 +38,6 @@ export default function ChatsSideBar(props: Props) { [channels, leftChannelsIds] ); - useEffect(() => { - if (username) { - if (username.startsWith("@")) { - getReceiverPubKey(formattedUserName(username)); - } - } - }, [username]); - const handleScroll = (event: React.UIEvent) => { var element = event.currentTarget; if (element.scrollTop > 2) { @@ -62,20 +52,6 @@ export default function ChatsSideBar(props: Props) { setIsScrollToTop(isScrollToTop); }; - const getReceiverPubKey = async (username: string) => { - const peer = directContacts?.find((x) => x.name === username)?.pubkey ?? ""; - if (peer) { - setReceiverPubKey(peer); - } else if (fullAccount) { - const pubkey = getUserChatPublicKey(fullAccount); - if (pubkey) { - setReceiverPubKey(pubkey); - } else { - setReceiverPubKey(""); - } - } - }; - return (
@@ -93,7 +69,7 @@ export default function ChatsSideBar(props: Props) { onClick={() => { setSearchQuery(""); setRevealPrivateKey(false); - getReceiverPubKey(user.account); + props.history.push(`/chats/@${user.account}`); }} key={user.account} /> diff --git a/src/common/features/chats/screens/chats.tsx b/src/common/features/chats/screens/chats.tsx index be7345db070..2cdcc659f35 100644 --- a/src/common/features/chats/screens/chats.tsx +++ b/src/common/features/chats/screens/chats.tsx @@ -16,9 +16,11 @@ import { useLeftCommunityChannelsQuery } from "../queries/left-community-channel import { useKeysQuery } from "../queries/keys-query"; import { ChatsWelcome } from "../components/chats-welcome"; import { useCommunityCache } from "../../../core"; -import SSRSuspense from "../../../components/ssr-suspense"; import { useGetAccountFullQuery } from "../../../api/queries"; import { getUserChatPublicKey } from "../utils"; +import useMountedState from "react-use/lib/useMountedState"; +import ChatsProfileBox from "../components/chat-profile-box"; +import { _t } from "../../../i18n"; interface Props extends PageProps { match: match<{ @@ -36,7 +38,7 @@ export const Chats = ({ match, history }: Props) => { const { data: community } = useCommunityCache(match.params.username); const { publicKey, privateKey } = useKeysQuery(); - const { data: userAccount } = useGetAccountFullQuery(match.params.username.replace("@", "")); + const { data: userAccount } = useGetAccountFullQuery(match.params.username?.replace("@", "")); const { data: channels } = useChannelsQuery(); const { data: leftCommunityChannelsIds } = useLeftCommunityChannelsQuery(); const { data: communityChannel } = useCommunityChannelQuery(community ?? undefined); @@ -61,69 +63,82 @@ export const Chats = ({ match, history }: Props) => { [isReady, receiverPubKey, revealPrivateKey, isChannel] ); const isShowDefaultScreen = useMemo( - () => isReady && !receiverPubKey && !isChannel && !revealPrivateKey, + () => isReady && !receiverPubKey && !isChannel && !revealPrivateKey && !match.params.username, [isReady, receiverPubKey, revealPrivateKey, isChannel] ); const isShowImportChats = useMemo(() => !isReady, [isReady]); + const isMounted = useMountedState(); + useEffect(() => { if (userAccount) { const key = getUserChatPublicKey(userAccount); - if (key) { - setReceiverPubKey(key); - } + setReceiverPubKey(key ?? ""); } }, [userAccount]); - return ( - }> -
- - + useEffect(() => { + document.body.style.overflow = "hidden"; + + return () => { + document.body.style.overflow = "auto"; + }; + }, []); -
-
-
- {isReady ? ( - - ) : ( - <> - )} -
-
- {isShowManageKey && ( -
-
- -
+ return isMounted() ? ( +
+ + + +
+
+
+ {isReady ? : <>} +
+
+ {isShowManageKey && ( +
+
+
- )} - {isShowImportChats && activeUser && ( -
- +
+ )} + {isShowImportChats && activeUser && ( +
+ +
+ )} + {isShowChatRoom && } + {!isShowChatRoom && isReady && match.params.username && ( +
+
{_t("chat.welcome.oops")}
+
+ {_t("chat.welcome.user-not-joined-yet")}
- )} - {isShowChatRoom && } - {isShowDefaultScreen && ( -
-
- Hello, @{activeUser?.username} -
-
Search a person or community and start messaging
+ +
+ )} + {isShowDefaultScreen && ( +
+
+ {_t("chat.welcome.hello")}, @{activeUser?.username}
- )} -
+
{_t("chat.welcome.start-description")}
+
+ )}
- +
+ ) : ( + <> ); }; export default connect(pageMapStateToProps, pageMapDispatchToProps)(Chats); diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 0907b356637..cc8f8d0be02 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1898,7 +1898,11 @@ "description": "Create an account or import chats with private key to start using chats", "already-joined-title": "We found You've joined to chats already", "already-joined-hint": "Please, verify your account and get access by entering PIN", - "pin-failed": "Invalid PIN. Failed to verify." + "pin-failed": "Invalid PIN. Failed to verify.", + "oops": "Oops!", + "user-not-joined-yet": "You can't send a message. This user or channel isn't joined to chats yet.", + "hello": "Hello", + "start-description": "Search a person or community and start messaging" }, "import": { "title": "Importing account", From 36872656cf1d564956228a2b2ea06773e4c9ad49 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Thu, 16 Nov 2023 22:36:55 +0600 Subject: [PATCH 141/179] Chats: fixed opening community channel from search list --- .../chats/components/chat-message-box.tsx | 31 +++++++------------ .../chat-sidebar-search-item.tsx | 3 +- .../chats/components/chats-sidebar/index.tsx | 1 - src/common/features/chats/screens/chats.tsx | 12 +++---- src/common/i18n/locales/en-US.json | 4 ++- 5 files changed, 22 insertions(+), 29 deletions(-) diff --git a/src/common/features/chats/components/chat-message-box.tsx b/src/common/features/chats/components/chat-message-box.tsx index 07cd81900b8..4f930eb55bc 100644 --- a/src/common/features/chats/components/chat-message-box.tsx +++ b/src/common/features/chats/components/chat-message-box.tsx @@ -12,6 +12,8 @@ import { useCommunityCache } from "../../../core"; import { useAddCommunityChannel } from "../mutations"; import ChatsProfileBox from "./chat-profile-box"; import { useAutoScrollInChatBox } from "../hooks"; +import { Channel } from "../nostr"; +import { _t } from "../../../i18n"; interface MatchParams { filter: string; @@ -24,6 +26,7 @@ interface MatchParams { interface Props { match: match; history: History; + channel: Channel; } export default function ChatsMessagesBox(props: Props) { @@ -41,24 +44,16 @@ export default function ChatsMessagesBox(props: Props) { const [inProgress, setInProgress] = useState(false); - const currentChannel = useMemo( - () => - [...(channels ?? []), ...(communityChannel ? [communityChannel] : [])]?.find( - (c) => c.communityName === community?.name - ), - [channels, community] - ); - const hasCommunityChat = useMemo(() => !!currentChannel, [currentChannel]); const hasLeftCommunity = useMemo( - () => leftCommunityChannelsIds?.includes(currentChannel?.id ?? ""), - [currentChannel] + () => leftCommunityChannelsIds?.includes(props.channel?.id ?? ""), + [props.channel] ); const isCommunityJoined = useMemo( () => getJoinedCommunities(channels ?? [], leftChannelsIds ?? []).some( - (channel) => channel.id === currentChannel?.id + (channel) => channel.id === props.channel?.id ), - [channels, currentChannel, leftChannelsIds] + [channels, props.channel, leftChannelsIds] ); return ( @@ -68,25 +63,25 @@ export default function ChatsMessagesBox(props: Props) { gridTemplateRows: "min-content 1fr min-content" }} > - {props.match.params.username.startsWith("@") || (hasCommunityChat && isCommunityJoined) ? ( + {props.match.params.username.startsWith("@") || isCommunityJoined ? ( <> {inProgress && } - ) : hasCommunityChat && !isCommunityJoined ? ( + ) : ( <>

{hasLeftCommunity - ? "You have left this community chat. Rejoin the chat now!" - : " You are not part of this community. Join the community chat now!"} + ? _t("chat.welcome.rejoin-description") + : _t("chat.welcome.join-description")}

- ) : ( -

Community chat not started yet

)}
); diff --git a/src/common/features/chats/components/chats-sidebar/chat-sidebar-search-item.tsx b/src/common/features/chats/components/chats-sidebar/chat-sidebar-search-item.tsx index 70a9181f14e..7ec5a5aa1a1 100644 --- a/src/common/features/chats/components/chats-sidebar/chat-sidebar-search-item.tsx +++ b/src/common/features/chats/components/chats-sidebar/chat-sidebar-search-item.tsx @@ -3,6 +3,7 @@ import { Link } from "react-router-dom"; import React from "react"; import { AccountWithReputation } from "../../types"; import UserAvatar from "../../../../components/user-avatar"; +import isCommunity from "../../../../helper/is-community"; interface Props { user: AccountWithReputation; @@ -12,7 +13,7 @@ interface Props { export function ChatSidebarSearchItem({ user, onClick }: Props) { return ( diff --git a/src/common/features/chats/components/chats-sidebar/index.tsx b/src/common/features/chats/components/chats-sidebar/index.tsx index c4ed92e1519..009c7349d98 100644 --- a/src/common/features/chats/components/chats-sidebar/index.tsx +++ b/src/common/features/chats/components/chats-sidebar/index.tsx @@ -69,7 +69,6 @@ export default function ChatsSideBar(props: Props) { onClick={() => { setSearchQuery(""); setRevealPrivateKey(false); - props.history.push(`/chats/@${user.account}`); }} key={user.account} /> diff --git a/src/common/features/chats/screens/chats.tsx b/src/common/features/chats/screens/chats.tsx index 2cdcc659f35..583d70ab883 100644 --- a/src/common/features/chats/screens/chats.tsx +++ b/src/common/features/chats/screens/chats.tsx @@ -12,7 +12,6 @@ import ChatsMessagesBox from "../components/chat-message-box"; import { classNameObject } from "../../../helper/class-name-object"; import "./_chats.scss"; import { useChannelsQuery, useCommunityChannelQuery } from "../queries"; -import { useLeftCommunityChannelsQuery } from "../queries/left-community-channels-query"; import { useKeysQuery } from "../queries/keys-query"; import { ChatsWelcome } from "../components/chats-welcome"; import { useCommunityCache } from "../../../core"; @@ -40,17 +39,14 @@ export const Chats = ({ match, history }: Props) => { const { publicKey, privateKey } = useKeysQuery(); const { data: userAccount } = useGetAccountFullQuery(match.params.username?.replace("@", "")); const { data: channels } = useChannelsQuery(); - const { data: leftCommunityChannelsIds } = useLeftCommunityChannelsQuery(); const { data: communityChannel } = useCommunityChannelQuery(community ?? undefined); const isChannel = useMemo( () => [...(channels ?? []), ...(communityChannel ? [communityChannel] : [])].some( - (channel) => - channel.communityName === match.params.username && - !leftCommunityChannelsIds?.includes(channel.name) + (channel) => channel.communityName === match.params.username ), - [channels, leftCommunityChannelsIds, match.params.username, communityChannel] + [channels, match.params.username, communityChannel] ); const isReady = useMemo( @@ -115,7 +111,9 @@ export const Chats = ({ match, history }: Props) => {
)} - {isShowChatRoom && } + {isShowChatRoom && ( + + )} {!isShowChatRoom && isReady && match.params.username && (
{_t("chat.welcome.oops")}
diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index cc8f8d0be02..9dd6149df8b 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1902,7 +1902,9 @@ "oops": "Oops!", "user-not-joined-yet": "You can't send a message. This user or channel isn't joined to chats yet.", "hello": "Hello", - "start-description": "Search a person or community and start messaging" + "start-description": "Search a person or community and start messaging", + "join-description": "You are not part of this community. Join the community chat now!", + "rejoin-description": "You have left this community chat. Rejoin the chat now!" }, "import": { "title": "Importing account", From da7576b0d9ef27928d0d72108540ccfdd6082c91 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Thu, 16 Nov 2023 22:47:06 +0600 Subject: [PATCH 142/179] Chats: added loader while message sending --- .../chats/components/chat-message-item.tsx | 20 +++++++++++-------- .../features/chats/mutations/send-message.ts | 1 + 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/common/features/chats/components/chat-message-item.tsx b/src/common/features/chats/components/chat-message-item.tsx index 8b1be70abe9..82344c15cb4 100644 --- a/src/common/features/chats/components/chat-message-item.tsx +++ b/src/common/features/chats/components/chat-message-item.tsx @@ -79,7 +79,13 @@ export function ChatMessageItem({ )} -
+
-
- {formatMessageTime(message.created)} -
- {message.sent === 0 && ( - - - + {message.sent == 1 && ( +
+ {formatMessageTime(message.created)} +
)} + {message.sent === 0 && } {message.sent === 2 && ( {failedMessageSvg} diff --git a/src/common/features/chats/mutations/send-message.ts b/src/common/features/chats/mutations/send-message.ts index 3d71b08104d..020df6338c7 100644 --- a/src/common/features/chats/mutations/send-message.ts +++ b/src/common/features/chats/mutations/send-message.ts @@ -57,6 +57,7 @@ export function useSendMessage( }, { onSuccess: (message) => { + message.sent = 0; queryClient.setQueryData( [ChatQueries.MESSAGES, currentChannel?.communityName ?? currentUser], [...messages, message] From f16502383edc0158a3ca24787c0256c109bcc852 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Mon, 20 Nov 2023 18:01:16 +0600 Subject: [PATCH 143/179] Chats: fixed community chat creation button and styling issues --- .../components/community-card/_index.scss | 9 +- .../components/community-card/index.tsx | 12 ++- .../components/community-cover/index.tsx | 3 - .../features/chats/chat-context-provider.tsx | 4 +- .../chat-popup/chat-popup-header.tsx | 66 ++++++++++---- .../chat-sidebar-search-item.tsx | 2 +- .../components/join-community-chat-btn.tsx | 40 ++++----- .../chats/mutations/create-community-chat.ts | 86 +++++++++++-------- 8 files changed, 133 insertions(+), 89 deletions(-) diff --git a/src/common/components/community-card/_index.scss b/src/common/components/community-card/_index.scss index 72d538a7b77..46d0a0c5c8e 100644 --- a/src/common/components/community-card/_index.scss +++ b/src/common/components/community-card/_index.scss @@ -147,6 +147,7 @@ font-size: 90%; @apply text-gray-steel; max-width: 95%; + p { word-break: break-all; } @@ -156,7 +157,10 @@ .community-controls { display: flex; + flex-wrap: wrap; + gap: 0.5rem; justify-content: center; + padding-bottom: 1rem; @media (min-width: $md-break) { justify-content: flex-start; @@ -182,6 +186,7 @@ .community-rewards { display: flex; justify-content: center; + padding-bottom: 1rem; @media (min-width: $md-break) { justify-content: flex-start; @@ -199,6 +204,7 @@ display: flex; flex-wrap: wrap; max-width: 95%; + .username { margin-right: 6px; } @@ -216,11 +222,12 @@ word-break: break-all; } } + .description-wrapper { height: auto; max-height: 350px; overflow-y: auto; border-radius: 10px; - padding: 0.5rem ; + padding: 0.5rem; } } diff --git a/src/common/components/community-card/index.tsx b/src/common/components/community-card/index.tsx index f8fd9ecb08a..abb02bc3ea7 100644 --- a/src/common/components/community-card/index.tsx +++ b/src/common/components/community-card/index.tsx @@ -29,6 +29,7 @@ import { renderPostBody } from "@ecency/render-helper"; import "./_index.scss"; import { Modal, ModalBody, ModalHeader, ModalTitle } from "@ui/modal"; import { Button } from "@ui/button"; +import JoinCommunityChatBtn from "../../features/chats/components/join-community-chat-btn"; interface EditPicProps { activeUser: ActiveUser; @@ -317,17 +318,20 @@ export class CommunityCard extends Component { )} {global.usePrivate && roleInTeam === ROLES.OWNER.toString() && (

- { +

)} + {info && ( {
{CommunityPostBtn({ ...this.props })} - -
{canUpdateCoverImage && ( { value={{ revealPrivateKey, receiverPubKey, - hasUserJoinedChat: hasKeys, + hasUserJoinedChat: useMemo(() => hasKeys, [hasKeys]), setRevealPrivateKey, setReceiverPubKey }} diff --git a/src/common/features/chats/components/chat-popup/chat-popup-header.tsx b/src/common/features/chats/components/chat-popup/chat-popup-header.tsx index b3c18fd0463..1ce3205b9d9 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-header.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-header.tsx @@ -6,7 +6,7 @@ import ChatsCommunityDropdownMenu from "../chats-community-actions"; import { history } from "../../../../store"; import ChatsDropdownMenu from "../chats-dropdown-menu"; import { classNameObject } from "../../../../helper/class-name-object"; -import React, { useContext } from "react"; +import React, { useContext, useMemo } from "react"; import UserAvatar from "../../../../components/user-avatar"; import { ChatContext } from "../../chat-context-provider"; import { useKeysQuery } from "../../queries/keys-query"; @@ -41,9 +41,31 @@ export function ChatPopupHeader({ const { revealPrivateKey, setRevealPrivateKey } = useContext(ChatContext); const { privateKey } = useKeysQuery(); + const title = useMemo(() => { + if (revealPrivateKey) { + return _t("chat.manage-chat-key"); + } + + if (currentUser) { + return currentUser; + } + + if (isCommunity) { + return communityName; + } + + if (showSearchUser) { + return _t("chat.new-message"); + } + + return _t("chat.messages"); + }, [currentUser, isCommunity, communityName, showSearchUser, revealPrivateKey]); return ( -
+
setExpanded(!expanded)} + >
{(currentUser || communityName || showSearchUser || revealPrivateKey) && expanded && ( @@ -51,7 +73,10 @@ export function ChatPopupHeader({ size="sm" noPadding={true} appearance="link" - onClick={handleBackArrowSvg} + onClick={(e: { stopPropagation: () => void }) => { + e.stopPropagation(); + handleBackArrowSvg(); + }} icon={arrowBackSvg} /> @@ -61,17 +86,7 @@ export function ChatPopupHeader({ )} -
- {currentUser - ? currentUser - : isCommunity - ? communityName - : showSearchUser - ? _t("chat.new-message") - : revealPrivateKey - ? _t("chat.manage-chat-key") - : _t("chat.messages")} -
+
{title}
@@ -81,7 +96,10 @@ export function ChatPopupHeader({ size="sm" appearance="gray-link" icon={extendedView} - onClick={handleExtendedView} + onClick={(e: { stopPropagation: () => void }) => { + e.stopPropagation(); + handleExtendedView(); + }} /> {canSendMessage && ( @@ -91,13 +109,22 @@ export function ChatPopupHeader({ size="sm" appearance="gray-link" icon={addMessageSvg} - onClick={handleMessageSvgClick} + onClick={(e: { stopPropagation: () => void }) => { + e.stopPropagation(); + handleMessageSvgClick(); + }} /> )} {isCommunity && } {!isCommunity && !isCurrentUser && privateKey && ( -
setExpanded(true)}> +
{ + e.stopPropagation(); + setExpanded(true); + }} + > setRevealPrivateKey(!revealPrivateKey)} @@ -113,7 +140,10 @@ export function ChatPopupHeader({ "duration-300": true, "rotate-180": !expanded })} - onClick={() => setExpanded(!expanded)} + onClick={(e: { stopPropagation: () => void }) => { + e.stopPropagation(); + setExpanded(!expanded); + }} icon={expandArrow} /> diff --git a/src/common/features/chats/components/chats-sidebar/chat-sidebar-search-item.tsx b/src/common/features/chats/components/chats-sidebar/chat-sidebar-search-item.tsx index 7ec5a5aa1a1..295ab33f03e 100644 --- a/src/common/features/chats/components/chats-sidebar/chat-sidebar-search-item.tsx +++ b/src/common/features/chats/components/chats-sidebar/chat-sidebar-search-item.tsx @@ -14,7 +14,7 @@ export function ChatSidebarSearchItem({ user, onClick }: Props) { return ( diff --git a/src/common/features/chats/components/join-community-chat-btn.tsx b/src/common/features/chats/components/join-community-chat-btn.tsx index 5d828e53de6..3af275d5ce0 100644 --- a/src/common/features/chats/components/join-community-chat-btn.tsx +++ b/src/common/features/chats/components/join-community-chat-btn.tsx @@ -9,7 +9,6 @@ import { Button } from "@ui/button"; import { useAddCommunityChannel, useCreateCommunityChat, - useJoinChat, useLeaveCommunityChannel } from "../mutations"; import { @@ -37,7 +36,6 @@ export default function JoinCommunityChatBtn(props: Props) { const { mutateAsync: addCommunityChannel, isLoading: isAddCommunityChannelLoading } = useAddCommunityChannel(currentChannel?.id); - const { mutateAsync: joinChat, isLoading: isJoinChatLoading } = useJoinChat(); const { mutateAsync: createCommunityChat, isLoading: isCreateCommunityChatLoading } = useCreateCommunityChat(props.community); const { mutateAsync: leaveCommunityChannel, isLoading: isLeavingCommunityChannelLoading } = @@ -61,38 +59,34 @@ export default function JoinCommunityChatBtn(props: Props) { await addCommunityChannel(); }; - return ( + return hasUserJoinedChat ? ( <> {props.community.name === activeUser?.username ? ( isCommunityChatJoined ? ( - + ) : !isChatEnabled ? ( - ) : !isCommunityChatJoined && isChatEnabled && hasUserJoinedChat ? ( + ) : !isCommunityChatJoined && isChatEnabled ? ( ) : isCommunityChatJoined ? (
- {(isCurrentUser || isCommunity) && ( + {((isCurrentUser && receiverPubKey) || isCommunity) && ( )}
diff --git a/src/common/features/chats/components/chats-direct-messages/index.tsx b/src/common/features/chats/components/chats-direct-messages/index.tsx index 1c7a3071b84..8435802ddc2 100644 --- a/src/common/features/chats/components/chats-direct-messages/index.tsx +++ b/src/common/features/chats/components/chats-direct-messages/index.tsx @@ -86,7 +86,12 @@ export default function ChatsDirectMessages(props: Props) { })} ) : ( -

{_t("chat.not-joined")}

+
+
{_t("chat.welcome.oops")}
+
+ {_t("chat.welcome.user-not-joined-yet")} +
+
)}
From 382d4520c167066089f2570d3b9181ae485ae151 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Mon, 20 Nov 2023 21:10:56 +0600 Subject: [PATCH 146/179] Chats: show only PIN in manage chat key --- .../chats/components/manage-chat-key.tsx | 29 +++++-------------- src/common/i18n/locales/en-US.json | 2 +- 2 files changed, 8 insertions(+), 23 deletions(-) diff --git a/src/common/features/chats/components/manage-chat-key.tsx b/src/common/features/chats/components/manage-chat-key.tsx index 2b039c7985c..77f8d5af764 100644 --- a/src/common/features/chats/components/manage-chat-key.tsx +++ b/src/common/features/chats/components/manage-chat-key.tsx @@ -3,36 +3,25 @@ import { _t } from "../../../i18n"; import { InputGroupCopyClipboard } from "@ui/input"; import qrcode from "qrcode"; import { classNameObject } from "../../../helper/class-name-object"; -import { useKeysQuery } from "../queries/keys-query"; -import { useGetAccountFullQuery } from "../../../api/queries"; import { useMappedStore } from "../../../store/use-mapped-store"; -import { getUserChatPrivateKey } from "../utils"; +import { PREFIX } from "../../../util/local-storage"; export default function ManageChatKey() { const { activeUser } = useMappedStore(); - const { privateKey, publicKey } = useKeysQuery(); - const { data: fullAccount } = useGetAccountFullQuery(activeUser?.username); + const pin = useMemo(() => localStorage.getItem(PREFIX + "_nostr_pr_" + activeUser?.username), []); const qrImgRef = useRef(null); const [isQrShow, setIsQrShow] = useState(false); - const { iv } = useMemo( - () => (fullAccount ? getUserChatPrivateKey(fullAccount) : { iv: undefined }), - [fullAccount] - ); - useEffect(() => { - if (privateKey) { + if (pin) { compileQR(); } - }, [privateKey]); + }, [pin]); const compileQR = async () => { if (qrImgRef.current) { - qrImgRef.current.src = await qrcode.toDataURL( - `Private key: ${privateKey}. Public key: ${publicKey}. Initial vector value: ${iv}`, - { width: 300 } - ); + qrImgRef.current.src = await qrcode.toDataURL(pin!!, { width: 300 }); setIsQrShow(true); } }; @@ -40,12 +29,8 @@ export default function ManageChatKey() { return (
{_t("chat.chat-priv-key")}
-
{_t("chat.public-key")}
- -
{_t("chat.private-key")}
- -
{_t("chat.import.iv")}
- +
PIN
+ Date: Mon, 20 Nov 2023 21:21:40 +0600 Subject: [PATCH 147/179] Chats: fixed titles --- .../chat-popup/chat-popup-header.tsx | 2 +- .../chats/components/chats-welcome.tsx | 35 +++++++++++-------- src/common/features/chats/screens/chats.tsx | 14 ++++++++ src/common/i18n/locales/en-US.json | 3 +- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/common/features/chats/components/chat-popup/chat-popup-header.tsx b/src/common/features/chats/components/chat-popup/chat-popup-header.tsx index 1ce3205b9d9..cf7a09dac94 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-header.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-header.tsx @@ -58,7 +58,7 @@ export function ChatPopupHeader({ return _t("chat.new-message"); } - return _t("chat.messages"); + return _t("chat.page-title"); }, [currentUser, isCommunity, communityName, showSearchUser, revealPrivateKey]); return ( diff --git a/src/common/features/chats/components/chats-welcome.tsx b/src/common/features/chats/components/chats-welcome.tsx index 5b76544d2ac..c3a3cd8a1e5 100644 --- a/src/common/features/chats/components/chats-welcome.tsx +++ b/src/common/features/chats/components/chats-welcome.tsx @@ -8,7 +8,6 @@ import { useGetAccountFullQuery } from "../../../api/queries"; import { useMappedStore } from "../../../store/use-mapped-store"; import { getUserChatPrivateKey, getUserChatPublicKey } from "../utils"; import { CreateAnAccount } from "./create-an-account"; -import { ChatsImport } from "./chats-import"; export function ChatsWelcome() { const { activeUser } = useMappedStore(); @@ -55,22 +54,28 @@ export function ChatsWelcome() {
{isAlreadyRegisteredInChats ? ( -
-
{_t("chat.welcome.already-joined-title")}
-
{_t("chat.welcome.already-joined-hint")}
- - {isRestoreFailed &&
{_t("chat.welcome.pin-failed")}
} -
+ <> +
+
{_t("chat.welcome.already-joined-title")}
+
{_t("chat.welcome.already-joined-hint")}
+ + {isRestoreFailed &&
{_t("chat.welcome.pin-failed")}
} +
+ +
+ +
+ ) : ( -
-
{_t("chat.welcome.description")}
-
+ <> +
+
{_t("chat.welcome.description")}
+
+
+ +
+ )} - -
- - -
); diff --git a/src/common/features/chats/screens/chats.tsx b/src/common/features/chats/screens/chats.tsx index 583d70ab883..506c036064c 100644 --- a/src/common/features/chats/screens/chats.tsx +++ b/src/common/features/chats/screens/chats.tsx @@ -20,6 +20,7 @@ import { getUserChatPublicKey } from "../utils"; import useMountedState from "react-use/lib/useMountedState"; import ChatsProfileBox from "../components/chat-profile-box"; import { _t } from "../../../i18n"; +import Meta from "../../../components/meta"; interface Props extends PageProps { match: match<{ @@ -66,6 +67,18 @@ export const Chats = ({ match, history }: Props) => { const isMounted = useMountedState(); + const title = useMemo(() => { + let title = _t("chat.page-title"); + + if (community) { + title = `${community.title} | ${title}`; + } else if (userAccount) { + title = `${userAccount.name} | ${title}`; + } + + return title; + }, [community, userAccount]); + useEffect(() => { if (userAccount) { const key = getUserChatPublicKey(userAccount); @@ -85,6 +98,7 @@ export const Chats = ({ match, history }: Props) => {
+
diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 5d41846fc75..1ec8b213637 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1831,6 +1831,7 @@ "update-success-message": "Your Recovery email has been updated successfully" }, "chat": { + "page-title": "Conversations", "title": "Chats", "messages": "Messages", "new-message": "New Message", @@ -1894,7 +1895,7 @@ "create-pin-description": "Please, set PIN for getting access to your chats from any device", "logout": "Logout", "welcome": { - "title": "Welcome to chats", + "title": "Welcome to conversations", "description": "Create an account or import chats with private key to start using chats", "already-joined-title": "We found You've joined to chats already", "already-joined-hint": "Please, verify your account and get access by entering PIN", From f1e68993a18538f88e3a63a5bc06602730b84f32 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Mon, 20 Nov 2023 22:50:54 +0600 Subject: [PATCH 148/179] Chats: added invitation links if user not joined yet --- .../chats-direct-messages/index.tsx | 49 ++++++++++++++++++- src/common/features/chats/mutations/index.ts | 1 + .../mutations/invite-via-post-comment.ts | 40 +++++++++++++++ src/common/i18n/locales/en-US.json | 4 ++ 4 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 src/common/features/chats/mutations/invite-via-post-comment.ts diff --git a/src/common/features/chats/components/chats-direct-messages/index.tsx b/src/common/features/chats/components/chats-direct-messages/index.tsx index 8435802ddc2..7ebc581c46e 100644 --- a/src/common/features/chats/components/chats-direct-messages/index.tsx +++ b/src/common/features/chats/components/chats-direct-messages/index.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect } from "react"; +import React, { useContext, useEffect, useState } from "react"; import usePrevious from "react-use/lib/usePrevious"; import mediumZoom, { Zoom } from "medium-zoom"; import { checkContiguousMessage, formatMessageDateAndDay } from "../../utils"; @@ -10,6 +10,10 @@ import "./index.scss"; import { ChatMessageItem } from "../chat-message-item"; import { useKeysQuery } from "../../queries/keys-query"; import { DirectMessage } from "../../nostr"; +import { Button } from "@ui/button"; +import { useInviteViaPostComment } from "../../mutations"; +import { FormControl } from "@ui/input"; +import { Alert } from "@ui/alert"; interface Props { directMessages: DirectMessage[]; @@ -26,10 +30,20 @@ export default function ChatsDirectMessages(props: Props) { const { global, activeUser } = useMappedStore(); const { receiverPubKey } = useContext(ChatContext); + const [initiatedInviting, setInitiatedInviting] = useState(false); + const [invitationText, setInvitationText] = useState( + "Hi! Let's start messaging. Follow to [Conversations](https://ecency.com/chats) and register an account." + ); let prevGlobal = usePrevious(global); const { publicKey } = useKeysQuery(); + const { + mutateAsync: invite, + isLoading: isInviting, + isSuccess: isInvited + } = useInviteViaPostComment(props.currentUser); + useEffect(() => { if (prevGlobal?.theme !== global.theme) { setBackground(); @@ -91,6 +105,39 @@ export default function ChatsDirectMessages(props: Props) {
{_t("chat.welcome.user-not-joined-yet")}
+ {!isInvited && + (initiatedInviting ? ( +
+ {_t("chat.specify-invitation-message")} + ) => + setInvitationText(e.target.value) + } + /> + +
+ ) : ( + + ))} + {isInvited && ( + + {_t("chat.successfully-invited")} + + )}
)}
diff --git a/src/common/features/chats/mutations/index.ts b/src/common/features/chats/mutations/index.ts index 66a67e794ef..9bca7e0d777 100644 --- a/src/common/features/chats/mutations/index.ts +++ b/src/common/features/chats/mutations/index.ts @@ -12,3 +12,4 @@ export * from "./update-channel-blocked-users"; export * from "./import-chat-by-keys"; export * from "./hide-message-in-channel"; export * from "./logout-from-chats"; +export * from "./invite-via-post-comment"; diff --git a/src/common/features/chats/mutations/invite-via-post-comment.ts b/src/common/features/chats/mutations/invite-via-post-comment.ts new file mode 100644 index 00000000000..974c20f4e10 --- /dev/null +++ b/src/common/features/chats/mutations/invite-via-post-comment.ts @@ -0,0 +1,40 @@ +import { useMutation } from "@tanstack/react-query"; +import { getAccountPosts } from "../../../api/bridge"; +import { comment } from "../../../api/operations"; +import tempEntry from "../../../helper/temp-entry"; +import { FullAccount } from "../../../store/accounts/types"; +import { createReplyPermlink } from "../../../helper/posting"; +import { useMappedStore } from "../../../store/use-mapped-store"; +import { error } from "../../../components/feedback"; +import { _t } from "../../../i18n"; + +export function useInviteViaPostComment(username: string) { + const { activeUser, addReply } = useMappedStore(); + + return useMutation(["chats/invite-via-post-comment"], async (text: string) => { + const response = await getAccountPosts("posts", username); + if (response && response.length > 0) { + const firstPost = response[0]; + + const { author: parentAuthor, permlink: parentPermlink } = firstPost; + const author = activeUser!!.username; + const permlink = createReplyPermlink(author); + await comment(author, parentAuthor, parentPermlink, permlink, "", text, {}, null, true); + addReply( + tempEntry({ + author: activeUser!!.data as FullAccount, + permlink, + parentAuthor, + parentPermlink, + title: "", + body: text, + tags: [], + description: null + }) + ); + } else { + error(_t("chat.no-posts-for-invite")); + throw new Error(_t("chat.no-posts-for-invite")); + } + }); +} diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 1ec8b213637..8c5a0a829ec 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1850,6 +1850,7 @@ "not-joined": "This user hasn't joined the chat yet", "subscribers": "Subscribers", "invite": "Invite", + "send-invite": "Send invitation", "leave:": "Leave", "refresh": "Refresh", "message-warning": "You cannot send messages in this group.", @@ -1894,6 +1895,9 @@ "create-description": "Welcome aboard! Your account is almost ready. Dive into conversations and enjoy connecting with others. Need help? We're here for you. Happy chatting!", "create-pin-description": "Please, set PIN for getting access to your chats from any device", "logout": "Logout", + "specify-invitation-message": "This invitation message will be sent as comment to latest user post", + "successfully-invited": "Successfully invited!", + "no-posts-for-invite": "Unfortunately, User hasn't posts.", "welcome": { "title": "Welcome to conversations", "description": "Create an account or import chats with private key to start using chats", From f12afbde1c4c08340bf79660a6f4165f8582df0d Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Sat, 25 Nov 2023 20:08:54 +0600 Subject: [PATCH 149/179] Added managing one single key instead of multiple --- .../chats/components/chats-import.tsx | 28 ++++--------- .../chats-sidebar/chat-sidebar-header.tsx | 18 +-------- .../chats/components/chats-welcome.tsx | 5 ++- .../chats/components/manage-chat-key.tsx | 39 +++++++++++++++++-- .../chats/mutations/import-chat-by-keys.ts | 26 +++++++++++-- .../features/chats/queries/keys-query.ts | 15 ++++--- src/common/features/chats/queries/queries.ts | 1 + src/common/features/chats/screens/chats.tsx | 20 ++++++++-- .../chats/utils/get-user-chat-private-key.ts | 8 ++-- .../chats/utils/get-user-chat-public-key.ts | 8 ++-- .../features/chats/utils/upload-chat-keys.ts | 8 ++-- src/common/i18n/locales/en-US.json | 6 ++- 12 files changed, 115 insertions(+), 67 deletions(-) diff --git a/src/common/features/chats/components/chats-import.tsx b/src/common/features/chats/components/chats-import.tsx index 9939473ba12..80dec872636 100644 --- a/src/common/features/chats/components/chats-import.tsx +++ b/src/common/features/chats/components/chats-import.tsx @@ -8,9 +8,7 @@ import { Alert } from "@ui/alert"; export function ChatsImport() { const [step, setStep] = useState(0); - const [publicKey, setPublicKey] = useState(""); - const [privateKey, setPrivateKey] = useState(""); - const [iv, setIv] = useState(""); + const [ecencyChatKey, setEcencyChatKey] = useState(""); const [pin, setPin] = useState(""); const { mutateAsync: importChatByKey } = useImportChatByKeys(); @@ -25,24 +23,12 @@ export function ChatsImport() { {_t("chat.import.title")}
{_t("chat.import.description")}
-
{_t("chat.public-key")}
+
{_t("chat.key")}
setPublicKey(e.target.value)} - /> -
{_t("chat.private-key")}
- setPrivateKey(e.target.value)} - /> -
{_t("chat.import.iv")}
- setIv(e.target.value)} + value={ecencyChatKey} + onChange={(e) => setEcencyChatKey(e.target.value)} /> {_t("chat.create-pin-description")} @@ -52,8 +38,8 @@ export function ChatsImport() { {_t("g.cancel")} diff --git a/src/common/features/chats/components/chats-sidebar/chat-sidebar-header.tsx b/src/common/features/chats/components/chats-sidebar/chat-sidebar-header.tsx index ddddb99b761..0cfcaad53bb 100644 --- a/src/common/features/chats/components/chats-sidebar/chat-sidebar-header.tsx +++ b/src/common/features/chats/components/chats-sidebar/chat-sidebar-header.tsx @@ -1,6 +1,3 @@ -import { Button } from "@ui/button"; -import { arrowBackSvg } from "../../../../img/svg"; -import Tooltip from "../../../../components/tooltip"; import { _t } from "../../../../i18n"; import ChatsDropdownMenu from "../chats-dropdown-menu"; import React, { useContext } from "react"; @@ -19,20 +16,7 @@ export function ChatSidebarHeader({ history }: Props) { return (
- {revealPrivateKey && ( - -
{!!privateKey && ( diff --git a/src/common/features/chats/components/chats-welcome.tsx b/src/common/features/chats/components/chats-welcome.tsx index c3a3cd8a1e5..6e7df51583a 100644 --- a/src/common/features/chats/components/chats-welcome.tsx +++ b/src/common/features/chats/components/chats-welcome.tsx @@ -8,6 +8,7 @@ import { useGetAccountFullQuery } from "../../../api/queries"; import { useMappedStore } from "../../../store/use-mapped-store"; import { getUserChatPrivateKey, getUserChatPublicKey } from "../utils"; import { CreateAnAccount } from "./create-an-account"; +import { ChatsImport } from "./chats-import"; export function ChatsWelcome() { const { activeUser } = useMappedStore(); @@ -63,15 +64,17 @@ export function ChatsWelcome() {
+
) : ( <> -
+
{_t("chat.welcome.description")}
+
diff --git a/src/common/features/chats/components/manage-chat-key.tsx b/src/common/features/chats/components/manage-chat-key.tsx index 77f8d5af764..a1fbf412406 100644 --- a/src/common/features/chats/components/manage-chat-key.tsx +++ b/src/common/features/chats/components/manage-chat-key.tsx @@ -5,23 +5,39 @@ import qrcode from "qrcode"; import { classNameObject } from "../../../helper/class-name-object"; import { useMappedStore } from "../../../store/use-mapped-store"; import { PREFIX } from "../../../util/local-storage"; +import { useKeysQuery } from "../queries/keys-query"; +import { Button } from "@ui/button"; export default function ManageChatKey() { const { activeUser } = useMappedStore(); - const pin = useMemo(() => localStorage.getItem(PREFIX + "_nostr_pr_" + activeUser?.username), []); const qrImgRef = useRef(null); const [isQrShow, setIsQrShow] = useState(false); + const { publicKey, privateKey, iv } = useKeysQuery(); + + const pin = useMemo(() => localStorage.getItem(PREFIX + "_nostr_pr_" + activeUser?.username), []); + const ecencyKey = useMemo( + () => + Buffer.from( + JSON.stringify({ + pub: publicKey, + priv: privateKey, + iv + }) + ).toString("base64"), + [publicKey, privateKey] + ); + useEffect(() => { - if (pin) { + if (ecencyKey) { compileQR(); } - }, [pin]); + }, [ecencyKey]); const compileQR = async () => { if (qrImgRef.current) { - qrImgRef.current.src = await qrcode.toDataURL(pin!!, { width: 300 }); + qrImgRef.current.src = await qrcode.toDataURL(ecencyKey!!, { width: 300 }); setIsQrShow(true); } }; @@ -31,6 +47,8 @@ export default function ManageChatKey() {
{_t("chat.chat-priv-key")}
PIN
+
Ecency key
+ +
); } diff --git a/src/common/features/chats/mutations/import-chat-by-keys.ts b/src/common/features/chats/mutations/import-chat-by-keys.ts index ff7add2e0c7..9f9149fb225 100644 --- a/src/common/features/chats/mutations/import-chat-by-keys.ts +++ b/src/common/features/chats/mutations/import-chat-by-keys.ts @@ -7,10 +7,8 @@ import { useNostrPublishMutation } from "../nostr"; import { Kind } from "../../../../lib/nostr-tools/event"; interface Payload { - publicKey: string; - privateKey: string; + ecencyChatKey: string; pin: string; - iv: string; } export function useImportChatByKeys(onSuccess?: () => void) { @@ -29,10 +27,30 @@ export function useImportChatByKeys(onSuccess?: () => void) { return useMutation( ["chats/import-chat-by-key"], - async ({ publicKey, privateKey, pin, iv }: Payload) => { + async ({ ecencyChatKey, pin }: Payload) => { if (!activeUser) { return; } + let publicKey; + let privateKey; + let iv; + + try { + const parsedObject = JSON.parse(Buffer.from(ecencyChatKey, "base64").toString()); + publicKey = parsedObject.pub; + privateKey = parsedObject.priv; + iv = parsedObject.iv; + } catch (e) { + throw new Error( + "[Chat][Nostr] – no private, public keys or initial vector value in importing" + ); + } + + if (!privateKey || !publicKey || !iv) { + throw new Error( + "[Chat][Nostr] – no private, public keys or initial vector value in importing" + ); + } const initialVector = Buffer.from(iv, "base64"); const encryptedKey = EncryptionTools.encrypt(privateKey, pin, initialVector); diff --git a/src/common/features/chats/queries/keys-query.ts b/src/common/features/chats/queries/keys-query.ts index 25faa8df11b..80d80ce2323 100644 --- a/src/common/features/chats/queries/keys-query.ts +++ b/src/common/features/chats/queries/keys-query.ts @@ -11,10 +11,7 @@ export function useKeysQuery() { const { data } = useGetAccountFullQuery(activeUser?.username); - const [ - { data: publicKey, refetch: refetchPublicKey }, - { data: privateKey, refetch: refetchPrivateKey } - ] = useQueries({ + const [{ data: publicKey }, { data: privateKey }, { data: iv }] = useQueries({ queries: [ { queryKey: [ChatQueries.PUBLIC_KEY, activeUser?.username], @@ -25,7 +22,7 @@ export function useKeysQuery() { queryKey: [ChatQueries.PRIVATE_KEY, activeUser?.username], queryFn: async () => { const pin = localStorage.getItem(PREFIX + "_nostr_pr_" + activeUser?.username); - const { key, iv } = await getUserChatPrivateKey(data!!); + const { key, iv } = getUserChatPrivateKey(data!!); if (key && pin && iv) { return EncryptionTools.decrypt(key, pin, Buffer.from(iv, "base64")); } @@ -33,6 +30,11 @@ export function useKeysQuery() { return undefined; }, enabled: !!data + }, + { + queryKey: [ChatQueries.ACCOUNT_IV, activeUser?.username], + queryFn: async () => getUserChatPrivateKey(data!!).iv, + enabled: !!data } ] }); @@ -43,7 +45,8 @@ export function useKeysQuery() { () => ({ publicKey, privateKey, - hasKeys + hasKeys, + iv }), [publicKey, privateKey, hasKeys] ); diff --git a/src/common/features/chats/queries/queries.ts b/src/common/features/chats/queries/queries.ts index d9de82888e1..62a4a9d9b2d 100644 --- a/src/common/features/chats/queries/queries.ts +++ b/src/common/features/chats/queries/queries.ts @@ -2,6 +2,7 @@ export enum ChatQueries { SEARCH_USER = "chats/search-user", PUBLIC_KEY = "chats/public-key", PRIVATE_KEY = "chats/private-key", + ACCOUNT_IV = "chats/account-iv", DIRECT_CONTACTS = "chats/direct-contacts", LAST_MESSAGES = "chats/last-messages", MESSAGES = "chats/messages", diff --git a/src/common/features/chats/screens/chats.tsx b/src/common/features/chats/screens/chats.tsx index 506c036064c..339c6150f5a 100644 --- a/src/common/features/chats/screens/chats.tsx +++ b/src/common/features/chats/screens/chats.tsx @@ -21,6 +21,8 @@ import useMountedState from "react-use/lib/useMountedState"; import ChatsProfileBox from "../components/chat-profile-box"; import { _t } from "../../../i18n"; import Meta from "../../../components/meta"; +import { Button } from "@ui/button"; +import { arrowBackSvg } from "../../../img/svg"; interface Props extends PageProps { match: match<{ @@ -34,7 +36,8 @@ interface Props extends PageProps { export const Chats = ({ match, history }: Props) => { const { activeUser, global } = useMappedStore(); - const { receiverPubKey, revealPrivateKey, setReceiverPubKey } = useContext(ChatContext); + const { receiverPubKey, revealPrivateKey, setReceiverPubKey, setRevealPrivateKey } = + useContext(ChatContext); const { data: community } = useCommunityCache(match.params.username); const { publicKey, privateKey } = useKeysQuery(); @@ -114,10 +117,21 @@ export const Chats = ({ match, history }: Props) => { })} > {isShowManageKey && ( -
-
+
+
+
+
+
)} {isShowImportChats && activeUser && ( diff --git a/src/common/features/chats/utils/get-user-chat-private-key.ts b/src/common/features/chats/utils/get-user-chat-private-key.ts index 6baeeec1764..9ffbd7583fd 100644 --- a/src/common/features/chats/utils/get-user-chat-private-key.ts +++ b/src/common/features/chats/utils/get-user-chat-private-key.ts @@ -4,9 +4,11 @@ export const getUserChatPrivateKey = (account: FullAccount): Record Date: Sat, 25 Nov 2023 20:36:33 +0600 Subject: [PATCH 150/179] Added reminder on main screen with information about saving chat keys --- src/common/features/chats/screens/chats.tsx | 32 +++++++++++++++++++++ src/common/i18n/locales/en-US.json | 2 ++ 2 files changed, 34 insertions(+) diff --git a/src/common/features/chats/screens/chats.tsx b/src/common/features/chats/screens/chats.tsx index 339c6150f5a..8dafeb302ee 100644 --- a/src/common/features/chats/screens/chats.tsx +++ b/src/common/features/chats/screens/chats.tsx @@ -23,6 +23,10 @@ import { _t } from "../../../i18n"; import Meta from "../../../components/meta"; import { Button } from "@ui/button"; import { arrowBackSvg } from "../../../img/svg"; +import moment from "moment/moment"; +import useLocalStorage from "react-use/lib/useLocalStorage"; +import { PREFIX } from "../../../util/local-storage"; +import { Alert } from "@ui/alert"; interface Props extends PageProps { match: match<{ @@ -45,6 +49,8 @@ export const Chats = ({ match, history }: Props) => { const { data: channels } = useChannelsQuery(); const { data: communityChannel } = useCommunityChannelQuery(community ?? undefined); + const [lastKeysSavingTime, setLastKeysSaving] = useLocalStorage(PREFIX + "_chats_lkst"); + const isChannel = useMemo( () => [...(channels ?? []), ...(communityChannel ? [communityChannel] : [])].some( @@ -68,6 +74,15 @@ export const Chats = ({ match, history }: Props) => { ); const isShowImportChats = useMemo(() => !isReady, [isReady]); + // We offer user to save account credentials each month + const isLastKeysSavingTimeExpired = useMemo( + () => + lastKeysSavingTime + ? moment(new Date(lastKeysSavingTime)).isBefore(moment().subtract(30, "days")) + : true, + [lastKeysSavingTime] + ); + const isMounted = useMountedState(); const title = useMemo(() => { @@ -157,6 +172,23 @@ export const Chats = ({ match, history }: Props) => { {_t("chat.welcome.hello")}, @{activeUser?.username}
{_t("chat.welcome.start-description")}
+ {isLastKeysSavingTimeExpired && ( + + {_t("chat.warn-key-saving")} + + + )}
)}
diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 484a012e0ea..053126a16dd 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1900,6 +1900,8 @@ "specify-invitation-message": "This invitation message will be sent as comment to latest user post", "successfully-invited": "Successfully invited!", "no-posts-for-invite": "Unfortunately, User hasn't posts.", + "warn-key-saving": "Save the Ecency key to access your messages. Keep it secure for uninterrupted service. Thank you!", + "view-and-save": "Show keys and save", "welcome": { "title": "Welcome to conversations", "description": "Create an account or import chats with private key to start using chats", From f88e36be615b35bfc2374eb313d8b0d0635e89b1 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Sat, 25 Nov 2023 20:58:21 +0600 Subject: [PATCH 151/179] Improved mobile version of chats page --- .../chats/components/chats-default-screen.tsx | 55 +++++++++++ .../screens/chats-manage-key-section.tsx | 28 ++++++ .../screens/chats-user-not-joined-section.tsx | 25 +++++ src/common/features/chats/screens/chats.tsx | 99 +++++-------------- 4 files changed, 132 insertions(+), 75 deletions(-) create mode 100644 src/common/features/chats/components/chats-default-screen.tsx create mode 100644 src/common/features/chats/screens/chats-manage-key-section.tsx create mode 100644 src/common/features/chats/screens/chats-user-not-joined-section.tsx diff --git a/src/common/features/chats/components/chats-default-screen.tsx b/src/common/features/chats/components/chats-default-screen.tsx new file mode 100644 index 00000000000..e3a81a21897 --- /dev/null +++ b/src/common/features/chats/components/chats-default-screen.tsx @@ -0,0 +1,55 @@ +import { _t } from "../../../i18n"; +import { Alert } from "@ui/alert"; +import { Button } from "@ui/button"; +import moment from "moment"; +import React, { HTMLProps, useContext, useMemo } from "react"; +import { useMappedStore } from "../../../store/use-mapped-store"; +import useLocalStorage from "react-use/lib/useLocalStorage"; +import { PREFIX } from "../../../util/local-storage"; +import { ChatContext } from "../chat-context-provider"; +import { classNameObject } from "../../../helper/class-name-object"; + +export function ChatsDefaultScreen(props: HTMLProps) { + const { activeUser } = useMappedStore(); + const { setRevealPrivateKey } = useContext(ChatContext); + + const [lastKeysSavingTime, setLastKeysSaving] = useLocalStorage(PREFIX + "_chats_lkst"); + + // We offer user to save account credentials each month + const isLastKeysSavingTimeExpired = useMemo( + () => + lastKeysSavingTime + ? moment(new Date(lastKeysSavingTime)).isBefore(moment().subtract(30, "days")) + : true, + [lastKeysSavingTime] + ); + + return ( +
+
+ {_t("chat.welcome.hello")}, @{activeUser?.username} +
+
{_t("chat.welcome.start-description")}
+ {isLastKeysSavingTimeExpired && ( + + {_t("chat.warn-key-saving")} + + + )} +
+ ); +} diff --git a/src/common/features/chats/screens/chats-manage-key-section.tsx b/src/common/features/chats/screens/chats-manage-key-section.tsx new file mode 100644 index 00000000000..89d4b9b2589 --- /dev/null +++ b/src/common/features/chats/screens/chats-manage-key-section.tsx @@ -0,0 +1,28 @@ +import { Button } from "@ui/button"; +import { arrowBackSvg } from "../../../img/svg"; +import { _t } from "../../../i18n"; +import ManageChatKey from "../components/manage-chat-key"; +import React, { useContext } from "react"; +import { ChatContext } from "../chat-context-provider"; + +export function ChatsManageKeySection() { + const { setRevealPrivateKey } = useContext(ChatContext); + + return ( +
+
+
+
+ +
+
+
+ ); +} diff --git a/src/common/features/chats/screens/chats-user-not-joined-section.tsx b/src/common/features/chats/screens/chats-user-not-joined-section.tsx new file mode 100644 index 00000000000..d380f283c89 --- /dev/null +++ b/src/common/features/chats/screens/chats-user-not-joined-section.tsx @@ -0,0 +1,25 @@ +import { _t } from "../../../i18n"; +import ChatsProfileBox from "../components/chat-profile-box"; +import React, { HTMLProps } from "react"; +import { match } from "react-router"; +import { classNameObject } from "../../../helper/class-name-object"; + +export function ChatsUserNotJoinedSection({ + match, + className +}: HTMLProps & { match: match<{ username: string }> }) { + return ( +
+
{_t("chat.welcome.oops")}
+
+ {_t("chat.welcome.user-not-joined-yet")} +
+ +
+ ); +} diff --git a/src/common/features/chats/screens/chats.tsx b/src/common/features/chats/screens/chats.tsx index 8dafeb302ee..aae194bb7d2 100644 --- a/src/common/features/chats/screens/chats.tsx +++ b/src/common/features/chats/screens/chats.tsx @@ -4,29 +4,24 @@ import { match } from "react-router"; import NavBar from "../../../components/navbar"; import { pageMapDispatchToProps, pageMapStateToProps, PageProps } from "../../../pages/common"; import ChatsSideBar from "../components/chats-sidebar"; -import ManageChatKey from "../components/manage-chat-key"; import Feedback from "../../../components/feedback"; import { useMappedStore } from "../../../store/use-mapped-store"; import { ChatContext } from "../chat-context-provider"; import ChatsMessagesBox from "../components/chat-message-box"; import { classNameObject } from "../../../helper/class-name-object"; import "./_chats.scss"; -import { useChannelsQuery, useCommunityChannelQuery } from "../queries"; +import { useChannelsQuery, useCommunityChannelQuery, useDirectContactsQuery } from "../queries"; import { useKeysQuery } from "../queries/keys-query"; import { ChatsWelcome } from "../components/chats-welcome"; import { useCommunityCache } from "../../../core"; import { useGetAccountFullQuery } from "../../../api/queries"; import { getUserChatPublicKey } from "../utils"; import useMountedState from "react-use/lib/useMountedState"; -import ChatsProfileBox from "../components/chat-profile-box"; import { _t } from "../../../i18n"; import Meta from "../../../components/meta"; -import { Button } from "@ui/button"; -import { arrowBackSvg } from "../../../img/svg"; -import moment from "moment/moment"; -import useLocalStorage from "react-use/lib/useLocalStorage"; -import { PREFIX } from "../../../util/local-storage"; -import { Alert } from "@ui/alert"; +import { ChatsDefaultScreen } from "../components/chats-default-screen"; +import { ChatsManageKeySection } from "./chats-manage-key-section"; +import { ChatsUserNotJoinedSection } from "./chats-user-not-joined-section"; interface Props extends PageProps { match: match<{ @@ -46,11 +41,10 @@ export const Chats = ({ match, history }: Props) => { const { publicKey, privateKey } = useKeysQuery(); const { data: userAccount } = useGetAccountFullQuery(match.params.username?.replace("@", "")); + const { data: directContacts } = useDirectContactsQuery(); const { data: channels } = useChannelsQuery(); const { data: communityChannel } = useCommunityChannelQuery(community ?? undefined); - const [lastKeysSavingTime, setLastKeysSaving] = useLocalStorage(PREFIX + "_chats_lkst"); - const isChannel = useMemo( () => [...(channels ?? []), ...(communityChannel ? [communityChannel] : [])].some( @@ -70,19 +64,10 @@ export const Chats = ({ match, history }: Props) => { ); const isShowDefaultScreen = useMemo( () => isReady && !receiverPubKey && !isChannel && !revealPrivateKey && !match.params.username, - [isReady, receiverPubKey, revealPrivateKey, isChannel] + [isReady, receiverPubKey, revealPrivateKey, isChannel, match] ); const isShowImportChats = useMemo(() => !isReady, [isReady]); - // We offer user to save account credentials each month - const isLastKeysSavingTimeExpired = useMemo( - () => - lastKeysSavingTime - ? moment(new Date(lastKeysSavingTime)).isBefore(moment().subtract(30, "days")) - : true, - [lastKeysSavingTime] - ); - const isMounted = useMountedState(); const title = useMemo(() => { @@ -120,35 +105,29 @@ export const Chats = ({ match, history }: Props) => {
-
+
{isReady ? : <>} + {(!directContacts?.length || !channels?.length) && isShowDefaultScreen && ( + + )} + {(!directContacts?.length || !channels?.length) && isShowImportChats && activeUser && ( +
+ +
+ )} + {!isShowChatRoom && isReady && match.params.username && ( + + )}
- {isShowManageKey && ( -
-
-
-
- -
-
-
- )} + {isShowManageKey && } {isShowImportChats && activeUser && (
@@ -158,39 +137,9 @@ export const Chats = ({ match, history }: Props) => { )} {!isShowChatRoom && isReady && match.params.username && ( -
-
{_t("chat.welcome.oops")}
-
- {_t("chat.welcome.user-not-joined-yet")} -
- -
- )} - {isShowDefaultScreen && ( -
-
- {_t("chat.welcome.hello")}, @{activeUser?.username} -
-
{_t("chat.welcome.start-description")}
- {isLastKeysSavingTimeExpired && ( - - {_t("chat.warn-key-saving")} - - - )} -
+ )} + {isShowDefaultScreen && }
From daa0ffce0646f88413a4ef06d0fcdf3a8b7880b1 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Wed, 29 Nov 2023 23:41:20 +0600 Subject: [PATCH 152/179] Chats: fixed minor bugs --- .../chat-popup/chat-popup-header.tsx | 53 ++++++++----------- .../chats/components/chat-popup/index.tsx | 12 ----- .../blocked-users-modal.tsx | 2 +- .../chats-direct-messages/index.tsx | 2 +- .../chats/components/chats-dropdown-menu.tsx | 27 +++++++++- src/common/features/chats/screens/chats.tsx | 5 +- src/common/i18n/locales/en-US.json | 13 ++--- 7 files changed, 59 insertions(+), 55 deletions(-) diff --git a/src/common/features/chats/components/chat-popup/chat-popup-header.tsx b/src/common/features/chats/components/chat-popup/chat-popup-header.tsx index cf7a09dac94..de9192ced13 100644 --- a/src/common/features/chats/components/chat-popup/chat-popup-header.tsx +++ b/src/common/features/chats/components/chat-popup/chat-popup-header.tsx @@ -1,7 +1,7 @@ import Tooltip from "../../../../components/tooltip"; import { _t } from "../../../../i18n"; import { Button } from "@ui/button"; -import { addMessageSvg, arrowBackSvg, expandArrow, extendedView } from "../../../../img/svg"; +import { addMessageSvg, arrowBackSvg, expandArrow } from "../../../../img/svg"; import ChatsCommunityDropdownMenu from "../chats-community-actions"; import { history } from "../../../../store"; import ChatsDropdownMenu from "../chats-dropdown-menu"; @@ -21,7 +21,6 @@ interface Props { isCurrentUser: boolean; handleBackArrowSvg: () => void; handleMessageSvgClick: () => void; - handleExtendedView: () => void; setExpanded: (v: boolean) => void; } @@ -35,7 +34,6 @@ export function ChatPopupHeader({ isCurrentUser, handleBackArrowSvg, handleMessageSvgClick, - handleExtendedView, setExpanded }: Props) { const { revealPrivateKey, setRevealPrivateKey } = useContext(ChatContext); @@ -81,27 +79,32 @@ export function ChatPopupHeader({ /> )} -
setExpanded(!expanded)}> +
setExpanded(!expanded)}> {(currentUser || isCommunity) && ( )}
{title}
+ + +
- -
)} - -
); diff --git a/src/common/features/chats/components/chat-popup/index.tsx b/src/common/features/chats/components/chat-popup/index.tsx index 781956e4000..8ac08d26dc0 100644 --- a/src/common/features/chats/components/chat-popup/index.tsx +++ b/src/common/features/chats/components/chat-popup/index.tsx @@ -1,6 +1,5 @@ import React, { useContext, useEffect, useMemo, useRef, useState } from "react"; import { useLocation } from "react-router"; -import { history } from "../../../../store"; import Tooltip from "../../../../components/tooltip"; import LinearProgress from "../../../../components/linear-progress"; import ManageChatKey from "../manage-chat-key"; @@ -176,16 +175,6 @@ export const ChatPopUp = () => { setRevealPrivateKey(false); }; - const handleExtendedView = () => { - if (!isCurrentUser && !isCommunity) { - history?.push("/chats"); - } else if (isCurrentUser) { - history?.push(`/chats/@${currentUser}`); - } else { - history?.push(`/chats/${communityName}`); - } - }; - return ( <> {show && ( @@ -204,7 +193,6 @@ export const ChatPopUp = () => { communityName={communityName} isCommunity={isCommunity} handleBackArrowSvg={handleBackArrowSvg} - handleExtendedView={handleExtendedView} handleMessageSvgClick={handleMessageSvgClick} showSearchUser={showSearchUser} /> diff --git a/src/common/features/chats/components/chats-community-actions/blocked-users-modal.tsx b/src/common/features/chats/components/chats-community-actions/blocked-users-modal.tsx index 3bcc5b6c7a7..5a5ccb4885d 100644 --- a/src/common/features/chats/components/chats-community-actions/blocked-users-modal.tsx +++ b/src/common/features/chats/components/chats-community-actions/blocked-users-modal.tsx @@ -29,7 +29,7 @@ export function BlockedUsersModal({ username, setShow, show }: Props) { return ( setShow(false)}> - {_t("chat.edit-community-roles")} + {_t("chat.blocked-users-management")}
{currentChannel?.communityModerators?.length !== 0 ? ( diff --git a/src/common/features/chats/components/chats-direct-messages/index.tsx b/src/common/features/chats/components/chats-direct-messages/index.tsx index 7ebc581c46e..8dba9a3d346 100644 --- a/src/common/features/chats/components/chats-direct-messages/index.tsx +++ b/src/common/features/chats/components/chats-direct-messages/index.tsx @@ -32,7 +32,7 @@ export default function ChatsDirectMessages(props: Props) { const [initiatedInviting, setInitiatedInviting] = useState(false); const [invitationText, setInvitationText] = useState( - "Hi! Let's start messaging. Follow to [Conversations](https://ecency.com/chats) and register an account." + "Hi! Let's start messaging privately. Register an account on [https://ecency.com/chats](https://ecency.com/chats)" ); let prevGlobal = usePrevious(global); diff --git a/src/common/features/chats/components/chats-dropdown-menu.tsx b/src/common/features/chats/components/chats-dropdown-menu.tsx index 4b78d7d553b..2b03a438ae3 100644 --- a/src/common/features/chats/components/chats-dropdown-menu.tsx +++ b/src/common/features/chats/components/chats-dropdown-menu.tsx @@ -1,18 +1,33 @@ import React from "react"; import { History } from "history"; -import { chatKeySvg, kebabMenuSvg, keySvg } from "../../../img/svg"; +import { chatKeySvg, extendedView, kebabMenuSvg, keySvg } from "../../../img/svg"; import { _t } from "../../../i18n"; import { Dropdown, DropdownItemWithIcon, DropdownMenu, DropdownToggle } from "@ui/dropdown"; import { Button } from "@ui/button"; import { useLogoutFromChats } from "../mutations"; +import { history } from "../../../store"; +import { useLocation } from "react-router"; interface Props { history: History | null; onManageChatKey?: () => void; + currentUser?: string; + communityName?: string; } const ChatsDropdownMenu = (props: Props) => { const { mutateAsync: logout } = useLogoutFromChats(); + const location = useLocation(); + + const handleExtendedView = () => { + if (props.currentUser) { + history?.push(`/chats/@${props.currentUser}`); + } else if (props.communityName) { + history?.push(`/chats/${props.communityName}`); + } else { + history?.push("/chats"); + } + }; return ( @@ -21,6 +36,16 @@ const ChatsDropdownMenu = (props: Props) => { {kebabMenuSvg} + {!location.pathname.startsWith("/chats") && ( + void }) => { + e.stopPropagation(); + handleExtendedView(); + }} + /> + )} { ); const isShowManageKey = useMemo(() => isReady && revealPrivateKey, [isReady, revealPrivateKey]); const isShowChatRoom = useMemo( - () => isReady && (!!receiverPubKey || isChannel) && !revealPrivateKey, - [isReady, receiverPubKey, revealPrivateKey, isChannel] + () => + isReady && (!!receiverPubKey || isChannel) && !revealPrivateKey && !!match.params.username, + [isReady, receiverPubKey, revealPrivateKey, isChannel, match] ); const isShowDefaultScreen = useMemo( () => isReady && !receiverPubKey && !isChannel && !revealPrivateKey && !match.params.username, diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 053126a16dd..000e1060d79 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1848,11 +1848,11 @@ "join-chat": "Join chat", "back": "Back", "no-chat": "No conversation found", - "start-chat": "Start chat", + "start-chat": "Start chatting", "not-joined": "This user hasn't joined the chat yet", "subscribers": "Subscribers", "invite": "Invite", - "send-invite": "Send invitation", + "send-invite": "Send an invite", "leave:": "Leave", "refresh": "Refresh", "message-warning": "You cannot send messages in this group.", @@ -1894,7 +1894,7 @@ "block-author": "Block author", "unblock-author": "Unblock author", "create-an-account": "Create chat account", - "create-description": "Welcome aboard! Your account is almost ready. Dive into conversations and enjoy connecting with others. Need help? We're here for you. Happy chatting!", + "create-description": "Welcome aboard! Your account is almost ready. Dive into conversations and enjoy connecting with others privately.", "create-pin-description": "Please, set PIN for getting access to your chats from any device", "logout": "Logout", "specify-invitation-message": "This invitation message will be sent as comment to latest user post", @@ -1902,14 +1902,15 @@ "no-posts-for-invite": "Unfortunately, User hasn't posts.", "warn-key-saving": "Save the Ecency key to access your messages. Keep it secure for uninterrupted service. Thank you!", "view-and-save": "Show keys and save", + "blocked-users-management": "Blocked users management", "welcome": { - "title": "Welcome to conversations", - "description": "Create an account or import chats with private key to start using chats", + "title": "Welcome to Chats", + "description": "Create or import an account start using Chats", "already-joined-title": "We found You've joined to chats already", "already-joined-hint": "Please, verify your account and get access by entering PIN", "pin-failed": "Invalid PIN. Failed to verify.", "oops": "Oops!", - "user-not-joined-yet": "You can't send a message. This user or channel isn't joined to chats yet.", + "user-not-joined-yet": "You can't send a message. This user or channel hasn't joined to chats yet.", "hello": "Hello", "start-description": "Search a person or community and start messaging", "join-description": "You are not part of this community. Join the community chat now!", From 4e75b7d43c54b62e3b04573398f9acfe85844ea6 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Wed, 29 Nov 2023 23:52:42 +0600 Subject: [PATCH 153/179] Chats: require the PIN when opening credentials management --- .../chats/components/manage-chat-key.tsx | 77 ++++++++++++------- src/common/i18n/locales/en-US.json | 3 +- 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/common/features/chats/components/manage-chat-key.tsx b/src/common/features/chats/components/manage-chat-key.tsx index a1fbf412406..4611b843bcd 100644 --- a/src/common/features/chats/components/manage-chat-key.tsx +++ b/src/common/features/chats/components/manage-chat-key.tsx @@ -1,6 +1,6 @@ import React, { useEffect, useMemo, useRef, useState } from "react"; import { _t } from "../../../i18n"; -import { InputGroupCopyClipboard } from "@ui/input"; +import { CodeInput, InputGroupCopyClipboard } from "@ui/input"; import qrcode from "qrcode"; import { classNameObject } from "../../../helper/class-name-object"; import { useMappedStore } from "../../../store/use-mapped-store"; @@ -12,6 +12,9 @@ export default function ManageChatKey() { const { activeUser } = useMappedStore(); const qrImgRef = useRef(null); + + const [isUnlocked, setIsUnlocked] = useState(false); + const [validationPin, setValidationPin] = useState(""); const [isQrShow, setIsQrShow] = useState(false); const { publicKey, privateKey, iv } = useKeysQuery(); @@ -29,6 +32,12 @@ export default function ManageChatKey() { [publicKey, privateKey] ); + useEffect(() => { + if (validationPin === pin) { + setIsUnlocked(true); + } + }, [validationPin, pin]); + useEffect(() => { if (ecencyKey) { compileQR(); @@ -44,32 +53,46 @@ export default function ManageChatKey() { return (
-
{_t("chat.chat-priv-key")}
-
PIN
- -
Ecency key
- - - + {isUnlocked ? ( + <> +
{_t("chat.chat-priv-key")}
+
PIN
+ +
Ecency key
+ + + + + ) : ( + <> +
+ {_t("chat.unlock-the-section")} +
+ + {validationPin !== pin && validationPin.length === pin?.length && ( +
{_t("chat.welcome.pin-failed")}
+ )} + + )}
); } diff --git a/src/common/i18n/locales/en-US.json b/src/common/i18n/locales/en-US.json index 000e1060d79..0991c937920 100644 --- a/src/common/i18n/locales/en-US.json +++ b/src/common/i18n/locales/en-US.json @@ -1870,7 +1870,7 @@ "error-updating-community": "Error occurred while updating community", "leave": "Leave", "edit-roles": "Edit Roles", - "manage-chat-key": "Manage chat key", + "manage-chat-key": "Credentials info", "block": "Block", "communities": "Communities", "dms": "DMs", @@ -1903,6 +1903,7 @@ "warn-key-saving": "Save the Ecency key to access your messages. Keep it secure for uninterrupted service. Thank you!", "view-and-save": "Show keys and save", "blocked-users-management": "Blocked users management", + "unlock-the-section": "Please, enter your PIN to see your credentials information", "welcome": { "title": "Welcome to Chats", "description": "Create or import an account start using Chats", From 21ec09c7eb94b055de4a1f1c20ba641cba1a62f1 Mon Sep 17 00:00:00 2001 From: "ildar.timerbaev" Date: Thu, 30 Nov 2023 00:27:53 +0600 Subject: [PATCH 154/179] Chats: autofocus to input after messaging sending --- .../features/chats/components/chat-input.tsx | 10 +++++-- .../features/ui/input/form-controls/index.tsx | 27 +++++++++++-------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/common/features/chats/components/chat-input.tsx b/src/common/features/chats/components/chat-input.tsx index 75775c6c9ad..71ef6557801 100644 --- a/src/common/features/chats/components/chat-input.tsx +++ b/src/common/features/chats/components/chat-input.tsx @@ -29,6 +29,7 @@ interface Props { export default function ChatInput({ currentChannel, currentUser }: Props) { useChannelsQuery(); + const inputRef = useRef(null); const fileInputRef = useRef(null); const emojiButtonRef = useRef(null); const gifPickerRef = useRef(null); @@ -104,10 +105,14 @@ export default function ChatInput({ currentChannel, currentUser }: Props) { />
{ + onSubmit={(e) => { e.preventDefault(); e.stopPropagation(); - sendMessage(message); + sendMessage(message).then(() => { + // Re-focus to input because when DOM changes and input position changes then + // focus is lost + setTimeout(() => inputRef.current?.focus(), 1); + }); }} className="w-full flex items-center gap-2 p-2" > @@ -129,6 +134,7 @@ export default function ChatInput({ currentChannel, currentUser }: Props) { { diff --git a/src/common/features/ui/input/form-controls/index.tsx b/src/common/features/ui/input/form-controls/index.tsx index efae39dc3b6..9e6f0206d86 100644 --- a/src/common/features/ui/input/form-controls/index.tsx +++ b/src/common/features/ui/input/form-controls/index.tsx @@ -1,26 +1,31 @@ -import React, { MutableRefObject } from "react"; +import React, { forwardRef } from "react"; import { Textarea, TextareaProps } from "./textarea"; import { Select, SelectProps } from "./select"; import { Input, InputProps } from "./input"; import { Checkbox, CheckboxProps } from "./checkbox"; import { Toggle } from "@ui/input/form-controls/toggle"; -type Props = (InputProps | TextareaProps | SelectProps | CheckboxProps) & { - ref?: MutableRefObject; -}; +type Props = InputProps | TextareaProps | SelectProps | CheckboxProps; -export function FormControl(props: Props) { +export const FormControl = forwardRef((props, ref) => { switch (props.type) { case "textarea": - return