diff --git a/packages/examples/sdk-frontend-react/src/app/ChatSupportTest.tsx b/packages/examples/sdk-frontend-react/src/app/ChatSupportTest.tsx index 636b42fe6..4e92657ba 100644 --- a/packages/examples/sdk-frontend-react/src/app/ChatSupportTest.tsx +++ b/packages/examples/sdk-frontend-react/src/app/ChatSupportTest.tsx @@ -35,8 +35,7 @@ export const ChatSupportTest = () => { { return produce(oldData, (draft) => { draft.local.address = this.account; @@ -89,7 +89,7 @@ export class Video { // Check if the chatId from the incoming video event matches the chatId of the current video instance if (chatId && chatId === videoV1Instance.data.meta.chatId) { // If the event is DenyVideo, destroy the local stream & reset the video call data - if (data.event === VideoEventType.DenyVideo) { + if (data.event === CONSTANTS.VIDEO.EVENT.DENY) { // destroy the local stream if (videoV1Instance.data.local.stream) { endStream(videoV1Instance.data.local.stream); @@ -100,15 +100,15 @@ export class Video { // If the event is ApproveVideo or RetryApproveVideo, connect to the video if ( - data.event === VideoEventType.ApproveVideo || - data.event === VideoEventType.RetryApproveVideo + data.event === CONSTANTS.VIDEO.EVENT.APPROVE || + data.event === CONSTANTS.VIDEO.EVENT.RETRY_APPROVE ) { videoV1Instance.connect({ peerAddress: address, signalData: signal }); } // If the event is RetryRequestVideo and the current instance is the initiator, send a request if ( - data.event === VideoEventType.RetryRequestVideo && + data.event === CONSTANTS.VIDEO.EVENT.RETRY_REQUEST && videoV1Instance.isInitiator() ) { videoV1Instance.request({ @@ -121,7 +121,7 @@ export class Video { // If the event is RetryRequestVideo and the current instance is not the initiator, accept the request if ( - data.event === VideoEventType.RetryRequestVideo && + data.event === CONSTANTS.VIDEO.EVENT.RETRY_REQUEST && !videoV1Instance.isInitiator() ) { videoV1Instance.acceptRequest({ diff --git a/packages/restapi/src/lib/pushstream/DataModifier.ts b/packages/restapi/src/lib/pushstream/DataModifier.ts index 2c4cb62cd..57cf9b387 100644 --- a/packages/restapi/src/lib/pushstream/DataModifier.ts +++ b/packages/restapi/src/lib/pushstream/DataModifier.ts @@ -203,6 +203,11 @@ export class DataModifier { includeRaw = false, eventType: MessageEventType ): MessageEvent { + + if (data.hasIntent === false && eventType === 'message') { + eventType = MessageEventType.Request; + } + const messageEvent: MessageEvent = { event: eventType, origin: data.messageOrigin, @@ -400,19 +405,19 @@ export class DataModifier { ): VideoEventType { switch (currentVideoStatus) { case VideoCallStatus.INITIALIZED: - return VideoEventType.RequestVideo; + return VideoEventType.REQUEST; case VideoCallStatus.RECEIVED: - return VideoEventType.ApproveVideo; + return VideoEventType.APPROVE; case VideoCallStatus.CONNECTED: - return VideoEventType.ConnectVideo; + return VideoEventType.CONNECT; case VideoCallStatus.ENDED: - return VideoEventType.DisconnectVideo; + return VideoEventType.DISCONNECT; case VideoCallStatus.DISCONNECTED: - return VideoEventType.DenyVideo; + return VideoEventType.DENY; case VideoCallStatus.RETRY_INITIALIZED: - return VideoEventType.RetryRequestVideo; + return VideoEventType.RETRY_REQUEST; case VideoCallStatus.RETRY_RECEIVED: - return VideoEventType.RetryApproveVideo; + return VideoEventType.RETRY_APPROVE; default: throw new Error(`Unknown video call status: ${currentVideoStatus}`); } @@ -426,7 +431,7 @@ export class DataModifier { const { senderAddress, signalData, status, chatId }: VideoDataType = JSON.parse(data.payload.data.additionalMeta?.data); - // To maintain backward compatibility, if the rules object is not present in the payload, + // To maintain backward compatibility, if the rules object is not present in the payload, // we create a new rules object with chatId from additionalMeta.data const rules = data.payload.rules ?? { access: { diff --git a/packages/restapi/src/lib/pushstream/pushStreamTypes.ts b/packages/restapi/src/lib/pushstream/pushStreamTypes.ts index 01cf61e7b..b72b26831 100644 --- a/packages/restapi/src/lib/pushstream/pushStreamTypes.ts +++ b/packages/restapi/src/lib/pushstream/pushStreamTypes.ts @@ -54,14 +54,14 @@ export enum GroupEventType { } export enum VideoEventType { - RequestVideo = 'video.request', - ApproveVideo = 'video.approve', - DenyVideo = 'video.deny', - ConnectVideo = 'video.connect', - DisconnectVideo = 'video.disconnect', + REQUEST = 'video.request', + APPROVE = 'video.approve', + DENY = 'video.deny', + CONNECT = 'video.connect', + DISCONNECT = 'video.disconnect', // retry events - RetryRequestVideo = 'video.retry.request', - RetryApproveVideo = 'video.retry.approve' + RETRY_REQUEST = 'video.retry.request', + RETRY_APPROVE = 'video.retry.approve' } export enum ProposedEventNames { diff --git a/packages/uiweb/README.md b/packages/uiweb/README.md index 44baaa442..a6e8ddda4 100644 --- a/packages/uiweb/README.md +++ b/packages/uiweb/README.md @@ -149,7 +149,7 @@ where | cta | string | Call To Action Link (given during notification creation) | | image | string | Any media link (given during notification creation) | | url | string | Channel Link (given during channel setup) | -| chainName | string | Can be anyone of the following blockchain networks on which the notification was sent - "ETH_MAINNET", "ETH_TEST_SEPOLIA", "POLYGON_MAINNET", "POLYGON_TEST_MUMBAI", "BSC_MAINNET, "BSC_TESTNET", "OPTIMISM_MAINNET", "OPTIMISM_TESTNET", "POLYGON_ZK_EVM_TESTNET", "POLYGON_ZK_EVM_MAINNET", "ARBITRUM_TESTNET", "ARBITRUMONE_MAINNET", "THE_GRAPH" | +| chainName | string | Can be anyone of the following blockchain networks on which the notification was sent - "ETH_MAINNET", "ETH_TEST_SEPOLIA", "POLYGON_MAINNET", "POLYGON_TEST_MUMBAI", "BSC_MAINNET, "BSC_TESTNET", "OPTIMISM_MAINNET", "OPTIMISM_TESTNET", "POLYGON_ZK_EVM_TESTNET", "POLYGON_ZK_EVM_MAINNET", "ARBITRUM_TESTNET", "ARBITRUMONE_MAINNET", "FUSE_TESTNET", "FUSE_MAINNET", "THE_GRAPH" | | theme | string | 'light' or 'dark' (customization to be given by the dApp) | | customTheme | INotificationItemTheme | custom theme object for the component | | isSpam | boolean | whether a spam notification or not | diff --git a/packages/uiweb/src/lib/components/chat/ChatProfile/ChatProfile.tsx b/packages/uiweb/src/lib/components/chat/ChatProfile/ChatProfile.tsx index d02c480e3..4358eef0d 100644 --- a/packages/uiweb/src/lib/components/chat/ChatProfile/ChatProfile.tsx +++ b/packages/uiweb/src/lib/components/chat/ChatProfile/ChatProfile.tsx @@ -46,7 +46,7 @@ export const ChatProfile: React.FC = ({ const theme = useContext(ThemeContext); const { account, env } = useChatData(); const { getGroupByID } = useGetGroupByID(); - const { fetchUserChatProfile } = useChatProfile(); + const { fetchChatProfile } = useChatProfile(); const [isGroup, setIsGroup] = useState(false); const [options, setOptions] = useState(false); @@ -69,7 +69,7 @@ export const ChatProfile: React.FC = ({ const fetchProfileData = async () => { if (isValidETHAddress(chatId)) { - const ChatProfile = await fetchUserChatProfile({ profileId: chatId }); + const ChatProfile = await fetchChatProfile({ profileId: chatId }); const result = await resolveNewEns(chatId, provider); setEnsName(result); setChatInfo(ChatProfile); diff --git a/packages/uiweb/src/lib/components/chat/CreateGroup/AddCriteria.tsx b/packages/uiweb/src/lib/components/chat/CreateGroup/AddCriteria.tsx index 6b986afa9..41feadd99 100644 --- a/packages/uiweb/src/lib/components/chat/CreateGroup/AddCriteria.tsx +++ b/packages/uiweb/src/lib/components/chat/CreateGroup/AddCriteria.tsx @@ -21,6 +21,7 @@ import { Checkbox } from '../reusables/Checkbox'; import OptionButtons from '../reusables/OptionButtons'; import EthereumSvg from '../../../icons/ethereum.svg'; +import FuseSVG from '../../../icons/fuse.svg'; import PolygonSvg from '../../../icons/polygon.svg'; import ArbitrumSvg from '../../../icons/arbitrum.svg'; import BSCSvg from '../../../icons/bsc.svg'; @@ -240,6 +241,13 @@ const AddCriteria = ({ icon: ArbitrumSvg, function: () => setSelectedChainValue(4), }, + { + id: 5, + value: BLOCKCHAIN_NETWORK[env].FUSE, + title: 'Fuse', + icon: FuseSVG, + function: () => setSelectedChainValue(5), + }, ]; const onQuantityChange = (e: any) => { diff --git a/packages/uiweb/src/lib/components/chat/MessageInput/MessageInput.tsx b/packages/uiweb/src/lib/components/chat/MessageInput/MessageInput.tsx index 1ae22ad36..d41775d41 100644 --- a/packages/uiweb/src/lib/components/chat/MessageInput/MessageInput.tsx +++ b/packages/uiweb/src/lib/components/chat/MessageInput/MessageInput.tsx @@ -142,6 +142,11 @@ export const MessageInput: React.FC = ({ } }, [textAreaRef, typedMessage]); + useEffect(() => { + if (!loading && textAreaRef.current) { + textAreaRef.current.focus(); + } + }, [loading, textAreaRef]); //need to do something about fetching connectedUser in every component useEffect(() => { (async () => { diff --git a/packages/uiweb/src/lib/components/chat/constants/chainDetails.tsx b/packages/uiweb/src/lib/components/chat/constants/chainDetails.tsx index c19731768..168d7aaff 100644 --- a/packages/uiweb/src/lib/components/chat/constants/chainDetails.tsx +++ b/packages/uiweb/src/lib/components/chat/constants/chainDetails.tsx @@ -7,6 +7,7 @@ import { BSCSvg } from "../../../icons/BSCSvg"; import { OptimismSvg } from "../../../icons/OptimismSvg"; import { PolygonzkevmSvg } from "../../../icons/PolygonzkevmSvg"; import { ArbitrumSvg } from "../../../icons/ArbitrumSvg" +import { FuseSvg } from "../../../icons/FuseSvg" import React from "react"; const createSVGIcon = (element:any, chainName: string) => { @@ -67,4 +68,12 @@ export const NETWORK_ICON_DETAILS = { label: 'ARBITRUM TESTNET', icon: createSVGIcon(, 'Arbitrum Testnet'), }, + 123 : { + label: 'FUSE TESTNET', + icon: createSVGIcon(, 'Fuse Testnet'), + }, + 122 : { + label: 'FUSE MAINNET', + icon: createSVGIcon(, 'Fuse Mainnet'), + } }; diff --git a/packages/uiweb/src/lib/components/chat/constants/index.ts b/packages/uiweb/src/lib/components/chat/constants/index.ts index c2ab37953..5aca45e5a 100644 --- a/packages/uiweb/src/lib/components/chat/constants/index.ts +++ b/packages/uiweb/src/lib/components/chat/constants/index.ts @@ -56,3 +56,5 @@ export const ACCESS_TYPE_TITLE = { }; export * from './chainDetails'; + +export const GUEST_MODE_ACCOUNT = '0x0000000000000000000000000000000000000000'; diff --git a/packages/uiweb/src/lib/components/notification/chainDetails.tsx b/packages/uiweb/src/lib/components/notification/chainDetails.tsx index 2b8eb2e82..7dc4452cc 100644 --- a/packages/uiweb/src/lib/components/notification/chainDetails.tsx +++ b/packages/uiweb/src/lib/components/notification/chainDetails.tsx @@ -1,71 +1,81 @@ - -import Tooltip from "../tooltip"; +import Tooltip from '../tooltip'; import { EthereumSvg } from '../../icons/EthereumSvg'; -import { PolygonSvg } from "../../icons/PolygonSvg"; -import { BSCSvg } from "../../icons/BSCSvg"; -import { OptimismSvg } from "../../icons/OptimismSvg"; -import { PolygonzkevmSvg } from "../../icons/PolygonzkevmSvg"; -import { TheGraphSvg } from "../../icons/TheGraphSvg"; -import { ArbitrumSvg } from "../../icons/ArbitrumSvg" - -const createSVGIcon = (element:any, chainName: string) => { +import { PolygonSvg } from '../../icons/PolygonSvg'; +import { BSCSvg } from '../../icons/BSCSvg'; +import { OptimismSvg } from '../../icons/OptimismSvg'; +import { PolygonzkevmSvg } from '../../icons/PolygonzkevmSvg'; +import { TheGraphSvg } from '../../icons/TheGraphSvg'; +import { ArbitrumSvg } from '../../icons/ArbitrumSvg'; +import { FuseSvg } from '../../icons/FuseSvg'; +const createSVGIcon = (element: any, chainName: string) => { return ( - - {element} - + {element} ); }; export default { ETH_TEST_SEPOLIA: { label: 'ETHEREUM SEPOLIA', - icon: createSVGIcon(, 'Ethereum Sepolia'), + icon: createSVGIcon(, 'Ethereum Sepolia'), }, ETH_MAINNET: { label: 'ETHEREUM MAINNET', - icon: createSVGIcon(, 'Ethereum Mainnet'), + icon: createSVGIcon(, 'Ethereum Mainnet'), }, POLYGON_TEST_MUMBAI: { label: 'POLYGON MUMBAI', - icon: createSVGIcon(, 'Polygon Mumbai'), + icon: createSVGIcon(, 'Polygon Mumbai'), }, POLYGON_MAINNET: { label: 'POLYGON MAINNET', - icon: createSVGIcon(, 'Polygon Mainnet'), + icon: createSVGIcon(, 'Polygon Mainnet'), }, BSC_TESTNET: { label: 'BSC TESTNET', - icon: createSVGIcon(, 'Bsc Testnet'), + icon: createSVGIcon(, 'Bsc Testnet'), }, BSC_MAINNET: { label: 'BSC MAINNET', - icon: createSVGIcon(, 'Bsc Mainnet'), + icon: createSVGIcon(, 'Bsc Mainnet'), }, OPTIMISM_TESTNET: { label: 'OPTIMISM TESTNET', - icon: createSVGIcon(, 'Optimism Testnet'), + icon: createSVGIcon(, 'Optimism Testnet'), }, OPTIMISM_MAINNET: { label: 'OPTIMISM MAINNET', - icon: createSVGIcon(, 'Optimism Mainnet'), + icon: createSVGIcon(, 'Optimism Mainnet'), }, POLYGON_ZK_EVM_TESTNET: { label: 'POLYGON ZK EVM TESTNET', - icon: createSVGIcon(, 'Polygon ZK EVM Testnet'), + icon: createSVGIcon(, 'Polygon ZK EVM Testnet'), }, POLYGON_ZK_EVM_MAINNET: { label: 'POLYGON ZK EVM MAINNET', - icon: createSVGIcon(, 'Polygon ZK EVM Mainnet'), + icon: createSVGIcon(, 'Polygon ZK EVM Mainnet'), }, ARBITRUMONE_MAINNET: { label: 'ARBITRUMONE MAINNET', - icon: createSVGIcon(, 'Arbitrum Mainnet'), + icon: createSVGIcon(, 'Arbitrum Mainnet'), }, ARBITRUM_TESTNET: { label: 'ARBITRUM TESTNET', - icon: createSVGIcon(, 'Arbitrum Testnet'), + icon: createSVGIcon(, 'Arbitrum Testnet'), + }, + + FUSE_MAINNET: { + label: 'FUSE MAINNNET', + icon: createSVGIcon(, 'Fuse Mainnet'), + }, + + FUSE_TESTNET: { + label: 'FUSE TESTNET', + icon: createSVGIcon(, 'Fuse Testnet'), + }, + THE_GRAPH: { + label: 'THE GRAPH', + icon: createSVGIcon(, 'The Graph'), }, - THE_GRAPH: { label: 'THE GRAPH', icon: createSVGIcon(, 'The Graph') }, }; diff --git a/packages/uiweb/src/lib/components/notification/index.tsx b/packages/uiweb/src/lib/components/notification/index.tsx index 4c8521fad..4a0aa631f 100644 --- a/packages/uiweb/src/lib/components/notification/index.tsx +++ b/packages/uiweb/src/lib/components/notification/index.tsx @@ -36,6 +36,8 @@ export type chainNameType = | 'POLYGON_ZK_EVM_MAINNET' | 'ARBITRUMONE_MAINNET' | 'ARBITRUM_TESTNET' + | 'FUSE_TESTNET' + | 'FUSE_MAINNET' | 'THE_GRAPH' | undefined; diff --git a/packages/uiweb/src/lib/components/supportChat/AddressInfo.tsx b/packages/uiweb/src/lib/components/supportChat/AddressInfo.tsx index 353719d24..544cc790a 100644 --- a/packages/uiweb/src/lib/components/supportChat/AddressInfo.tsx +++ b/packages/uiweb/src/lib/components/supportChat/AddressInfo.tsx @@ -1,9 +1,10 @@ import React, { useContext, useEffect, useState } from 'react'; import styled from 'styled-components'; import { SupportChatPropsContext } from '../../context'; -import { Constants } from '../../config'; -import { copyToClipboard, pCAIP10ToWallet } from '../../helpers'; +import { Constants, ENV, InfuraAPIKey, allowedNetworks } from '../../config'; +import { copyToClipboard, pCAIP10ToWallet, resolveNewEns } from '../../helpers'; import { CopySvg } from '../../icons/CopySvg'; +import { ethers } from 'ethers'; export const AddressInfo: React.FC = () => { const { supportAddress, env, theme, pushUser } = useContext(SupportChatPropsContext); @@ -11,11 +12,15 @@ export const AddressInfo: React.FC = () => { const [user, setUser] = useState({}); const [isCopied, setIsCopied] = useState(false); const walletAddress = pCAIP10ToWallet(supportAddress); + // const l1ChainId = (allowedNetworks[env]?.includes(1)) ? 1 : 5; + // const provider = new ethers.providers.InfuraProvider(l1ChainId, InfuraAPIKey); useEffect(() => { const getUser = async () => { if(pushUser){ const user = await pushUser.info(); +// const ensNameResult = await resolveNewEns(supportAddress, provider) +// setEnsName(ensNameResult!) setUser(user); } diff --git a/packages/uiweb/src/lib/components/supportChat/Chat.tsx b/packages/uiweb/src/lib/components/supportChat/Chat.tsx index c1caa964c..9fad073d2 100644 --- a/packages/uiweb/src/lib/components/supportChat/Chat.tsx +++ b/packages/uiweb/src/lib/components/supportChat/Chat.tsx @@ -3,7 +3,7 @@ import { PushAPI } from '@pushprotocol/restapi'; import { ChatIcon } from '../../icons/ChatIcon'; import { Modal } from './Modal'; import styled from 'styled-components'; -import { handleOnChatIconClick } from '../../helpers'; +import { getAddress, handleOnChatIconClick } from '../../helpers'; import { SupportChatMainStateContext, SupportChatPropsContext, @@ -16,6 +16,7 @@ import { useSDKSocket } from '../../hooks/useSDKSocket'; import { Div } from '../reusables/sharedStyling'; import { getAddressFromSigner } from '../../helpers'; import { sign } from 'crypto'; + export type ChatProps = { account?: string; signer: SignerType; @@ -50,6 +51,7 @@ export type ButtonStyleProps = { const [chats, setChats] = useState([]); const [accountadd, setAccountadd] = useState(account) const [pushUser, setPushUser] = useState(null); + const [resolvedSupportAddress, setResolvedSupportAddress] = useState(''); const setChatsSorted = (chats: IMessageIPFS[]) => { const chatsWithNumericTimestamps = chats.map(item => ({ @@ -72,7 +74,7 @@ export type ButtonStyleProps = { env, apiKey, pushUser: pushUser!, - supportAddress, + supportAddress: resolvedSupportAddress, signer }); @@ -81,7 +83,7 @@ export type ButtonStyleProps = { account : accountadd, signer, pushUser, - supportAddress, + supportAddress : resolvedSupportAddress, greetingMsg, modalTitle, theme: { ...lightTheme, ...theme }, @@ -89,6 +91,18 @@ export type ButtonStyleProps = { env, }; + useEffect(() => { + + const getNewSupportAddress = async() => { + if(supportAddress.includes(".")){ + const newAddress = await getAddress(supportAddress, env) +setResolvedSupportAddress(newAddress!); + }else{ +setResolvedSupportAddress(supportAddress); + } + } + getNewSupportAddress(); + },[supportAddress, pushUser, env]) useEffect(() => { diff --git a/packages/uiweb/src/lib/config/constants.ts b/packages/uiweb/src/lib/config/constants.ts index def327d3b..ecee60a6f 100644 --- a/packages/uiweb/src/lib/config/constants.ts +++ b/packages/uiweb/src/lib/config/constants.ts @@ -38,9 +38,9 @@ export const NETWORK_DETAILS = { //todo: need to change o sepolia export const CoreContractChainId = { prod: 1, - dev: 5, - staging: 5, - local: 5, + dev: 11155111, + staging: 11155111, + local: 11155111, }; @@ -51,7 +51,8 @@ const TESTNET_NETWORK = { BSC: 'eip155:97', OPTIMISM: 'eip155:420', POLYGON_ZK_EVM: 'eip155:1442', - ARBITRUM:'eip155:421613' + ARBITRUM:'eip155:421613', + FUSE: 'eip155:123' }; const MAINET_NETWORK = { ETHEREUM: 'eip155:1', @@ -59,7 +60,8 @@ const MAINET_NETWORK = { BSC: 'eip155:56', OPTIMISM: 'eip155:10', POLYGON_ZK_EVM: 'eip155:1101', - ARBITRUM:'eip155:42161' + ARBITRUM:'eip155:42161', + FUSE: 'eip155:122' }; export const BLOCKCHAIN_NETWORK = { @@ -75,14 +77,16 @@ export const allowedNetworks = { 137, //for polygon mainnet 56, // for bnb mainnet 10, // for optimism mainnet - 42161 // for arbitrum mainnet + 42161, // for arbitrum mainnet + 122 // for fuse mainnet ], dev: [ 11155111, // for eth sepolia 80001, //for mumbai polygon 97, // bnb testnet 420, // optimism goerli testnet - 421613 // for arbitrum testnet + 421613, // for arbitrum testnet + 123 // for fuse testnet ], staging: [ // 42, //for kovan @@ -90,14 +94,16 @@ export const allowedNetworks = { 80001, //for mumbai polygon 97, // bnb testnet 420, // optimism goerli testnet - 421613 // for arbitrum testnet + 421613, // for arbitrum testnet + 123 // for fuse testnet ], local: [ 11155111, // for eth sepolia 80001, //for mumbai polygon 97, // bnb testnet 420, // optimism goerli testnet - 421613 // for arbitrum testnet + 421613, // for arbitrum testnet + 123 // for fuse testnet ] } diff --git a/packages/uiweb/src/lib/context/chatContext.ts b/packages/uiweb/src/lib/context/chatContext.ts index 1757a940a..6ec5b2364 100644 --- a/packages/uiweb/src/lib/context/chatContext.ts +++ b/packages/uiweb/src/lib/context/chatContext.ts @@ -1,4 +1,4 @@ -import { Env,IMessageIPFS, IUser, SignerType } from "@pushprotocol/restapi"; +import { Env,IMessageIPFS, IUser, PushAPI, SignerType } from "@pushprotocol/restapi"; import { Constants } from "../config"; import { createContext } from "react"; @@ -18,6 +18,12 @@ export interface IChatDataContextValues { setIsPushChatSocketConnected: React.Dispatch>; connectedProfile: IUser | undefined; setConnectedProfile: (connectedProfile: IUser) => void; + pushUser: PushAPI | undefined; + setPushUser: React.Dispatch>; + pushChatStream: any; + setPushChatStream: React.Dispatch>; + isPushChatStreamConnected: boolean; + setIsPushChatStreamConnected: React.Dispatch>; } export const initialChatDataContextValues: IChatDataContextValues = { @@ -48,7 +54,19 @@ export const initialChatDataContextValues: IChatDataContextValues = { connectedProfile: undefined, setConnectedProfile: () => { /** */ - } + }, + pushUser: undefined, + setPushUser: () => { + /** */ + }, + pushChatStream: null, + setPushChatStream: () => { + /** */ + }, + isPushChatStreamConnected: false, + setIsPushChatStreamConnected: () => { + /** */ + }, } diff --git a/packages/uiweb/src/lib/dataProviders/ChatDataProvider.tsx b/packages/uiweb/src/lib/dataProviders/ChatDataProvider.tsx index a6f969222..fd26430f8 100644 --- a/packages/uiweb/src/lib/dataProviders/ChatDataProvider.tsx +++ b/packages/uiweb/src/lib/dataProviders/ChatDataProvider.tsx @@ -6,12 +6,15 @@ import { } from '../context/chatContext'; import { ThemeContext } from '../components/chat/theme/ThemeProvider'; import useGetChatProfile from '../hooks/useGetChatProfile'; -import { IUser, SignerType } from '@pushprotocol/restapi'; +import { IUser, PushAPI, SignerType } from '@pushprotocol/restapi'; import { IChatTheme, lightChatTheme } from '../components/chat/theme'; import { getAddressFromSigner, pCAIP10ToWallet } from '../helpers'; import useCreateChatProfile from '../hooks/useCreateChatProfile'; import useDecryptPGPKey from '../hooks/useDecryptPGPKey'; - +import useInitializePushUser from '../hooks/chat/useInitializePushUser'; +import useChatProfile from '../hooks/chat/useChatProfile'; +import { GUEST_MODE_ACCOUNT } from '../components/chat/constants'; +import usePushUserInfoUtilities from '../hooks/chat/usePushUserInfoUtilities'; export interface IChatUIProviderProps { children: ReactNode; @@ -19,109 +22,109 @@ export interface IChatUIProviderProps { account?: string | null; signer?: SignerType | undefined; pgpPrivateKey?: string | null; + pushUser?: PushAPI | undefined; env?: ENV; } export const ChatUIProvider = ({ children, - account = null, + account = undefined, + pushUser = undefined, theme, pgpPrivateKey = null, signer = undefined, env = Constants.ENV.PROD, }: IChatUIProviderProps) => { - const [accountVal, setAccountVal] = useState(pCAIP10ToWallet(account!)); - const [pushChatSocket, setPushChatSocket] = useState(null); - const [signerVal, setSignerVal] = useState(signer); + const [accountVal, setAccountVal] = useState( + pCAIP10ToWallet(account!) + ); + const [pushChatSocket, setPushChatSocket] = useState(null); + const [signerVal, setSignerVal] = useState(signer); + const [pushChatStream, setPushChatStream] = useState(null); + const [pushUserVal, setPushUserVal] = useState(pushUser); - const [pgpPrivateKeyVal, setPgpPrivateKeyVal] = - useState(pgpPrivateKey); + const [pgpPrivateKeyVal, setPgpPrivateKeyVal] = useState( + pgpPrivateKey + ); const [envVal, setEnvVal] = useState(env); - const {fetchChatProfile} = useGetChatProfile(); - const [connectedProfile,setConnectedProfile]=useState(undefined); + const [connectedProfile, setConnectedProfile] = useState( + undefined + ); const [isPushChatSocketConnected, setIsPushChatSocketConnected] = - useState(false); - const {createChatProfile} = useCreateChatProfile(); - const {decryptPGPKey} = useDecryptPGPKey(); + useState(false); + const { fetchEncryptionInfo } = usePushUserInfoUtilities(); + const { initializePushUser } = useInitializePushUser(); + const { fetchChatProfile } = useChatProfile(); + const [isPushChatStreamConnected, setIsPushChatStreamConnected] = + useState(false); useEffect(() => { - (async()=>{ + (async () => { resetStates(); setEnvVal(env); - - if (signer) { - if (!account) { - const address = await getAddressFromSigner(signer); - setAccountVal(address); - } - else{ - setAccountVal(account); - } - } + + if (Object.keys(signer || {}).length && !pushUser) { + const address = await getAddressFromSigner(signer!); + setAccountVal(address); + } else if (!signer && pushUser) { + const profile = await fetchChatProfile({}); + setAccountVal(profile?.wallets); + } else { + setAccountVal(GUEST_MODE_ACCOUNT); + } setSignerVal(signer); + setPushUserVal(pushUser); setPgpPrivateKeyVal(pgpPrivateKey); - })() - - }, [env,account,signer,pgpPrivateKey]) - + })(); + }, [env, account, signer, pgpPrivateKey,pushUser]); useEffect(() => { (async () => { - if (accountVal && signerVal) { - if (!pgpPrivateKeyVal) await handleUserCreation(); - } - })(); - }, [accountVal, signerVal]); - - const handleUserCreation = async () => { - if (!accountVal && !envVal) return; - try { - let user = await fetchChatProfile({ profileId: accountVal! ,env:envVal}); - if (!user) { - if (!signerVal) return; - user = await createChatProfile({ signer: signerVal ,env:envVal}); - } - if (user?.encryptedPrivateKey && !pgpPrivateKey) { - const decryptedPgpKey = await decryptPGPKey({ - encryptedPrivateKey: user.encryptedPrivateKey, - account: accountVal!, + if (accountVal && envVal ) { + const pushUser = await initializePushUser({ signer: signerVal, + account: accountVal!, env: envVal, }); - if(decryptedPgpKey) - setPgpPrivateKeyVal(decryptedPgpKey); + setPushUserVal(pushUser); } - } catch (e: any) { - console.log(e); - } - }; - - + })(); + }, [signerVal, accountVal, envVal]); -const resetStates = () => { - setPushChatSocket(null); - setIsPushChatSocketConnected(false); - -}; + useEffect(() => { + (async () => { + if (pushUserVal && !pgpPrivateKeyVal) { + const encryptionInfo = await fetchEncryptionInfo({pushUser:pushUserVal}); + if (encryptionInfo) + setPgpPrivateKeyVal(encryptionInfo.decryptedPgpPrivateKey); + } + })(); + }, [pushUserVal]); + const resetStates = () => { + setPushChatSocket(null); + setIsPushChatSocketConnected(false); + setPushChatStream(null); + setIsPushChatStreamConnected(false); + }; -useEffect(() => { + useEffect(() => { (async () => { let user; if (account) { - user = await fetchChatProfile({ profileId: account,env }); + user = await fetchChatProfile({ profileId: account, env }); if (user) setConnectedProfile(user); } })(); - }, [account,env,pgpPrivateKey]); + }, [account, env, pgpPrivateKey]); const value: IChatDataContextValues = { account: accountVal, - signer:signerVal, - setSigner:setSignerVal, + signer: signerVal, + setSigner: setSignerVal, setAccount: setAccountVal, pgpPrivateKey: pgpPrivateKeyVal, setPgpPrivateKey: setPgpPrivateKeyVal, @@ -132,10 +135,15 @@ useEffect(() => { isPushChatSocketConnected, setIsPushChatSocketConnected, connectedProfile, - setConnectedProfile + setConnectedProfile, + pushChatStream, + setPushChatStream, + isPushChatStreamConnected, + setIsPushChatStreamConnected, + pushUser: pushUserVal, + setPushUser: setPushUserVal, }; - const PROVIDER_THEME = Object.assign({}, lightChatTheme, theme); return ( diff --git a/packages/uiweb/src/lib/helpers/chat/search.ts b/packages/uiweb/src/lib/helpers/chat/search.ts index 35140895b..d3f9dba31 100644 --- a/packages/uiweb/src/lib/helpers/chat/search.ts +++ b/packages/uiweb/src/lib/helpers/chat/search.ts @@ -56,6 +56,7 @@ type getNewChatUserParamType = { searchText: string; fetchChatProfile: ({ profileId, + env }: GetProfileParams) => Promise; env: Env; }; diff --git a/packages/uiweb/src/lib/hooks/chat/index.ts b/packages/uiweb/src/lib/hooks/chat/index.ts index 16e3f5f39..7b3e13bc7 100644 --- a/packages/uiweb/src/lib/hooks/chat/index.ts +++ b/packages/uiweb/src/lib/hooks/chat/index.ts @@ -8,3 +8,6 @@ export * from './usePushSendMessage'; export * from './useGetGroupByID'; export * from './useAccount'; export * from './useUpdateGroup'; +export * from './useInitializePushUser'; +export * from './usePushChatStream'; +export * from './usePushUserInfoUtilities'; \ No newline at end of file diff --git a/packages/uiweb/src/lib/hooks/chat/useChatProfile.ts b/packages/uiweb/src/lib/hooks/chat/useChatProfile.ts index 8a6d71411..4bf6832d1 100644 --- a/packages/uiweb/src/lib/hooks/chat/useChatProfile.ts +++ b/packages/uiweb/src/lib/hooks/chat/useChatProfile.ts @@ -1,32 +1,37 @@ import * as PushAPI from '@pushprotocol/restapi'; import { useCallback, useContext } from 'react'; import { useChatData } from './useChatData'; +import { Env } from '@pushprotocol/restapi'; -export interface ProfileParams { - profileId: string; +export interface FetchProfileParams { + profileId?: string; + env?: Env; } const useChatProfile = () => { - const { env } = useChatData(); - const fetchUserChatProfile = useCallback( + const { pushUser } = useChatData(); + const fetchChatProfile = useCallback( async ({ - profileId - }: ProfileParams): Promise => { + profileId, + //note: remove env when chat and notification component is shifted to class based + env + }: FetchProfileParams): Promise => { try { - const profile = await PushAPI.user.get({ - env: env, - account: profileId, - }); - return profile; + let userReadOnly; + if(profileId) + userReadOnly = await pushUser!.info({ overrideAccount: profileId }); + else + userReadOnly = await pushUser!.info(); + return userReadOnly; } catch (error) { console.log(error); return; } }, - [env] + [] ); - return { fetchUserChatProfile }; + return { fetchChatProfile }; }; export default useChatProfile; diff --git a/packages/uiweb/src/lib/hooks/chat/useInitializePushUser.ts b/packages/uiweb/src/lib/hooks/chat/useInitializePushUser.ts new file mode 100644 index 000000000..0b3b4dd7f --- /dev/null +++ b/packages/uiweb/src/lib/hooks/chat/useInitializePushUser.ts @@ -0,0 +1,31 @@ +import { PushAPI, SignerType } from "@pushprotocol/restapi"; +import { useCallback, useContext } from "react"; +import { useChatData } from "./index"; +import { ENV } from "../../config"; + +export interface InitializePushUserParams { + signer?: SignerType; + account: string; + env: ENV; +} + +const useInitializePushUser = () => { + + const initializePushUser = useCallback(async({signer, account,env}: InitializePushUserParams): Promise => { + try { + const pushUser = await PushAPI.initialize(signer?? undefined, { + env: env, + account: account, + alpha: { feature: ['SCALABILITY_V2'] }, + }) + return pushUser; + } catch (error) { + console.log(error); + return; + } + }, + []) + return {initializePushUser}; +}; + +export default useInitializePushUser; \ No newline at end of file diff --git a/packages/uiweb/src/lib/hooks/chat/usePushChatStream.ts b/packages/uiweb/src/lib/hooks/chat/usePushChatStream.ts new file mode 100644 index 000000000..bb50c32cb --- /dev/null +++ b/packages/uiweb/src/lib/hooks/chat/usePushChatStream.ts @@ -0,0 +1,126 @@ +/* eslint-disable react-hooks/rules-of-hooks */ +import { useEffect, useState } from 'react'; +import { CONSTANTS, PushAPI, SignerType } from '@pushprotocol/restapi'; +import { ENV } from '../../config'; +import { useChatData } from './useChatData'; + + +export const usePushChatStream = () => { + + const { + account, + pushChatStream, + setPushChatStream, + setIsPushChatStreamConnected, + env, + pushUser + + } = useChatData(); + + + const [chatStream, setChatStream] = useState({}) // to track any new messages + const [chatRequestStream, setChatRequestStream] = useState({}); // any message in request + const [groupMetaStream, setGroupMetaStream] = useState({}); //group info + + const addSocketEvents = async () => { + console.warn('\n--> addChatSocketEvents - stream'); + pushChatStream?.on(CONSTANTS.STREAM.CONNECT, (err: Error) => { + console.log('CONNECTED - stream: ', err); + setIsPushChatStreamConnected(true); + }); + + pushChatStream?.on(CONSTANTS.STREAM.DISCONNECT, (err: Error) => { + console.log('DIS-CONNECTED: - stream ', err); + setIsPushChatStreamConnected(false); + }); + + + //Listen for chat messages, your message, request, accept, rejected, + pushChatStream?.on(CONSTANTS.STREAM.CHAT, (message: any) => { + if ((message.event === "chat.request")) { + setChatRequestStream(message); + } else { + setChatStream(message); + } + + }); + pushChatStream?.on(CONSTANTS.STREAM.CHAT_OPS, (chatops: any) => { + setGroupMetaStream(chatops) + }); + + }; + + + + const removeSocketEvents = () => { + pushChatStream?.disconnect(); + }; + + // eslint-disable-next-line react-hooks/rules-of-hooks + useEffect(() => { + if (pushChatStream) { + addSocketEvents(); + } + + return () => { + if (pushChatStream) { + removeSocketEvents(); + } + } + }, [pushChatStream]); + + + /** + * Whenever the requisite params to create a connection object change + * - disconnect the old connection + * - create a new connection object + */ + + useEffect(() => { + if (pushUser) { + if(pushChatStream){ + pushChatStream?.disconnect(); + } + else { + console.log(pushChatStream) + const main = async () => { + const newstream = await pushUser?.initStream( + [ + CONSTANTS.STREAM.CHAT, + CONSTANTS.STREAM.CHAT_OPS, + CONSTANTS.STREAM.CONNECT, + CONSTANTS.STREAM.DISCONNECT, + + ], + { + + connection: { + retries: 3, // number of retries in case of error + }, + raw: true + } + ); + console.log('new connection object: ---- ', newstream); + await newstream?.connect(); + setPushChatStream(newstream); + + }; + main().catch((err) => + console.log("error initializing the stream", err) + ); + } + + + } + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [account, env,pushUser]); + + + + return { + chatStream, + groupMetaStream, + chatRequestStream, + } +}; \ No newline at end of file diff --git a/packages/uiweb/src/lib/hooks/chat/usePushUserInfoUtilities.ts b/packages/uiweb/src/lib/hooks/chat/usePushUserInfoUtilities.ts new file mode 100644 index 000000000..f3239d83c --- /dev/null +++ b/packages/uiweb/src/lib/hooks/chat/usePushUserInfoUtilities.ts @@ -0,0 +1,25 @@ +import { useCallback } from "react"; +import { useChatData } from "./index"; +import { PushAPI } from "@pushprotocol/restapi"; + +export interface FetchEncryptionInfoParams { + pushUser:PushAPI +} + +const usePushUserInfoUtilities = () => { + const fetchEncryptionInfo = useCallback(async({pushUser}:FetchEncryptionInfoParams): Promise => { + try { + + const encryptionResponse = await pushUser?.encryption.info(); + + return encryptionResponse; + } catch (error) { + console.log(error); + return; + } + }, + []) + return {fetchEncryptionInfo}; +}; + +export default usePushUserInfoUtilities; \ No newline at end of file diff --git a/packages/uiweb/src/lib/icons/Fuse.tsx b/packages/uiweb/src/lib/icons/Fuse.tsx new file mode 100644 index 000000000..13a5a301c --- /dev/null +++ b/packages/uiweb/src/lib/icons/Fuse.tsx @@ -0,0 +1,21 @@ +import React from "react"; + +function FuseIcon() { + return ( + + + + + ); +} + +export default FuseIcon; \ No newline at end of file diff --git a/packages/uiweb/src/lib/icons/FuseSVG.tsx b/packages/uiweb/src/lib/icons/FuseSVG.tsx new file mode 100644 index 000000000..ad808aa07 --- /dev/null +++ b/packages/uiweb/src/lib/icons/FuseSVG.tsx @@ -0,0 +1,19 @@ +import * as React from "react" +import { SVGProps } from "react" + +export const FuseSvg = (props: SVGProps) => ( + + + + +) + diff --git a/packages/uiweb/src/lib/icons/FuseSvg.tsx b/packages/uiweb/src/lib/icons/FuseSvg.tsx new file mode 100644 index 000000000..ad808aa07 --- /dev/null +++ b/packages/uiweb/src/lib/icons/FuseSvg.tsx @@ -0,0 +1,19 @@ +import * as React from "react" +import { SVGProps } from "react" + +export const FuseSvg = (props: SVGProps) => ( + + + + +) + diff --git a/packages/uiweb/src/lib/icons/fuse.svg b/packages/uiweb/src/lib/icons/fuse.svg new file mode 100644 index 000000000..2b61baee4 --- /dev/null +++ b/packages/uiweb/src/lib/icons/fuse.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/uiweb/src/lib/types/index.ts b/packages/uiweb/src/lib/types/index.ts index 9375255bf..d7654613e 100644 --- a/packages/uiweb/src/lib/types/index.ts +++ b/packages/uiweb/src/lib/types/index.ts @@ -52,19 +52,30 @@ export interface ITheme { moduleColor?: string; } -type ethersV5SignerType = { +export type ethersV5SignerType = { _signTypedData: ( domain: TypedDataDomain, types: Record>, value: Record ) => Promise; - getChainId: () => Promise; getAddress: () => Promise; - signMessage: (message: Bytes | string) => Promise; + signMessage: (message: Uint8Array | string) => Promise; + privateKey?: string; + provider?: any; +}; + +export type ethersV6SignerType = { + signTypedData: ( + domain: TypedDataDomain, + types: Record>, + value: Record + ) => Promise; + getAddress: () => Promise; + signMessage: (message: Uint8Array | string) => Promise; privateKey?: string; - provider?: providers.Provider; + provider?: any; }; -type viemSignerType = { +export type viemSignerType = { signTypedData: (args: { account: any; domain: any; @@ -80,10 +91,10 @@ type viemSignerType = { }) => Promise<`0x${string}`>; account: { [key: string]: any }; privateKey?: string; - provider?: providers.Provider; + provider?: any; }; -export type SignerType = ethersV5SignerType | viemSignerType; +export type SignerType = ethersV5SignerType| ethersV6SignerType| viemSignerType;; export type ParsedNotificationType = ParsedResponseType & { channel:string;