+
{message.map((msg) => (
-
+
))}
)
diff --git a/packages/examples/sdk-frontend-react/src/app/ChatUITest/ChatViewComponent.tsx b/packages/examples/sdk-frontend-react/src/app/ChatUITest/ChatViewComponent.tsx
new file mode 100644
index 000000000..bf1273fa6
--- /dev/null
+++ b/packages/examples/sdk-frontend-react/src/app/ChatUITest/ChatViewComponent.tsx
@@ -0,0 +1,30 @@
+import styled from 'styled-components';
+
+import { Section } from '../components/StyledComponents';
+
+import { ChatViewComponent } from '@pushprotocol/uiweb';
+
+
+const ChatViewComponentTest = () => {
+
+
+
+ return (
+
+
Chat UI Test page
+
+ {/* */}
+
+
+
+
+
+ );
+};
+
+export default ChatViewComponentTest;
+
+
+const ChatViewComponentCard = styled(Section)`
+height:60vh;
+`;
\ No newline at end of file
diff --git a/packages/examples/sdk-frontend-react/src/app/ChatUITest/ChatViewListTest.tsx b/packages/examples/sdk-frontend-react/src/app/ChatUITest/ChatViewListTest.tsx
new file mode 100644
index 000000000..28eec588d
--- /dev/null
+++ b/packages/examples/sdk-frontend-react/src/app/ChatUITest/ChatViewListTest.tsx
@@ -0,0 +1,39 @@
+import { useContext, useEffect, useState } from 'react';
+import styled from 'styled-components';
+import * as PUSHAPI from '@pushprotocol/restapi';
+import { Link } from 'react-router-dom';
+import { Section } from '../components/StyledComponents';
+import { ChatViewList } from '@pushprotocol/uiweb';
+import { EnvContext, Web3Context } from '../context';
+import { usePushChatSocket } from '@pushprotocol/uiweb';
+import { MessageInput } from '@pushprotocol/uiweb';
+
+const ChatViewListTest = () => {
+ const { account, pgpPrivateKey } = useContext
(Web3Context)
+
+ const { env } = useContext(EnvContext);
+
+
+ usePushChatSocket();
+ return (
+
+
Chat UI Test page
+
+ {/* */}
+
+
+
+
+
+
+
+ );
+};
+
+export default ChatViewListTest;
+
+
+const ChatViewListCard = styled(Section)`
+height:40vh;
+background:black;
+`;
\ No newline at end of file
diff --git a/packages/examples/sdk-frontend-react/src/app/ChatUITest/MessageListTest.tsx b/packages/examples/sdk-frontend-react/src/app/ChatUITest/MessageListTest.tsx
deleted file mode 100644
index 27cf0844c..000000000
--- a/packages/examples/sdk-frontend-react/src/app/ChatUITest/MessageListTest.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import { useContext, useEffect, useState } from 'react';
-import styled from 'styled-components';
-import * as PUSHAPI from '@pushprotocol/restapi';
-import { Link } from 'react-router-dom';
-import { Section } from '../components/StyledComponents';
-import { MessageList } from '@pushprotocol/uiweb';
-import { EnvContext, Web3Context } from '../context';
-import { usePushChatSocket } from '@pushprotocol/uiweb';
-
-const MessageListTest = () => {
- const { account } = useContext(Web3Context)
-
- const { env } = useContext(EnvContext);
- const [ conversationHash , setConversationHash] = useState('');
-
- const fetchConversationHash = async() =>{
- const ConversationHash = await PUSHAPI.chat.conversationHash({
- account: `eip155:${account}`,
- conversationId: '24b029b8e07e60291bf9d8c0c48ff993fa1e0a99105459f7404c425c92e91bac',
- env: env
- });
- setConversationHash(ConversationHash.threadHash);
- }
-console.log(conversationHash)
- useEffect(()=>{
- fetchConversationHash();
- })
-
- usePushChatSocket();
- return (
-
-
Chat UI Test page
-
- {/* */}
-
-
-
-
-
-
-
- );
-};
-
-export default MessageListTest;
-
-
-const MessageListCard = styled(Section)`
-height:40vh;
-`;
\ No newline at end of file
diff --git a/packages/examples/sdk-frontend-react/src/app/app.tsx b/packages/examples/sdk-frontend-react/src/app/app.tsx
index 0acb0d4f5..a84f521dd 100644
--- a/packages/examples/sdk-frontend-react/src/app/app.tsx
+++ b/packages/examples/sdk-frontend-react/src/app/app.tsx
@@ -1,4 +1,4 @@
-import { useEffect, useMemo, useState } from 'react';
+import { useContext, useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import { Route, Routes, Link } from 'react-router-dom';
import { useWeb3React } from '@web3-react/core';
@@ -74,10 +74,13 @@ import {
ChatUIProvider,
SpacesUI,
SpacesUIProvider,
+ darkChatTheme,
} from '@pushprotocol/uiweb';
import ChatUITest from './ChatUITest/ChatUITest';
-import MessageListTest from './ChatUITest/MessageListTest';
-import { MessageBubbles } from './ChatUITest/MessageBubbles';
+import { ChatProfileTest } from './ChatUITest/ChatProfile';
+import ChatViewListTest from './ChatUITest/ChatViewListTest';
+import { ChatViewBubbles } from './ChatUITest/ChatViewBubble';
+import ChatViewComponentTest from './ChatUITest/ChatViewComponent';
import { lightChatTheme } from '@pushprotocol/uiweb';
window.Buffer = window.Buffer || Buffer;
@@ -209,8 +212,7 @@ const checkForWeb3Data = ({
export function App() {
const { account, library, active, chainId } = useWeb3React();
-
- const [env, setEnv] = useState(ENV.STAGING);
+ const [env, setEnv] = useState(ENV.PROD);
const [isCAIP, setIsCAIP] = useState(false);
const { SpaceWidgetComponent } = useSpaceComponents();
@@ -303,60 +305,60 @@ export function App() {
-
-
-
-
-
- NOTIFICATIONS
-
-
- SECRET NOTIFICATION
-
-
- CHANNELS
-
-
- ALIAS
-
-
- DELEGATIONS
-
-
- PAYLOADS
-
-
- SOCKET
-
-
- EMBED
-
-
- CHAT
-
-
- CHAT UI
-
-
- SPACE
-
-
- SPACE UI
-
-
- }
- />
- }
- />
- }
- />
+
+
+
+
+
+ NOTIFICATIONS
+
+
+ SECRET NOTIFICATION
+
+
+ CHANNELS
+
+
+ ALIAS
+
+
+ DELEGATIONS
+
+
+ PAYLOADS
+
+
+ SOCKET
+
+
+ EMBED
+
+
+ CHAT
+
+
+ CHAT UI
+
+
+ SPACE
+
+
+ SPACE UI
+
+
+ }
+ />
+ }
+ />
+ }
+ />
} />
@@ -505,27 +507,35 @@ export function App() {
/>
- {/* spaces ui components routes */}
- } />
- } />
- } />
- }
- />
- }
- />
+ {/* spaces ui components routes */}
+ } />
+ } />
+ } />
+ }
+ />
+ }
+ />
- {/* chat ui components routes */}
- }
+ {/* chat ui components routes */}
+ }
/>
- }
+ element={}
+ />
+ }
+ />
+ }
/>
{/* */}
@@ -536,7 +546,17 @@ export function App() {
- ) : null}
+ ) :
+
+
+ } />
+ }
+ />
+
+
+ }
);
diff --git a/packages/restapi/CHANGELOG.md b/packages/restapi/CHANGELOG.md
index d1fc77947..31e752569 100644
--- a/packages/restapi/CHANGELOG.md
+++ b/packages/restapi/CHANGELOG.md
@@ -2,6 +2,7 @@
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
+## [1.4.9](https://github.com/ethereum-push-notification-service/push-sdk/compare/restapi-1.4.8...restapi-1.4.9) (2023-08-11)
## [0.0.1-alpha.29](https://github.com/ethereum-push-notification-service/push-sdk/compare/restapi-0.0.1-alpha.28...restapi-0.0.1-alpha.29) (2023-08-02)
diff --git a/packages/uiweb/CHANGELOG.md b/packages/uiweb/CHANGELOG.md
index 59cb82a9b..4589c675c 100644
--- a/packages/uiweb/CHANGELOG.md
+++ b/packages/uiweb/CHANGELOG.md
@@ -2,11 +2,49 @@
This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
+## [1.1.10](https://github.com/ethereum-push-notification-service/push-sdk/compare/uiweb-1.1.9...uiweb-1.1.10) (2023-08-10)
+
+
+
+## [1.1.9](https://github.com/ethereum-push-notification-service/push-sdk/compare/uiweb-1.1.8...uiweb-1.1.9) (2023-08-10)
## [0.0.1-alpha.0](https://github.com/ethereum-push-notification-service/push-sdk/compare/uiweb-1.0.1...uiweb-0.0.1-alpha.0) (2023-08-09)
### Bug Fixes
+* hide few spaces features ([#622](https://github.com/ethereum-push-notification-service/push-sdk/issues/622)) ([0e2556c](https://github.com/ethereum-push-notification-service/push-sdk/commit/0e2556c6bbe3438cd30851ffdd9764b027f42f6e))
+* Merge branch 'main' into deployment ([843cd01](https://github.com/ethereum-push-notification-service/push-sdk/commit/843cd0169a270bbab69922021edf312616de3802))
+
+
+
+## [1.1.8](https://github.com/ethereum-push-notification-service/push-sdk/compare/uiweb-1.1.7...uiweb-1.1.8) (2023-08-04)
+
+
+### Bug Fixes
+
+* merge main ([b9e4440](https://github.com/ethereum-push-notification-service/push-sdk/commit/b9e44408fa4c97720b12217486e8d13ef3caeb00))
+
+
+
+## [1.1.7](https://github.com/ethereum-push-notification-service/push-sdk/compare/uiweb-1.1.6...uiweb-1.1.7) (2023-07-31)
+
+
+### Bug Fixes
+
+* Merge branch 'main' into deployment ([9755baf](https://github.com/ethereum-push-notification-service/push-sdk/commit/9755baf3d4bcd3ab3fd365fad9d8fb7623fda58f))
+
+
+
+## [1.1.6](https://github.com/ethereum-push-notification-service/push-sdk/compare/uiweb-1.1.5...uiweb-1.1.6) (2023-07-28)
+
+
+### Bug Fixes
+
+* Merge branch 'main' into deployment ([e33017a](https://github.com/ethereum-push-notification-service/push-sdk/commit/e33017afb2d4e9361d5df47e0f7e726ecdffbc32))
+
+
+
+## [1.1.5](https://github.com/ethereum-push-notification-service/push-sdk/compare/uiweb-1.1.4...uiweb-1.1.5) (2023-07-27)
* add alpha support to UI web ([8ecf5d9](https://github.com/ethereum-push-notification-service/push-sdk/commit/8ecf5d92cc1b5c25562ba0d6d1ae1137877a5be7))
* fixed bugs ([#566](https://github.com/ethereum-push-notification-service/push-sdk/issues/566)) ([481d8fc](https://github.com/ethereum-push-notification-service/push-sdk/commit/481d8fcd7c40325654ba490640daabc38ee2f96e))
* fixed build issues ([#550](https://github.com/ethereum-push-notification-service/push-sdk/issues/550)) ([0ce6e18](https://github.com/ethereum-push-notification-service/push-sdk/commit/0ce6e18a82901478fe3157788c716e5224f14bdb))
diff --git a/packages/uiweb/package-lock.json b/packages/uiweb/package-lock.json
index a204cad1a..898a92ce8 100644
--- a/packages/uiweb/package-lock.json
+++ b/packages/uiweb/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@pushprotocol/uiweb",
- "version": "1.1.4",
+ "version": "0.0.1-alpha.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@pushprotocol/uiweb",
- "version": "1.1.4",
+ "version": "0.0.1-alpha.0",
"dependencies": {
"@livepeer/react": "^2.6.0",
"@pushprotocol/socket": "^0.5.0",
@@ -17,6 +17,7 @@
"gif-picker-react": "^1.1.0",
"html-react-parser": "^1.4.13",
"moment": "^2.29.4",
+ "react-toastify": "^9.1.3",
"react-twitter-embed": "^4.0.4"
},
"peerDependencies": {
@@ -3380,6 +3381,18 @@
}
}
},
+ "node_modules/react-toastify": {
+ "version": "9.1.3",
+ "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz",
+ "integrity": "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==",
+ "dependencies": {
+ "clsx": "^1.1.1"
+ },
+ "peerDependencies": {
+ "react": ">=16",
+ "react-dom": ">=16"
+ }
+ },
"node_modules/react-twitter-embed": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/react-twitter-embed/-/react-twitter-embed-4.0.4.tgz",
diff --git a/packages/uiweb/package.json b/packages/uiweb/package.json
index 9e52107ad..7e3eea05b 100644
--- a/packages/uiweb/package.json
+++ b/packages/uiweb/package.json
@@ -8,12 +8,15 @@
"@livepeer/react": "^2.6.0",
"@pushprotocol/socket": "^0.5.0",
"@unstoppabledomains/resolution": "^8.5.0",
+ "@web3-react/injected-connector": "^6.0.7",
"date-fns": "^2.28.0",
"emoji-picker-react": "^4.4.9",
"font-awesome": "^4.7.0",
"gif-picker-react": "^1.1.0",
"html-react-parser": "^1.4.13",
"moment": "^2.29.4",
+ "react-icons": "^4.10.1",
+ "react-toastify": "^9.1.3",
"react-twitter-embed": "^4.0.4"
},
"peerDependencies": {
diff --git a/packages/uiweb/src/lib/components/chat/ChatProfile/AddWalletContent.tsx b/packages/uiweb/src/lib/components/chat/ChatProfile/AddWalletContent.tsx
new file mode 100644
index 000000000..0ba4639b9
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/ChatProfile/AddWalletContent.tsx
@@ -0,0 +1,380 @@
+import { useContext, useEffect, useState } from "react";
+import styled from "styled-components";
+import { ThemeContext } from "../theme/ThemeProvider";
+import { useChatData } from "../../../hooks";
+import { displayDefaultUser, getAddress, walletToPCAIP10 } from "../../../helpers";
+import { IToast, ModalButtonProps, User } from "../exportedTypes";
+import * as PushAPI from '@pushprotocol/restapi';
+import { ethers } from "ethers";
+import { addWalletValidation } from "../helpers/helper";
+import ArrowGreyIcon from '../../../icons/CaretDownGrey.svg'
+import ArrowLeftIcon from '../../../icons/ArrowLeft.svg';
+import CloseIcon from '../../../icons/close.svg';
+import { Spinner } from "../../supportChat/spinner/Spinner";
+import { MoreLightIcon } from '../../../icons/MoreLight';
+import { MoreDarkIcon } from '../../../icons/MoreDark';
+import { SearchIcon } from '../../../icons/SearchIcon';
+import { Section, Span, Image } from "../../reusables/sharedStyling";
+import { AddUserDarkIcon } from '../../../icons/Adddark';
+import { device } from "../../../config";
+import { MemberListContainer } from "./MemberListContainer";
+import useMediaQuery from "../helpers/useMediaQuery";
+import useToast from "../helpers/NewToast";
+import { MdCheckCircle, MdError } from "react-icons/md";
+import { Modal } from "../helpers/Modal";
+
+
+
+export const AddWalletContent = ({ onSubmit, handlePrevious, onClose, memberList, handleMemberList, title, groupMembers, isLoading }: {onSubmit: ()=> void ,onClose: ()=> void, handlePrevious: ()=> void, memberList: any, handleMemberList: any, title: string, groupMembers: any, isLoading?: boolean }) => {
+ const theme = useContext(ThemeContext);
+
+ const [searchedUser, setSearchedUser] = useState('');
+ const [filteredUserData, setFilteredUserData] = useState(null);
+ const [isInValidAddress, setIsInvalidAddress] = useState(false);
+ const [isLoadingSearch, setIsLoadingSearch] = useState(false);
+ const { account, env } = useChatData();
+ const isMobile = useMediaQuery(device.mobileL);
+ const groupInfoToast = useToast();
+
+
+ useEffect(() => {
+ if (isInValidAddress) {
+ groupInfoToast.showMessageToast({
+ toastTitle: 'Error',
+ toastMessage: 'Invalid Address',
+ toastType: 'ERROR',
+ getToastIcon: (size) => (
+
+ ),
+ });
+ }
+ }, [isInValidAddress]);
+
+ const onChangeSearchBox = (e: any) => {
+ setSearchedUser(e.target.value);
+ };
+
+ const handleUserSearch = async (userSearchData: string): Promise => {
+ try{
+ const caip10 = walletToPCAIP10(userSearchData);
+ let filteredData: User;
+
+ if (userSearchData.length) {
+ filteredData = await PushAPI.user.get({
+ account: caip10,
+ env: env
+ });
+
+ if (filteredData !== null) {
+ setFilteredUserData(filteredData);
+ }
+ // User is not in the protocol. Create new user
+ else {
+ if (ethers.utils.isAddress(userSearchData)) {
+ const displayUser = displayDefaultUser({ caip10 });
+ setFilteredUserData(displayUser);
+ } else {
+ setIsInvalidAddress(true);
+ setFilteredUserData(null);
+ }
+ }
+ } else {
+ setFilteredUserData(null);
+ }
+ setIsLoadingSearch(false);
+ }
+ catch(error){
+ groupInfoToast.showMessageToast({
+ toastTitle: 'Error',
+ toastMessage: 'Unsuccessful search, Try again',
+ toastType: 'ERROR',
+ getToastIcon: (size) => (
+
+ ),
+ });
+ }
+ };
+
+ const handleSearch = async (e: any): Promise => {
+ setIsLoadingSearch(true);
+ setIsInvalidAddress(false);
+ e.preventDefault();
+ if (!ethers.utils.isAddress(searchedUser)) {
+ let address: string;
+ try {
+ address = await getAddress(searchedUser, env) as string;
+ // if (!address) {
+ // address = await library.resolveName(searchedUser);
+ // }
+ // this ensures address are checksummed
+ address = ethers.utils.getAddress(address?.toLowerCase());
+ if (address) {
+ handleUserSearch(address);
+ } else {
+ setIsInvalidAddress(true);
+ setFilteredUserData(null);
+ }
+ } catch (err) {
+ setIsInvalidAddress(true);
+ setFilteredUserData(null);
+ } finally {
+ setIsLoadingSearch(false);
+ }
+ } else {
+ handleUserSearch(searchedUser);
+ }
+ };
+
+ const clearInput = () => {
+ setSearchedUser('');
+ setFilteredUserData(null);
+ setIsLoadingSearch(false);
+ };
+
+ const addMemberToList = (member: User) => {
+ let errorMessage = '';
+
+ errorMessage = addWalletValidation(member, memberList, groupMembers, account);
+
+ if (errorMessage) {
+ groupInfoToast.showMessageToast({
+ toastTitle: 'Error',
+ toastMessage: errorMessage,
+ toastType: 'ERROR',
+ getToastIcon: (size) => (
+
+ ),
+ });
+ } else {
+ handleMemberList((prev: any) => [...prev, { ...member, isAdmin: false }]);
+ }
+
+ setFilteredUserData('');
+ clearInput();
+ };
+
+ const removeMemberFromList = (member: User) => {
+ const filteredMembers = memberList?.filter((user: any) => user.wallets !== member.wallets);
+ handleMemberList(filteredMembers);
+ };
+
+ return (
+
+
+
+ handlePrevious()} cursor='pointer' />
+
+ Add Wallets
+
+ onClose()} cursor='pointer' />
+
+
+
+ Add Wallets
+
+
+ {groupMembers
+ ? `0${memberList?.length + groupMembers?.length} / 09 Members`
+ : `0${memberList?.length} / 09 Members`}
+
+
+
+
+
+ {filteredUserData ? (
+
+ }
+ darkIcon={}
+ />
+
+ ) : isLoadingSearch ? (
+
+ ) : null}
+
+
+ {memberList?.map((member: any, index: any) => (
+ }
+ darkIcon={}
+ />
+ ))}
+
+
+
+ onSubmit()}
+ isLoading={isLoading}
+ memberListCount={memberList?.length > 0}
+ theme={theme}
+ >
+ {!isLoading && groupMembers ? 'Add To Group' : ''}
+ {isLoading && }
+ ModalConfirmButton>
+
+
+
+ )
+}
+
+const SearchBarContent = styled.form`
+ position: relative;
+ display: flex;
+ flex: 1;
+`;
+
+const Input = styled.input`
+ box-sizing: border-box;
+ display: flex;
+ flex: 1;
+// min-width: 445px;
+ height: 48px;
+ padding: 0px 50px 0px 16px;
+ margin: 10px 0px 0px;
+ border-radius: 99px;
+ border: 1px solid;
+ border-color: ${(props) => props.theme.modalSearchBarBorderColor};
+ background: ${(props) => props.theme.modalSearchBarBackground};
+ color: ${(props) => props.color || '#000'};
+ &:focus {
+ outline: none;
+ background-image: linear-gradient(
+ ${(props) => props.theme.snapFocusBg},
+ ${(props) => props.theme.snapFocusBg}
+ ),
+ linear-gradient(
+ to right,
+ rgba(182, 160, 245, 1),
+ rgba(244, 110, 246, 1),
+ rgba(255, 222, 211, 1),
+ rgba(255, 207, 197, 1)
+ );
+ background-origin: border;
+ border: 1px solid transparent !important;
+ background-clip: padding-box, border-box;
+ }
+ &::placeholder {
+ color: #657795;
+ }
+ @media ${device.mobileL} {
+ min-width: 100%;
+ }
+`;
+
+const MemberList = styled.div`
+ // justify-content: flex-start;
+ // padding: 0px 2px;
+ // margin: 0 0 34px 0;
+ flex: 1;
+ // background: red;
+ width: 100%;
+`;
+
+const MultipleMemberList = styled.div`
+ // overflow-y: auto;
+ height: fit-content;
+ max-height: 216px;
+ padding: 0px 2px;
+ // overflow-x: hidden;
+ width: 100%;
+
+ &::-webkit-scrollbar-track {
+ background-color: ${(props) => props.theme.scrollBg};
+ }
+
+ &::-webkit-scrollbar {
+ background-color: ${(props) => props.theme.scrollBg};
+ width: 6px;
+ }
+
+ @media (max-width: 768px) {
+ padding: 0px 0px 0px 0px;
+ max-height: 35vh;
+
+ &::-webkit-scrollbar-track {
+ background-color: none;
+ border-radius: 9px;
+ }
+
+ &::-webkit-scrollbar {
+ background-color: none;
+ width: 4px;
+ }
+ }
+
+ &::-webkit-scrollbar-thumb {
+ border-radius: 10px;
+ background-image: -webkit-gradient(
+ linear,
+ left top,
+ left bottom,
+ color-stop(0.44, #cf1c84),
+ color-stop(0.72, #cf1c84),
+ color-stop(0.86, #cf1c84)
+ );
+ }
+`;
+
+const ModalConfirmButton = styled.button`
+ margin: 60px 0 0 0;
+ background: ${(props) => props.memberListCount ? '#CF1C84' : props.theme.groupButtonBackgroundColor};
+ color: ${(props) => props.memberListCount ? '#fff' : props.theme.groupButtonTextColor};
+ border: ${(props) => props.memberListCount ? 'none' : props.theme.modalConfirmButtonBorder};
+ min-width: 50%;
+ box-sizing: border-box;
+ cursor: pointer;
+ border-radius: 15px;
+ padding: 16px;
+ font-size: 1.125rem;
+ font-weight: 500;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: none;
+`;
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/components/chat/ChatProfile/ChatProfile.tsx b/packages/uiweb/src/lib/components/chat/ChatProfile/ChatProfile.tsx
new file mode 100644
index 000000000..4949a44b6
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/ChatProfile/ChatProfile.tsx
@@ -0,0 +1,241 @@
+// @typescript-eslint/no-non-null-asserted-optional-chain
+
+import { useContext, useEffect, useRef, useState } from "react";
+import { Image, Section, Span } from "../../reusables";
+import styled from "styled-components";
+import TokenGatedIcon from '../../../icons/Token-Gated.svg';
+import PublicChatIcon from '../../../icons/Public-Chat.svg';
+import VideoChatIcon from '../../../icons/VideoCallIcon.svg';
+import GreyImage from '../../../icons/greyImage.png';
+import InfoIcon from '../../../icons/infodark.svg';
+import VerticalEllipsisIcon from '../../../icons/VerticalEllipsis.svg';
+import type { IUser } from '@pushprotocol/restapi';
+import { useChatData, useClickAway, useDeviceWidthCheck } from "../../../hooks";
+import { ThemeContext } from "../theme/ThemeProvider";
+import { IChatTheme } from "../theme";
+import { pCAIP10ToWallet, resolveEns, resolveNewEns, shortenText } from "../../../helpers";
+import useGetGroupByID from "../../../hooks/chat/useGetGroupByID";
+import useChatProfile from "../../../hooks/chat/useChatProfile";
+import { IGroup } from "../../../types";
+import { GroupInfoModal } from "./GroupInfoModal";
+import { isValidETHAddress } from "../helpers/helper";
+import { ethers } from "ethers";
+import { IChatProfile, IToast, OptionProps } from "../exportedTypes";
+import { InfuraAPIKey, allowedNetworks, device } from "../../../config";
+import Toast from "../helpers/Toast";
+import useMediaQuery from "../helpers/useMediaQuery";
+import { createBlockie } from "../../space/helpers/blockies";
+// import { NewToast } from "../helpers/NewToast";
+import { ToastContainer, toast } from 'react-toastify';
+import 'react-toastify/dist/ReactToastify.min.css';
+
+
+
+const Options = ({ options, setOptions, isGroup, chatInfo, groupInfo, setGroupInfo,theme }: OptionProps) => {
+ const DropdownRef = useRef(null);
+ const [modal, setModal] = useState(false);
+
+ useClickAway(DropdownRef, () => {
+ setOptions(false);
+ });
+
+ const ShowModal = () => {
+ setModal(true);
+ }
+
+ if (groupInfo && isGroup){
+ return (
+
+
+
+ {groupInfo?.isPublic &&
+ ()}
+
+ setOptions(true)}>
+
+
+ {options &&
+ (
+
+
+
+
+ Group Info
+
+
+ )}
+
+ {modal &&
+ ()}
+
+
+ )
+ } else {
+ return null }
+ };
+
+
+
+
+
+export const ChatProfile: React.FC = ({ chatId, style }: {chatId: string, style: "Info" | "Preview"}) => {
+ const theme = useContext(ThemeContext);
+ const { account, env } = useChatData();
+ const { getGroupByID } = useGetGroupByID();
+ const { fetchUserChatProfile } = useChatProfile();
+
+ const [isGroup, setIsGroup] = useState(false);
+ const [options, setOptions] = useState(false);
+ const [chatInfo, setChatInfo ] = useState();
+ const [groupInfo, setGroupInfo ] = useState();
+ const [ensName, setEnsName ] = useState('');
+ const isMobile = useMediaQuery(device.tablet);
+ const l1ChainId = allowedNetworks[env].includes(1) ? 1 : 5;
+ const provider = new ethers.providers.InfuraProvider(l1ChainId, InfuraAPIKey);
+
+
+
+ const fetchProfileData = async () => {
+ if(isValidETHAddress(chatId)){
+ const ChatProfile = await fetchUserChatProfile({ profileId: chatId });
+ setChatInfo(ChatProfile);
+ setGroupInfo(null);
+ setIsGroup(false);
+ } else {
+ const GroupProfile = await getGroupByID({ groupId : chatId})
+ setGroupInfo(GroupProfile);
+ setChatInfo(null);
+ setIsGroup(true);
+ }
+ }
+
+ const getName = async (chatId: string) => {
+ if(isValidETHAddress(chatId)){
+ const result = await resolveNewEns(chatId, provider);
+ // if(result)
+ console.log(result);
+ setEnsName(result);
+ }
+ }
+
+
+ useEffect(()=> {
+ if(!chatId) return;
+ fetchProfileData();
+ getName(chatId);
+ },[chatId, account, env])
+
+ if (chatId && style === 'Info') {
+ return (
+
+ {chatInfo || groupInfo ? (
+
+ ) : ()}
+
+
+
+ {isGroup ? groupInfo?.groupName : ensName ? `${ensName} (${isMobile ? shortenText(chatInfo?.did?.split(':')[1] ?? '', 4, true) : chatId})`: chatInfo ? shortenText(chatInfo.did?.split(':')[1] ?? '', 6, true) : shortenText(chatId,6, true)}
+
+
+
+
+
+ {/* {!isGroup &&
+
+
+
+ } */}
+
+
+
+
+
+ )
+ } else {
+ return null;
+ }
+}
+
+
+const Container = styled.div`
+ width: 100%;
+ background: ${(props) => props.theme.bgColorPrimary};
+ border-radius: 32px;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ padding: 6px;
+ box-sizing: border-box;
+ position: relative;
+`;
+
+const ImageItem = styled.div`
+ position: relative;
+`;
+
+const DummyImage = styled.div`
+ height: 48px;
+ width: 48px;
+ border-radius: 100%;
+ background: #ccc;
+`;
+
+const DropDownBar = styled.div`
+ position: absolute;
+ top: 30px;
+ left: -130px;
+ display: block;
+ min-width: 140px;
+ color: rgb(101, 119, 149);
+ border: ${(props) => `1px solid ${props.theme.defaultBorder}`};
+ background: ${(props) => props.theme.bgColorPrimary};
+ z-index: 10;
+ border-radius: 16px;
+`;
+
+const VideoChatSection = styled.div`
+ margin: 0 25px 0 auto;
+`;
+
+const DropDownItem = styled(Span)`
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 8px;
+ padding: 10px 16px;
+ border-radius: 16px;
+ z-index: 3000000;
+ width: 100%;
+`;
+
+const TextItem = styled(Span)`
+ white-space: nowrap;
+ overflow: hidden;
+`;
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/uiweb/src/lib/components/chat/ChatProfile/DropDown.tsx b/packages/uiweb/src/lib/components/chat/ChatProfile/DropDown.tsx
new file mode 100644
index 000000000..991c1e6c8
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/ChatProfile/DropDown.tsx
@@ -0,0 +1,205 @@
+// React + Web3 Essentials
+import { useContext } from 'react';
+
+// External Packages
+import styled from 'styled-components';
+import { shortenText } from "../../../helpers";
+
+// Internal Components
+import { Image, Section, Span } from "../../reusables";
+import { ThemeContext } from "../theme/ThemeProvider";
+
+
+export type DropdownValueType = {
+ id: number|string,
+ value?: string,
+ title: string,
+ icon: string,
+ textColor?: string,
+ function: () => void,
+}
+
+type DropdownProps = {
+ dropdownValues: any[];
+ textColor?: string;
+ iconFilter?: string;
+ hoverBGColor?: string;
+};
+
+
+// Create Dropdown
+function Dropdown({ dropdownValues, textColor, iconFilter, hoverBGColor }: DropdownProps) {
+ const theme = useContext(ThemeContext);
+
+
+ const getTextColor = (dropdownValue:DropdownValueType) => {
+ return dropdownValue.textColor ? dropdownValue.textColor : textColor ? textColor : theme.snackbarBorderText;
+ }
+
+
+ const copyToClipboard = (address:string) => {
+ if (navigator && navigator.clipboard) {
+ navigator.clipboard.writeText(address);
+ } else {
+ const el = document.createElement('textarea');
+ el.value = address;
+ document.body.appendChild(el);
+ el.select();
+ document.execCommand('copy');
+ document.body.removeChild(el);
+ }
+ };
+ return (
+ <>
+ {dropdownValues.map((dropdownValue) =>
+ dropdownValue?.id === 'walletAddress' ? (
+ dropdownValue?.function()}
+ >
+
+ {dropdownValue?.title}
+
+ {shortenText(dropdownValue?.title,6)}
+
+
+ {dropdownValue?.invertedIcon && (
+ {
+ copyToClipboard(dropdownValue?.value);
+ }}
+ />
+ )}
+ {dropdownValue?.icon && (
+ {
+ copyToClipboard(dropdownValue?.value);
+ }}
+ />
+ )}
+
+ ) : (
+ dropdownValue?.function()}>
+ {dropdownValue?.invertedIcon && (
+
+ )}
+ {dropdownValue?.icon && (
+
+ )}
+ {!dropdownValue?.link && dropdownValue?.function && (
+
+ {dropdownValue.title}
+
+ )}
+ {dropdownValue?.link && (
+
+ {dropdownValue.title}
+
+ )}
+
+ )
+ )}
+ >
+ );
+}
+
+// css styles
+const SpanAddress = styled(Span)`
+ margin: 11px 22px 11px 2px;
+ font-weight: 400;
+ size: 14px;
+ text-transform: uppercase;
+ color: #fff;
+ spacing: 1px;
+ width: max-content;
+`;
+
+const MobileAddress = styled(SpanAddress)`
+ @media (min-width: 993px) {
+ display: none;
+ }
+`;
+
+const DesktopAddress = styled(SpanAddress)`
+ @media (max-width: 992px) {
+ display: none;
+ }
+`;
+
+const DropdownItemContainer = styled(Section)<{hoverBGColor?: string}>`
+ width: 12.5rem;
+ justify-content: flex-start;
+ flex-wrap: nowrap;
+ margin: 1px 0;
+ padding: 2px 8px;
+ border-radius: 12px;
+ cursor: pointer;
+ text-align: left;
+
+ &:hover {
+ background-color: ${(props) => props.hoverBGColor || 'none'};
+ }
+`;
+
+
+const A = styled.a`
+ margin: 8px 10px;
+ font-weight: 400;
+ font-size: 16px;
+ width: max-content;
+
+ background: ${(props) => props.color};
+ z-index: 11;
+ &. hover {
+ background: transparent !important;
+ }
+`;
+
+export default Dropdown;
diff --git a/packages/uiweb/src/lib/components/chat/ChatProfile/GroupInfoModal.tsx b/packages/uiweb/src/lib/components/chat/ChatProfile/GroupInfoModal.tsx
new file mode 100644
index 000000000..c5a03ca8b
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/ChatProfile/GroupInfoModal.tsx
@@ -0,0 +1,635 @@
+import { useRef, useState } from "react";
+import styled from "styled-components";
+import { useChatData, useClickAway } from "../../../hooks";
+import { IGroup } from "../../../types";
+import { IChatTheme } from "../theme";
+import * as PushAPI from '@pushprotocol/restapi';
+import { IToast, ShadowedProps, UpdateGroupType } from "../exportedTypes";
+import { convertToWalletAddressList, getAdminList, getUpdatedAdminList, getUpdatedMemberList } from "../helpers/helper";
+import { DropdownValueType } from "./DropDown";
+import DismissAdmin from '../../../icons/dismissadmin.svg';
+import AddAdmin from '../../../icons/addadmin.svg';
+import Remove from '../../../icons/remove.svg';
+import { Section, Span, Image } from "../../reusables/sharedStyling";
+import CloseIcon from '../../../icons/close.svg';
+import { ProfileCard } from "./ProfileCard";
+import addIcon from '../../../icons/addicon.svg';
+import { pCAIP10ToWallet, shortenText } from "../../../helpers";
+import LockIcon from '../../../icons/Lock.png'
+import LockSlashIcon from '../../../icons/LockSlash.png'
+import { AddWalletContent } from './AddWalletContent'
+import ArrowIcon from '../../../icons/CaretDown.svg'
+import { Modal } from "../helpers/Modal";
+import { device } from "../../../config";
+import useMediaQuery from "../helpers/useMediaQuery";
+import useToast from "../helpers/NewToast";
+import { MdCheckCircle, MdError } from "react-icons/md";
+
+
+
+const PendingMembers = ({ groupInfo, setShowPendingRequests, showPendingRequests, theme }: {groupInfo?: IGroup | null, setShowPendingRequests: React.Dispatch>, showPendingRequests: boolean, theme: IChatTheme }) => {
+ if(groupInfo){
+ return (
+
+ setShowPendingRequests(!showPendingRequests)}>
+ Pending Requests
+ {groupInfo?.pendingMembers?.length}
+
+
+ {/* */}
+
+
+ {showPendingRequests && (
+
+ {groupInfo?.pendingMembers && groupInfo?.pendingMembers?.length > 0 && groupInfo?.pendingMembers.map((item) => (
+
+
+
+
+ {shortenText(item?.wallet?.split(':')[1] ?? '', 6, true)}
+
+
+
+ ))}
+
+ )}
+
+ )
+ } else {return null }
+}
+
+export const GroupInfoModal = ({ theme, modal, setModal, groupInfo, setGroupInfo }: { theme: IChatTheme, modal: boolean, setModal: React.Dispatch>, groupInfo: IGroup, setGroupInfo: React.Dispatch> }) => {
+ const { account, env, pgpPrivateKey } = useChatData();
+ const [showAddMoreWalletModal, setShowAddMoreWalletModal] = useState(false);
+ const [showPendingRequests, setShowPendingRequests] = useState(false);
+ const [memberList, setMemberList] = useState([]);
+ const [isLoading, setIsLoading] = useState(false);
+ const [selectedMemberAddress, setSelectedMemberAddress] = useState(null);
+
+ const handleClose = () => onClose();
+ const dropdownRef = useRef(null);
+ useClickAway(dropdownRef, () => setSelectedMemberAddress(null));
+ const groupInfoToast = useToast();
+
+
+ const groupCreator = groupInfo?.groupCreator;
+ const membersExceptGroupCreator = groupInfo?.members?.filter((x) => x.wallet?.toLowerCase() !== groupCreator?.toLowerCase());
+ const groupMembers = [...membersExceptGroupCreator, ...groupInfo.pendingMembers];
+
+
+ const updateGroup = async (options:UpdateGroupType) => {
+ const { groupInfo, connectedUser,adminList,memberList } = options;
+ const updateResponse = await PushAPI.chat.updateGroup({
+ chatId: groupInfo?.chatId,
+ groupName: groupInfo?.groupName,
+ groupDescription: groupInfo?.groupDescription ?? '',
+ groupImage: groupInfo?.groupImage,
+ members: memberList,
+ admins: adminList,
+ account: connectedUser?.wallets,
+ pgpPrivateKey: pgpPrivateKey,
+ env: env,
+ });
+ let updatedCurrentChat = null;
+ if(typeof updateResponse !== 'string')
+ {
+ updatedCurrentChat = groupInfo;
+ updatedCurrentChat = updateResponse;
+ }
+ return {updateResponse,updatedCurrentChat};
+ }
+
+ const addMembers = async () => {
+ //Already Present Members and PendingMembers
+ const groupMemberList = convertToWalletAddressList([
+ ...groupInfo.members,
+ ...groupInfo.pendingMembers,
+ ]);
+
+ //Newly Added Members and alreadyPresent Members in the groupchat
+ const newMembersToAdd = memberList.map((member: any) => member.wallets);
+ const members = [...groupMemberList, ...newMembersToAdd];
+
+ //Admins wallet address from both members and pendingMembers
+ const adminList = getAdminList?.(groupInfo);
+
+
+ try {
+ setIsLoading(true);
+ const connectedUser = await PushAPI.user.get({ account: account as string, env });
+ const { updateResponse, updatedCurrentChat } = await updateGroup({
+ groupInfo,
+ connectedUser,
+ adminList,
+ memberList: members,
+ });
+
+ if (typeof updateResponse !== 'string') {
+ setSelectedMemberAddress(null);
+ setGroupInfo(updateResponse);
+ } else {
+ groupInfoToast.showMessageToast({
+ toastTitle: 'Error',
+ toastMessage: updateResponse,
+ toastType: 'ERROR',
+ getToastIcon: (size) => (
+
+ ),
+ });
+ setSelectedMemberAddress(null);
+ }
+ setIsLoading(false);
+ groupInfoToast.showMessageToast({
+ toastTitle: 'Success',
+ toastMessage: 'Group Invitation sent',
+ toastType: 'SUCCESS',
+ getToastIcon: (size) => (
+
+ ),
+ });
+ handleClose();
+ } catch (error) {
+ setIsLoading(false);
+ console.log('Error', error);
+ groupInfoToast.showMessageToast({
+ toastTitle: 'Error',
+ toastMessage: 'Please, try again',
+ toastType: 'ERROR',
+ getToastIcon: (size) => (
+
+ ),
+ });
+ }
+ };
+
+
+ const makeGroupAdmin = async () => {
+ const groupMemberList = convertToWalletAddressList([
+ ...groupInfo.members,
+ ...groupInfo.pendingMembers,
+ ]);
+ const newAdminList = getUpdatedAdminList(groupInfo, selectedMemberAddress, false);
+ try {
+ const connectedUser = await PushAPI.user.get({ account: account as string, env });
+ const { updateResponse, updatedCurrentChat } = await updateGroup({
+ groupInfo,
+ connectedUser,
+ adminList: newAdminList,
+ memberList: groupMemberList,
+ });
+ if (typeof updateResponse !== 'string') {
+ setSelectedMemberAddress(null);
+ setGroupInfo(updateResponse);
+
+ groupInfoToast.showMessageToast({
+ toastTitle: 'Success',
+ toastMessage: 'Admin added successfully',
+ toastType: 'SUCCESS',
+ getToastIcon: (size) => (
+ ),
+ });
+ } else {
+ groupInfoToast.showMessageToast({
+ toastTitle: 'Error',
+ toastMessage: updateResponse,
+ toastType: 'ERROR',
+ getToastIcon: (size) => (
+
+ ),
+ });
+ setSelectedMemberAddress(null);
+ }
+ } catch (e) {
+ console.error('Error while adding admin', e);
+ groupInfoToast.showMessageToast({
+ toastTitle: 'Error',
+ toastMessage: 'Error',
+ toastType: 'ERROR',
+ getToastIcon: (size) => (
+
+ ),
+ });
+ }
+ setSelectedMemberAddress(null);
+ };
+
+ const dismissGroupAdmin = async () => {
+ const groupMemberList = convertToWalletAddressList([
+ ...groupInfo.members,
+ ...groupInfo.pendingMembers,
+ ]);
+ const newAdminList = getUpdatedAdminList(groupInfo, selectedMemberAddress, true);
+ try {
+ const connectedUser = await PushAPI.user.get({ account: account as string, env });
+ const { updateResponse, updatedCurrentChat } = await updateGroup({
+ groupInfo,
+ connectedUser,
+ adminList: newAdminList,
+ memberList: groupMemberList,
+ });
+ if (typeof updateResponse !== 'string') {
+ setSelectedMemberAddress(null);
+ setGroupInfo(updateResponse);
+
+ groupInfoToast.showMessageToast({
+ toastTitle: 'Success',
+ toastMessage: 'Admin removed successfully',
+ toastType: 'SUCCESS',
+ getToastIcon: (size) => (
+ ),
+ });
+
+ } else {
+ groupInfoToast.showMessageToast({
+ toastTitle: 'Error',
+ toastMessage: updateResponse,
+ toastType: 'ERROR',
+ getToastIcon: (size) => (
+
+ ),
+ });
+ setSelectedMemberAddress(null);
+ }
+ } catch (e) {
+ console.error('Error while dismissing admin', e);
+ groupInfoToast.showMessageToast({
+ toastTitle: 'Error',
+ toastMessage: 'Please, try again',
+ toastType: 'ERROR',
+ getToastIcon: (size) => (
+
+ ),
+ });
+ }
+ setSelectedMemberAddress(null);
+ };
+
+ const removeMember = async () => {
+ const updatedMemberList = getUpdatedMemberList(groupInfo, selectedMemberAddress!);
+ const adminList = getUpdatedAdminList(groupInfo, selectedMemberAddress, true);
+ try {
+ const connectedUser = await PushAPI.user.get({ account: account as string, env });
+ const { updateResponse, updatedCurrentChat } = await updateGroup({
+ groupInfo,
+ connectedUser,
+ adminList,
+ memberList: updatedMemberList,
+ });
+
+ if (typeof updateResponse !== 'string') {
+ setSelectedMemberAddress(null);
+ setGroupInfo(updateResponse);
+
+ groupInfoToast.showMessageToast({
+ toastTitle: 'Success',
+ toastMessage: 'Removed Member successfully',
+ toastType: 'SUCCESS',
+ getToastIcon: (size) => (
+
+ ),
+ });
+ } else {
+ groupInfoToast.showMessageToast({
+ toastTitle: 'Error',
+ toastMessage: updateResponse,
+ toastType: 'ERROR',
+ getToastIcon: (size) => (
+
+ ),
+ });
+ setSelectedMemberAddress(null);
+ }
+ } catch (error) {
+ console.error('Error in removing member', error);
+ groupInfoToast.showMessageToast({
+ toastTitle: 'Error',
+ toastMessage: 'Please, try again',
+ toastType: 'ERROR',
+ getToastIcon: (size) => (
+
+ ),
+ });
+ }
+ setSelectedMemberAddress(null);
+ };
+
+ // const messageUserDropdown: DropdownValueType = {
+ // id: 'message_user',
+ // title: 'Message user',
+ // icon: Message,
+ // function: () => messageUser(),
+ // };
+ const removeAdminDropdown: DropdownValueType = {
+ id: 'dismiss_admin',
+ title: 'Dismiss as admin',
+ icon: DismissAdmin,
+ function: () => dismissGroupAdmin(),
+ };
+ const addAdminDropdown: DropdownValueType = {
+ id: 'add_admin',
+ title: 'Make group admin',
+ icon: AddAdmin,
+ function: () => makeGroupAdmin(),
+ };
+ const removeMemberDropdown: DropdownValueType = {
+ id: 'remove_member',
+ title: 'Remove',
+ icon: Remove,
+ function: () => removeMember(),
+ textColor: '#ED5858',
+ };
+
+
+
+ const isAccountOwnerAdmin = groupInfo?.members?.some(
+ (member) => pCAIP10ToWallet(member?.wallet)?.toLowerCase() === account?.toLowerCase() && member?.isAdmin
+ );
+
+ const handlePrevious = () => {
+ setShowAddMoreWalletModal(false);
+ };
+
+ const onClose = () => {
+ setModal(false);
+ }
+
+ const isMobile = useMediaQuery(device.mobileL);
+ if(groupInfo){
+ return(
+
+ {!showAddMoreWalletModal && (
+
+
+
+
+ Group Info
+
+ onClose()} cursor='pointer' />
+
+
+
+
+
+
+ {groupInfo?.groupName}
+ {groupInfo?.members?.length} Members
+
+
+
+
+ Group Description
+ {groupInfo?.groupDescription}
+
+
+
+
+
+
+ {groupInfo?.isPublic ? 'Public' : 'Private'}
+ {groupInfo?.isPublic ? 'Chats are not encrypted' : 'Chats are encrypted'}
+
+
+
+ {isAccountOwnerAdmin && groupInfo?.members && groupInfo?.members?.length < 10 && (
+ setShowAddMoreWalletModal(true)}
+ >
+
+
+
+ Add more wallets
+
+ )}
+
+
+ {groupInfo?.pendingMembers?.length > 0 && (
+
+ )}
+
+
+
+ {groupInfo?.members && groupInfo?.members?.length > 0 && groupInfo?.members.map((item, index) => (
+
+ ))}
+
+
+ )}
+
+
+
+ {showAddMoreWalletModal && (
+
+ )}
+
+ )
+} else { return null }
+
+}
+
+const ProfileDiv = styled.div<{minHeight?: number}>`
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ padding-right: 3px;
+ align-items: center;
+ min-width: 445px;
+ min-height: 72px;
+ max-height: 216px;
+ min-height: ${(props) => `${props.minHeight}px`};
+ overflow-y: auto;
+ overflow-x: hidden;
+ &&::-webkit-scrollbar {
+ width: 4px;
+ }
+ &&::-webkit-scrollbar-thumb {
+ background: #cf1c84;
+ border-radius: 10px;
+ }
+ @media (max-width: 480px) {
+ min-width: 300px;
+ }
+`;
+
+const GroupHeader = styled.div`
+ margin-top: 34px;
+ display: flex;
+ flex-direction: row;
+ width: 100%;
+ gap: 19px;
+`;
+
+const GroupDescription = styled.div`
+ margin-top: 34px;
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ align-items: flex-start;
+ gap: 5px;
+`;
+
+
+const PublicEncrypted = styled.div`
+ margin-top: 20px;
+ display: flex;
+ flex-direction: row;
+ width: 100%;
+ gap: 19px;
+ align-items: center;
+ border: ${(props) => `1px solid ${props.theme.defaultBorder}`};
+ border-radius: 16px;
+ padding: 16px;
+ box-sizing: border-box;
+`;
+
+const GroupMembers = styled.div`
+ margin-top: 20px;
+ display: flex;
+ flex-direction: row;
+ width: 100%;
+ align-items: center;
+`;
+
+const AdminItem = styled.div`
+ background: rgb(244, 220, 234);
+ color: rgb(213, 58, 148);
+ margin-left: auto;
+ font-size: 10px;
+ padding: 6px;
+ border-radius: 8px;
+`;
+
+const AddWalletContainer = styled.div`
+ margin-top: 20px;
+ border: ${(props) => `1px solid ${props.theme.defaultBorder}`};
+ border-radius: 16px;
+ width: 100%;
+ padding: 20px 16px;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+ cursor: pointer;
+ align-items: center;
+`;
+
+const GroupPendingMembers = styled.div`
+ margin-top: 3px;
+ display: flex;
+ flex-direction: row;
+ width: 100%;
+ align-items: center;
+ background: ${(props) => props.theme.pendingCardBackground};
+ padding: 10px 15px;
+ box-sizing: border-box;
+
+ &:last-child {
+ border-radius: 0px 0px 16px 16px;
+ }
+`;
+
+
+const PendingRequestWrapper = styled.div`
+ width: 100%;
+ margin-top: 20px;
+ border: ${(props) => `1px solid ${props.theme.defaultBorder}`};
+ border-radius: 16px;
+ padding: 0px 0px;
+ box-sizing: border-box;
+`;
+
+const PendingSection = styled.div`
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ flex: 1;
+ cursor: pointer;
+ padding: 15px 20px;
+ box-sizing: border-box;
+`;
+
+const ArrowImage = styled(Image)`
+ margin-left: auto;
+ transform: ${(props) => props?.setPosition ? 'rotate(0)' : 'rotate(180deg)'};
+`;
+
+
+const Badge = styled.div`
+ margin: 0 0 0 5px;
+ font-size: 13px;
+ background: rgb(207, 28, 132);
+ padding: 4px 8px;
+ border-radius: 7px;
+ color: white;
+ font-weight: 700;
+`;
diff --git a/packages/uiweb/src/lib/components/chat/ChatProfile/MemberListContainer.tsx b/packages/uiweb/src/lib/components/chat/ChatProfile/MemberListContainer.tsx
new file mode 100644
index 000000000..c92529fef
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/ChatProfile/MemberListContainer.tsx
@@ -0,0 +1,191 @@
+import { useContext, useRef, useState } from "react";
+import { ThemeContext } from "../theme/ThemeProvider";
+import { useClickAway } from "../../../hooks";
+import Dropdown, { DropdownValueType } from "./DropDown";
+import DismissAdmin from '../../../icons/dismissadmin.svg';
+import AddAdmin from '../../../icons/addadmin.svg';
+import Remove from '../../../icons/remove.svg';
+import styled from "styled-components";
+import { Section, Image, Span } from "../../reusables/sharedStyling";
+import { MemberListContainerType, WalletProfileContainerProps } from "../exportedTypes";
+import { findObject } from "../helpers/helper";
+import { device } from "../../../config";
+import { shortenText } from "../../../helpers";
+
+export const MemberListContainer = ({ key, memberData, handleMembers, handleMemberList, lightIcon, darkIcon, memberList }: MemberListContainerType) => {
+ const theme = useContext(ThemeContext);
+ const [selectedWallet, setSelectedWallet] = useState(null);
+ const [dropdownHeight, setDropdownHeight] = useState(0);
+ const dropdownRef = useRef(null);
+
+
+ useClickAway(dropdownRef, () => setSelectedWallet(null));
+
+ const removeAdminDropdown: DropdownValueType =
+ { id: 'dismiss_admin', title: 'Dismiss as admin', icon: DismissAdmin, function: () => dismissGroupAdmin() }
+
+ const addAdminDropdown: DropdownValueType =
+ { id: 'dismiss_admin', title: 'Make group admin', icon: AddAdmin, function: () => makeGroupAdmin() }
+
+ const removeUserDropdown: DropdownValueType =
+ { id: 'remove_user', title: 'Remove', icon: Remove, function: () => removeUser() }
+
+ const dismissGroupAdmin = () => {
+ const updatedMembers = memberList.map((member:any) => member?.wallets?.toLowerCase() == memberData?.wallets?.toLowerCase() ? ({ ...member, isAdmin: false }) : member)
+ handleMembers?.(updatedMembers)
+ setSelectedWallet(null)
+ }
+
+ const makeGroupAdmin = () => {
+ const updatedMembers = memberList.map((member: any) => member?.wallets?.toLowerCase() == memberData?.wallets?.toLowerCase() ? ({ ...member, isAdmin: true }) : member)
+ handleMembers?.(updatedMembers)
+ setSelectedWallet(null)
+ }
+
+ const removeUser = () => {
+ handleMemberList(memberData)
+ setSelectedWallet(null)
+ }
+
+
+ const handleHeight = (id: any) => {
+ const containerHeight = document.getElementById(id)?.getBoundingClientRect();
+ setDropdownHeight(containerHeight?.top);
+ };
+
+
+ return (
+
+
+
+
+ {shortenText(memberData?.wallets?.split(':')[1], 8, true)}
+
+
+
+ {memberData?.isAdmin && (
+
+ Admin
+
+ )}
+ {
+ handleHeight(memberData?.wallets);
+ setSelectedWallet(null)
+ memberList
+ ? findObject(memberData, memberList, 'wallets')
+ ? setSelectedWallet(memberData?.wallets)
+ : handleMemberList(memberData)
+ : handleMemberList(memberData)
+ }}
+ >
+ {/* {theme === 'light' ? lightIcon : darkIcon} */}
+ {darkIcon}
+
+
+
+ {selectedWallet?.toLowerCase() == memberData?.wallets?.toLowerCase() && (
+ 500 ? '30%' : "45%" }} ref={dropdownRef} theme={theme}>
+
+
+ )}
+
+ WalletProfileContainer>
+ )
+}
+
+
+const WalletProfileContainer = styled(Section)`
+ // position: relative;
+ // padding: 5px 16px;
+ // margin: 8px 0px;
+ // justify-content: space-between;
+ // // min-width: 450px;
+ // min-width: 100%;
+ // box-sizing: border-box;
+ // align-items: center;
+ // border-radius: 16px;
+
+ // @media (max-width: 480px) {
+ // // min-width: 300px;
+ // }
+
+ justify-content: space-between;
+ padding: 8px 16px;
+ border-radius: 16px;
+ position: relative;
+ box-sizing: border-box;
+ width: 100%;
+ // background-color: ${(props) => props.theme.snapFocusBg};
+ max-height: 64px;
+ align-self: stretch;
+ display: flex;
+ height: auto;
+ z-index: auto;
+ flex: 1;
+ @media (max-width: 480px) {
+ max-width: 100%;
+ }
+
+`;
+
+const WalletProfile = styled(Section)`
+ justify-content: flex-start;
+`;
+
+const DropdownContainer = styled.div`
+ // position: absolute;
+ // left: 48%;
+ // border-radius: 16px;
+ // padding: 14px 8px;
+ // background: ${(props) => props.theme.modalContentBackground};
+ // border: 1px solid ${(props) => props.theme.modalBorderColor};
+ // z-index: 400;
+ // @media ${device.mobileL} {
+ // left: 27%;
+ // }
+ // @media (min-width: 426px) and (max-width: 1150px) {
+ // left: 47%;
+ // }
+ position: absolute;
+ left: 48%;
+ top: 69%;
+ border-radius: 16px;
+ padding: 14px 8px;
+ z-index: 999999999999 !important;
+ display: flex;
+ flex-direction: column !important;
+ background: ${(props) => props.theme.modalContentBackground};
+ border: 1px solid ${(props) => props.theme.modalBorderColor};
+
+ @media ${device.mobileL} {
+ left: 27%;
+ }
+ @media (min-width: 426px) and (max-width: 1150px) {
+ left: 48%;
+ }
+ @media (max-width: 480px){
+ left: 25%;
+ }
+`;
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/components/chat/ChatProfile/ProfileCard.tsx b/packages/uiweb/src/lib/components/chat/ChatProfile/ProfileCard.tsx
new file mode 100644
index 000000000..2ee09770f
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/ChatProfile/ProfileCard.tsx
@@ -0,0 +1,158 @@
+// React + Web3 Essentials
+import React, { useContext ,useState } from 'react';
+
+// External Packages
+import styled from 'styled-components';
+import { ethers } from 'ethers';
+
+// Internal Components
+import { MoreLightIcon } from '../../../icons/MoreLight';
+import { MoreDarkIcon } from '../../../icons/MoreDark';
+import { shortenText } from "../../../helpers";
+import { ThemeContext } from "../theme/ThemeProvider";
+import { useChatData, useClickAway} from "../../../hooks";
+import { Image, Section, Span } from "../../reusables";
+import Dropdown from './DropDown';
+import { pCAIP10ToWallet } from '../../../helpers';
+import { device } from "../../../config";
+
+
+type ProfileCardProps = {
+ key?: number | string,
+ member?: any,
+ dropdownValues?: any;
+ selectedMemberAddress?: any;
+ setSelectedMemberAddress?: any;
+ dropdownRef?: any;
+}
+
+export const ProfileCard = ({
+ key,
+ member,
+ dropdownValues,
+ selectedMemberAddress,
+ setSelectedMemberAddress,
+ dropdownRef,
+}: ProfileCardProps) => {
+ const theme = useContext(ThemeContext);
+ const { account } = useChatData();
+
+ const [dropdownHeight, setDropdownHeight] = useState(0);
+
+ const handleHeight = (id: any) => {
+ const containerHeight = document.getElementById(id)?.getBoundingClientRect();
+ setDropdownHeight(containerHeight?.top);
+ };
+
+ return (
+
+
+
+
+ {shortenText(member?.wallet?.split(':')[1], 6, true)}
+
+
+
+ {member?.isAdmin && (
+
+ Admin
+
+ )}
+ {pCAIP10ToWallet(member?.wallet)?.toLowerCase() !== account?.toLowerCase() && dropdownValues.length > 0 && (
+ {
+ handleHeight(member.wallet);
+ setSelectedMemberAddress(member?.wallet)
+ }}
+ style={{ cursor: 'pointer' }}
+ >
+ {theme ? : }
+
+ )}
+
+ {selectedMemberAddress?.toLowerCase() == member?.wallet?.toLowerCase() && (
+ 570 ? '30%' : '40%' }}
+ theme={theme}
+ ref={dropdownRef}>
+
+
+ )}
+
+ );
+};
+
+const ProfileCardItem = styled(Section)<{id: any, key: any, background: any}>`
+ justify-content: space-between;
+ padding: 8px 16px;
+ border-radius: 16px;
+ position: relative;
+ box-sizing: border-box;
+ width: 100%;
+ // background-color: ${(props) => props.theme.snapFocusBg};
+ max-height: 64px;
+ align-self: stretch;
+ display: flex;
+ height: auto;
+ z-index: auto;
+ flex: 1;
+ @media (max-width: 480px) {
+ max-width: 100%;
+ }
+`;
+
+const DropdownContainer = styled(Section)`
+ position: absolute;
+ left: 48%;
+ top: 69%;
+ border-radius: 16px;
+ padding: 14px 8px;
+ z-index: 999999999999 !important;
+ display: flex;
+ flex-direction: column !important;
+ background: ${(props) => props.theme.modalContentBackground};
+ border: 1px solid ${(props) => props.theme.modalBorderColor};
+
+ @media ${device.mobileL} {
+ left: 27%;
+ }
+ @media (min-width: 426px) and (max-width: 1150px) {
+ left: 48%;
+ }
+ @media (max-width: 480px){
+ left: 25%;
+ }
+`;
diff --git a/packages/uiweb/src/lib/components/chat/ChatProfile/index.tsx b/packages/uiweb/src/lib/components/chat/ChatProfile/index.tsx
new file mode 100644
index 000000000..c84eff807
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/ChatProfile/index.tsx
@@ -0,0 +1 @@
+export { ChatProfile } from './ChatProfile'
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/components/chat/MessageBubble/MessageBubble.tsx b/packages/uiweb/src/lib/components/chat/ChatViewBubble/ChatViewBubble.tsx
similarity index 97%
rename from packages/uiweb/src/lib/components/chat/MessageBubble/MessageBubble.tsx
rename to packages/uiweb/src/lib/components/chat/ChatViewBubble/ChatViewBubble.tsx
index 625957a10..9ad6ce257 100644
--- a/packages/uiweb/src/lib/components/chat/MessageBubble/MessageBubble.tsx
+++ b/packages/uiweb/src/lib/components/chat/ChatViewBubble/ChatViewBubble.tsx
@@ -273,8 +273,8 @@ const TwitterCard = ({ chat, tweetId, isGroup, position }: { chat: IMessagePaylo
)
}
-export const MessageBubble = ({ chat }: { chat: IMessagePayload }) => {
- const { account } = useChatData();
+export const ChatViewBubble = ({ chat }: { chat: IMessagePayload }) => {
+ const { account, setAccount, pgpPrivateKey, setPgpPrivateKey, env, setEnv } = useChatData();
const position = pCAIP10ToWallet(chat.fromDID).toLowerCase() !== account?.toLowerCase() ? 0 : 1;
const { tweetId, messageType }: TwitterFeedReturnType = checkTwitterUrl({ message: chat?.messageContent });
const [isGroup, setIsGroup] = useState(false);
@@ -290,6 +290,12 @@ export const MessageBubble = ({ chat }: { chat: IMessagePayload }) => {
}
}, [chat.toDID, isGroup])
+ // useEffect(() => {
+ // setAccount("");
+ // setPgpPrivateKey("");
+ // setEnv(env);
+ // }, [account, env, pgpPrivateKey])
+
if (messageType === 'TwitterFeedLink') {
chat.messageType = 'TwitterFeedLink';
}
diff --git a/packages/uiweb/src/lib/components/chat/ChatViewBubble/index.ts b/packages/uiweb/src/lib/components/chat/ChatViewBubble/index.ts
new file mode 100644
index 000000000..25dc326f2
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/ChatViewBubble/index.ts
@@ -0,0 +1 @@
+export { ChatViewBubble } from './ChatViewBubble';
diff --git a/packages/uiweb/src/lib/components/chat/ChatViewComponent/ChatViewComponent.tsx b/packages/uiweb/src/lib/components/chat/ChatViewComponent/ChatViewComponent.tsx
new file mode 100644
index 000000000..846112317
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/ChatViewComponent/ChatViewComponent.tsx
@@ -0,0 +1,86 @@
+import React, { useContext} from 'react';
+import { IChatViewComponentProps } from '../exportedTypes';
+
+import { Section, } from '../../reusables';
+import { ChatViewList } from '../ChatViewList';
+import { chatLimit } from '../../../config';
+
+import { ThemeContext } from '../theme/ThemeProvider';
+import { useChatData } from '../../../hooks/chat/useChatData';
+import { MessageInput } from '../MessageInput';
+import { ChatProfile } from '../ChatProfile';
+
+
+
+export const ChatViewComponent: React.FC = (
+ options: IChatViewComponentProps
+) => {
+ const {
+ chatId,
+ messageInput = true,
+ chatViewList = true,
+ chatProfile = true,
+ limit = chatLimit,
+ emoji = true,
+ file = true,
+ gif = true,
+ isConnected = true,
+ } = options || {};
+
+ const {env } = useChatData();
+
+ console.log(env);
+
+ // const [conversationHash, setConversationHash] = useState();
+
+ const theme = useContext(ThemeContext);
+
+
+
+
+
+
+
+
+
+
+ return (
+
+
+ {chatProfile && }
+
+
+
+ {chatId && chatViewList && }
+
+
+
+ {/* )} */}
+
+ {messageInput && (
+
+ )}
+
+ );
+};
+
+//styles
+
diff --git a/packages/uiweb/src/lib/components/chat/ChatViewComponent/index.ts b/packages/uiweb/src/lib/components/chat/ChatViewComponent/index.ts
new file mode 100644
index 000000000..25a649344
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/ChatViewComponent/index.ts
@@ -0,0 +1 @@
+export {ChatViewComponent} from './ChatViewComponent';
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/components/chat/ChatViewList/ApproveRequestBubble.tsx b/packages/uiweb/src/lib/components/chat/ChatViewList/ApproveRequestBubble.tsx
new file mode 100644
index 000000000..afdc1724a
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/ChatViewList/ApproveRequestBubble.tsx
@@ -0,0 +1,119 @@
+import { IFeeds } from '@pushprotocol/restapi';
+import { ThemeContext } from '../theme/ThemeProvider';
+import { Dispatch, useContext } from 'react';
+import { Div, Section, Span, Spinner } from '../../reusables';
+import useApproveChatRequest from '../../../hooks/chat/useApproveChatRequest';
+import { useChatData } from '../../../hooks';
+import { TickSvg } from '../../../icons/Tick';
+import styled from 'styled-components';
+import { IChatTheme } from '../theme';
+
+/**
+ * @interface IThemeProps
+ * this interface is used for defining the props for styled components
+ */
+interface IThemeProps {
+ theme?: IChatTheme;
+}
+export interface IApproveRequestBubbleProps {
+ chatId: string;
+ chatFeed: IFeeds;
+ setChatFeed: Dispatch;
+}
+
+export const ApproveRequestBubble = ({
+ chatFeed,
+ chatId,
+ setChatFeed,
+}: IApproveRequestBubbleProps) => {
+ const { account, pgpPrivateKey, env } = useChatData();
+
+ const ApproveRequestText = {
+ GROUP: `You were invited to the group ${chatFeed?.groupInformation?.groupName}. Please accept to continue messaging in this group.`,
+ W2W: ` Please accept to enable push chat from this wallet`,
+ };
+ const theme = useContext(ThemeContext);
+ const { approveChatRequest, loading: approveLoading } =
+ useApproveChatRequest();
+
+ const handleApproveChatRequest = async () => {
+ try {
+ if (!pgpPrivateKey) {
+ return;
+ }
+ const response = await approveChatRequest({
+ chatId,
+ });
+ if (response) {
+ const updatedChatFeed = { ...(chatFeed as IFeeds) };
+ updatedChatFeed.intent = response;
+
+ setChatFeed(updatedChatFeed);
+ }
+ } catch (error_: Error | any) {
+ console.log(error_.message);
+ }
+ };
+ return (
+
+
+ {chatFeed?.groupInformation
+ ? ApproveRequestText.GROUP
+ : ApproveRequestText.W2W}
+
+
+ {/* (!approveLoading ? handleApproveChatRequest() : null)}
+ >
+ {approveLoading ? : }
+
*/}
+
+ );
+};
+
+//styles
+const Button = styled.button`
+ border: none;
+ cursor: pointer;
+ border-radius: 8px;
+ background: ${(props) => props.theme.accentBgColor};
+ border: none;
+ color: white;
+ width: 100%;
+ font-size: 16px;
+ font-weight: 600;
+ line-height: 24px;
+ max-height: 48px;
+ min-height: 48px;
+ padding: 0px 24px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+`;
diff --git a/packages/uiweb/src/lib/components/chat/ChatViewList/ChatViewList.tsx b/packages/uiweb/src/lib/components/chat/ChatViewList/ChatViewList.tsx
new file mode 100644
index 000000000..220aef905
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/ChatViewList/ChatViewList.tsx
@@ -0,0 +1,351 @@
+import React, { useContext, useEffect, useRef, useState } from 'react';
+import { ChatDataContext } from '../../../context';
+import { IChatViewListProps } from '../exportedTypes';
+import { chatLimit } from '../../../config';
+import { IFeeds, IMessageIPFS } from '@pushprotocol/restapi';
+import useFetchHistoryMessages from '../../../hooks/chat/useFetchHistoryMessages';
+import styled from 'styled-components';
+import { Div, Section, Span, Spinner } from '../../reusables';
+import moment from 'moment';
+import { ChatViewBubble } from '../ChatViewBubble';
+import {
+ appendUniqueMessages,
+ checkIfIntent,
+ checkIfSameChat,
+ dateToFromNowDaily,
+ getDefaultFeedObject,
+ getNewChatUser,
+ pCAIP10ToWallet,
+} from '../../../helpers';
+import { useChatData, usePushChatSocket } from '../../../hooks';
+import { Messagetype } from '../../../types';
+import { ThemeContext } from '../theme/ThemeProvider';
+import { IChatTheme } from '../theme';
+import useFetchConversationHash from '../../../hooks/chat/useFetchConversationHash';
+
+import { EncryptionMessage } from './MessageEncryption';
+import useGetGroup from '../../../hooks/chat/useGetGroup';
+import useGetChatProfile from '../../../hooks/useGetChatProfile';
+import useFetchChat from '../../../hooks/chat/useFetchChat';
+import { ApproveRequestBubble } from './ApproveRequestBubble';
+
+/**
+ * @interface IThemeProps
+ * this interface is used for defining the props for styled components
+ */
+interface IThemeProps {
+ theme?: IChatTheme;
+}
+const ChatStatus = {
+ FIRST_CHAT: `This is your first conversation with recipient.\n Start the conversation by sending a message.`,
+ INVALID_CHAT: 'Invalid chatId',
+};
+
+export const ChatViewList: React.FC = (
+ options: IChatViewListProps
+) => {
+ const { chatId, limit = chatLimit } = options || {};
+ const { pgpPrivateKey, account } = useChatData();
+ const [chatFeed, setChatFeed] = useState({} as IFeeds);
+ const [chatStatusText, setChatStatusText] = useState('');
+ const [messages, setMessages] = useState();
+ const [ loading,setLoading] = useState(true);
+ const [conversationHash, setConversationHash] = useState();
+ const { historyMessages, loading:messageLoading } = useFetchHistoryMessages();
+ const listInnerRef = useRef(null);
+ const bottomRef = useRef(null);
+ const { fetchChat } = useFetchChat();
+ const { fetchChatProfile } = useGetChatProfile();
+ const { getGroup } = useGetGroup();
+
+ const { messagesSinceLastConnection, groupInformationSinceLastConnection } =
+ usePushChatSocket();
+ const { fetchConversationHash } = useFetchConversationHash();
+ const theme = useContext(ThemeContext);
+ const dates = new Set();
+ const { env } = useChatData();
+
+ useEffect(() => {
+ setChatStatusText('');
+ }, [chatId, account, env]);
+
+ useEffect(() => {
+ setMessages(undefined);
+ setConversationHash(undefined);
+ }, [chatId, account, pgpPrivateKey, env]);
+
+ useEffect(() => {
+ (async () => {
+ const chat = await fetchChat({ chatId });
+ if (Object.keys(chat || {}).length) setChatFeed(chat as IFeeds);
+ else {
+ let newChatFeed;
+ let group;
+ const result = await getNewChatUser({
+ searchText: chatId,
+ fetchChatProfile,
+ env,
+ });
+ if (result) {
+ newChatFeed = getDefaultFeedObject({ user: result });
+ } else {
+ group = await getGroup({ searchText: chatId });
+ if (group) {
+ newChatFeed = getDefaultFeedObject({ groupInformation: group });
+ }
+ }
+ if (newChatFeed) {
+ if (!newChatFeed?.groupInformation) {
+ setChatStatusText(ChatStatus.FIRST_CHAT);
+ }
+ console.log(chatFeed)
+ setChatFeed(newChatFeed);
+ } else {
+ setChatStatusText(ChatStatus.INVALID_CHAT);
+ }
+
+ }
+ setLoading(false);
+ })();
+ }, [chatId, pgpPrivateKey, account, env]);
+
+ useEffect(() => {
+ if (checkIfSameChat(messagesSinceLastConnection, account!, chatId)) {
+ if (!Object.keys(messages || {}).length) {
+ setMessages({
+ messages: [messagesSinceLastConnection],
+ lastThreadHash: messagesSinceLastConnection.cid,
+ });
+ setConversationHash(messagesSinceLastConnection.cid);
+ } else {
+ const newChatViewList = appendUniqueMessages(
+ messages as Messagetype,
+ [messagesSinceLastConnection],
+ false
+ );
+ setMessages({
+ messages: newChatViewList,
+ lastThreadHash: messages!.lastThreadHash,
+ });
+ }
+ scrollToBottom(null);
+ }
+ }, [messagesSinceLastConnection]);
+
+ useEffect(() => {
+ (async function () {
+ const hash = await fetchConversationHash({ conversationId: chatId });
+ setConversationHash(hash?.threadHash);
+ })();
+ }, [chatId, account, env, pgpPrivateKey]);
+
+ useEffect(() => {
+ if (conversationHash) {
+ (async function () {
+ await getMessagesCall();
+ })();
+ }
+ }, [conversationHash, pgpPrivateKey, account, env]);
+
+ useEffect(() => {
+ scrollToBottom(null);
+ }, [conversationHash]);
+
+ useEffect(() => {
+ if (
+ conversationHash &&
+ Object.keys(messages || {}).length &&
+ messages?.messages.length &&
+ messages?.messages.length <= limit
+ ) {
+ scrollToBottom(null);
+ }
+ }, [messages]);
+
+ useEffect(() => {
+ if (
+ Object.keys(messagesSinceLastConnection || {}).length &&
+ Object.keys(chatFeed || {}).length &&
+ checkIfSameChat(messagesSinceLastConnection, account!, chatId)
+ ) {
+ const updatedChatFeed = chatFeed;
+ updatedChatFeed.msg = messagesSinceLastConnection;
+
+ setChatStatusText('');
+ setChatFeed(updatedChatFeed);
+ }
+ }, [messagesSinceLastConnection]);
+
+ const scrollToBottom = (behavior?: string | null) => {
+ bottomRef?.current?.scrollIntoView(
+ !behavior ? true : { behavior: 'smooth' }
+ );
+ };
+
+ useEffect(() => {
+ if (Object.keys(groupInformationSinceLastConnection || {}).length) {
+ if (
+ chatFeed?.groupInformation?.chatId.toLowerCase() ===
+ groupInformationSinceLastConnection.chatId.toLowerCase()
+ ) {
+ const updateChatFeed = chatFeed;
+ updateChatFeed.groupInformation = groupInformationSinceLastConnection;
+ setChatFeed(updateChatFeed);
+ }
+ }
+ }, [groupInformationSinceLastConnection]);
+
+ const onScroll = async () => {
+ if (listInnerRef.current) {
+ const { scrollTop } = listInnerRef.current;
+ if (scrollTop === 0) {
+ const content = listInnerRef.current;
+ const curScrollPos = content.scrollTop;
+ const oldScroll = content.scrollHeight - content.clientHeight;
+
+ await getMessagesCall();
+
+ const newScroll = content.scrollHeight - content.clientHeight;
+ content.scrollTop = curScrollPos + (newScroll - oldScroll);
+ }
+ }
+ };
+
+ const getMessagesCall = async () => {
+ let threadHash = null;
+ if (!messages) {
+ threadHash = conversationHash;
+ } else {
+ threadHash = messages?.lastThreadHash;
+ }
+ if (threadHash && account) {
+ const chatHistory = await historyMessages({
+ limit: limit,
+ threadHash,
+ });
+ if (chatHistory?.length) {
+ if (Object.keys(messages || {}) && messages?.messages.length) {
+ const newChatViewList = appendUniqueMessages(
+ messages,
+ chatHistory,
+ true
+ );
+ setMessages({
+ messages: newChatViewList,
+ lastThreadHash: chatHistory[0].link,
+ });
+ } else {
+ setMessages({
+ messages: chatHistory,
+ lastThreadHash: chatHistory[0].link,
+ });
+ }
+ }
+ }
+ };
+
+ type RenderDataType = {
+ chat: IMessageIPFS;
+ dateNum: string;
+ };
+
+ const renderDate = ({ chat, dateNum }: RenderDataType) => {
+ const timestampDate = dateToFromNowDaily(chat.timestamp as number);
+ dates.add(dateNum);
+ return (
+
+ {timestampDate}
+
+ );
+ };
+ return (
+ onScroll()}
+ >
+ {loading ? : ''}
+ {!loading &&
+ <>
+ {chatFeed &&
+ (chatFeed.publicKey ||
+ (chatFeed?.groupInformation &&
+ !chatFeed?.groupInformation?.isPublic)) ? (
+
+ ) : (
+
+ )}
+
+ {chatStatusText && (
+
+ )}
+ {messageLoading ? : ''}
+
+ {
+ !messageLoading &&
+ <>
+
+ {messages?.messages &&
+ messages?.messages?.map((chat: IMessageIPFS, index: number) => {
+ const dateNum = moment(chat.timestamp).format('L');
+ const position =
+ pCAIP10ToWallet(chat.fromDID).toLowerCase() !==
+ account?.toLowerCase()
+ ? 0
+ : 1;
+ return (
+ <>
+ {dates.has(dateNum) ? null : renderDate({ chat, dateNum })}
+
+ >
+ );
+ })}
+
+
+ {chatFeed && checkIfIntent({ chat: chatFeed as IFeeds, account: account! }) && (
+
+ )}
+ >
+ }
+ >
+ }
+
+ );
+};
+
+//styles
+const ChatViewListCard = styled(Section)`
+ &::-webkit-scrollbar-thumb {
+ background: ${(props) => props.theme.accentBgColor};
+ border-radius: 10px;
+ }
+
+ &::-webkit-scrollbar {
+ width: 5px;
+ }
+`;
diff --git a/packages/uiweb/src/lib/components/chat/ChatViewList/MessageEncryption.tsx b/packages/uiweb/src/lib/components/chat/ChatViewList/MessageEncryption.tsx
new file mode 100644
index 000000000..d2519e6ed
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/ChatViewList/MessageEncryption.tsx
@@ -0,0 +1,55 @@
+import styled from "styled-components";
+import { Div, Section, Span } from "../../reusables";
+import { useDeviceWidthCheck } from "../../../hooks";
+import { useContext } from "react";
+import { ThemeContext } from "../theme/ThemeProvider";
+import { NoEncryptionIcon } from "../../../icons/NoEncryption";
+import { EncryptionIcon } from "../../../icons/Encryption";
+
+const EncryptionMessageContent = {
+ ENCRYPTED: {
+ IconComponent: ,
+ text: 'Messages are end-to-end encrypted. Only users in this chat can view or listen to them. Click to learn more.',
+ },
+ NO_ENCRYPTED: {
+ IconComponent: ,
+ text: `Messages are not encrypted`,
+ },
+ };
+ export const EncryptionMessage = ({ id }: { id: 'ENCRYPTED' | 'NO_ENCRYPTED' }) => {
+ console.log(id)
+ const theme = useContext(ThemeContext);
+ const isMobile = useDeviceWidthCheck(771);
+ return (
+
+
+ {EncryptionMessageContent[id].IconComponent}
+
+
+ {EncryptionMessageContent[id].text}
+
+
+
+ );
+ };
+
+ //styles
+ const EncryptionMessageDiv = styled(Div)`
+ text-align: center;
+ svg {
+ vertical-align: middle;
+ }
+`;
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/components/chat/ChatViewList/index.ts b/packages/uiweb/src/lib/components/chat/ChatViewList/index.ts
new file mode 100644
index 000000000..d91caff1c
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/ChatViewList/index.ts
@@ -0,0 +1 @@
+export {ChatViewList} from './ChatViewList';
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/components/chat/ConnectButton/ConnectButton.tsx b/packages/uiweb/src/lib/components/chat/ConnectButton/ConnectButton.tsx
new file mode 100644
index 000000000..d196b00b7
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/ConnectButton/ConnectButton.tsx
@@ -0,0 +1,174 @@
+import React, { useContext, useEffect } from "react";
+import styled from "styled-components";
+import { InjectedConnector } from "@web3-react/injected-connector";
+import { useWeb3React } from "@web3-react/core";
+import { useChatData } from "../../../hooks";
+import * as PUSHAPI from "@pushprotocol/restapi"
+import { Spinner } from "../../reusables";
+import { ThemeContext } from "../theme/ThemeProvider";
+
+interface NwMappingType {
+ [key: number]: string;
+}
+
+
+const NETWORK_MAPPING: NwMappingType = {
+ 1: 'ETH_MAIN_NET',
+ 5: 'ETH_GOERLI',
+ 3: 'ETH_ROPSTEN',
+ 137: 'POLYGON_MAINNET',
+ 80001: 'POLYGON_MUMBAI',
+ 56: 'BSC_MAINNET',
+ 97: 'BSC_TESTNET',
+ 420: 'OPTIMISM_TESTNET',
+ 10: 'OPTIMISM_MAINNET',
+ 1442: 'POLYGON_ZK_EVM_TESTNET',
+ 1101: 'POLYGON_ZK_EVM_MAINNET',
+};
+
+const injected = new InjectedConnector({
+ supportedChainIds: [1, 3, 4, 5, 42, 137, 80001, 56, 97, 10, 420, 1442, 1101],
+});
+
+const ConnectWrapper = styled.div`
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ margin: 20;
+
+ & .account {
+ font-size: 1.2rem;
+ border: 1px solid green;
+ border-radius: 3px;
+ padding: 4px 7px;
+ font-weight: 500;
+ font-family: monospace;
+ }
+
+ & .network {
+ margin: 5px 0;
+ }
+ `;
+
+const StyledButton = styled.button`
+ border: 0px;
+ outline: 0px;
+ padding: 24px 9px;
+ font-weight: 500;
+ margin: 10px;
+ border-radius: 12px;
+ font-size: 17px;
+ cursor: pointer;
+ width: 165px;
+ height: 44px;
+ text-align: start;
+ align-items: center;
+ display: flex;
+ justify-content: center;
+ `;
+
+const Connect = styled(StyledButton)`
+ color: rgb(255, 255, 255);
+ background: #D53A94;
+ `;
+
+const Disconnect = styled(StyledButton)`
+display: flex;
+padding: 9px 24px;
+justify-content: center;
+align-items: center;
+gap: 10px;
+background: var(--general-use-creamy-pink, #D53A94);
+color: var(--general-use-white, #ffffff);
+ `;
+
+export const ConnectButton = () => {
+ const { active, activate, library } = useWeb3React();
+ const { pgpPrivateKey, account, env, setPgpPrivateKey } = useChatData();
+ const theme = useContext(ThemeContext);
+
+ useEffect(() => {
+ if (active && account && env && library) {
+ const librarySigner = library.getSigner();
+
+ const connectBtn = async () => {
+ const user = await PUSHAPI.user.get({ account: account, env: env });
+ if (!user) {
+ await createProfile();
+ }
+ if (user?.encryptedPrivateKey && !pgpPrivateKey) {
+ const decryptPgpKey = await PUSHAPI.chat.decryptPGPKey({
+ encryptedPGPPrivateKey: user.encryptedPrivateKey,
+ account: account,
+ signer: librarySigner,
+ env: env,
+ });
+ setPgpPrivateKey(decryptPgpKey);
+ }
+ };
+
+ connectBtn();
+ }
+ }, [active, account, env, library]);
+
+ const createProfile = async () => {
+ if (!account || !env || !library) return;
+
+ const librarySigner = library.getSigner();
+
+ const user = await PUSHAPI.user.create({
+ signer: librarySigner,
+ env: env,
+ });
+
+ const createdUser = await PUSHAPI.user.get({
+ account: account,
+ env: env,
+ });
+
+ const pvtKey = await PUSHAPI.chat.decryptPGPKey({
+ encryptedPGPPrivateKey: createdUser.encryptedPrivateKey ? createdUser.encryptedPrivateKey : "",
+ signer: librarySigner,
+ env: env,
+ toUpgrade: true,
+ });
+
+ setPgpPrivateKey(pvtKey);
+ };
+
+ async function connect() {
+ try {
+ await activate(injected);
+ } catch (ex) {
+ console.log(ex);
+ }
+ }
+
+ const connectWalletOnPageLoad = async () => {
+ if (!pgpPrivateKey && !account) {
+ try {
+ await activate(injected);
+ } catch (ex) {
+ console.log(ex);
+ }
+ }
+ };
+
+ useEffect(() => {
+ connectWalletOnPageLoad();
+ }, [activate]);
+
+ return (
+
+ {active ? (
+ <>
+
+
+
+ >
+ ) : (
+ Connect Wallet
+ )}
+
+ );
+};
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/components/chat/ConnectButton/index.ts b/packages/uiweb/src/lib/components/chat/ConnectButton/index.ts
new file mode 100644
index 000000000..d5457ba77
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/ConnectButton/index.ts
@@ -0,0 +1 @@
+export { ConnectButton } from './ConnectButton';
diff --git a/packages/uiweb/src/lib/components/chat/MessageBubble/index.ts b/packages/uiweb/src/lib/components/chat/MessageBubble/index.ts
deleted file mode 100644
index fd86462dc..000000000
--- a/packages/uiweb/src/lib/components/chat/MessageBubble/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { MessageBubble } from './MessageBubble';
diff --git a/packages/uiweb/src/lib/components/chat/MessageInput/MessageInput.tsx b/packages/uiweb/src/lib/components/chat/MessageInput/MessageInput.tsx
new file mode 100644
index 000000000..a1305be48
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/MessageInput/MessageInput.tsx
@@ -0,0 +1,340 @@
+import { useChatData, useClickAway, useDeviceWidthCheck } from "../../../hooks";
+import type { FileMessageContent } from "../../../types";
+import type { ChatMainStateContextType } from "../../../context/chatAndNotification/chat/chatMainStateContext";
+import { ChangeEvent, useContext, useEffect, useRef, useState } from "react";
+import { GIFType, IChatTheme, MessageInputProps } from "../exportedTypes";
+import styled from "styled-components";
+import { PUBLIC_GOOGLE_TOKEN, device } from "../../../config";
+import { Section, Div, Span } from "../../reusables";
+import { EmojiIcon } from "../../../icons/Emoji";
+import EmojiPicker, { EmojiClickData } from "emoji-picker-react";
+import * as PUSHAPI from "@pushprotocol/restapi";
+import { GifIcon } from "../../../icons/Gif";
+import GifPicker from "gif-picker-react";
+import { AttachmentIcon } from "../../../icons/Attachment";
+import usePushSendMessage from "../../../hooks/chat/usePushSendMessage";
+import { SendCompIcon } from "../../../icons/SendCompIcon";
+import { Spinner } from "../../reusables";
+import { ThemeContext } from "../theme/ThemeProvider";
+import { ConnectButton } from "../ConnectButton";
+
+
+/**
+ * @interface IThemeProps
+ * this interface is used for defining the props for styled components
+ */
+interface IThemeProps {
+ theme?: IChatTheme;
+}
+
+export const MessageInput: React.FC = ({ chatId, Emoji = true, GIF = true, File = true, isConnected = true }) => {
+ const [typedMessage, setTypedMessage] = useState("");
+ const [showEmojis, setShowEmojis] = useState(false);
+ const [gifOpen, setGifOpen] = useState(false);
+ const [newChat, setNewChat] = useState(false);
+ const modalRef = useRef(null);
+ const fileUploadInputRef = useRef(null);
+ const [fileUploading, setFileUploading] = useState(false);
+ const onChangeTypedMessage = (val: string) => {
+ setTypedMessage(val.trim());
+ };
+ const theme = useContext(ThemeContext);
+ const isMobile = useDeviceWidthCheck(425);
+ const { sendMessage, loading } = usePushSendMessage();
+ const { pgpPrivateKey, setPgpPrivateKey } = useChatData();
+
+ useClickAway(modalRef, () => {
+ setShowEmojis(false);
+ setGifOpen(false);
+ });
+ const textAreaRef = useRef(null);
+ useEffect(() => {
+ if (textAreaRef?.current?.style) {
+ textAreaRef.current.style.height = 25 + 'px';
+ const scrollHeight = textAreaRef.current?.scrollHeight;
+ textAreaRef.current.style.height = scrollHeight + 'px';
+ }
+ }, [textAreaRef, typedMessage])
+
+ const addEmoji = (emojiData: EmojiClickData, event: MouseEvent): void => {
+ setTypedMessage(typedMessage + emojiData.emoji);
+ setShowEmojis(false);
+ }
+
+ const handleUploadFile = () => {
+ if (fileUploadInputRef.current) {
+ fileUploadInputRef.current.click();
+ }
+ }
+
+ const uploadFile = async (
+ e: ChangeEvent
+ ): Promise => {
+ if (!(e.target instanceof HTMLInputElement)) {
+ return;
+ }
+ if (!e.target.files) {
+ return;
+ }
+ if (
+ e.target &&
+ (e.target as HTMLInputElement).files &&
+ ((e.target as HTMLInputElement).files as FileList).length
+ ) {
+ const file: File = e.target.files[0];
+ if (file) {
+ try {
+ const TWO_MB = 1024 * 1024 * 2;
+ if (file.size > TWO_MB) {
+ console.log('Files larger than 2mb is now allowed');
+ throw new Error('Files larger than 2mb is now allowed');
+ }
+ setFileUploading(true);
+ const messageType = file.type.startsWith('image') ? 'Image' : 'File';
+ const reader = new FileReader();
+ let fileMessageContent: FileMessageContent;
+ reader.readAsDataURL(file);
+ reader.onloadend = async (e): Promise => {
+ fileMessageContent = {
+ content: e.target!.result as string,
+ name: file.name,
+ type: file.type,
+ size: file.size,
+ };
+
+ sendPushMessage(JSON.stringify(fileMessageContent), messageType);
+ };
+ } catch (err) {
+ console.log(err);
+ } finally {
+ setFileUploading(false);
+ }
+ }
+ }
+ };
+
+ const sendPushMessage = async (content: string, type: string) => {
+ try {
+ await sendMessage({
+ message: content,
+ chatId,
+ messageType: type as any,
+ });
+ } catch (error) {
+ console.log(error);
+ }
+ }
+
+ const sendTextMsg = async () => {
+ if (typedMessage.trim() !== '') {
+ await sendPushMessage(typedMessage as string, 'Text');
+ setTypedMessage('');
+ }
+ }
+
+ // useEffect(() => {
+ // setPgpPrivateKey
+ // }, [pgpPrivateKey])
+
+ const sendGIF = async (emojiObject: GIFType) => {
+ sendPushMessage(emojiObject.url as string, 'GIF');
+ setGifOpen(false);
+ }
+
+ return (
+
+ {/* {isConnected && (
+
+ )} */}
+
+ {!pgpPrivateKey && isConnected && (
+ // align this button in right corner
+
+
+
+ You need to connect your wallet to get started
+
+
+
+ )
+ }
+ {pgpPrivateKey &&
+ <>
+
+ {Emoji &&
+ setShowEmojis(!showEmojis)}
+ >
+
+
+ }
+ {showEmojis && (
+
+ )}
+ {
+ if (event.key === 'Enter' && !event.shiftKey) {
+ event.preventDefault();
+ sendTextMsg();
+ }
+ }}
+ placeholder="Type your message..."
+ onChange={(e) => onChangeTypedMessage(e.target.value)}
+ value={typedMessage}
+ ref={textAreaRef}
+ rows={1}
+ />
+
+
+ {GIF &&
+ setGifOpen(!gifOpen)}>
+
+
+ }
+ {gifOpen && (
+
+ )}
+
+ {!fileUploading && File && (
+ <>
+ setNewChat(true)}
+ >
+
+
+ uploadFile(e)}
+ />
+ >
+ )}
+
+ {!(loading || fileUploading) && (
+
+ )}
+
+ {(loading || fileUploading) && (
+
+ )}
+
+ >
+ }
+
+
+ )
+}
+
+const Container = styled.div`
+ width: 100%;
+ overflow: hidden;
+`;
+const TypebarSection = styled(Section)`
+ gap: 10px;
+ @media ${device.mobileL} {
+ gap: 0px;
+ }
+`;
+const SendSection = styled(Section)`
+ gap: 11.5px;
+ @media ${device.mobileL} {
+ gap: 7.5px;
+ }
+`;
+const MultiLineInput = styled.textarea`
+ font-family: inherit;
+ font-weight: 400;
+ transform: translateY(3px);
+ font-size: 16px;
+ outline: none;
+ overflow-y: auto;
+ box-sizing: border-box;
+ background:${(props) => props.theme.bgColorPrimary};
+ border: none;
+ color: ${(props) => props.theme.textColorSecondary};
+ resize: none;
+ flex: 1;
+ padding-right: 5px;
+ align-self: end;
+ @media ${device.mobileL} {
+ font-size: 14px;
+ }
+ &&::-webkit-scrollbar {
+ width: 4px;
+ padding-right: 0px;
+ }
+ ::-webkit-scrollbar-thumb {
+ background: rgb(181 181 186);
+ border-radius: 10px;
+ height: 50px;
+ }
+ ::placeholder {
+ color: ${(props) => props.theme.textColorSecondary};
+ transform: translateY(1px);
+ @media ${device.mobileL} {
+ font-size: 14px;
+ }
+ }
+
+ min-height: 25px;
+ max-height: 80px;
+ word-break: break-word;
+`;
+const FileInput = styled.input`
+ display: none;
+`;
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/components/chat/MessageInput/index.ts b/packages/uiweb/src/lib/components/chat/MessageInput/index.ts
new file mode 100644
index 000000000..b0ef7c589
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/MessageInput/index.ts
@@ -0,0 +1 @@
+export { MessageInput } from './MessageInput';
diff --git a/packages/uiweb/src/lib/components/chat/MessageList/MessageList.tsx b/packages/uiweb/src/lib/components/chat/MessageList/MessageList.tsx
deleted file mode 100644
index d85ba01da..000000000
--- a/packages/uiweb/src/lib/components/chat/MessageList/MessageList.tsx
+++ /dev/null
@@ -1,210 +0,0 @@
-import React, { useContext, useEffect, useRef, useState } from 'react';
-import { ChatDataContext } from '../../../context';
-import { IMessageListProps } from '../exportedTypes';
-import { chatLimit } from '../../../config';
-import { IMessageIPFS } from '@pushprotocol/restapi';
-import useFetchHistoryMessages from '../../../hooks/chat/useFetchHistoryMessages';
-import styled from 'styled-components';
-import { Section, Span, Spinner } from '../../reusables';
-import moment from 'moment';
-import { MessageBubble } from '../MessageBubble';
-import { appendUniqueMessages, dateToFromNowDaily, pCAIP10ToWallet } from '../../../helpers';
-import { useChatData, usePushChatSocket } from '../../../hooks';
-import { Messagetype } from '../../../types';
-import { ThemeContext } from '../theme/ThemeProvider';
-import { IChatTheme } from '../theme';
-
-
-
-/**
- * @interface IThemeProps
- * this interface is used for defining the props for styled components
- */
-interface IThemeProps {
- theme?: IChatTheme;
-
- }
-
-export const MessageList: React.FC = (
- options: IMessageListProps
-) => {
- const { conversationHash, limit = chatLimit } = options || {};
- const { pgpPrivateKey, account } = useChatData();
- const [messages, setMessages] = useState();
- const { historyMessages, loading } = useFetchHistoryMessages();
- const listInnerRef = useRef(null);
- const bottomRef = useRef(null);
- const { messagesSinceLastConnection } = usePushChatSocket();
- const theme = useContext(ThemeContext);
- const dates = new Set();
-
- useEffect(() => {
- if (
- Object.keys(messagesSinceLastConnection || {}).length
- ) {
- if (!Object.keys(messages || {}).length) {
- setMessages({
- messages: messagesSinceLastConnection,
- lastThreadHash: messagesSinceLastConnection.lastThreadHash,
- });
- } else {
- const newMessageList = appendUniqueMessages(messages as Messagetype,[messagesSinceLastConnection],false);
- setMessages( {
-
- messages: newMessageList,
- lastThreadHash: messages!.lastThreadHash,
-
- });
- }
- scrollToBottom(null);
- }
- }, [messagesSinceLastConnection]);
-
- useEffect(() => {
- if (conversationHash) {
- (async function () {
- await getMessagesCall();
- })();
- }
- }, [conversationHash, pgpPrivateKey, account]);
-
- useEffect(() => {
- scrollToBottom(null);
- }, [conversationHash]);
-
- useEffect(() => {
- if (
- conversationHash &&
- Object.keys(messages || {}).length &&
- messages?.messages.length &&
- messages?.messages.length <= limit
- ) {
- scrollToBottom(null);
- }
- }, [messages]);
-
- const scrollToBottom = (behavior?: string | null) => {
- bottomRef?.current?.scrollIntoView(
- !behavior ? true : { behavior: 'smooth' }
- );
- };
-
- const onScroll = async () => {
- if (listInnerRef.current) {
- const { scrollTop } = listInnerRef.current;
- if (scrollTop === 0) {
- const content = listInnerRef.current;
- const curScrollPos = content.scrollTop;
- const oldScroll = content.scrollHeight - content.clientHeight;
-
- await getMessagesCall();
-
- const newScroll = content.scrollHeight - content.clientHeight;
- content.scrollTop = curScrollPos + (newScroll - oldScroll);
- }
- }
- };
-
- const getMessagesCall = async () => {
- let threadHash = null;
- if (!messages) {
- threadHash = conversationHash;
- } else {
- threadHash = messages?.lastThreadHash;
- }
- if (threadHash) {
- const chatHistory = await historyMessages({
- limit: limit,
- threadHash,
- });
- if (chatHistory?.length) {
- if (Object.keys(messages || {}) && messages?.messages.length) {
- const newMessageList = appendUniqueMessages(messages,chatHistory,true);
- setMessages({
- messages: newMessageList,
- lastThreadHash: chatHistory[0].link,
- });
- } else {
- setMessages({
- messages: chatHistory,
- lastThreadHash: chatHistory[0].link,
- });
- }
- }
- }
- };
-
-
- type RenderDataType = {
- chat: IMessageIPFS;
- dateNum: string;
- };
-
- const renderDate = ({ chat, dateNum }: RenderDataType) => {
- const timestampDate = dateToFromNowDaily(chat.timestamp as number);
- dates.add(dateNum);
- return (
-
- {timestampDate}
-
- );
- };
- return (
- onScroll()}
- >
- {loading ? : ''}
-
-
- {messages?.messages.map((chat: IMessageIPFS, index: number) => {
- const dateNum = moment(chat.timestamp).format('L');
- const position =
- pCAIP10ToWallet(chat.fromDID).toLowerCase() !==
- account?.toLowerCase()
- ? 0
- : 1;
- return (
- <>
- {dates.has(dateNum) ? null : renderDate({ chat, dateNum })}
-
- >
- );
- })}
-
-
-
- );
-};
-
-//styles
-const MessageListCard = styled(Section)`
-&::-webkit-scrollbar-thumb {
- background: ${(props) => props.theme.accentBgColor};
- border-radius: 10px;
- }
-
- &::-webkit-scrollbar {
- width: 5px;
- }
-
-`;
diff --git a/packages/uiweb/src/lib/components/chat/MessageList/index.ts b/packages/uiweb/src/lib/components/chat/MessageList/index.ts
deleted file mode 100644
index 80a558955..000000000
--- a/packages/uiweb/src/lib/components/chat/MessageList/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export {MessageList} from './MessageList';
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/components/chat/exportedTypes.ts b/packages/uiweb/src/lib/components/chat/exportedTypes.ts
index a0a0dbcf1..4da3d5691 100644
--- a/packages/uiweb/src/lib/components/chat/exportedTypes.ts
+++ b/packages/uiweb/src/lib/components/chat/exportedTypes.ts
@@ -1,22 +1,152 @@
-import type { IMessageIPFS } from '@pushprotocol/restapi';
+import type { IMessageIPFS } from '@pushprotocol/restapi';
+import { IChatTheme } from "./theme";
+import { IGroup } from '../../types'
-export interface IMessageListProps {
- conversationHash: string;
- limit?: number;
- }
+export interface IChatViewListProps {
+ chatId: string;
+ limit?: number;
+}
+
+export interface IChatViewComponentProps {
+ messageInput?: boolean;
+ chatViewList?: boolean;
+ chatProfile?: boolean; //name needs to change
+ chatId: string; //need confirmation on this
+ limit?: number;
+ emoji?: boolean;
+ gif?: boolean;
+ file?: boolean;
+ isConnected?: boolean;
+}
+
+export interface IChatProfile {
+ chatId: string;
+ style: "Info" | "Preview";
+}
export interface TwitterFeedReturnType {
- tweetId: string;
- messageType: string;
+ tweetId: string;
+ messageType: string;
+}
+
+export interface IToast {
+ message: string;
+ status: string;
}
+export type OptionProps = {
+ options: boolean;
+ setOptions: React.Dispatch>;
+ isGroup: boolean;
+ chatInfo: any;
+ groupInfo: IGroup | null | undefined ,
+ setGroupInfo: React.Dispatch>;
+ theme: IChatTheme;
+}
export type IMessagePayload = IMessageIPFS;
export const CHAT_THEME_OPTIONS = {
LIGHT: 'light',
DARK: 'dark',
-
} as const;
-export type ChatThemeOptions = (typeof CHAT_THEME_OPTIONS)[keyof typeof CHAT_THEME_OPTIONS];
+export type GIFType = {
+ url: string;
+ width: number;
+ height: number;
+};
+
+export interface MessageInputProps {
+ chatId: string;
+ Emoji?: boolean;
+ GIF?: boolean;
+ File?: boolean;
+ Image?: boolean;
+ isConnected?: boolean;
+}
+
+export type UpdateGroupType = {
+ groupInfo: IGroup,
+ connectedUser: User,
+ adminList: Array,
+ memberList: Array,
+}
+
+export type MemberListContainerType = {
+ key?: number;
+ memberData: User;
+ handleMemberList: (member: User) => void;
+ handleMembers?: (value: User[]) => void;
+ lightIcon: any;
+ darkIcon: any;
+ memberList?: any;
+};
+
+export interface WalletProfileContainerProps {
+ id?: any;
+ background?: any;
+ border?: any;
+
+};
+
+export interface MessageIPFS {
+ fromCAIP10: string
+ toCAIP10: string
+ fromDID: string
+ toDID: string
+ messageType: string
+ messageContent: string
+ signature: string
+ sigType: string
+ link: string | null
+ timestamp?: number
+ encType: string
+ encryptedSecret: string
+}
+
+export interface Feeds {
+ chatId?: string;
+ msg: MessageIPFS;
+ did: string;
+ wallets: string;
+ profilePicture: string | null;
+ publicKey: string | null;
+ about: string | null;
+ threadhash: string | null;
+ intent: string | null;
+ intentSentBy: string | null;
+ intentTimestamp: Date;
+ combinedDID: string;
+ cid?: string;
+ groupInformation?: IGroup
+}
+
+export interface User {
+ did: string;
+ wallets: string;
+ profilePicture: string | null;
+ publicKey: string;
+ encryptedPrivateKey: string;
+ encryptionType: string;
+ signature: string;
+ sigType: string;
+ about: string | null;
+ name: string | null;
+ numMsg: number;
+ allowedNumMsg: number;
+ linkedListHash?: string | null;
+ isAdmin?:boolean;
+}
+
+export interface ShadowedProps {
+ setPosition: boolean;
+};
+
+export interface ModalButtonProps {
+ memberListCount?: boolean;
+ theme?: IChatTheme;
+ isLoading?: boolean;
+};
+
+
export {IChatTheme} from './theme';
diff --git a/packages/uiweb/src/lib/components/chat/helpers/Modal.tsx b/packages/uiweb/src/lib/components/chat/helpers/Modal.tsx
new file mode 100644
index 000000000..c7cbef9b4
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/helpers/Modal.tsx
@@ -0,0 +1,99 @@
+/**
+ * @file Modal
+ * generic modal component for spaces UI
+ * does not handle any business logic, acts only as a container
+ */
+import { useRef, useContext } from 'react';
+import styled from 'styled-components'
+import { ThemeContext } from '../theme/ThemeProvider';
+import { useClickAway } from '../../../hooks';
+
+// import { ThemeContext } from '../theme/ThemeProvider';
+
+// import { useClickAway } from '../../../hooks';
+
+interface IModalProps {
+ width?: string;
+ clickawayClose?: () => void;
+ children: any;
+}
+
+const ClickawayCloseModal = ({ children, clickawayClose, width }: IModalProps) => {
+ const modalRef = useRef(null);
+ const theme = useContext(ThemeContext)
+
+ useClickAway(modalRef, () => {
+ if (clickawayClose) {
+ clickawayClose();
+ }
+ });
+
+ return (
+
+ {children}
+
+ );
+};
+
+export const Modal = ({ clickawayClose, children, width }: IModalProps) => {
+ const theme = useContext(ThemeContext)
+ return (
+
+ {clickawayClose ? (
+ {children}
+ ) : (
+
+ { children }
+
+ )}
+
+ );
+};
+
+/* styling */
+
+const ModalOverlay = styled.div`
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(0, 0, 0, 0.4); /* Black with 40% opacity */
+ display: flex;
+ color: ${props => props.theme.textColorPrimary ?? '#000'};
+ justify-content: center;
+ align-items: center;
+ z-index: 2000;
+
+ max-height: 100vh;
+ overflow-y: auto;
+ margin: auto !important;
+`;
+
+const ModalParent = styled.div`
+ // position: absolute;
+ // top: 50%;
+ // left: 50%;
+ // transform: translate(-50%, -50%);
+
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 24px 20px;
+
+ background: ${(props => props.theme.bgColorPrimary)};
+ border-radius: 12px;
+
+ width: ${(props => props.width ? props.width : 'auto')};
+ margin: auto !important;
+
+ @media (max-width: 425px) {
+ min-width: 300px;
+ max-width: 300px;
+ }
+`;
diff --git a/packages/uiweb/src/lib/components/chat/helpers/NewToast.tsx b/packages/uiweb/src/lib/components/chat/helpers/NewToast.tsx
new file mode 100644
index 000000000..3009b3054
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/helpers/NewToast.tsx
@@ -0,0 +1,228 @@
+/* eslint-disable @typescript-eslint/no-inferrable-types */
+// // React + Web3 Essentials
+
+// // External Packages
+import { Spinner } from '../../supportChat/spinner/Spinner';
+import { toast } from 'react-toastify';
+import styled, { ThemeProvider } from 'styled-components';
+import CloseIcon from '../../../icons/close.svg';
+import useMediaQuery from './useMediaQuery';
+import { useContext, useRef } from 'react';
+import { Image } from '../../reusables';
+import { device } from '../../../config';
+import { ThemeContext } from '../theme/ThemeProvider';
+import { MdOutlineClose } from 'react-icons/md';
+// import useMediaQuery from './useMediaQuery';
+
+
+// Types
+type LoaderToastType = { msg: string; loaderColor: string; textColor: string };
+
+const override: React.CSSProperties = {
+ // width: "fit-content",
+ height: '45px',
+};
+
+
+const LoaderToast = ({ msg, loaderColor, textColor }: LoaderToastType) => (
+
+
+
+ {msg}
+
+
+);
+
+const CloseButton = ({ closeToast }:{ closeToast: any }) => (
+
+);
+
+export type ShowLoaderToastType = ({ loaderMessage }: { loaderMessage: string }) => React.ReactText;
+
+export type ShowMessageToastType = ({
+ toastTitle,
+ toastMessage,
+ toastType,
+ getToastIcon,
+}: {
+ toastTitle: string;
+ toastMessage: string;
+ toastType: 'SUCCESS' | 'ERROR';
+ getToastIcon?: (size: number) => JSX.Element;
+}) => void;
+
+const useToast = (
+ autoClose: number = 3000,
+ position: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' = 'top-right'
+) => {
+ const toastId = useRef(null);
+ const theme = useContext(ThemeContext);
+ const isMobile = useMediaQuery(device.tablet);
+
+ let isLoaderToastShown = false;
+
+ const showLoaderToast: ShowLoaderToastType = ({ loaderMessage }) => {
+ isLoaderToastShown = true;
+ return (toastId.current = toast(
+
+
+ ,
+ {
+ position,
+ autoClose: false,
+ hideProgressBar: true,
+ closeOnClick: true,
+ pauseOnHover: true,
+ draggable: true,
+ progress: undefined,
+ closeButton: false,
+ style: {
+ background: theme.mainBg,
+ border: `1px solid ${theme.toastBorderColor}`,
+ boxShadow: `8px 8px 8px ${theme.toastShadowColor}`,
+ borderRadius: '20px',
+ },
+ }
+ ));
+ };
+
+ const showMessageToast: ShowMessageToastType = ({ toastTitle, toastMessage, toastType, getToastIcon }) => {
+
+ const toastUI = (
+
+ {getToastIcon ? getToastIcon(30) : ''}
+
+
+ {toastTitle}
+
+
+ {toastMessage}
+
+
+
+ );
+
+ const toastRenderParams = {
+ position,
+ hideProgressBar: true,
+ closeOnClick: true,
+ pauseOnHover: true,
+ draggable: true,
+ progress: undefined,
+ type: toast.TYPE.DEFAULT,
+ closeButton: CloseButton,
+ autoClose: autoClose,
+ style: {
+ background: toastType === 'SUCCESS' ? theme.toastSuccessBackground : theme.toastErrorBackground,
+ boxShadow: `10px 10px 10px ${theme.toastShadowColor}`,
+ borderRadius: '20px',
+ margin: isMobile ? '20px' : '0px',
+ },
+ };
+
+ if (!isLoaderToastShown) {
+ // render a new toast
+ toastId.current = toast(toastUI, {
+ ...toastRenderParams,
+ });
+ }
+
+ // update the old toast
+ toast.update(toastId.current, {
+ render: toastUI,
+ ...toastRenderParams,
+ });
+ };
+
+ return {
+ showLoaderToast,
+ showMessageToast,
+ };
+};
+
+const LoaderNotification = styled.div`
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ justify-content: center;
+ margin: 1% 3%;
+`;
+const LoaderMessage = styled.div`
+ margin-left: 3%;
+ font-size: 1rem;
+ font-weight: 600;
+ line-height: 1.3rem;
+ letter-spacing: 0em;
+ text-align: left;
+`;
+
+const Toast = styled.div`
+ display: flex;
+ flex-direction: row;
+ align-items: flex-start;
+ margin: 1.5% 1%;
+`;
+const ToastIcon = styled.div`
+ width: 15%;
+ margin-right: 4%;
+`;
+const ToastContent = styled.div`
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+`;
+const ToastTitle = styled.div`
+ font-weight: 500;
+ font-size: 1.125rem;
+ letter-spacing: -0.019em;
+ line-height: 1.4rem;
+ letter-spacing: 0em;
+ text-align: left;
+ margin-bottom: 1%;
+`;
+const ToastMessage = styled.div`
+ font-weight: 400;
+ font-size: 0.9375rem;
+ line-height: 1.3rem;
+ text-align: left;
+`;
+
+const Button = styled.button`
+ cursor: pointer;
+ background: none;
+ margin: 0;
+ padding: 0;
+ width: 1.3rem;
+ height: 1.3rem;
+ border: none;
+`;
+
+export default useToast;
diff --git a/packages/uiweb/src/lib/components/chat/helpers/Toast.tsx b/packages/uiweb/src/lib/components/chat/helpers/Toast.tsx
new file mode 100644
index 000000000..0411a0c6a
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/helpers/Toast.tsx
@@ -0,0 +1,190 @@
+import { useState, useEffect, Fragment, useContext } from 'react';
+import styled, { keyframes } from 'styled-components';
+import { Image, Section } from '../../reusables';
+import CloseIcon from '../../../icons/close.svg';
+import { CheckCircleIcon } from '../../../icons/CheckCircle';
+import { ThemeContext } from '../theme/ThemeProvider';
+import { IChatTheme } from '../theme';
+import InfoIcon from '../../../icons/infodark.svg';
+
+
+type toastProps = {
+ toastMessage: string;
+ position?: string;
+ status: string;
+}
+
+const Toast = ({ toastMessage, position, status }: toastProps) => {
+ const [message, setMessage] = useState('');
+ const theme = useContext(ThemeContext);
+
+ useEffect(() => {
+ setMessage(toastMessage);
+
+ setTimeout(() => {
+ setMessage('');
+ }, 5000);
+ }, [toastMessage]);
+
+ const closeToast = () => {
+ setMessage('');
+ }
+ if (message !== '') {
+ return (
+
+
+
+
+ {status === 'success' ? : }
+
+
+
+ {status === 'success' ? 'Success' : 'Error'}
+
+ {toastMessage}
+
+
+
+
+
+
+ );
+ } else return null
+}
+
+
+const Container = styled.div`
+ position: fixed !important;
+ z-index: 999999;
+ width: 100vw;
+ top: 0;
+ left: 0;
+ height: 100vh;
+`;
+
+const toastInRight = keyframes`
+ from {
+ transform: translateX(100%);
+ }
+ to {
+ transform: translateX(0);
+ }
+`;
+
+const toastInLeft = keyframes`
+ from {
+ transform: translateX(-100%);
+ }
+ to {
+ transform: translateX(0);
+ }
+`;
+
+const Notification = styled.div<{theme?: IChatTheme, status?: string}>`
+ background: ${(props) => props.status === 'success' ? props.theme.toastSuccessBackground : props.theme.toastErrorBackground};
+ transition: .3s ease;
+ position: fixed;
+ pointer-events: auto;
+ overflow: hidden;
+ margin: 0 0 6px;
+ margin-bottom: 15px;
+ // width: 300px;
+ max-height: 100px;
+ border-radius: 16px;
+ box-shadow: 0 0 10px #999;
+ box-shadow: 10px 10px 10px ${(props) => props.theme.toastShadowColor};
+ opacity: 1;
+ display: flex;
+ flex-direction: row;
+ gap: 10px;
+
+
+ &:hover {
+ box-shadow: 0 0 12px #fff;
+ opacity: 1;
+ cursor: pointer;
+ }
+
+ &.toast {
+ height: fit-content;
+ width: fit-content;
+ padding: 20px 15px;
+ }
+
+ &.top-right {
+ top: 20px;
+ right: 20px;
+ animation: ${toastInRight} .3s ease-in-out .3s both;
+ }
+
+ &.bottom-right {
+ bottom: 20px;
+ right: 20px;
+ transition: transform .6s ease-in-out;
+ animation: ${toastInRight} .7s;
+ }
+
+ &.top-left {
+ top: 20px;
+ left: 20px;
+ transition: transform .6s ease-in;
+ animation: ${toastInLeft} .7s;
+ }
+
+ &.bottom-left {
+ bottom: 20px;
+ left: 20px;
+ transition: transform .6s ease-in;
+ animation: ${toastInLeft} .7s;
+ }
+`;
+
+
+
+const NotificationImage = styled.div`
+`;
+
+const NotificationTitle = styled.div`
+ font-weight: 700;
+ font-size: 16px;
+ text-align: left;
+ margin-top: 0;
+ margin-bottom: 5px;
+ min-width: 250px;
+ color: ${(props) => props.theme.textColorPrimary};
+
+`;
+
+const NotificationMessage = styled.div`
+ margin: 0;
+ text-align: left;
+ white-space: nowrap;
+ color: ${(props) => props.theme.snackbarBorderText};
+
+ font-weight: 400;
+ font-size: 14px;
+`;
+
+const Button = styled.div`
+ position: relative;
+ font-weight: 700;
+ color: #fff;
+ outline: none;
+ border: none;
+ text-shadow: 0 1px 0 #fff;
+ opacity: 1;
+ line-height: 1;
+ font-size: 16px;
+ padding: 0;
+ cursor: pointer;
+ background: 0 0;
+ border: 0;
+`;
+
+export default Toast;
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/components/chat/helpers/helper.ts b/packages/uiweb/src/lib/components/chat/helpers/helper.ts
index 2f6aff6a6..d4db16867 100644
--- a/packages/uiweb/src/lib/components/chat/helpers/helper.ts
+++ b/packages/uiweb/src/lib/components/chat/helpers/helper.ts
@@ -1,2 +1,106 @@
-import { IMessagePayload } from "../exportedTypes";
+import { IMessagePayload, User } from "../exportedTypes";
+import { ethers } from "ethers";
+import { IGroup } from "../../../types";
+import { walletToPCAIP10 } from "../../../helpers";
+export const getAdminList = (groupInformation: IGroup): Array => {
+ const adminsFromMembers = convertToWalletAddressList(groupInformation?.members.filter((admin) => admin.isAdmin == true));
+ const adminsFromPendingMembers = convertToWalletAddressList(groupInformation?.pendingMembers.filter((admin) => admin.isAdmin == true));
+ const adminList = [...adminsFromMembers,...adminsFromPendingMembers];
+ return adminList
+ };
+
+export const convertToWalletAddressList = (
+ memberList: { wallet: string }[]
+ ): string[] => {
+ return memberList ? memberList.map((member) => member.wallet) : [];
+ }
+
+export const getUpdatedMemberList = (groupInfo: IGroup ,walletAddress:string): Array =>{
+ const members = groupInfo?.members?.filter((i) => i.wallet?.toLowerCase() !== walletAddress?.toLowerCase());
+ return convertToWalletAddressList([...members,...groupInfo.pendingMembers]);
+}
+
+export const getUpdatedAdminList = (groupInfo: IGroup, walletAddress: string | null, toRemove: boolean): Array => {
+ const groupAdminList: any = getAdminList(groupInfo);
+ if (!toRemove) {
+ return [...groupAdminList, walletAddress];
+ } else {
+ const newAdminList = groupAdminList.filter((wallet: any) => wallet !== walletAddress);
+ return newAdminList;
+ }
+ };
+
+export const profilePicture = `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAvklEQVR4AcXBsW2FMBiF0Y8r3GQb6jeBxRauYRpo4yGQkMd4A7kg7Z/GUfSKe8703fKDkTATZsJsrr0RlZSJ9r4RLayMvLmJjnQS1d6IhJkwE2bT13U/DBzp5BN73xgRZsJMmM1HOolqb/yWiWpvjJSUiRZWopIykTATZsJs5g+1N6KSMiO1N/5DmAkzYTa9Lh6MhJkwE2ZzSZlo7xvRwson3txERzqJhJkwE2bT6+JhoKTMJ2pvjAgzYSbMfgDlXixqjH6gRgAAAABJRU5ErkJggg==`;
+
+
+export const displayDefaultUser = ({ caip10 }: { caip10: string }): User => {
+ const userCreated: User = {
+ did: caip10,
+ wallets: caip10,
+ publicKey: 'temp',
+ profilePicture: profilePicture,
+ encryptedPrivateKey: 'temp',
+ encryptionType: 'temp',
+ signature: 'temp',
+ sigType: 'temp',
+ about: null,
+ name: null,
+ numMsg: 1,
+ allowedNumMsg: 100,
+ linkedListHash: null,
+ };
+ return userCreated;
+ };
+
+export const findObject = (data: any,parentArray: any[],property: string ): boolean => {
+ let isPresent = false;
+ if(data) {
+ parentArray.map((value) => {
+ if (value[property] == data[property]) {
+ isPresent = true;
+ }
+ });
+ }
+ return isPresent;
+ }
+
+export const MemberAlreadyPresent = (member: any, groupMembers: any )=>{
+ const memberCheck = groupMembers?.find((x: any)=>x.wallet?.toLowerCase() == member.wallets?.toLowerCase());
+ if(memberCheck){
+ return true;
+ }
+ return false;
+ }
+
+export const addWalletValidation = (member:User,memberList:any,groupMembers:any,account: any) =>{
+ const checkIfMemberisAlreadyPresent = MemberAlreadyPresent(member, groupMembers);
+
+ let errorMessage = '';
+
+ if (checkIfMemberisAlreadyPresent) {
+ errorMessage = "This Member is Already present in the group"
+ }
+
+ if (memberList?.length + groupMembers?.length >= 9) {
+ errorMessage = 'No More Addresses can be added'
+ }
+
+ if (memberList?.length >= 9) {
+ errorMessage = 'No More Addresses can be added'
+ }
+
+ if (findObject(member, memberList, 'wallets')) {
+ errorMessage = 'Address is already added'
+ }
+
+ if (member?.wallets?.toLowerCase() === walletToPCAIP10(account)?.toLowerCase()) {
+ errorMessage = 'Group Creator cannot be added as Member'
+ }
+
+ return errorMessage;
+ }
+
+export function isValidETHAddress(address: string) {
+ return ethers.utils.isAddress(address);
+ }
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/components/chat/helpers/index.ts b/packages/uiweb/src/lib/components/chat/helpers/index.ts
new file mode 100644
index 000000000..eb150f0e9
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/helpers/index.ts
@@ -0,0 +1 @@
+export * from './twitter';
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/components/chat/helpers/useMediaQuery.ts b/packages/uiweb/src/lib/components/chat/helpers/useMediaQuery.ts
new file mode 100644
index 000000000..f5fa9d7d8
--- /dev/null
+++ b/packages/uiweb/src/lib/components/chat/helpers/useMediaQuery.ts
@@ -0,0 +1,43 @@
+import { useEffect, useState } from 'react';
+
+function useMediaQuery(query: string): boolean {
+ const getMatches = (query: string): boolean => {
+ // Prevents SSR issues
+ if (typeof window !== 'undefined') {
+ return window.matchMedia(query).matches;
+ }
+ return false;
+ };
+
+ const [matches, setMatches] = useState(getMatches(query));
+
+ function handleChange() {
+ setMatches(getMatches(query));
+ }
+
+ useEffect(() => {
+ const matchMedia = window.matchMedia(query);
+
+ // Triggered at the first client-side load and if query changes
+ handleChange();
+
+ // Listen matchMedia
+ if (matchMedia.addListener) {
+ matchMedia.addListener(handleChange);
+ } else {
+ matchMedia.addEventListener('change', handleChange);
+ }
+
+ return () => {
+ if (matchMedia.removeListener) {
+ matchMedia.removeListener(handleChange);
+ } else {
+ matchMedia.removeEventListener('change', handleChange);
+ }
+ };
+ }, [query]);
+
+ return matches;
+}
+
+export default useMediaQuery;
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/components/chat/index.ts b/packages/uiweb/src/lib/components/chat/index.ts
index 95973691a..10531f89e 100644
--- a/packages/uiweb/src/lib/components/chat/index.ts
+++ b/packages/uiweb/src/lib/components/chat/index.ts
@@ -1,4 +1,7 @@
-export { MessageBubble } from './MessageBubble';
-export * from './MessageList';
+export { ChatViewBubble } from './ChatViewBubble';
+export * from './ChatViewList';
export * from './exportedTypes';
+export * from "./ChatProfile"
+export * from './ChatViewComponent'
+export * from "./MessageInput"
export * from './theme';
diff --git a/packages/uiweb/src/lib/components/chat/theme/index.ts b/packages/uiweb/src/lib/components/chat/theme/index.ts
index 9a198030a..34947cdb4 100644
--- a/packages/uiweb/src/lib/components/chat/theme/index.ts
+++ b/packages/uiweb/src/lib/components/chat/theme/index.ts
@@ -17,6 +17,32 @@ export interface IChatTheme {
iconColorPrimary?: string;
fontFamily?: string;
chatBubblePrimaryBgColor?: string;
+ fileIconColor?: string;
+ dropdownBorderColor?: string;
+ modalPrimaryTextColor?: string;
+ modalSearchBarBorderColor?: string;
+ modalSearchBarBackground?: string;
+ snapFocusBg?: string;
+ groupButtonBackgroundColor?: string;
+ groupButtonTextColor?: string;
+ modalConfirmButtonBorder?: string;
+ groupSearchProfilBackground?: string,
+ modalInputBorderColor?: string,
+ snackbarBorderText?: string,
+ snackbarBorderIcon?: string,
+ modalContentBackground?: string,
+ modalProfileTextColor?: string,
+ toastSuccessBackground?: string;
+ toastErrorBackground?: string;
+ toastShadowColor?: string;
+ toastBorderColor?: string;
+ mainBg?: string;
+ modalBorderColor?: string;
+ modalDescriptionTextColor?: string;
+ modalIconColor?: string;
+ pendingCardBackground?: string,
+ modalHeadingColor?: string;
+ defaultBorder?: string;
}
export const lightChatTheme: IChatTheme = {
@@ -31,12 +57,38 @@ export const lightChatTheme: IChatTheme = {
accentTextColor: '#fff',
btnColorPrimary: 'rgb(202, 89, 155)',
border: 'none',
- borderRadius: '32px',
+ borderRadius: '24px',
iconColorPrimary: 'none',
+ fileIconColor: '#000',
+ dropdownBorderColor: '1px solid rgb(229, 232, 246)',
+ modalPrimaryTextColor: '#1E1E1E',
+ modalSearchBarBorderColor: '#BAC4D6',
+ modalSearchBarBackground: '#FFF',
+ snapFocusBg: '#F4F5FA',
+ groupButtonBackgroundColor: '#ADB0BE',
+ groupButtonTextColor: '#FFF',
+ modalConfirmButtonBorder: '1px solid #F4DCEA',
+ groupSearchProfilBackground: '#F4F5FA',
+ modalInputBorderColor: '#C2CBDB',
+ snackbarBorderText: '#000',
+ snackbarBorderIcon: 'none',
+ modalContentBackground: '#FFFFFF',
+ modalProfileTextColor: '#1E1E1E',
+ toastSuccessBackground: 'linear-gradient(90.15deg, #30CC8B -125.65%, #30CC8B -125.63%, #F3FFF9 42.81%)',
+ toastErrorBackground: 'linear-gradient(90.15deg, #FF2070 -125.65%, #FF2D79 -125.63%, #FFF9FB 42.81%)',
+ toastShadowColor: '#ccc',
+ toastBorderColor: '#F4F3FF',
+ mainBg: '#fff',
+ modalBorderColor: '#E5E8F6',
+ modalDescriptionTextColor: '#575D73',
+ modalIconColor: '#657795',
+ pendingCardBackground: 'rgba(173, 176, 190, 0.12)',
+ modalHeadingColor: '#333333',
+ defaultBorder: '#E5E8F6',
};
export const darkChatTheme: IChatTheme = {
- chatBubblePrimaryBgColor: 'fff',
+ chatBubblePrimaryBgColor: '#fff',
bgColorPrimary: 'rgb(47, 49, 55)',
bgColorSecondary: 'rgb(40, 42, 46)',
textColorPrimary: '#fff',
@@ -46,8 +98,35 @@ export const darkChatTheme: IChatTheme = {
accentTextColor: '#fff',
btnColorPrimary: 'rgb(202, 89, 155)',
border: 'none',
- borderRadius: '32px',
+ borderRadius: '24px',
iconColorPrimary:
'brightness(0) saturate(100%) invert(89%) sepia(8%) saturate(1567%) hue-rotate(191deg) brightness(86%) contrast(93%)',
+ dropdownBorderColor: '1px solid rgb(74, 79, 103)',
+ fileIconColor: '#fff',
+ modalPrimaryTextColor: '#B6BCD6',
+ modalSearchBarBorderColor: '#4A4F67',
+ modalSearchBarBackground: '#282A2E',
+ snapFocusBg: '#404650',
+ groupButtonBackgroundColor: '#2F3137',
+ groupButtonTextColor: '#787E99',
+ modalConfirmButtonBorder: '1px solid #787E99',
+ groupSearchProfilBackground: '#404650',
+ modalInputBorderColor: '#4A4F67',
+ snackbarBorderText: '#B6BCD6',
+ snackbarBorderIcon:
+ 'brightness(0) saturate(100%) invert(89%) sepia(8%) saturate(1567%) hue-rotate(191deg) brightness(86%) contrast(93%)',
+ modalContentBackground: '#2F3137',
+ modalProfileTextColor: '#B6BCD6',
+ toastSuccessBackground: 'linear-gradient(90.15deg, #30CC8B -125.65%, #30CC8B -125.63%, #2F3137 42.81%)',
+ toastErrorBackground: 'linear-gradient(89.96deg, #FF2070 -101.85%, #2F3137 51.33%)',
+ toastShadowColor: '#00000010',
+ toastBorderColor: '#4A4F67',
+ mainBg: '#000',
+ modalBorderColor: '#4A4F67',
+ modalDescriptionTextColor: '#787E99',
+ modalIconColor: '#787E99',
+ pendingCardBackground: 'rgba(173, 176, 190, 0.08)',
+ modalHeadingColor: '#B6BCD6',
+ defaultBorder: '#4A4F67'
};
diff --git a/packages/uiweb/src/lib/components/chatAndNotification/ChatAndNotification.tsx b/packages/uiweb/src/lib/components/chatAndNotification/ChatAndNotification.tsx
index afef6def9..b65c46478 100644
--- a/packages/uiweb/src/lib/components/chatAndNotification/ChatAndNotification.tsx
+++ b/packages/uiweb/src/lib/components/chatAndNotification/ChatAndNotification.tsx
@@ -128,7 +128,7 @@ export const ChatAndNotification = () => {
(async () => {
let user;
if (account) {
- user = await fetchChatProfile({ profileId: account });
+ user = await fetchChatProfile({ profileId: account ,env});
if (user) setConnectedProfile(user);
}
diff --git a/packages/uiweb/src/lib/components/reusables/sharedStyling.tsx b/packages/uiweb/src/lib/components/reusables/sharedStyling.tsx
index 8fa096b69..6ce67adbf 100644
--- a/packages/uiweb/src/lib/components/reusables/sharedStyling.tsx
+++ b/packages/uiweb/src/lib/components/reusables/sharedStyling.tsx
@@ -41,7 +41,7 @@ export const Span = styled.span`
cursor: ${(props) => props.cursor || 'default'};
margin: ${(props) => props.margin || '0px'};
padding: ${(props) => props.padding || '0px'};
- position: ${(props) => props.position || 'static'};
+ position: ${(props) => props.position || 'relative'};
right: ${(props) => props.right || 'auto'};
text-align: ${(props) => props.textAlign || 'center'};
text-transform: ${(props) => props.textTransform || 'inherit'};
@@ -100,7 +100,7 @@ export const Section = styled.div`
width: ${(props) => props.width || 'auto'};
overflow: ${(props) => props.overflow || 'default'};
padding: ${(props) => props.padding || '0px'};
- position: ${(props) => props.position || 'static'};
+ position: ${(props) => props.position || 'relative'};
background: ${(props) =>
props.gradient
? props.gradient
@@ -155,6 +155,7 @@ type DivStyleProps = {
cursor?: string;
alignSelf?:string;
margin?:string;
+ textAlign?:string;
};
export const Div = styled.div`
height: ${(props) => props.height || 'auto'};
@@ -162,4 +163,5 @@ export const Div = styled.div`
margin: ${(props) => props.margin || '0px'};
cursor: ${(props) => props.cursor || 'default'};
align-self: ${(props) => props.alignSelf || 'center'};
+ text-align: ${(props) => props.textAlign || 'default'};
`;
diff --git a/packages/uiweb/src/lib/components/supportChat/spinner/Spinner.tsx b/packages/uiweb/src/lib/components/supportChat/spinner/Spinner.tsx
index 83627c35b..9aa0086c7 100644
--- a/packages/uiweb/src/lib/components/supportChat/spinner/Spinner.tsx
+++ b/packages/uiweb/src/lib/components/supportChat/spinner/Spinner.tsx
@@ -5,17 +5,18 @@ import { SpinnerSvg } from '../../../icons/SpinnerSvg';
type SpinnerPropType = {
size?: string;
+ color?: string
};
type SpinLoaderPropType = {
width?: string;
};
-export const Spinner: React.FC = ({ size = 42 }) => {
+export const Spinner: React.FC = ({ size = 42, color }) => {
const { theme } = useContext(SupportChatPropsContext);
return (
-
+
);
};
diff --git a/packages/uiweb/src/lib/context/chatContext.ts b/packages/uiweb/src/lib/context/chatContext.ts
index 330936e67..c075f3377 100644
--- a/packages/uiweb/src/lib/context/chatContext.ts
+++ b/packages/uiweb/src/lib/context/chatContext.ts
@@ -27,7 +27,7 @@ export const initialChatDataContextValues: IChatDataContextValues = {
setPgpPrivateKey: () => {
/**/
},
- env: Constants.ENV.DEV,
+ env: Constants.ENV.PROD,
setEnv: () => {
/**/
},
diff --git a/packages/uiweb/src/lib/dataProviders/ChatDataProvider.tsx b/packages/uiweb/src/lib/dataProviders/ChatDataProvider.tsx
index e099818fe..278f9f5ca 100644
--- a/packages/uiweb/src/lib/dataProviders/ChatDataProvider.tsx
+++ b/packages/uiweb/src/lib/dataProviders/ChatDataProvider.tsx
@@ -8,7 +8,6 @@ import { ThemeContext } from '../components/chat/theme/ThemeProvider';
import useGetChatProfile from '../hooks/useGetChatProfile';
import { IUser } from '@pushprotocol/restapi';
import { IChatTheme, lightChatTheme } from '../components/chat/theme';
-import { ChatThemeOptions } from '../components';
export interface IChatUIProviderProps {
children: ReactNode;
@@ -35,22 +34,37 @@ export const ChatUIProvider = ({
const [isPushChatSocketConnected, setIsPushChatSocketConnected] =
useState(false);
+
-useEffect(()=>{
+ useEffect(() => {
+ resetStates();
+ setEnvVal(env);
+ setAccountVal(account);
+ setPgpPrivateKeyVal(pgpPrivateKey);
+ }, [env,account,pgpPrivateKey])
+
+ useEffect(() => {
setAccountVal(account)
- setPgpPrivateKeyVal(pgpPrivateKey)
-},[pgpPrivateKey])
+ }, [account])
+
+
+
+const resetStates = () => {
+ setPushChatSocket(null);
+ setIsPushChatSocketConnected(false);
+
+};
useEffect(() => {
(async () => {
let user;
if (account) {
- user = await fetchChatProfile({ profileId: account });
+ user = await fetchChatProfile({ profileId: account,env });
if (user) setConnectedProfile(user);
}
})();
- }, [account]);
+ }, [account,env]);
const value: IChatDataContextValues = {
account: accountVal,
@@ -69,7 +83,7 @@ useEffect(() => {
const PROVIDER_THEME = Object.assign({}, lightChatTheme, theme);
-
+console.log(PROVIDER_THEME)
return (
diff --git a/packages/uiweb/src/lib/helpers/address.ts b/packages/uiweb/src/lib/helpers/address.ts
index 5c1912572..5af01ad2c 100644
--- a/packages/uiweb/src/lib/helpers/address.ts
+++ b/packages/uiweb/src/lib/helpers/address.ts
@@ -1,5 +1,5 @@
import { ethers } from 'ethers';
-import type { Web3Provider } from '@ethersproject/providers';
+import type { Web3Provider, InfuraProvider } from '@ethersproject/providers';
/**
*
@@ -64,6 +64,36 @@ export const resolveEns = (address: string, provider: Web3Provider) => {
});
};
+export const resolveNewEns = async (address: string, provider: InfuraProvider) => {
+ const walletLowercase = pCAIP10ToWallet(address).toLowerCase();
+ const checksumWallet = ethers.utils.getAddress(walletLowercase);
+ let result;
+ // let provider = ethers.getDefaultProvider('mainnet');
+ // if (
+ // window.location.hostname == 'app.push.org' ||
+ // window.location.hostname == 'staging.push.org' ||
+ // window.location.hostname == 'dev.push.org' ||
+ // window.location.hostname == 'alpha.push.org' ||
+ // window.location.hostname == 'w2w.push.org'
+ // ) {
+ // provider = new ethers.providers.InfuraProvider(
+ // 'mainnet',
+ // appConfig.infuraAPIKey
+ // );
+ // }
+
+ await provider.lookupAddress(checksumWallet).then((ens) => {
+ if (ens) {
+ result = ens;
+ return ens;
+ } else {
+ result = null;
+ return null;
+ }
+ });
+ return result;
+};
+
export const isPCAIP = (id: string) => {
const prefix = `eip155:`;
return id?.startsWith(prefix);
diff --git a/packages/uiweb/src/lib/helpers/chat/chat.ts b/packages/uiweb/src/lib/helpers/chat/chat.ts
index 8990916c9..357dce16e 100644
--- a/packages/uiweb/src/lib/helpers/chat/chat.ts
+++ b/packages/uiweb/src/lib/helpers/chat/chat.ts
@@ -1,11 +1,12 @@
import * as PushAPI from '@pushprotocol/restapi';
import type { ENV } from '../../config';
import { Constants } from '../../config';
-import type { AccountEnvOptionsType, IMessageIPFS, Messagetype } from '../../types';
+import type { AccountEnvOptionsType, IGroup, IMessageIPFS, Messagetype } from '../../types';
import { ChatFeedsType } from '../../types';
import type { Env, IConnectedUser, IFeeds, IUser } from '@pushprotocol/restapi';
import { isPCAIP, pCAIP10ToWallet, walletToPCAIP10 } from '../address';
import { getData } from './localStorage';
+import { ethers } from 'ethers';
type HandleOnChatIconClickProps = {
isModalOpen: boolean;
@@ -127,7 +128,7 @@ export const copyToClipboard = (address: string): void => {
};
-export const getDefaultFeedObject = ({user}:{user:IUser}):IFeeds => {
+export const getDefaultFeedObject = ({user,groupInformation}:{user?:IUser,groupInformation?:IGroup}) :IFeeds => {
const feed = {
msg: {
messageContent: '',
@@ -143,21 +144,21 @@ export const getDefaultFeedObject = ({user}:{user:IUser}):IFeeds => {
toDID: '',
toCAIP10: '',
},
- wallets: user.wallets,
- did: user.did,
+ wallets: groupInformation?null: user!.wallets,
+ did: groupInformation?null: user!.did,
threadhash: null,
- profilePicture: user?.profile?.picture,
+ profilePicture: groupInformation?groupInformation.groupImage:user?.profile.picture,
name: null,
- about: user.about,
+ about: groupInformation?null:user!.about,
intent: null,
intentSentBy: null,
intentTimestamp: new Date(),
- publicKey: user.publicKey,
+ publicKey: groupInformation?null: user!.publicKey,
combinedDID: '',
cid: '',
groupInformation: undefined,
};
- return feed;
+ return feed as IFeeds;
}
type CheckIfIntentType = {
@@ -165,6 +166,8 @@ type CheckIfIntentType = {
account:string,
}
export const checkIfIntent = ({chat,account}:CheckIfIntentType):boolean => {
+ console.log(chat)
+ console.log(account)
if(Object.keys(chat || {}).length && (chat.combinedDID.toLowerCase()).includes(walletToPCAIP10(account).toLowerCase()))
{
if( chat.intent && (chat.intent.toLowerCase()).includes(walletToPCAIP10(account).toLowerCase()))
@@ -211,4 +214,28 @@ export const appendUniqueMessages = (parentList:Messagetype,newlist:IMessageIPFS
)
);
return newMessageList
-}
\ No newline at end of file
+}
+
+export const checkIfSameChat = (
+ msg: IMessageIPFS,
+ account: string,
+ chatId: string
+) => {
+ if (ethers.utils.isAddress(chatId)) {
+ chatId = walletToPCAIP10(chatId);
+ }
+ if (
+ Object.keys(msg || {}).length &&
+ ((chatId.toLowerCase() ===
+ msg.fromCAIP10?.toLowerCase() &&
+ walletToPCAIP10(account!).toLowerCase() ===
+ msg.toCAIP10?.toLowerCase()) ||
+ (chatId.toLowerCase() ===
+ msg.toCAIP10?.toLowerCase() &&
+ walletToPCAIP10(account!).toLowerCase() ===
+ msg.fromCAIP10?.toLowerCase()))
+ ) {
+ return true;
+ }
+ return false;
+};
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/helpers/chat/search.ts b/packages/uiweb/src/lib/helpers/chat/search.ts
index 9c0bd1546..cad3ae4c4 100644
--- a/packages/uiweb/src/lib/helpers/chat/search.ts
+++ b/packages/uiweb/src/lib/helpers/chat/search.ts
@@ -59,7 +59,7 @@ export const getNewChatUser = async ({
address = await getAddress(searchText, env);
if (address) {
- chatProfile = await fetchChatProfile({ profileId: address });
+ chatProfile = await fetchChatProfile({ profileId: address ,env});
if (!chatProfile)
chatProfile = displayDefaultUser({ caip10: walletToPCAIP10(address) });
return chatProfile;
diff --git a/packages/uiweb/src/lib/helpers/chat/time.ts b/packages/uiweb/src/lib/helpers/chat/time.ts
index 30b211377..496105bff 100644
--- a/packages/uiweb/src/lib/helpers/chat/time.ts
+++ b/packages/uiweb/src/lib/helpers/chat/time.ts
@@ -6,7 +6,7 @@ export const dateToFromNowDaily = (timestamp: number): string => {
lastDay: '[Yesterday]',
sameDay: '[Today]',
nextWeek: 'dddd',
- sameElse: 'L'
+ sameElse: 'LL'
});
return timestampDate;
};
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/hooks/chat/index.ts b/packages/uiweb/src/lib/hooks/chat/index.ts
index 93b5dafbe..8aa6ea6bb 100644
--- a/packages/uiweb/src/lib/hooks/chat/index.ts
+++ b/packages/uiweb/src/lib/hooks/chat/index.ts
@@ -1,4 +1,11 @@
export * from './useFetchHistoryMessages';
export * from './useChatData';
-export * from './usePushChatSocket';
\ No newline at end of file
+export * from './useChatProfile';
+export * from './usePushChatSocket';
+export * from './usePushSendMessage';
+export * from './useGetGroupByID';
+export * from './useFetchChat';
+export * from './useFetchConversationHash';
+export * from './usePushSendMessage';
+export * from './useGetGroupByID';
diff --git a/packages/uiweb/src/lib/hooks/chat/useApproveChatRequest.ts b/packages/uiweb/src/lib/hooks/chat/useApproveChatRequest.ts
new file mode 100644
index 000000000..474e64735
--- /dev/null
+++ b/packages/uiweb/src/lib/hooks/chat/useApproveChatRequest.ts
@@ -0,0 +1,41 @@
+import * as PushAPI from '@pushprotocol/restapi';
+import { useCallback, useContext, useState } from 'react';
+import { useChatData } from './useChatData';
+
+interface ApproveChatParams {
+ chatId: string;
+}
+
+const useApproveChatRequest = () => {
+ const [error, setError] = useState();
+ const [loading, setLoading] = useState(false);
+ const { account, env,pgpPrivateKey } =useChatData();
+ const approveChatRequest = useCallback(async (options:ApproveChatParams) => {
+ const {
+
+ chatId,
+ } = options || {};
+ setLoading(true);
+ try {
+ const response = await PushAPI.chat.approve({
+ status: 'Approved',
+ account: account,
+ senderAddress: chatId, // receiver's address or chatId of a group
+ pgpPrivateKey:pgpPrivateKey,
+ env: env,
+ });
+ return response;
+ } catch (error: Error | any) {
+ setLoading(false);
+ setError(error.message);
+ console.log(error);
+ return;
+ }
+ },
+ []
+ );
+
+ return { approveChatRequest, error, loading };
+};
+
+export default useApproveChatRequest;
diff --git a/packages/uiweb/src/lib/hooks/chat/useChatProfile.ts b/packages/uiweb/src/lib/hooks/chat/useChatProfile.ts
new file mode 100644
index 000000000..8a6d71411
--- /dev/null
+++ b/packages/uiweb/src/lib/hooks/chat/useChatProfile.ts
@@ -0,0 +1,32 @@
+import * as PushAPI from '@pushprotocol/restapi';
+import { useCallback, useContext } from 'react';
+import { useChatData } from './useChatData';
+
+export interface ProfileParams {
+ profileId: string;
+}
+
+const useChatProfile = () => {
+ const { env } = useChatData();
+ const fetchUserChatProfile = useCallback(
+ async ({
+ profileId
+ }: ProfileParams): Promise => {
+ try {
+ const profile = await PushAPI.user.get({
+ env: env,
+ account: profileId,
+ });
+ return profile;
+ } catch (error) {
+ console.log(error);
+ return;
+ }
+ },
+ [env]
+ );
+
+ return { fetchUserChatProfile };
+};
+
+export default useChatProfile;
diff --git a/packages/uiweb/src/lib/hooks/chat/useFetchChat.ts b/packages/uiweb/src/lib/hooks/chat/useFetchChat.ts
new file mode 100644
index 000000000..4a0887956
--- /dev/null
+++ b/packages/uiweb/src/lib/hooks/chat/useFetchChat.ts
@@ -0,0 +1,46 @@
+import * as PushAPI from '@pushprotocol/restapi';
+import { Env } from '@pushprotocol/restapi';
+import { useCallback, useContext, useState } from 'react';
+import { useChatData } from './useChatData';
+
+
+interface fetchChat {
+ chatId: string;
+}
+
+const useFetchChat = () => {
+ const [error, setError] = useState();
+ const [loading, setLoading] = useState(false);
+ const { account, env,pgpPrivateKey } = useChatData();
+
+
+ const fetchChat = useCallback(
+ async ({ chatId}: fetchChat) => {
+ console.log(env);
+ setLoading(true);
+ try {
+ const chat = await PushAPI.chat.chat({
+ account: account!,
+ toDecrypt: pgpPrivateKey ? true : false,
+ pgpPrivateKey: String(pgpPrivateKey),
+ recipient: chatId,
+ env: env
+ });
+ return chat;
+ } catch (error: Error | any) {
+ setLoading(false);
+ setError(error.message);
+ console.log(error);
+ return;
+ }
+ finally {
+ setLoading(false);
+ }
+ },
+ [pgpPrivateKey,env]
+ );
+
+ return { fetchChat, error, loading };
+};
+
+export default useFetchChat;
diff --git a/packages/uiweb/src/lib/hooks/chat/useFetchConversationHash.ts b/packages/uiweb/src/lib/hooks/chat/useFetchConversationHash.ts
new file mode 100644
index 000000000..407e34576
--- /dev/null
+++ b/packages/uiweb/src/lib/hooks/chat/useFetchConversationHash.ts
@@ -0,0 +1,38 @@
+import * as PushAPI from '@pushprotocol/restapi';
+import { Env } from '@pushprotocol/restapi';
+import { useCallback, useContext, useState } from 'react';
+import { useChatData } from './useChatData';
+
+interface conversationHashParams {
+ conversationId: string;
+}
+
+const useFetchConversationHash = () => {
+ const [error, setError] = useState();
+ const [loading, setLoading] = useState(false);
+ const { account, env } = useChatData();
+
+ const fetchConversationHash = useCallback(
+ async ({ conversationId }: conversationHashParams) => {
+ setLoading(true);
+ try {
+ const response = await PushAPI.chat.conversationHash({
+ conversationId,
+ account: account!,
+ env: env,
+ });
+ setLoading(false);
+ return response;
+ } catch (error: Error | any) {
+ setLoading(false);
+ setError(error.message);
+ console.log(error);
+ return;
+ }
+ },
+ [env, account]
+ );
+ return { fetchConversationHash, error, loading };
+};
+
+export default useFetchConversationHash;
diff --git a/packages/uiweb/src/lib/hooks/chat/useFetchHistoryMessages.ts b/packages/uiweb/src/lib/hooks/chat/useFetchHistoryMessages.ts
index bddf1e64c..cdd2ee84c 100644
--- a/packages/uiweb/src/lib/hooks/chat/useFetchHistoryMessages.ts
+++ b/packages/uiweb/src/lib/hooks/chat/useFetchHistoryMessages.ts
@@ -3,6 +3,7 @@ import * as PushAPI from '@pushprotocol/restapi';
import type { IMessageIPFS } from '@pushprotocol/restapi';
import { useCallback, useContext, useState } from 'react';
import { ChatDataContext } from '../../context';
+import { useChatData } from './useChatData';
@@ -18,8 +19,7 @@ const useFetchHistoryMessages
const [error, setError] = useState();
const [loading, setLoading] = useState(false);
- const { account, env,pgpPrivateKey } =
- useContext(ChatDataContext);
+ const { account, env,pgpPrivateKey } = useChatData();
const historyMessages = useCallback(async ({threadHash,limit = 10,}:HistoryMessagesParams) => {
@@ -27,7 +27,7 @@ const useFetchHistoryMessages
try {
const chatHistory:IMessageIPFS[] = await PushAPI.chat.history({
threadhash: threadHash,
- account: account,
+ account: account!,
toDecrypt: pgpPrivateKey ? true : false,
pgpPrivateKey: String(pgpPrivateKey),
limit: limit,
diff --git a/packages/uiweb/src/lib/hooks/chat/useGetGroup.ts b/packages/uiweb/src/lib/hooks/chat/useGetGroup.ts
new file mode 100644
index 000000000..d98059dca
--- /dev/null
+++ b/packages/uiweb/src/lib/hooks/chat/useGetGroup.ts
@@ -0,0 +1,51 @@
+import * as PushAPI from '@pushprotocol/restapi';
+import { Env } from '@pushprotocol/restapi';
+import { useCallback, useContext, useState } from 'react';
+import { useChatData } from './useChatData';
+import { IGroup } from '../../types';
+
+interface getGroup {
+ searchText: string;
+}
+
+const useGetGroup = () => {
+ const [error, setError] = useState();
+ const [loading, setLoading] = useState(false);
+ const { env } = useChatData();
+
+ const getGroup = useCallback(
+ async ({ searchText }: getGroup) => {
+ setLoading(true);
+ let group: IGroup;
+ try {
+ group = await PushAPI.chat.getGroup({ chatId: searchText, env });
+ } catch (error: Error | any) {
+ if ((error.message as string).includes('No group with chatId')) {
+ try {
+ group = await PushAPI.chat.getGroupByName({
+ groupName: searchText,
+ env,
+ });
+ } catch (err) {
+ setLoading(false);
+ setError(error.message);
+ console.log(error);
+ return;
+ }
+ } else {
+ setLoading(false);
+ setError(error.message);
+ console.log(error);
+ return;
+ }
+ }
+ return group;
+
+ },
+ [ env]
+ );
+
+ return { getGroup, error, loading };
+};
+
+export default useGetGroup;
diff --git a/packages/uiweb/src/lib/hooks/chat/useGetGroupByID.ts b/packages/uiweb/src/lib/hooks/chat/useGetGroupByID.ts
new file mode 100644
index 000000000..93881577f
--- /dev/null
+++ b/packages/uiweb/src/lib/hooks/chat/useGetGroupByID.ts
@@ -0,0 +1,37 @@
+import * as PushAPI from '@pushprotocol/restapi';
+import { Env } from '@pushprotocol/restapi';
+import { useCallback, useContext, useState } from 'react';
+import { useChatData } from './useChatData';
+import { IGroup } from '../../types';
+
+interface getGroup {
+ groupId: string;
+}
+
+const useGetGroupByID = () => {
+ const [error, setError] = useState();
+ const [loading, setLoading] = useState(false);
+ const { env } = useChatData();
+
+ const getGroupByID = useCallback(
+ async ({ groupId }: getGroup) => {
+ setLoading(true);
+ let group: IGroup;
+ try {
+ group = await PushAPI.chat.getGroup({
+ chatId: groupId,
+ env: env,
+ });
+ } catch (error) {
+ console.log(error);
+ return;
+ }
+ return group;
+ },
+ [env]
+ );
+
+ return { getGroupByID, error, loading };
+};
+
+export default useGetGroupByID;
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/hooks/chat/usePushChatSocket.ts b/packages/uiweb/src/lib/hooks/chat/usePushChatSocket.ts
index 81e173358..7d5484a7c 100644
--- a/packages/uiweb/src/lib/hooks/chat/usePushChatSocket.ts
+++ b/packages/uiweb/src/lib/hooks/chat/usePushChatSocket.ts
@@ -8,7 +8,6 @@ import { useChatData } from './useChatData';
import { SOCKET_TYPE } from '../../types';
import { getChatId } from '../../helpers';
-
export type PushChatSocketHookOptions = {
account?: string | null;
env?: ENV;
@@ -23,10 +22,21 @@ export const usePushChatSocket = () => {
setIsPushChatSocketConnected,
isPushChatSocketConnected,
connectedProfile,
- env
+ env,
} = useChatData();
- const [messagesSinceLastConnection,setMessagesSinceLastConnection] = useState({});
+ const [messagesSinceLastConnection, setMessagesSinceLastConnection] =
+ useState({});
+ const [
+ groupInformationSinceLastConnection,
+ setGroupInformationSinceLastConnection,
+ ] = useState({});
+
+ // useEffect(() => {
+ // // console.log(pgpPrivateKey, "pgppppppppp")
+ // // console.log(connectedProfile, "connectedProfileeeeeeeee")
+ // }, [pgpPrivateKey, connectedProfile])
+
const addSocketEvents = useCallback(() => {
console.log('addSocketEvents');
pushChatSocket?.on(EVENTS.CONNECT, () => {
@@ -37,35 +47,41 @@ export const usePushChatSocket = () => {
setIsPushChatSocketConnected(false);
});
+ pushChatSocket?.on(EVENTS.CHAT_RECEIVED_MESSAGE, async (chat: any) => {
+ console.log(chat)
+ console.log(connectedProfile)
+ console.log(pgpPrivateKey)
+ if (!connectedProfile || !pgpPrivateKey) {
+ return;
+ }
+ console.log(chat)
+ if (
+ ( chat.messageCategory === 'Request') &&
+ (chat.messageContent === null) &&
+ (chat.messageType === null)
+ ) {
+ return;
+ }
+ console.log(chat)
+ const response = await PushAPI.chat.decryptConversation({
+ messages: [chat],
+ connectedUser: connectedProfile,
+ pgpPrivateKey: pgpPrivateKey,
+ env: env,
+ });
+ console.log(chat)
+
+ if (response && response.length) {
+ setMessagesSinceLastConnection(response[0]);
+ }
+ });
+ pushChatSocket?.on(EVENTS.CHAT_GROUPS, (groupInfo: any) => {
+ /**
+ * We receive a group creation or updated event.
+ */
+ setGroupInformationSinceLastConnection(groupInfo);
+ });
- pushChatSocket?.on(
- EVENTS.CHAT_RECEIVED_MESSAGE,
- async (chat: any) => {
- if (!connectedProfile || !pgpPrivateKey) {
- return;
- }
- // const chatId = getChatId({ msg: chat, account:account! }).toLowerCase();
- if (
- chat.messageCategory === 'Request' &&
- chat.messageContent === null &&
- chat.messageType === null
- ) {
- return;
- }
-
- const response = await PushAPI.chat.decryptConversation({
- messages: [chat],
- connectedUser: connectedProfile,
- pgpPrivateKey: pgpPrivateKey,
- env: env,
- });
- if (response && response.length) {
- setMessagesSinceLastConnection(response[0]);
- }
- }
- );
-
-
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
pushChatSocket,
@@ -73,7 +89,7 @@ export const usePushChatSocket = () => {
pgpPrivateKey,
messagesSinceLastConnection,
env,
-
+ connectedProfile,
]);
const removeSocketEvents = useCallback(() => {
@@ -93,7 +109,7 @@ export const usePushChatSocket = () => {
removeSocketEvents();
}
};
- }, [pushChatSocket]);
+ }, [pushChatSocket, pgpPrivateKey, connectedProfile]);
/**
* Whenever the required params to create a connection object change
@@ -103,24 +119,21 @@ export const usePushChatSocket = () => {
useEffect(() => {
if (account) {
if (pushChatSocket && pushChatSocket.connected) {
- // console.log('=================>>> disconnection in the hook');
// pushChatSocket?.disconnect();
- }
- else {
+ } else {
const main = async () => {
- const connectionObject = createSocketConnection({
- user: account,
- env,
- socketType: SOCKET_TYPE.CHAT,
- socketOptions: { autoConnect: true, reconnectionAttempts: 3 },
- });
- console.warn('new connection object: ', connectionObject);
-
- setPushChatSocket(connectionObject);
- };
- main().catch((err) => console.error(err));
+ const connectionObject = createSocketConnection({
+ user: account,
+ env,
+ socketType: SOCKET_TYPE.CHAT,
+ socketOptions: { autoConnect: true, reconnectionAttempts: 3 },
+ });
+ console.warn('new connection object: ', connectionObject);
+
+ setPushChatSocket(connectionObject);
+ };
+ main().catch((err) => console.error(err));
}
-
}
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -129,6 +142,7 @@ export const usePushChatSocket = () => {
return {
pushChatSocket,
isPushChatSocketConnected,
- messagesSinceLastConnection
+ messagesSinceLastConnection,
+ groupInformationSinceLastConnection,
};
};
diff --git a/packages/uiweb/src/lib/hooks/chat/usePushSendMessage.ts b/packages/uiweb/src/lib/hooks/chat/usePushSendMessage.ts
new file mode 100644
index 000000000..dbba7517d
--- /dev/null
+++ b/packages/uiweb/src/lib/hooks/chat/usePushSendMessage.ts
@@ -0,0 +1,50 @@
+import * as PushAPI from '@pushprotocol/restapi';
+import { useCallback, useContext, useState } from 'react';
+
+import { useChatData } from '..';
+import { ENV } from '../../config';
+
+interface SendMessageParams {
+ message: string;
+ chatId: string;
+ messageType?: 'Text' | 'Image' | 'File' | 'GIF' | 'MediaEmbed';
+}
+
+const usePushSendMessage = () => {
+ const [error, setError] = useState();
+ const [loading, setLoading] = useState(false);
+
+ const { pgpPrivateKey, env, account } = useChatData();
+
+ const sendMessage = useCallback(
+ async (options: SendMessageParams) => {
+ const { chatId, message, messageType } = options || {};
+ setLoading(true);
+ try {
+ const response = await PushAPI.chat.send({
+ messageContent: message,
+ messageType: messageType,
+ receiverAddress: chatId,
+ account: account ? account : undefined,
+ pgpPrivateKey: pgpPrivateKey ? pgpPrivateKey : undefined,
+ env: env,
+ });
+ setLoading(false);
+ if (!response) {
+ return false;
+ }
+ return;
+ } catch (error: Error | any) {
+ setLoading(false);
+ setError(error.message);
+ console.log(error);
+ return;
+ }
+ },
+ [pgpPrivateKey, account]
+ );
+
+ return { sendMessage, error, loading };
+};
+
+export default usePushSendMessage;
diff --git a/packages/uiweb/src/lib/hooks/chatAndNotification/chat/useFetchHistoryMessages.ts b/packages/uiweb/src/lib/hooks/chatAndNotification/chat/useFetchHistoryMessages.ts
index a5394d73d..48f23c7d0 100644
--- a/packages/uiweb/src/lib/hooks/chatAndNotification/chat/useFetchHistoryMessages.ts
+++ b/packages/uiweb/src/lib/hooks/chatAndNotification/chat/useFetchHistoryMessages.ts
@@ -36,6 +36,7 @@ const useFetchHistoryMessages
limit: limit,
env: env
});
+ console.log(chatHistory)
chatHistory.reverse();
if (chats.get(selectedChatId as string)) {
const uniqueMap: { [timestamp: number]: IMessageIPFS } = {};
diff --git a/packages/uiweb/src/lib/hooks/chatAndNotification/useChatNotificationSocket.ts b/packages/uiweb/src/lib/hooks/chatAndNotification/useChatNotificationSocket.ts
index 921043f0d..927c85e9f 100644
--- a/packages/uiweb/src/lib/hooks/chatAndNotification/useChatNotificationSocket.ts
+++ b/packages/uiweb/src/lib/hooks/chatAndNotification/useChatNotificationSocket.ts
@@ -111,7 +111,7 @@ const useChatNotificationSocket = ({
chat.messageType === null
) {
if (chat.messageOrigin === 'other') {
- const user = await fetchChatProfile({ profileId: chatId });
+ const user = await fetchChatProfile({ profileId: chatId,env });
if (user || Object.keys(user || {}).length) {
let newOne: IFeeds = {} as IFeeds;
diff --git a/packages/uiweb/src/lib/hooks/useGetChatProfile.ts b/packages/uiweb/src/lib/hooks/useGetChatProfile.ts
index 6bfb1e5e5..8937a7026 100644
--- a/packages/uiweb/src/lib/hooks/useGetChatProfile.ts
+++ b/packages/uiweb/src/lib/hooks/useGetChatProfile.ts
@@ -4,13 +4,14 @@ import { ChatAndNotificationPropsContext } from '../context';
export interface GetProfileParams {
profileId: string;
+ env:PushAPI.Env
}
const useGetChatProfile = () => {
- const { env } = useContext(ChatAndNotificationPropsContext);
const fetchChatProfile = useCallback(
async ({
- profileId
+ profileId,
+ env
}: GetProfileParams): Promise => {
try {
const profile = await PushAPI.user.get({
@@ -23,7 +24,7 @@ const useGetChatProfile = () => {
return;
}
},
- [env]
+ []
);
return { fetchChatProfile };
diff --git a/packages/uiweb/src/lib/icons/Accept.svg b/packages/uiweb/src/lib/icons/Accept.svg
deleted file mode 100644
index 183d2cbb7..000000000
--- a/packages/uiweb/src/lib/icons/Accept.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/packages/uiweb/src/lib/icons/Adddark.tsx b/packages/uiweb/src/lib/icons/Adddark.tsx
new file mode 100644
index 000000000..b53d89497
--- /dev/null
+++ b/packages/uiweb/src/lib/icons/Adddark.tsx
@@ -0,0 +1,12 @@
+import React from 'react';
+
+export const AddUserDarkIcon = () => {
+ return (
+
+
+ );
+};
diff --git a/packages/uiweb/src/lib/icons/Attachment.tsx b/packages/uiweb/src/lib/icons/Attachment.tsx
index 2e4f8a8d5..c8979b221 100644
--- a/packages/uiweb/src/lib/icons/Attachment.tsx
+++ b/packages/uiweb/src/lib/icons/Attachment.tsx
@@ -1,6 +1,10 @@
import React from 'react';
-export const AttachmentIcon = () => {
+interface IAttachmentIconProps {
+ color?: string;
+}
+
+export const AttachmentIcon: React.FC = ({ color = "#494D5F" }) => {
return (
);
-};
+};
\ No newline at end of file
diff --git a/packages/uiweb/src/lib/icons/BRBIcon.svg b/packages/uiweb/src/lib/icons/BRBIcon.svg
new file mode 100644
index 000000000..1b4183b04
--- /dev/null
+++ b/packages/uiweb/src/lib/icons/BRBIcon.svg
@@ -0,0 +1,54 @@
+
diff --git a/packages/uiweb/src/lib/icons/CaretDownGrey.tsx b/packages/uiweb/src/lib/icons/CaretDownGrey.tsx
new file mode 100644
index 000000000..1e2fe2b7f
--- /dev/null
+++ b/packages/uiweb/src/lib/icons/CaretDownGrey.tsx
@@ -0,0 +1,9 @@
+import React from 'react';
+
+export const CaretDownIcon = () => {
+ return (
+
+ );
+};
diff --git a/packages/uiweb/src/lib/icons/CheckCircle.tsx b/packages/uiweb/src/lib/icons/CheckCircle.tsx
index b45fceb11..f537b2fcc 100644
--- a/packages/uiweb/src/lib/icons/CheckCircle.tsx
+++ b/packages/uiweb/src/lib/icons/CheckCircle.tsx
@@ -1,9 +1,9 @@
import React from 'react';
-export const CheckCircleIcon = () => {
+export const CheckCircleIcon = ({ stroke, height, width }: { stroke?: string, height?: string, width?: string }) => {
return (
-