diff --git a/package.json b/package.json index 1601d4a943..092eb4f937 100644 --- a/package.json +++ b/package.json @@ -33,9 +33,9 @@ "@mui/icons-material": "^5.8.4", "@mui/lab": "^5.0.0-alpha.72", "@mui/material": "^5.5.0", - "@pushprotocol/restapi": "1.6.10", + "@pushprotocol/restapi": "0.0.1-alpha.73", "@pushprotocol/socket": "0.5.3", - "@pushprotocol/uiweb": "0.0.1-alpha.41", + "@pushprotocol/uiweb": "1.3.1-alpha.6", "@reduxjs/toolkit": "^1.7.1", "@testing-library/dom": "^9.0.1", "@testing-library/jest-dom": "^4.2.4", diff --git a/src/components/MetaInfoDisplayer.js b/src/components/MetaInfoDisplayer.js index fc2dac8a8f..62d536c141 100644 --- a/src/components/MetaInfoDisplayer.js +++ b/src/components/MetaInfoDisplayer.js @@ -8,8 +8,30 @@ import styled from 'styled-components'; import { A, Item, ItemH, Span } from '../primaries/SharedStyling'; import { Device } from "assets/Device"; +interface MetaInfoDisplayProps { + externalIcon?: React.ReactNode; // Optional because it's checked before usage + internalIcon?: React.ReactNode; // Optional for the same reason + text: string; + bgColor: string; + color: string; + onClick?: React.MouseEventHandler<HTMLDivElement>; // Optional based on conditional use + onMouseEnter?: React.MouseEventHandler<HTMLDivElement>; // Optional based on conditional use + onMouseLeave?: React.MouseEventHandler<HTMLDivElement>; // Optional based on conditional use + padding: string; +} + // Faucet URLs -function MetaInfoDisplay({ externalIcon, internalIcon, text, bgColor, onClick, onMouseEnter, onMouseLeave, padding, color }) { +function MetaInfoDisplay({ + externalIcon = null, + internalIcon = null, + text, + bgColor, + onClick = () => {}, + onMouseEnter = () => {}, + onMouseLeave = () => {}, + padding, + color +}) { // render return ( diff --git a/src/components/ViewChannelItem.js b/src/components/ViewChannelItem.js index 436ae13b4b..98fc951af4 100644 --- a/src/components/ViewChannelItem.js +++ b/src/components/ViewChannelItem.js @@ -48,7 +48,7 @@ import UpdateChannelTooltipContent from './UpdateChannelTooltipContent'; import VerifiedTooltipContent from "./VerifiedTooltipContent"; // Create Header -function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) { +function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal, profileType }) { const dispatch = useDispatch(); const themes = useTheme(); @@ -56,7 +56,7 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) const { run, stepIndex } = useSelector((state) => state.userJourney); const { userPushSDKInstance } = useSelector((state) => { return state.user; - }); + }); const { epnsReadProvider, epnsWriteProvider, epnsCommReadProvider, pushAdminAddress, ZERO_ADDRESS } = useSelector( (state) => state.contracts ); @@ -99,6 +99,8 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) setSubscribed(subscriptionStatus[channelObject.channel]); }, [subscriptionStatus]); + // console.log("Channel Object >>>>>",channelObject); + useEffect(() => { setIsPushAdmin(pushAdminAddress == account); }, [pushAdminAddress, account]); @@ -106,6 +108,7 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) const fetchChannelJsonWithBlock = async () => { try { const channelJson = await ChannelsDataStore.instance.getChannelJsonStartBlockAsync(channelObject.channel); + // console.log("Channel JSON !!!!!",channelJson); return channelJson; } catch (err) { console.error(err); @@ -119,6 +122,8 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) const url = IPFS_GATEWAY + channelObject.ipfshash; const response = await axios.get(url); + // console.log("Response >>>>>",response); + if (response.data) setChannelObjectFromHash(response.data); if (response.data.icon) setChannelIcon(response.data.icon); }, [channelObject]); @@ -156,7 +161,7 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) let verifierAddress = null; const response = await epnsReadProvider.channels(channelObject.channel); verifierAddress = response.verifiedBy; - + if (channelsCache[verifierAddress]) { setVerifierDetails(channelsCache[verifierAddress]); } else { @@ -169,7 +174,7 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) } else { verifierAddrDetails = await userPushSDKInstance.channel.info(convertAddressToAddrCaip(verifierAddress, appConfig.coreContractChain)); } - + dispatch( cacheChannelInfo({ address: verifierAddress, @@ -184,7 +189,7 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) setLoading(false); } } - + }, [account, channelObject, userPushSDKInstance]); let isOwner; @@ -406,6 +411,7 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) key={channelObject.channel} id={channelObject.channel} minimal={minimal} + profileType={profileType} > {isMobile && ( <ChannelLogoContainer> @@ -423,8 +429,8 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) </ChannelLogoInner> </ChannelLogoOuter> </ChannelLogo> - - {!minimal && + + {!minimal && <ChannelTitle> {loading ? ( <Skeleton @@ -447,19 +453,19 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) placementProps={ tooltTipHeight < 250 ? { - background: 'none', - // bottom: "25px", - top: '20px', - // right: "-175px", - left: mobileToolTip ? '-100px' : '5px', - } + background: 'none', + // bottom: "25px", + top: '20px', + // right: "-175px", + left: mobileToolTip ? '-100px' : '5px', + } : { - background: 'none', - bottom: '25px', - // top: "20px", - // right: "-175px", - left: mobileToolTip ? '-100px' : '5px', - } + background: 'none', + bottom: '25px', + // top: "20px", + // right: "-175px", + left: mobileToolTip ? '-100px' : '5px', + } } tooltipContent={ <UpdateChannelTooltipContent @@ -499,15 +505,15 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) placementProps={ tooltTipHeight < 160 ? { - background: 'none', - top: '20px', // for lower displaying - left: '7px', - } + background: 'none', + top: '20px', // for lower displaying + left: '7px', + } : { - background: 'none', - bottom: '28px', // above display - left: '7px', - } + background: 'none', + bottom: '28px', // above display + left: '7px', + } } tooltipContent={ <VerifiedTooltipContent @@ -548,9 +554,8 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) !MaskedAliasChannels[+channelObject?.alias_blockchain_id][channelObject?.channel] && ( <Span padding="0 0 0 5px"> <Image - src={`./svg/${ - CHAIN_DETAILS[+channelObject.alias_blockchain_id]?.label?.split(' ')[0] - }.svg`} + src={`./svg/${CHAIN_DETAILS[+channelObject.alias_blockchain_id]?.label?.split(' ')[0] + }.svg`} alt="Polygon" width="20px" height="20px" @@ -569,7 +574,7 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) {!isMobile && ( <> - <ChannelLogo + <ChannelLogo minimal={minimal} onClick={() => { navigate(generateChannelProfileLink(channelObject.channel, false)) @@ -591,7 +596,7 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) </> )} - {!minimal && + {!minimal && <ChannelInfo> {!isMobile && ( <ChannelTitle> @@ -615,19 +620,19 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) placementProps={ tooltTipHeight < 250 ? { - background: 'none', - // bottom: "25px", - top: '20px', - // right: "-175px", - left: '5px', - } + background: 'none', + // bottom: "25px", + top: '20px', + // right: "-175px", + left: '5px', + } : { - background: 'none', - bottom: '25px', - // top: "20px", - // right: "-175px", - left: '5px', - } + background: 'none', + bottom: '25px', + // top: "20px", + // right: "-175px", + left: '5px', + } } tooltipContent={ <UpdateChannelTooltipContent @@ -651,12 +656,12 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) <Span onClick={() => navigate(generateChannelProfileLink(channelObject.channel, false))}>{channelObject.name}</Span> - + {isVerified == 1 && ( <Span - margin="3px 5px 0px" - style={{ display: 'flex' }} - > + margin="3px 5px 0px" + style={{ display: 'flex' }} + > <Tooltip wrapperProps={{ width: 'fit-content', @@ -666,15 +671,15 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) placementProps={ tooltTipHeight < 160 ? { - background: 'none', - top: '20px', // for lower displaying - left: '7px', - } + background: 'none', + top: '20px', // for lower displaying + left: '7px', + } : { - background: 'none', - bottom: '28px', // above display - left: '7px', - } + background: 'none', + bottom: '28px', // above display + left: '7px', + } } tooltipContent={ <VerifiedTooltipContent @@ -769,7 +774,7 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) <ChannelDescLabel>{channelObject.info}</ChannelDescLabel> )} </ChannelDesc> - + <ChannelMeta> {loading ? ( <> @@ -845,7 +850,7 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) <Skeleton color={themes.interfaceSkeleton} /> </SkeletonButton> )} - {!loading && isPushAdmin && ( + {!loading && isPushAdmin && (profileType == "Channel") && ( <SubscribeButton onClick={blockChannel} disabled={bLoading} @@ -862,7 +867,7 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) <ActionTitle hideit={bLoading}>Block channel</ActionTitle> </SubscribeButton> )} - {!loading && (isPushAdmin || canVerify) && !isVerified && ( + {!loading && (isPushAdmin || canVerify) && !isVerified && (profileType == "Channel") && ( <SubscribeButton onClick={verifyChannel} disabled={vLoading} @@ -879,7 +884,7 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) <ActionTitle hideit={vLoading}>Verify Channel</ActionTitle> </SubscribeButton> )} - {!loading && (isPushAdmin || canUnverify) && isVerified && ( + {!loading && (isPushAdmin || canUnverify) && isVerified && (profileType == "Channel") && ( <UnsubscribeButton onClick={unverifyChannel} disabled={vLoading} @@ -898,10 +903,21 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) )} {!loading && !subscribed && ( <> - {isOwner && <OwnerButton disabled>Owner</OwnerButton>} + {/* {isOwner && <OwnerButton disabled>Owner</OwnerButton>} */} + {isOwner && ( + <> + {(profileType == "Profile") ? ( + <DashboardButton onClick={()=>navigate("/dashboard")}> + Go To Dashboard + </DashboardButton> + ) : ( + <OwnerButton disabled>Owner</OwnerButton> + )} + </> + )} {!isOwner && ( - <OptinNotifSettingDropdown - channelDetail={channelObject} + <OptinNotifSettingDropdown + channelDetail={channelObject} setLoading={setTxInProgress} onSuccessOptin={() => { setSubscribed(true); @@ -909,7 +925,7 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) }} > <SubscribeButton - onClick={() => {}} + onClick={() => { }} disabled={txInProgress} className="optin" > @@ -930,9 +946,21 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) )} {!loading && subscribed && ( <> - {isOwner && <OwnerButton disabled>Owner</OwnerButton>} + {/* {isOwner && <OwnerButton disabled>Owner</OwnerButton>} */} + {isOwner && ( + <> + {(profileType == "Profile") ? ( + <DashboardButton onClick={()=>navigate("/dashboard")}> + Go To Dashboard + </DashboardButton> + ) : ( + <OwnerButton disabled>Owner</OwnerButton> + )} + </> + )} + {!isOwner && ( - <ManageNotifSettingDropdown + <ManageNotifSettingDropdown centerOnMobile={true} channelDetail={channelObject} setSubscribed={setSubscribed} @@ -944,7 +972,7 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser, minimal }) }} > <UnsubscribeButton - onClick={() => {}} + onClick={() => { }} disabled={txInProgress} > {txInProgress && ( @@ -990,17 +1018,16 @@ const ColumnFlex = styled(FlexBox)` `; // css styles const Container = styled.div` - flex: 1; + // flex: 1; display: flex; - flex-wrap: wrap; - border: 1px solid ${(props) => props.minimal ? 'transparent' : props.theme.default.border}; + flex-wrap: nowrap; + border: ${(props) => props.profileType == 'Profile' ? 'none' : `1px solid ${(props) => props.minimal ? 'transparent' : props.theme.default.border}`}; border-bottom: none; border-left: none; border-right: none; margin: 0px 5px; justify-content: center; padding: ${(props) => props.minimal ? '5px 0px' : '25px 10px'}; - align-self: stretch; @media (max-width: 768px) { display: flex; @@ -1412,6 +1439,14 @@ const OwnerButton = styled(ChannelActionButton)` min-width: 108px; `; +const DashboardButton = styled(ChannelActionButton)` + background: #e20880; + border-radius: 8px; + padding: 7px 14px; + min-height: 36px; + min-width: max-content; +` + const Toaster = styled.div` display: flex; flex-direction: row; diff --git a/src/components/channel/ChannelProfileComponent.tsx b/src/components/channel/ChannelProfileComponent.tsx new file mode 100644 index 0000000000..1bd75aa51a --- /dev/null +++ b/src/components/channel/ChannelProfileComponent.tsx @@ -0,0 +1,605 @@ +import { ItemH } from 'components/SharedStyling'; +import React, { useEffect, useState } from 'react'; +import { useSelector } from 'react-redux'; +import ChannelsDataStore from 'singletons/ChannelsDataStore'; +import styled, { useTheme } from 'styled-components'; +import Skeleton from '@yisheng90/react-loading'; +import { Image, Span } from 'primaries/SharedStyling'; +import { appConfig } from 'config'; +import { LOGO_FROM_CHAIN_ID, MaskedAliasChannels, shortenText } from 'helpers/UtilityHelper'; +import { ButtonV2 } from 'components/reusables/SharedStylingV2'; +import { IPFSGateway } from 'helpers/IpfsHelper'; +import axios from 'axios'; +import MetaInfoDisplayer from 'components/MetaInfoDisplayer'; +import OptinNotifSettingDropdown from 'components/dropdowns/OptinNotifSettingDropdown'; + + +const ChannelProfileComponent = ({ + channelID, + channelDetails +}) => { + + const themes = useTheme(); + const { channels, page, ZERO_ADDRESS } = useSelector((state: any) => state.channels); + + const [isLoading, setIsLoading] = useState(false); + const [channelIcon, setChannelIcon] = React.useState(''); + const [channelObjectFromHash, setChannelObjectFromHash] = React.useState({}); + const [copyText, setCopyText] = React.useState(channelDetails?.channel); + const [txInProgress, setTxInProgress] = React.useState(false); + + useEffect(() => { + + // Getting Channel Icon from Channel IPFS Hash + if (channelDetails) { + (async () => { + if (!channelDetails.ipfshash) return; + + const IPFS_GATEWAY = IPFSGateway; + const url = IPFS_GATEWAY + channelDetails.ipfshash; + const response = await axios.get(url); + if (response.data) setChannelObjectFromHash(response.data); + if (response.data.icon) setChannelIcon(response.data.icon); + })() + } + + }, [channelDetails]); + + const formatAddress = (addressText) => { + return addressText.length > 40 ? `${shortenText(addressText, 4, 6)}` : addressText; + }; + + const copyToClipboard = (address) => { + // const url = generateChannelProfileLink(address, true); + + // fallback for non navigator browser support + 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 ( + <Container> + + <ChannelLogo> + <ChannelLogoOuter> + <ChannelLogoInner> + {isLoading ? ( + <Skeleton + color={themes.interfaceSkeleton} + height="100%" + /> + ) : ( + <ChannelLogoImg src={`${channelIcon}`} /> + )} + </ChannelLogoInner> + </ChannelLogoOuter> + </ChannelLogo> + + <ChannelInfo> + + <ChannelTitle> + {isLoading || !channelDetails ? ( + <Skeleton + color={themes.interfaceSkeleton} + width="50%" + height={24} + /> + ) : ( + <ChannelTitleLink> + <Span style={{ display: 'flex', alignItems: 'center' }}> + {/* ToolTip for Channel Changed warning */} + {/* {showChannelChangedWarning && ( + <Tooltip + wrapperProps={{ + width: 'fit-content', + maxWidth: 'fit-content', + minWidth: 'fit-content', + // zIndex: "10", + }} + placementProps={ + tooltTipHeight < 250 + ? { + background: 'none', + // bottom: "25px", + top: '20px', + // right: "-175px", + left: '5px', + } + : { + background: 'none', + bottom: '25px', + // top: "20px", + // right: "-175px", + left: '5px', + } + } + tooltipContent={ + <UpdateChannelTooltipContent + height={tooltTipHeight} + channelName={channelObjectStartBlock.name} + channelDescription={channelObjectStartBlock.info} + channelLogoSrc={channelObjectStartBlock.icon} + /> + } + > + <div + onMouseEnter={() => { + handleHeight(channelObject.channel); + }} + style={{ cursor: 'pointer' }} + > + <ImageInfo src={InfoImage} /> + </div> + </Tooltip> + )} */} + + <Span>{channelDetails.name}</Span> + + + {/* {isVerified == 1 && ( + <Span + margin="3px 5px 0px" + style={{ display: 'flex' }} + > + <Tooltip + wrapperProps={{ + width: 'fit-content', + maxWidth: 'fit-content', + minWidth: 'fit-content', + }} + placementProps={ + tooltTipHeight < 160 + ? { + background: 'none', + top: '20px', // for lower displaying + left: '7px', + } + : { + background: 'none', + bottom: '28px', // above display + left: '7px', + } + } + tooltipContent={ + <VerifiedTooltipContent + height={tooltTipHeight} + verifierIcon={verifierDetails?.icon} + verifierName={verifierDetails?.name} + /> + } + > + <div + style={{ cursor: 'pointer' }} + onMouseEnter={() => { + handleHeight(channelObject.channel); + }} + > + <GoVerified + size={18} + color={themes.viewChannelVerifiedBadge} + /> + </div> + </Tooltip> + </Span> + )} */} + {channelDetails && channelDetails?.channel && ( + <Span padding="0 0 0 5px"> + <Image + src={`./svg/Ethereum.svg`} + alt="Ethereum" + width="20px" + height="20px" + /> + </Span> + )} + {channelDetails && + channelDetails?.alias_address != null && + channelDetails?.alias_address != 'NULL' && + appConfig.allowedNetworks.includes(+channelDetails?.alias_blockchain_id) && + !MaskedAliasChannels[+channelDetails?.alias_blockchain_id][channelDetails?.channel] && ( + <Span padding="0 0 0 5px"> + <Image + src={`./svg/${LOGO_FROM_CHAIN_ID[+channelDetails.alias_blockchain_id]}`} + alt="Alias Chain Logo" + width="20px" + height="20px" + /> + </Span> + )} + </Span> + </ChannelTitleLink> + )} + </ChannelTitle> + + <ChannelDesc> + {isLoading ? ( + <> + <SkeletonWrapper + atH={5} + atW={100} + > + <Skeleton + color={themes.interfaceSkeleton} + width="100%" + height={5} + /> + </SkeletonWrapper> + + <SkeletonWrapper + atH={5} + atW={100} + > + <Skeleton + color={themes.interfaceSkeleton} + width="100%" + height={5} + /> + </SkeletonWrapper> + + <SkeletonWrapper + atH={5} + atW={100} + > + <Skeleton + color={themes.interfaceSkeleton} + width="40%" + height={5} + /> + </SkeletonWrapper> + </> + ) : ( + <ChannelDescLabel>{channelDetails.info}</ChannelDescLabel> + )} + </ChannelDesc> + + <ChannelMeta> + {isLoading ? ( + <> + <SkeletonWrapper + atH={10} + atW={30} + marginBottom="0" + > + <Skeleton color={themes.interfaceSkeleton} /> + </SkeletonWrapper> + </> + ) : ( + <ItemH + align="center" + justify="flex-start" + margin="0px -5px" + > + <ItemBody> + <MetaInfoDisplayer + externalIcon={ + <Image + src="./svg/users.svg" + alt="users" + width="14px" + height="14px" + /> + } + internalIcon={null} + text={channelDetails.subscriber_count} + padding="5.3px 10px" + bgColor={themes.viewChannelSecondaryBG} + color={themes.viewChannelSecondaryText} + /> + + <MetaInfoDisplayer + text={formatAddress(copyText)} + bgColor={themes.viewChannelSearchBg} + padding="6px 16px" + color={themes.viewChannelPrimaryText} + onClick={() => { + copyToClipboard(channelDetails.channel); + setCopyText('copied'); + }} + onMouseEnter={() => { + setCopyText('click to copy'); + }} + onMouseLeave={() => { + setCopyText(channelDetails.channel); + }} + /> + + {/* {isChannelTutorialized(channelObject.channel) && ( + <ChannelTutorial + addr={channelObject.channel} + bgColor={themes.viewChannelSearchBg} + loadTeaser={loadTeaser} + playTeaser={playTeaser} + /> + )} */} + </ItemBody> + </ItemH> + )} + </ChannelMeta> + + </ChannelInfo> + + {/* {!loading && !subscribed && ( + <> + {isOwner && <OwnerButton disabled>Owner</OwnerButton>} + {!isOwner && ( + <OptinNotifSettingDropdown + channelDetail={channelDetails} + setLoading={setTxInProgress} + onSuccessOptin={() => { + setSubscribed(true); + setSubscriberCount((prevSubscriberCount) => prevSubscriberCount + 1) + }} + > + <SubscribeButton + onClick={() => {}} + disabled={txInProgress} + className="optin" + > + {txInProgress && ( + <ActionLoader> + <LoaderSpinner + type={LOADER_TYPE.SEAMLESS} + spinnerSize={16} + spinnerColor="#FFF" + /> + </ActionLoader> + )} + <ActionTitle hideit={txInProgress}>Opt-In</ActionTitle> + </SubscribeButton> + </OptinNotifSettingDropdown> + )} + </> + )} */} + + + + + </Container> + ); +}; + +export default ChannelProfileComponent; + +const Container = styled.div` +// flex: 1; + display: flex; + flex-wrap: wrap; + border: 1px solid ${(props) => props.minimal ? 'transparent' : props.theme.default.border}; + border-bottom: none; + border-left: none; + border-right: none; + margin: 0px 5px; + justify-content: center; + padding: ${(props) => props.minimal ? '5px 0px' : '25px 10px'}; + + align-self: stretch; + @media (max-width: 768px) { + display: flex; + flex-direction: column; + border-bottom: 1px solid ${(props) => props.minimal ? 'transparent' : props.theme.default.border}; + border-top: none; + border-left: none; + border-right: none; + } +`; + +const ChannelLogoInner = styled.div` + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + overflow: hidden; + border-radius:20px; + display: flex; + justify-content: center; + align-items: center; + +`; + +const ChannelLogoOuter = styled.div` + padding-top: 100%; + width: 100%; + position: relative; +`; + +const ChannelLogo = styled(ButtonV2)` + max-width: 100px; + min-width: 48px; + flex: 1; + margin: 5px; + padding: 10px; + border: 1px solid ${(props) => props.theme.viewChannelIconBorder}; + background: transparent; + overflow: hidden; + border-radius: 20px; + display: flex; + flex-direction: column; + justify-content: center; + align-self: flex-start; + + @media (max-width: 768px) { + align-self: center; + min-width: 100px; + max-width: 100px; + } + + @media (max-width: 600px) { + align-self: center; + } +`; + +const ChannelLogoImg = styled.img` + object-fit: contain; + width: 100%; + border-radius: 20px; + overflow: hidden; +`; + + +const ChannelTitle = styled(ItemH)` + padding: 5px 10px 5px 0px; + position: relative; + justify-content: flex-start; + margin: 0; + flex: initial; + align-items: center; + @media (max-width: 768px) { + align-self: center; + margin-top: 10px; + } + @media (max-width: 600px) { + flex: 5; + padding-left: 5px; + } +`; + +const ChannelTitleLink = styled.a` + text-decoration: none; + display: flex; + flex: inherit; + align-item: center; + &:hover { + text-decoration: underline; + cursor: pointer; + pointer: hand; + } + + & > span > span { + font-weight: 500; + color: ${(props) => props.theme.viewChannelLink}; + font-size: 18px; + cursor: pointer; + } + + & > span > span { + vertical-align: middle; + } +`; + +const ChannelInfo = styled.div` + flex: 1; + margin: 5px 10px; + min-width: 240px; + flex-grow: 4; + flex-direction: column; + display: flex; + + @media (max-width: 480px) { + min-width: 210px; + } +`; + +const ChannelDesc = styled.div` + flex: 1; + display: flex; + font-size: 15px; + color: rgba(0, 0, 0, 0.75); + padding: 5px 0px 10px 0px; + font-weight: 400; + flex-direction: column; + color: ${(props) => props.theme.color}; + @media (max-width: 768px) { + align-self: center; + text-align: center; + } + + @media (max-width: 600px) { + align-self: flex-start; + text-align: left; + } +`; + +const ChannelDescLabel = styled.label` + flex: 1; + line-height: 165%; + color: ${(props) => props.theme.viewChannelPrimaryTextColor}; +`; + +const SkeletonWrapper = styled.div` + overflow: hidden; + width: ${(props) => props.atW + '%' || '100%'}; + height: ${(props) => props.atH}px; + border-radius: ${(props) => props.borderRadius || 10}px; + margin-bottom: ${(props) => props.marginBottom || 5}px; + margin-right: ${(props) => props.marginRight || 0}px; +`; + +const ChannelMeta = styled.div` + display: flex; + flex-wrap: wrap; + flex-direction: row; + padding: 5px 0px; + font-size: 13px; + @media (max-width: 768px) { + align-self: center; + } +`; + +const ItemBody = styled.div` + width: 100%; + display: flex; + flex-wrap: wrap; + flex-direction: row; + align-items: center; + @media (max-width: 768px) { + width: 100%; + align-self: center; + justify-content: center; + } +`; + + +const ChannelActionButton = styled.button` + border: 0; + outline: 0; + display: flex; + align-items: center; + justify-content: center; + color: #fff; + border-radius: 5px; + font-size: 14px; + font-weight: 400; + position: relative; + &:hover { + opacity: 0.9; + cursor: pointer; + pointer: hand; + } + &:active { + opacity: 0.75; + cursor: pointer; + pointer: hand; + } + ${(props) => + props.disabled && + css` + &:hover { + opacity: 1; + cursor: default; + pointer: default; + } + &:active { + opacity: 1; + cursor: default; + pointer: default; + } + `} +`; + +const OwnerButton = styled(ChannelActionButton)` + background: #35c5f3; + border-radius: 8px; + min-height: 36px; + min-width: 108px; +`; diff --git a/src/config/config-alpha.js b/src/config/config-alpha.js new file mode 100644 index 0000000000..c278d73804 --- /dev/null +++ b/src/config/config-alpha.js @@ -0,0 +1,170 @@ +// environmental configurations for the dapp for different environments +export const config = { + /** + * Push Nodes Environment - can be dev, staging or prod - important to keep one on one connection + */ + pushNodesEnv: 'prod', + + /** + * API Calls Related + */ + appEnv: 'prod', // helps in deciding some text, links, etc, DO NOT CHANGE + + pushNodeApiVersion: 1, + apiUrl: 'https://backend.epns.io/apis', + w2wApiUrl: 'https://backend.epns.io/apis', + toolingApiUrl: 'https://tooling.epns.io/apis', + + ipfsInfuraAPIKey: process.env.REACT_APP_IPFS_INFURA_API_KEY || '22rfiNb1J645FdehoqbKMpLbF6V', + ipfsInfuraAPISecret: process.env.REACT_APP_IPFS_INFURA_API_SECRET || 'a757597f020425c3ae532e6be84de552', + + /** + * Allowed Networks + */ + allowedNetworks: [ + 1, //for ethereum mainnet + 137, //for polygon mainnet + 56, // for bnb mainnet + // 10, // for optimism mainnet + 42161, // arbitrum mainnet + 1101, // polygon zkevm mainnet + // 122 // fuse mainnet + ], + + /** + * Core Network Related Data + */ + coreContractChain: 1, //the chain id of the network which the core contract relies on + coreRPC: 'https://mainnet.infura.io/v3/4ff53a5254144d988a8318210b56f47a', + mainnetCoreRPC: 'https://mainnet.infura.io/v3/4ff53a5254144d988a8318210b56f47a', + mainnetCoreContractChain: 1, + aliasRPC: { + 137: "https://polygon-mainnet.infura.io/v3/150f25623ae64d08ab7ec7dd0c6b6ee9", + 56: "https://bsc-dataseed.binance.org/", + 10: "https://opt-mainnet.g.alchemy.com/v2/JYW0UaSC5Zd0hrI6vE2K9VN1wJupoY5B", + 42161: "https://arb1.arbitrum.io/rpc", + 1101: 'https://rpc.polygon-zkevm.gateway.fm', + }, + infuraApiUrl: 'https:/infura-ipfs.io/ipfs/', + + /** + * Analaytics + Firebase + */ + googleAnalyticsId: 'UA-165415629-1', + vapidKey: 'BFRmmAEEXOhk31FIsooph5CxlXKh6N0_NocUWHzvtpoUEvqQTwLXu6XtwkrH7ckyr2CvVz1ll-8q4oo6-ZqFJPY', + firebaseConfig: { + apiKey: 'AIzaSyClOk4qP0ttFW-BPnXy7WT920xfdXSbFu8', + authDomain: 'epns-internal.firebaseapp.com', + databaseURL: 'https://epns-internal.firebaseio.com', + projectId: 'epns-internal', + storageBucket: 'epns-internal.appspot.com', + messagingSenderId: '755180533582', + appId: '1:755180533582:web:752ff8db31905506b7d01f', + measurementId: 'G-ZJH2T7R9S1', + }, + + /** + * External links + */ + links: { + ios: 'https://apps.apple.com/app/ethereum-push-service-epns/id1528614910', + android: 'https://play.google.com/store/apps/details?id=io.epns.epns', + extension: 'https://chrome.google.com/webstore/detail/epns-protocol-beta/lbdcbpaldalgiieffakjhiccoeebchmg', + howto: 'https://push.org/docs', + }, +}; + +/** + * Smart Contract Related + */ +export const addresses = { + ceaErc20: '0xc1C0472c0C80bCcDC7F5D01A376Bd97a734B8815', // mainnet address + epnscore: '0x66329Fdd4042928BfCAB60b179e1538D56eeeeeE', // mainnet address core + epnsEthComm: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa', // mainnet address eth comm + epnsPolyComm: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa', // mainnet address polygon comm + pushToken: '0xf418588522d5dd018b425E472991E52EBBeEEEEE', // mainnet address + pushChannelAdmin: "0x65193c896eC400f731712D4f71046CeDd11Bff27", // mainnet push core admin + aDai: '0xcB1Fe6F440c49E9290c3eb7f158534c2dC374201', // mainnet address + staking: '0xB72ff1e675117beDefF05a7D0a472c3844cfec85', // mainnet address + yieldFarmPUSH: '0x6019B84E2eE9EB62BC42E32AB6375A7095886366', // mainnet address + yieldFarmLP: '0xbB2A70e67770D0A7F5f42d883C5BBE9b85e0DcD6', // mainnet address + epnsToken: '0xf418588522d5dd018b425E472991E52EBBeEEEEE', // mainnet address + epnsLPToken: '0xaf31fd9c3b0350424bf96e551d2d1264d8466205', // mainnet address + rockstar: '0x3f8C2152b79276b78315CAF66cCF951780580A8a', // mainnet address + batchMintNFT: '0x6BaeeD93336B277D8949Cb89161269032698f443', // mainnet address + NFTRewards: '0xc4708BB6EC3B797344f123126171302e4e3E68E2', // mainnet address + distributor: '0x64CfAb2eA55ADAe08c9040fdA247828444fB9D0D', // mainnet address + uniswapV2Router02: '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', + WETHAddress: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // mainnet address + USDTAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7', // mainnet address + rockstarV2: '0xA2b885e7065EA59a3251489715ca80DE5Ff642f8', //mainnet address + NFTRewardsV2: '0xdc66567a990B7fa10730459537620857c9e03287', //mainnet address + + //For Yield Farm V1(Deprecated) + staking: "0xB72ff1e675117beDefF05a7D0a472c3844cfec85", // deprecated staking mainnet addresses + depYieldFarmPUSH: "0x6019B84E2eE9EB62BC42E32AB6375A7095886366", // deprecated YieldFarmPUSH mainnet address + depYieldFarmLP:"0xbB2A70e67770D0A7F5f42d883C5BBE9b85e0DcD6",// deprecated YieldFarmLP mainnet addresses + + //For Yield Farm V2 + stakingV2:"0x9D2513F5b539DC774C66b28ACEc94e4bD00105C2",//mainnet address + yieldFarmLP: "0x9af118D9fA1eFEa5b5a792847554960217DEdb04",//mainnet address + uniV2LPToken: "0xaf31fd9c3b0350424bf96e551d2d1264d8466205",//same as epnsLPToken + pushCoreV2: "0x66329Fdd4042928BfCAB60b179e1538D56eeeeeE",//not upgraded + + + +}; + +export const CHAIN_DETAILS = { + 1: { + label: "Ethereum Mainnet", + name: "ETH_MAINNET", + chainId: 1, + rpcUrl: "https://mainnet.infura.io/v3/4ff53a5254144d988a8318210b56f47a", + commAddress: "0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa", + network: "mainnet", + }, + 137: { + label: "Polygon Mainnet", + name: "POLYGON_MAINNET", + chainId: 137, + rpcUrl: "https://polygon-mainnet.infura.io/v3/150f25623ae64d08ab7ec7dd0c6b6ee9", + commAddress: "0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa", + network: "polygon-mainnet", + }, + 56: { + label: "BNB Mainnet", + name: "BNB_MAINNET", + chainId: 56, + rpcUrl: "https://bsc-dataseed.binance.org/", + commAddress: "0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa" + }, + 10: { + label: "Optimism Mainnet", + name: "OPTIMISM_MAINNET", + chainId: 10, + rpcUrl: 'https://opt-mainnet.g.alchemy.com/v2/JYW0UaSC5Zd0hrI6vE2K9VN1wJupoY5B', + commAddress: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa' + }, + 1101: { + label: "Polygon zkEVM Mainnet", + name: "POLYGON_ZK_EVM_MAINNET", + chainId: 1101, + rpcUrl: 'https://rpc.polygon-zkevm.gateway.fm', + commAddress: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa' + }, + 42161: { + label: "Arbitrum Mainnet", + name: "ARBITRUMONE_MAINNET", + chainId: 42161, + rpcUrl: 'https://arb1.arbitrum.io/rpc', + commAddress: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa' + }, + 122: { + label: "Fuse Mainnet", + name: "FUSE_MAINNET", + chainId: 122, + rpcUrl: 'https://rpc.fuse.io', + commAddress: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa' + }, +} \ No newline at end of file diff --git a/src/config/index.js b/src/config/index.js index aab9cdf67e..885c4a0c45 100644 --- a/src/config/index.js +++ b/src/config/index.js @@ -12,7 +12,7 @@ if (process.env.REACT_APP_DEPLOY_ENV == 'PROD') { } else if (process.env.REACT_APP_DEPLOY_ENV == 'W2W') { appendName = 'staging'; } else if (process.env.REACT_APP_DEPLOY_ENV == 'ALPHA') { - appendName = 'staging'; + appendName = 'prod'; } else { throw new Error('⚠️ Provide proper REACT_APP_DEPLOY_ENV in .env ⚠️'); } @@ -29,4 +29,5 @@ const abis = require('./config-general').abis; const appConfig = { ...dynamicConfig, ...generalConfig }; // export it out -export { appConfig, addresses, abis, CHAIN_DETAILS }; +export { CHAIN_DETAILS, abis, addresses, appConfig }; + diff --git a/src/modules/channels/ChannelsModule.tsx b/src/modules/channels/ChannelsModule.tsx index 0d1c401086..b02bb9f6b7 100644 --- a/src/modules/channels/ChannelsModule.tsx +++ b/src/modules/channels/ChannelsModule.tsx @@ -13,6 +13,11 @@ import ViewChannels from "segments/ViewChannels"; import APP_PATHS from "config/AppPaths"; import GLOBALS, { device, globalsMargin } from "config/Globals"; +export enum ChannelTYPE { + CHANNEL = 'Channel', + CHANNEL_PROFILE = 'Profile', +} + // Create Channels Module const ChannelsModule = ({ channelID, loadTeaser, playTeaser }) => { ReactGA.pageview(APP_PATHS.Channels); @@ -23,7 +28,13 @@ const ChannelsModule = ({ channelID, loadTeaser, playTeaser }) => { <Interface> <ViewChannels loadTeaser={loadTeaser} playTeaser={playTeaser} minimal={channelID ? true : false} /> {channelID && - <ChannelProfile channelID={channelID} /> + <ChannelProfile + channelID={channelID} + loadTeaser={loadTeaser} + playTeaser={playTeaser} + minimal={false} + profileType={ChannelTYPE.CHANNEL_PROFILE} + /> } </Interface> </Container> diff --git a/src/segments/ChannelProfile.tsx b/src/segments/ChannelProfile.tsx index 03fe6ac650..e7c9101a34 100644 --- a/src/segments/ChannelProfile.tsx +++ b/src/segments/ChannelProfile.tsx @@ -15,16 +15,21 @@ import LoaderSpinner, { LOADER_TYPE } from 'components/reusables/loaders/LoaderS // Internal Configs import { latest } from "@pushprotocol/restapi/src/lib/chat"; +import ViewChannelItem from "components/ViewChannelItem"; +import ChannelProfileComponent from "components/channel/ChannelProfileComponent"; import { ItemVV2, SpanV2 } from "components/reusables/SharedStylingV2"; import { appConfig } from "config"; import APP_PATHS from "config/AppPaths"; import { device } from "config/Globals"; +import ChannelsDataStore from "singletons/ChannelsDataStore"; + + // Constants const NOTIFICATIONS_PER_PAGE = 20; // Create Header -const ChannelProfile = ({ channelID }) => { +const ChannelProfile = ({ channelID, loadTeaser, playTeaser, minimal, profileType }) => { const dispatch = useDispatch(); const { userPushSDKInstance } = useSelector((state: any) => { return state.user; @@ -36,30 +41,49 @@ const ChannelProfile = ({ channelID }) => { // loading const [loading, setLoading] = useState(true); - const [notifications, setNotifications] = useState([]); + const [loadingNotifs, setLoadingNotifs] = useState(true); + const [notifications, setNotifications] = useState([]); + const [channelDetails, setChannelDetails] = useState(null); // Setup navigation const navigate = useNavigate(); - // load notifications useEffect(() => { + setChannelDetails(null) if (userPushSDKInstance) { setLoading(true); - userPushSDKInstance.channel.notifications(channelID, { - page: 1, - limit: NOTIFICATIONS_PER_PAGE, - }).then((response) => { - setNotifications(response.feeds); + (async () => { + try { + const channelBasedOnChannelID = await userPushSDKInstance.channel.info(channelID); + setChannelDetails(channelBasedOnChannelID); setLoading(false); + } catch (error) { + console.log("Error", error); + } + })() + } + + }, [channelID, userPushSDKInstance]) - // ENABLE PAGINATION HERE - }).catch((err) => { - // ENABLE NO NOTIFICATION FOUND HERE + // load notifications + useEffect(() => { + if (userPushSDKInstance) { + setLoading(true); + userPushSDKInstance.channel.notifications(channelID, { + page: 1, + limit: NOTIFICATIONS_PER_PAGE, + }).then((response) => { + setNotifications(response.feeds); + setLoadingNotifs(false); + + // ENABLE PAGINATION HERE + }).catch((err) => { + // ENABLE NO NOTIFICATION FOUND HERE }); }; return () => { setNotifications([]); - setLoading(true); + setLoadingNotifs(true); } }, [channelID]); @@ -70,30 +94,52 @@ const ChannelProfile = ({ channelID }) => { <ItemVV2 flex="initial" alignSelf="flex-start" - padding="0px 0px 20px 0px" + padding="0px" > <SpanV2 alignSelf="flex-start" > - <Back + <Back onClick={() => { navigate(APP_PATHS.Channels); }} /> </SpanV2> - <SpanV2> - Channel Profile Here - </SpanV2> </ItemVV2> + {/* New Channel Profile Component */} + {channelDetails && !loading && + <ViewChannelItem + channelObjectProp={channelDetails} + loadTeaser={loadTeaser} + playTeaser={playTeaser} + minimal={minimal} + profileType={profileType} + /> + } + + {/* Show Latest Notifications of the Channel */} + <TextContainer> + <SpanV2 + fontSize="20px" + fontWeight="500" + > + Recent Notifications + </SpanV2> + <Notice> + Showing preview of the latest non-encrypted notifications sent by the channel. + </Notice> + </TextContainer> + + <ScrollItem> - {loading && + {loadingNotifs && <LoaderSpinner type={LOADER_TYPE.SEAMLESS} spinnerSize={40} /> } - + {notifications.map((item, index) => { const payload = item.payload; @@ -130,6 +176,10 @@ const Container = styled.div` justify-content: center; font-weight: 200; margin: 20px 0px 0px 20px; + + @media (max-width: 768px) { + margin: 20px 10px 0px 10px; + } `; const NotifsOuter = styled.div` @@ -158,7 +208,7 @@ const ScrollItem = styled(ItemVV2)` } @media (max-width: 768px) { - padding: 0px; + padding: 0px 20px; &::-webkit-scrollbar-track { background-color: none; @@ -184,5 +234,17 @@ const ScrollItem = styled(ItemVV2)` } `; +const TextContainer = styled(ItemVV2)` + flex:0; + border-bottom: 1px solid rgba(0, 0, 0, 0.10); + padding: 10px; + align-items:baseline; + margin:7px 20px 24px 5px; +` + +const Notice = styled(SpanV2)` + font-size: 0.8em; +` + // Export Default export default ChannelProfile; \ No newline at end of file diff --git a/src/segments/ViewChannels.tsx b/src/segments/ViewChannels.tsx index 9aed7fed87..f8abef5a22 100644 --- a/src/segments/ViewChannels.tsx +++ b/src/segments/ViewChannels.tsx @@ -406,6 +406,10 @@ const Container = styled.div` align-items: center; justify-content: center; max-height: 100vh; + + @media (max-width: 768px) { + display: ${props => props.minimal ? 'none' : 'flex'}; + } `; const ContainerInfo = styled.div` diff --git a/yarn.lock b/yarn.lock index 5224040235..f3d00f511d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5405,9 +5405,9 @@ __metadata: "@mui/icons-material": ^5.8.4 "@mui/lab": ^5.0.0-alpha.72 "@mui/material": ^5.5.0 - "@pushprotocol/restapi": 1.6.10 + "@pushprotocol/restapi": 0.0.1-alpha.73 "@pushprotocol/socket": 0.5.3 - "@pushprotocol/uiweb": 0.0.1-alpha.41 + "@pushprotocol/uiweb": 1.3.1-alpha.6 "@reduxjs/toolkit": ^1.7.1 "@testing-library/dom": ^6.12.2 "@testing-library/jest-dom": ^4.2.4 @@ -5594,9 +5594,9 @@ __metadata: languageName: node linkType: hard -"@pushprotocol/restapi@npm:1.6.10": - version: 1.6.10 - resolution: "@pushprotocol/restapi@npm:1.6.10" +"@pushprotocol/restapi@npm:0.0.1-alpha.73": + version: 0.0.1-alpha.73 + resolution: "@pushprotocol/restapi@npm:0.0.1-alpha.73" dependencies: "@metamask/eth-sig-util": ^5.0.2 axios: ^0.27.2 @@ -5619,7 +5619,7 @@ __metadata: peerDependenciesMeta: ethers: optional: true - checksum: 5c01d0c2fb4b4076c6e09d06dbf4a76cb6f552c0830c10bebce4bad91d96c86556fa5a034499d7b43d72541b60de805e34a9fb7578d1669fe1cfa5ab27edb9c7 + checksum: 9013dd092976461a80caeb502e6ae6fe53986b9d477b0f96a4d9f322a99acd05eb6729994740b11ad3fc1e492725536793db53da4218e2b16f0da87f316d187f languageName: node linkType: hard @@ -5647,9 +5647,9 @@ __metadata: languageName: node linkType: hard -"@pushprotocol/uiweb@npm:0.0.1-alpha.41": - version: 0.0.1-alpha.41 - resolution: "@pushprotocol/uiweb@npm:0.0.1-alpha.41" +"@pushprotocol/uiweb@npm:1.3.1-alpha.6": + version: 1.3.1-alpha.6 + resolution: "@pushprotocol/uiweb@npm:1.3.1-alpha.6" dependencies: "@livekit/components-react": ^1.2.2 "@livekit/components-styles": ^1.0.6 @@ -5681,7 +5681,8 @@ __metadata: axios: ^0.27.2 react: ">=16.8.0" styled-components: ^6.0.8 - checksum: bbd07715409e0d22b17f825124202772f65169bbeec4c4b05cc4e5b9020e129957f78b3031549ec5c638046247375fecd8b3653c1dbc039363293763b4d7ba51 + viem: ^1.3.0 + checksum: b931b0e27ef2d209d46d19c651de538a0843f914509a53f7bdbb69e71156406287d2c1561c037f4762523e48bf3d2704e0c2a273b9b0736dcf4ab203f19894d4 languageName: node linkType: hard