From 58ac27c0bde98e9c603cdde03a9c7a2c69f43814 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Tue, 26 Sep 2023 17:54:36 +0530 Subject: [PATCH 01/18] Updated send notif page for notif settings design --- src/components/SendNotifications.tsx | 569 +++++--------------- src/components/reusables/SharedStylingV2.js | 2 + src/config/Themization.js | 2 + 3 files changed, 125 insertions(+), 448 deletions(-) diff --git a/src/components/SendNotifications.tsx b/src/components/SendNotifications.tsx index e1494aaabc..509e55b0ad 100644 --- a/src/components/SendNotifications.tsx +++ b/src/components/SendNotifications.tsx @@ -1,7 +1,6 @@ // React + Web3 Essentials import { useWeb3React } from '@web3-react/core'; -import { ethers } from 'ethers'; -import React, { useEffect } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; // External Packages import Switch from '@material-ui/core/Switch'; @@ -19,7 +18,7 @@ import styled, { useTheme } from 'styled-components'; import * as PushAPI from '@pushprotocol/restapi'; import { postReq } from 'api'; import LoaderSpinner, { LOADER_TYPE } from 'components/reusables/loaders/LoaderSpinner'; -import { SectionV2 } from 'components/reusables/SharedStylingV2'; +import { SectionV2, AInlineV2 } from 'components/reusables/SharedStylingV2'; import { convertAddressToAddrCaip } from 'helpers/CaipHelper'; import CryptoHelper from 'helpers/CryptoHelper'; import { IPFSupload } from 'helpers/IpfsHelper'; @@ -129,23 +128,65 @@ function SendNotifications() { }); const onCoreNetwork = CORE_CHAIN_ID === chainId; - const [nfProcessing, setNFProcessing] = React.useState(0); - const [channelAddress, setChannelAddress] = React.useState(''); - const [nfRecipient, setNFRecipient] = React.useState(account); - const [multipleRecipients, setMultipleRecipients] = React.useState([]); - const [tempRecipeint, setTempRecipient] = React.useState(''); // to temporarily hold the address of one recipient who would be entered into the recipeints array above. - const [nfType, setNFType] = React.useState('1'); - const [nfSub, setNFSub] = React.useState(''); - const [nfSubEnabled, setNFSubEnabled] = React.useState(false); - const [nfMsg, setNFMsg] = React.useState(''); - const [nfCTA, setNFCTA] = React.useState(''); - const [nfCTAEnabled, setNFCTAEnabled] = React.useState(false); - const [nfMedia, setNFMedia] = React.useState(''); - const [nfMediaEnabled, setNFMediaEnabled] = React.useState(false); - const [nfInfo, setNFInfo] = React.useState(''); - const [delegateeOptions, setDelegateeOptions] = React.useState([]); - const [errorMessage, setErrorMessage] = React.useState(''); - + const [nfProcessing, setNFProcessing] = useState(0); + const [channelAddress, setChannelAddress] = useState(''); + const [nfRecipient, setNFRecipient] = useState(account); + const [multipleRecipients, setMultipleRecipients] = useState([]); + const [tempRecipeint, setTempRecipient] = useState(''); // to temporarily hold the address of one recipient who would be entered into the recipeints array above. + const [nfType, setNFType] = useState('1'); + const [nfSub, setNFSub] = useState(''); + const [nfSubEnabled, setNFSubEnabled] = useState(false); + const [nfMsg, setNFMsg] = useState(''); + const [nfCTA, setNFCTA] = useState(''); + const [nfCTAEnabled, setNFCTAEnabled] = useState(false); + const [nfMedia, setNFMedia] = useState(''); + const [nfMediaEnabled, setNFMediaEnabled] = useState(false); + const [nfInfo, setNFInfo] = useState(''); + const [nfSettingType, setNFSettingType] = useState(null); + const [delegateeOptions, setDelegateeOptions] = useState([]); + + const channelDetailsFromBackend = useMemo(() => { + if (delegatees) { + return delegatees.find(delegatee => delegatee.channel === channelAddress); + } + // Return a default value or handle the case when delegatees is not defined. + return null; // or some other default value + }, [delegatees, channelAddress]); + + const channelSettings = useMemo(() => { + if (channelDetailsFromBackend) { + const { channel_settings } = channelDetailsFromBackend; + + if (channel_settings !== null) { + return JSON.parse(channel_settings); + } + } + // Return a default value or handle the case when channelDetailsFromBackend is not defined. + return null; // or some other default value + }, [channelDetailsFromBackend]); + + const channelSettingsOptions = useMemo(() => { + const defaultOption = { label: 'Default', value: null }; + + if (channelSettings) { + const settingsOptions = channelSettings.map(setting => ({ + label: setting.description, + value: setting.index, + })); + + return [defaultOption, ...settingsOptions]; + } + // If channelSettings is not defined, just return the default option. + return [defaultOption]; + }, [channelSettings]); + + const openManageSettings = () => { + const newPageUrl = '/channels'; // Replace with the URL of the manage settings later + + // Use window.open() to open the URL in a new tab + window.open(newPageUrl, '_blank'); + } + useEffect(() => { if (canSend !== 1) { const url = window.location.origin; @@ -164,7 +205,7 @@ function SendNotifications() { (delegatees.length === 1 && delegatees[0].alias_address === account) || !delegatees.length; // construct a list of channel delegators - React.useEffect(() => { + useEffect(() => { if (!account) return; if (!delegatees || !delegatees.length) { setChannelAddress(account); @@ -191,31 +232,6 @@ function SendNotifications() { } }, [delegatees, account]); - // const isAllFieldsFilled = () => { - // if (nfRecipient == "" || - // nfType == "" || - // nfMsg == "" || - // (nfSubEnabled && nfSub == "") || - // (nfCTAEnabled && nfCTA == "") || - // (nfMediaEnabled && nfMedia == "") - // ) { - // return false; - // } - // return true; - // }; - - // const previewNotif = (e: any) => { - // e.preventDefault(); - // if(isAllFieldsFilled()) - // setPreviewNotifModalOpen(true) - // else { - // setNFInfo("Please fill all fields to preview"); - // setTimeout(() => { - // setNFInfo(''); - // }, 2000); - // } - // } - // on change for the subset type notifications input const handleSubsetInputChange = (e: any) => { // if the user enters in a comma or an enter then separate the addresses @@ -239,7 +255,7 @@ function SendNotifications() { setMultipleRecipients(filteredRecipients); }; - React.useEffect(() => { + useEffect(() => { const broadcastIds = ['1']; //id's of notifications which qualify as broadcast setMultipleRecipients([]); //reset array when type changes/ if (broadcastIds.includes(nfType)) { @@ -297,128 +313,6 @@ function SendNotifications() { let acta = nfCTA; let aimg = nfMedia; - // Decide type and storage - // switch (nfType) { - // // Broadcast Notification - // case "1": - // break; - - // // Targeted Notification - // case "3": - // break; - - // // Old Secret Notification - // // case "2": - // // // Create secret - // // let secret = CryptoHelper.makeid(14); - - // // // Encrypt payload and change sub and nfMsg in notification - // // nsub = "You have a secret message!"; - // // nmsg = "Open the app to see your secret message!"; - - // // // get public key from EPNSCoreHelper - // // let k = await EPNSCoreHelper.getPublicKey( - // // nfRecipient, - // // epnsCommWriteProvider - // // ); - // // if (k == null) { - // // // No public key, can't encrypt - // // setNFInfo( - // // "Public Key Registration is required for encryption!" - // // ); - // // setNFProcessing(2); - - // // toast.update(notificationToast, { - // // render: "Unable to encrypt for this user, no public key registered", - // // type: toast.TYPE.ERROR, - // // autoClose: 5000, - // // }); - - // // return; - // // } - - // // let publickey = k.toString().substring(2); - // // //console.log("This is public Key: " + publickey); - - // // secretEncrypted = await CryptoHelper.encryptWithECIES( - // // secret, - // // publickey - // // ); - // // asub = CryptoHelper.encryptWithAES(nfSub, secret); - // // amsg = CryptoHelper.encryptWithAES(nfMsg, secret); - // // acta = CryptoHelper.encryptWithAES(nfCTA, secret); - // // aimg = CryptoHelper.encryptWithAES(nfMedia, secret); - // // break; - - // // Targeted Notification - // case "4": - // break; - - // // Secret Notification - // case "5": - // // Create secret - // let secret = CryptoHelper.makeid(8); - - // // Encrypt payload and change sub and nfMsg in notification - // nsub = "You have a secret message!"; - // nmsg = "Click on Decrypt button to see your secret message!"; - - // // get public key from Backend API - // let encryptionKey = await postReq('/encryption_key/get_encryption_key', { - // address: nfRecipient, - // op: "read" - // }).then(res => { - // return res.data?.encryption_key; - // }); - - // if (encryptionKey == null) { - // // No public key, can't encrypt - // setNFInfo( - // "Public Key Registration is required for encryption!" - // ); - // setNFProcessing(2); - - // toast.update(notificationToast, { - // render: "Unable to encrypt for this user, no public key registered", - // type: toast.TYPE.ERROR, - // autoClose: 5000, - // }); - - // return; - // } - - // let publickey = encryptionKey; - - // secretEncrypted = await CryptoHelper.encryptWithRPCEncryptionPublicKey( - // secret, - // publickey - // ); - // // console.log(secretEncrypted); - // if(nfSubEnabled) asub = CryptoHelper.encryptWithAES(nfSub, secret); - // amsg = CryptoHelper.encryptWithAES(nfMsg, secret); - // if(nfCTAEnabled) acta = CryptoHelper.encryptWithAES(nfCTA, secret); - // if(nfMediaEnabled) aimg = CryptoHelper.encryptWithAES(nfMedia, secret); - // break; - - // // Offchain Notification - // case "6": - // console.log( - // nsub, - // nmsg, - // nfType, - // asub, - // amsg, - // acta, - // aimg, - // "case 5" - // ); - - // break; - - // default: - // break; - // } - // Handle Storage let storagePointer = ''; @@ -477,105 +371,8 @@ function SendNotifications() { }); return; } - - // const jsonPayload = { - // notification: { - // title: nsub, - // body: nmsg, - // }, - // data: { - // type: nfType, - // secret: secretEncrypted, - // asub: asub, - // amsg: amsg, - // acta: acta, - // aimg: aimg, - // }, - // }; - - // // if we are sending a subset type, then include recipients - // if (nfType === "4") { - // jsonPayload["recipients"] = [...multipleRecipients]; - // } - - // const input = JSON.stringify(jsonPayload); - // console.log(input); - - // console.log("Uploding to IPFS..."); - // toast.update(notificationToast, { - // render: "Preparing Payload for upload", - // }); - - // const ipfs = require("nano-ipfs-store").at( - // "https://ipfs.infura.io:5001" - // ); - - // try { - // // storagePointer = await ipfs.add(input); - // storagePointer = await IPFSupload(input); - // } catch (e) { - // setNFProcessing(2); - // setNFInfo("IPFS Upload Error"); - // } - - // console.log("IPFS cid: %o", storagePointer); } if (nfType === '1' || nfType === '2' || nfType === '3' || nfType === '4' || nfType === '5') { - // Prepare Identity and send notification - // const identity = nfType + "+" + storagePointer; - // const identityBytes = ethers.utils.toUtf8Bytes(identity); - // console.log({ - // identityBytes, - // }); - // const EPNS_DOMAIN = { - // name: "Push (EPNS) COMM V1", - // chainId: chainId, - // verifyingContract: epnsCommReadProvider.address, - // }; - - // const type = { - // Data: [ - // { name: "acta", type: "string" }, - // { name: "aimg", type: "string" }, - // { name: "amsg", type: "string" }, - // { name: "asub", type: "string" }, - // { name: "type", type: "string" }, - // { name: "secret", type: "string" }, - // ], - // }; - - // const payload = { - // data: { - // acta: acta, - // aimg: aimg, - // amsg: amsg, - // asub: asub, - // type: nfType, - // secret: "", - // }, - - // notification: { - // body: amsg, - // title: asub, - // }, - // }; - - // if (nfType === "5" || nfType === "2") { - // payload.notification = { - // body: nmsg, - // title: nsub - // }; - // payload.data.secret = secretEncrypted; - // } - - // const message = payload.data; - // console.log(payload, "payload"); - // console.log("chainId", chainId); - // const signature = await library - // .getSigner(account) - // ._signTypedData(EPNS_DOMAIN, type, message); - // console.log("case5 signature", signature); - try { // apiResponse?.status === 204, if sent successfully! @@ -660,161 +457,7 @@ function SendNotifications() { setNFProcessing(0); console.log(err); } - - // var anotherSendTxPromise; - - // anotherSendTxPromise = communicatorContract.sendNotification( - // channelAddress, - // nfRecipient, - // identityBytes - // ); - - // console.log("Sending Transaction... "); - // toast.update(notificationToast, { - // render: "Sending Notification...", - // }); - - // anotherSendTxPromise - // .then(async (tx) => { - // console.log(tx); - // console.log("Transaction Sent!"); - - // toast.update(notificationToast, { - // render: "Notification Sent", - // type: toast.TYPE.INFO, - // autoClose: 5000, - // }); - - // await tx.wait(1); - // console.log("Transaction Mined!"); - - // setNFProcessing(2); - // setNFType(""); - // setNFInfo("Notification Sent"); - - // toast.update(notificationToast, { - // render: "Transaction Mined / Notification Sent", - // type: toast.TYPE.SUCCESS, - // autoClose: 5000, - // }); - // }) - // .catch((err) => { - // console.log("!!!Error handleSendMessage() --> %o", err); - // setNFInfo("Transaction Failed, please try again"); - - // toast.update(notificationToast, { - // render: "Transacion Failed: " + err, - // type: toast.TYPE.ERROR, - // autoClose: 5000, - // }); - // setNFProcessing(0); - // }); } - // if (nfType === "6") { - // // const jsonPayload = { - // // notification: { - // // title: nsub, - // // body: nmsg, - // // }, - // // data: { - // // type: nfType, - // // secret: secretEncrypted, - // // asub: asub, - // // amsg: amsg, - // // acta: acta, - // // aimg: aimg, - // // }, - // // }; - - // const EPNS_DOMAIN = { - // name: "Push (EPNS) COMM V1", - // chainId: chainId, - // verifyingContract: epnsCommReadProvider.address, - // }; - - // const type = { - // Data: [ - // { name: "acta", type: "string" }, - // { name: "aimg", type: "string" }, - // { name: "amsg", type: "string" }, - // { name: "asub", type: "string" }, - // { name: "type", type: "string" }, - // { name: "secret", type: "string" }, - // ], - // }; - - // const payload = { - // data: { - // acta: acta, - // aimg: aimg, - // amsg: amsg, - // asub: asub, - // type: nfType, - // secret: "", - // }, - - // notification: { - // body: amsg, - // title: asub, - // }, - // }; - - // const message = payload.data; - // console.log(payload, "payload"); - // console.log("chainId", chainId); - // const signature = await library - // .getSigner(account) - // ._signTypedData(EPNS_DOMAIN, type, message); - // console.log("case5 signature", signature); - // try { - // postReq("/payloads/add_manual_payload", { - // signature, - // op: "write", - // chainId: chainId.toString(), - // channel: channelAddress, - // recipient: nfRecipient, - // deployedContract: epnsCommReadProvider.address, - // payload: payload, - // type: "3", - // }).then(async (res) => { - // toast.update(notificationToast, { - // render: "Notification Sent", - // type: toast.TYPE.INFO, - // autoClose: 5000, - // }); - - // setNFProcessing(2); - // setNFType(""); - // setNFInfo("Notification Sent"); - - // toast.update(notificationToast, { - // render: "Notification Sent", - // type: toast.TYPE.SUCCESS, - // autoClose: 5000, - // }); - // console.log(res); - // }); - // } catch (err) { - // if (err.code === 4001) { - // // EIP-1193 userRejectedRequest error - // toast.update(notificationToast, { - // render: "User denied message signature.", - // type: toast.TYPE.ERROR, - // autoClose: 5000, - // }); - // } else { - // setNFInfo("Sending Notification Failed, please try again"); - - // toast.update(notificationToast, { - // render: "Notification Failed: " + err, - // type: toast.TYPE.ERROR, - // autoClose: 5000, - // }); - // } - // setNFProcessing(0); - // console.log(err); - // } - // } }; const isEmpty = (field: any) => { @@ -921,32 +564,8 @@ function SendNotifications() { }} placeholder="Select a Channel" value={delegateeOptions[0]} - // value={delegateeOptions.find( - // (d) => - // d.value == - // channelAddress - // )} /> - {/* - - SEND NOTIFICATION ON BEHALF - OF - - { - setChannelAddress( - option.value - ); - }} - value={delegateeOptions.find( - (d) => - d.value == - channelAddress - )} - /> - */} )} @@ -994,7 +613,7 @@ function SendNotifications() { color="#1E1E1E" padding="5px 15px" radius="30px"> - Subject + Title setNFSubEnabled(!nfSubEnabled)} /> @@ -1007,7 +626,7 @@ function SendNotifications() { color="#1E1E1E" padding="5px 15px" radius="30px"> - Media + Media URL setNFMediaEnabled(!nfMediaEnabled)} /> @@ -1026,7 +645,7 @@ function SendNotifications() { )} - + {(nfType === '2' || nfType === '3' || nfType === '5') && ( @@ -1108,7 +727,7 @@ function SendNotifications() { justify="space-between" > )} + {nfType && ( + <> + + + + + Manage Settings + + + + { + setNFSettingType(option.value); + console.log(option); + }} + value={channelSettingsOptions[0]} + /> + + + { + setNFSettingType(e.target.value); + }} + /> + + )} + {nfType && nfMediaEnabled && ( @@ -1235,6 +907,7 @@ function SendNotifications() {
{nfInfo}
)} +
{showPreview && ( props.fontSize || 'inherit'}; + font-weight: ${(props) => props.fontWeight || '300'}; color: ${(props) => props.color || '#e1087f'}; display: inline; letter-spacing: inherit; diff --git a/src/config/Themization.js b/src/config/Themization.js index b4aa0f7d6d..13802a867a 100644 --- a/src/config/Themization.js +++ b/src/config/Themization.js @@ -11,6 +11,7 @@ const themeLight = { secondaryColor: '#657795', hover: '#F3F3FF', borderColor: '#dfdee9', + primaryPushThemeTextColor: '#cf1c84' }, // Login Theme @@ -281,6 +282,7 @@ const themeDark = { secondaryColor: '#B6BCD6', hover: '#00000033', borderColor: '#4A4F67', + primaryPushThemeTextColor: '#cf1c84' }, // Login Theme From d38152363a7ee0b058607ce7f9018974f7d7d4ea Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Fri, 29 Sep 2023 15:48:40 +0530 Subject: [PATCH 02/18] Added changes for user settings flow --- package.json | 1 + public/svg/manageSettings.svg | 11 + public/svg/optout.svg | 6 + src/components/ViewChannelItem.js | 76 +++--- .../dropdowns/DropdownBtnHandler.tsx | 68 +++++ .../dropdowns/ManageNotifSettingDropdown.tsx | 97 ++++++++ .../dropdowns/OptinNotifSettingDropdown.tsx | 235 ++++++++++++++++++ .../dropdowns/UpdateNotifSettingDropdown.tsx | 235 ++++++++++++++++++ src/config/Themization.js | 12 + 9 files changed, 710 insertions(+), 31 deletions(-) create mode 100644 public/svg/manageSettings.svg create mode 100644 public/svg/optout.svg create mode 100644 src/components/dropdowns/DropdownBtnHandler.tsx create mode 100644 src/components/dropdowns/ManageNotifSettingDropdown.tsx create mode 100644 src/components/dropdowns/OptinNotifSettingDropdown.tsx create mode 100644 src/components/dropdowns/UpdateNotifSettingDropdown.tsx diff --git a/package.json b/package.json index 66933146bd..68293ca4e0 100644 --- a/package.json +++ b/package.json @@ -105,6 +105,7 @@ "react-icons": "^4.7.1", "react-image-file-resizer": "^0.4.7", "react-images-upload": "^1.2.8", + "react-input-slider": "^6.0.1", "react-joyride": "^2.4.0", "react-loader-spinner": "^5.3.4", "react-multi-select-component": "^4.2.3", diff --git a/public/svg/manageSettings.svg b/public/svg/manageSettings.svg new file mode 100644 index 0000000000..5d6138abd1 --- /dev/null +++ b/public/svg/manageSettings.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/public/svg/optout.svg b/public/svg/optout.svg new file mode 100644 index 0000000000..632a2fa47d --- /dev/null +++ b/public/svg/optout.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/components/ViewChannelItem.js b/src/components/ViewChannelItem.js index ff3699540b..a3dc96735c 100644 --- a/src/components/ViewChannelItem.js +++ b/src/components/ViewChannelItem.js @@ -36,6 +36,9 @@ import InfoImage from '../assets/info.svg'; import VerifiedTooltipContent from "./VerifiedTooltipContent"; import { IPFSGateway } from 'helpers/IpfsHelper'; import { useDeviceWidthCheck } from 'hooks'; +import ManageNotifSettingDropdown from './dropdowns/ManageNotifSettingDropdown'; +import OptinNotifSettingDropdown from './dropdowns/OptinNotifSettingDropdown'; +import { ImageV2 } from './reusables/SharedStylingV2'; // Create Header function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { @@ -1039,22 +1042,24 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { <> {isOwner && Owner} {!isOwner && ( - - {txInProgress && ( - - - - )} - Opt-In - + + {}} + disabled={txInProgress} + className="optin" + > + {txInProgress && ( + + + + )} + Opt-In + + )} )} @@ -1062,21 +1067,30 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { <> {isOwner && Owner} {!isOwner && ( - - {txInProgress && ( - - - - )} - Opt-out - + + {}} + disabled={txInProgress} + > + {txInProgress && ( + + + + )} + Manage + + + )} )} diff --git a/src/components/dropdowns/DropdownBtnHandler.tsx b/src/components/dropdowns/DropdownBtnHandler.tsx new file mode 100644 index 0000000000..989af156ef --- /dev/null +++ b/src/components/dropdowns/DropdownBtnHandler.tsx @@ -0,0 +1,68 @@ +import React, { useRef } from 'react'; + +// External Packages +import styled from 'styled-components'; + +// Internal Configs +import { useClickAway } from 'hooks/useClickAway'; +import { ItemHV2 } from 'components/reusables/SharedStylingV2'; + +interface DropdownBtnHandlerProps { + children: React.ReactNode; + renderDropdownContainer: React.ReactNode; + showDropdown: boolean; + toggleDropdown: () => void; + closeDropdown: () => void; +} + +export const DropdownBtnHandler: React.FC = ({ + children, + renderDropdownContainer, + showDropdown, + toggleDropdown, + closeDropdown +}) => { + const dropdownRef = useRef(null); + const renderDropdownContainerRef = useRef(null); + + useClickAway(dropdownRef, renderDropdownContainerRef, closeDropdown); + + return ( + + {children} + {showDropdown && ( + +
e.stopPropagation()}> + {renderDropdownContainer} +
+
+ )} +
+ ); +}; + +const Container = styled.button` + position:relative; + margin: 0; + padding: 0; + background: none; + border: 0; + outline: 0; +` + +const DropdownContainer = styled(ItemHV2)` + background: ${(props)=>props.theme.default.secondaryBg}; + border:1px solid; + border-color:${(props)=>props.theme.snackbarBorderColor}; + border-radius: 8px; + align-items:flex-start; + padding:7px 14px 7px 14px; + position:absolute; + top:0rem; + z-index:10; + right:-0.5rem; + + @media (max-width:768px){ + right:0px; + } +`; \ No newline at end of file diff --git a/src/components/dropdowns/ManageNotifSettingDropdown.tsx b/src/components/dropdowns/ManageNotifSettingDropdown.tsx new file mode 100644 index 0000000000..4b17ce7179 --- /dev/null +++ b/src/components/dropdowns/ManageNotifSettingDropdown.tsx @@ -0,0 +1,97 @@ +// React + Web3 Essentials +import React, { useState } from "react"; + +// External Packages +import styled, { useTheme } from "styled-components"; + +// Internal Components +import { DropdownBtnHandler } from "./DropdownBtnHandler"; +import UpdateNotifSettingDropdown from "./UpdateNotifSettingDropdown"; + +// Internal Configs +import { ImageV2, SpanV2 } from "components/reusables/SharedStylingV2"; + +interface ManageNotifSettingDropdownProps { + children: React.ReactNode; +} + +const ManageNotifSettingDropdownContainer: React.FC = () => { + + const theme = useTheme(); + + return ( + + + + + + Manage Settings + + + + + + Opt-out + + + ); +}; + +const ManageNotifSettingDropdown: React.FC = ({ children }) => { + const [isOpen, setIsOpen] = useState(false); + + const toggleDropdown = () => { + setIsOpen(!isOpen); + }; + + const closeDropdown = () => { + setIsOpen(false); + }; + + // render + return ( + } + > + {children} + + ); +} + +// Export Default +export default ManageNotifSettingDropdown; + +const DropdownOuterContainer = styled.div` + min-width: max-content; + gap: 5px; + display: flex; + flex-direction: column; +`; + +const DropdownInnerContainer = styled.div` + display: flex; + flex-direction: column; +`; + +const DropdownBtn = styled.button` + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + background: transparent; + cursor: pointer; + gap: 5px; +`; diff --git a/src/components/dropdowns/OptinNotifSettingDropdown.tsx b/src/components/dropdowns/OptinNotifSettingDropdown.tsx new file mode 100644 index 0000000000..1f886e5799 --- /dev/null +++ b/src/components/dropdowns/OptinNotifSettingDropdown.tsx @@ -0,0 +1,235 @@ +// React + Web3 Essentials +import React, { useState } from "react"; + +// External Packages +import Switch from 'react-switch'; +import Slider from 'react-input-slider'; +import styled, { css, useTheme } from "styled-components"; + +// Internal Components +import { DropdownBtnHandler } from "./DropdownBtnHandler"; + +// Internal Configs +import { SpanV2 } from "components/reusables/SharedStylingV2"; + +interface OptinNotifSettingDropdownProps { + children: React.ReactNode; +} + +const OptinNotifSettingDropdownContainer: React.FC<{ settings: any[] }> = ({ settings }) => { + const [modifiedSettings, setModifiedSettings] = useState([...settings]); + + const theme = useTheme(); + + console.log(modifiedSettings); + + const handleSliderChange = (index: number, value: number) => { + const updatedSettings = [...modifiedSettings]; + updatedSettings[index].defaultValue = value; + setModifiedSettings(updatedSettings); + }; + + const handleSwitchChange = (index: number) => { + const updatedSettings = [...modifiedSettings]; + updatedSettings[index].default = !updatedSettings[index].default; + setModifiedSettings(updatedSettings); + }; + + return ( + + {modifiedSettings.map((setting, index) => ( + + + {setting.description} + handleSwitchChange(index)} checked={setting.default} + checkedIcon={false} + uncheckedIcon={false} + onColor="#D53A94" + offColor="#A0A3B1" + height={16} + width={32} + handleDiameter={12} + /> + + {setting.type === 2 && setting.default && ( + + handleSliderChange(index, x)} + xstep={1} + xmin={setting.lowerLimit} + xmax={setting.upperLimit} + /> + {setting.defaultValue} + + )} + + ))} + + You will receive all important updates from this channel. + Opt-in + + + ); +}; + +// Faucet URLs +const OptinNotifSettingDropdown: React.FC = ({ children }) => { + const [isOpen, setIsOpen] = useState(false); + + const toggleDropdown = () => { + setIsOpen(!isOpen); + }; + + const closeDropdown = () => { + setIsOpen(false); + }; + + const settings = [ + { + "type": 1, + "index": 1, + "default": false, + "description": "Giveaway Alerts" + }, + { + "type": 2, + "index": 2, + "default": true, + "defaultValue": 50, + "lowerLimit": 20, + "upperLimit": 100, + "description": "ETH Price Drop Alert" + }, + { + "type": 1, + "index": 3, + "default": true, + "description": "Partnership Announcements" + }, + { + "type": 2, + "index": 4, + "default": false, + "defaultValue": 45, + "lowerLimit": 10, + "upperLimit": 150, + "description": "Marketing" + } +] + + // render + return ( + } + > + {children} + + ); +} + +// Export Default +export default OptinNotifSettingDropdown; + +const DropdownOuterContainer = styled.div` + min-width: 280px; +`; + +const DropdownInnerContainer = styled.div<{ hasBottomBorder: boolean }>` + display: flex; + flex-direction: column; + min-width: 250px; + + ${(props) => + props.hasBottomBorder && + css` + border-bottom: 1px solid ${(props) => props.theme.settingsModalBorderBottomColor}; + `} +`; + +const DropdownSwitchItem = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 0px; +`; + +const DropdownSubmitItem = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 0px; +`; + +const DropdownSubmitButton = styled.button` + border: 0; + outline: 0; + display: flex; + align-items: center; + min-width: max-content; + justify-content: center; + margin: 0px 0px 0px 10px; + color: #fff; + font-size: 14px; + font-weight: 400; + position: relative; + background: #e20880; + border-radius: 8px; + padding: 9px 15px; + @media (max-width: 768px) { + padding: 9px 30px; + } + &: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 DropdownSliderItem = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: 10px; +`; diff --git a/src/components/dropdowns/UpdateNotifSettingDropdown.tsx b/src/components/dropdowns/UpdateNotifSettingDropdown.tsx new file mode 100644 index 0000000000..2d34c6ecd4 --- /dev/null +++ b/src/components/dropdowns/UpdateNotifSettingDropdown.tsx @@ -0,0 +1,235 @@ +// React + Web3 Essentials +import React, { useState } from "react"; + +// External Packages +import Switch from 'react-switch'; +import Slider from 'react-input-slider'; +import styled, { css, useTheme } from "styled-components"; + +// Internal Components +import { DropdownBtnHandler } from "./DropdownBtnHandler"; + +// Internal Configs +import { SpanV2 } from "components/reusables/SharedStylingV2"; + +interface UpdateNotifSettingDropdownProps { + children: React.ReactNode; +} + +const UpdateNotifSettingDropdownContainer: React.FC<{ settings: any[] }> = ({ settings }) => { + const [modifiedSettings, setModifiedSettings] = useState([...settings]); + + const theme = useTheme(); + + console.log(modifiedSettings); + + const handleSliderChange = (index: number, value: number) => { + const updatedSettings = [...modifiedSettings]; + updatedSettings[index].defaultValue = value; + setModifiedSettings(updatedSettings); + }; + + const handleSwitchChange = (index: number) => { + const updatedSettings = [...modifiedSettings]; + updatedSettings[index].default = !updatedSettings[index].default; + setModifiedSettings(updatedSettings); + }; + + return ( + + {modifiedSettings.map((setting, index) => ( + + + {setting.description} + handleSwitchChange(index)} checked={setting.default} + checkedIcon={false} + uncheckedIcon={false} + onColor="#D53A94" + offColor="#A0A3B1" + height={16} + width={32} + handleDiameter={12} + /> + + {setting.type === 2 && setting.default && ( + + handleSliderChange(index, x)} + xstep={1} + xmin={setting.lowerLimit} + xmax={setting.upperLimit} + /> + {setting.defaultValue} + + )} + + ))} + + You will receive all important updates from this channel. + Save + + + ); +}; + +// Faucet URLs +const UpdateNotifSettingDropdown: React.FC = ({ children }) => { + const [isOpen, setIsOpen] = useState(false); + + const toggleDropdown = () => { + setIsOpen(!isOpen); + }; + + const closeDropdown = () => { + setIsOpen(false); + }; + + const settings = [ + { + "type": 1, + "index": 1, + "default": false, + "description": "Giveaway Alerts" + }, + { + "type": 2, + "index": 2, + "default": true, + "defaultValue": 50, + "lowerLimit": 20, + "upperLimit": 100, + "description": "ETH Price Drop Alert" + }, + { + "type": 1, + "index": 3, + "default": true, + "description": "Partnership Announcements" + }, + { + "type": 2, + "index": 4, + "default": false, + "defaultValue": 45, + "lowerLimit": 10, + "upperLimit": 150, + "description": "Marketing" + } +] + + // render + return ( + } + > + {children} + + ); +} + +// Export Default +export default UpdateNotifSettingDropdown; + +const DropdownOuterContainer = styled.div` + min-width: 280px; +`; + +const DropdownInnerContainer = styled.div<{ hasBottomBorder: boolean }>` + display: flex; + flex-direction: column; + min-width: 250px; + + ${(props) => + props.hasBottomBorder && + css` + border-bottom: 1px solid ${(props) => props.theme.settingsModalBorderBottomColor}; + `} +`; + +const DropdownSwitchItem = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 0px; +`; + +const DropdownSubmitItem = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + padding: 10px 0px; +`; + +const DropdownSubmitButton = styled.button` + border: 0; + outline: 0; + display: flex; + align-items: center; + justify-content: center; + margin: 0px 0px 0px 10px; + color: #fff; + font-size: 14px; + font-weight: 400; + position: relative; + background: #e20880; + border-radius: 8px; + padding: 9px 15px; + min-width: 60px; + @media (max-width: 768px) { + padding: 9px 30px; + } + &: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 DropdownSliderItem = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + padding-bottom: 10px; +`; diff --git a/src/config/Themization.js b/src/config/Themization.js index b4aa0f7d6d..df9ef04a4d 100644 --- a/src/config/Themization.js +++ b/src/config/Themization.js @@ -239,6 +239,12 @@ const themeLight = { activeButtonText:'#657795', stakingBorder:'#BAC4D6', stakingEmptyButtonBG:'#DEDFE1', + + // notif settings modal + settingsModalBorderBottomColor: '#D4DCEA', + sliderActiveColor: '#CF1C84', + sliderTrackColor: '#BAC4D6', + settingsModalPrimaryTextColor: '#1E1E1E', //spaces spaceHostTextColor: '#1e1e1e', @@ -515,6 +521,12 @@ const themeDark = { emptyButtonText:'#2D313C', emptyButtonBg:'', + // notif settings modal + settingsModalBorderBottomColor: '#4A4F67', + sliderActiveColor: '#CF1C84', + sliderTrackColor: '#4A4F67', + settingsModalPrimaryTextColor: '#fff', + //spaces spaceHostTextColor: '#ffff', From 0cd78b6c964753a5399a0e9eb50d1a9196eb78af Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Fri, 29 Sep 2023 17:41:55 +0530 Subject: [PATCH 03/18] css changes --- src/components/dropdowns/ManageNotifSettingDropdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dropdowns/ManageNotifSettingDropdown.tsx b/src/components/dropdowns/ManageNotifSettingDropdown.tsx index 4b17ce7179..5293ac4f61 100644 --- a/src/components/dropdowns/ManageNotifSettingDropdown.tsx +++ b/src/components/dropdowns/ManageNotifSettingDropdown.tsx @@ -90,7 +90,7 @@ const DropdownBtn = styled.button` display: flex; flex-direction: row; align-items: center; - justify-content: center; + justify-content: flex-start; background: transparent; cursor: pointer; gap: 5px; From a8a43394758efa716b433c96549d9b714abf78d4 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Fri, 29 Sep 2023 17:45:22 +0530 Subject: [PATCH 04/18] setting min-width --- src/components/dropdowns/UpdateNotifSettingDropdown.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dropdowns/UpdateNotifSettingDropdown.tsx b/src/components/dropdowns/UpdateNotifSettingDropdown.tsx index 2d34c6ecd4..e64de1a4e6 100644 --- a/src/components/dropdowns/UpdateNotifSettingDropdown.tsx +++ b/src/components/dropdowns/UpdateNotifSettingDropdown.tsx @@ -188,6 +188,7 @@ const DropdownSubmitButton = styled.button` outline: 0; display: flex; align-items: center; + min-width: max-content; justify-content: center; margin: 0px 0px 0px 10px; color: #fff; @@ -197,7 +198,6 @@ const DropdownSubmitButton = styled.button` background: #e20880; border-radius: 8px; padding: 9px 15px; - min-width: 60px; @media (max-width: 768px) { padding: 9px 30px; } From aeb4b2601a3c0925e152ce66d35d2985147172c9 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Fri, 29 Sep 2023 17:52:31 +0530 Subject: [PATCH 05/18] positioning modal in centre in smaller screens --- src/components/dropdowns/DropdownBtnHandler.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/dropdowns/DropdownBtnHandler.tsx b/src/components/dropdowns/DropdownBtnHandler.tsx index 989af156ef..1fa9d67650 100644 --- a/src/components/dropdowns/DropdownBtnHandler.tsx +++ b/src/components/dropdowns/DropdownBtnHandler.tsx @@ -63,6 +63,8 @@ const DropdownContainer = styled(ItemHV2)` right:-0.5rem; @media (max-width:768px){ - right:0px; + left: 50%; + transform: translateX(-50%); + width: fit-content; } `; \ No newline at end of file From 53479cd6b5334fb5c99b5900c14d84acdc7b5050 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Tue, 3 Oct 2023 15:28:22 +0530 Subject: [PATCH 06/18] some UI changes --- src/components/ViewChannelItem.js | 20 +++++++------------ .../dropdowns/DropdownBtnHandler.tsx | 14 +++++++------ .../dropdowns/ManageNotifSettingDropdown.tsx | 13 ++++++------ .../dropdowns/OptinNotifSettingDropdown.tsx | 12 +++++------ .../dropdowns/UpdateNotifSettingDropdown.tsx | 12 +++++------ src/config/Themization.js | 4 ++++ 6 files changed, 36 insertions(+), 39 deletions(-) diff --git a/src/components/ViewChannelItem.js b/src/components/ViewChannelItem.js index a3dc96735c..78193b3abe 100644 --- a/src/components/ViewChannelItem.js +++ b/src/components/ViewChannelItem.js @@ -1087,7 +1087,6 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { src="/svg/arrow.svg" height="10px" width="12px" - margin="0px 0px 0px 8px" /> @@ -1448,8 +1447,6 @@ const ChannelActionButton = styled.button` display: flex; align-items: center; justify-content: center; - padding: 8px 15px; - margin: 10px; color: #fff; border-radius: 5px; font-size: 14px; @@ -1518,11 +1515,9 @@ const SkeletonButton = styled.div` const SubscribeButton = styled(ChannelActionButton)` background: #e20880; border-radius: 8px; - padding: 9px 15px; - min-width: 80px; - @media (max-width: 768px) { - padding: 9px 30px; - } + padding: 0px; + min-height: 36px; + min-width: 108px; `; const UnsubscribeButton = styled(ChannelActionButton)` @@ -1530,11 +1525,10 @@ const UnsubscribeButton = styled(ChannelActionButton)` color: ${(props) => props.theme.viewChannelPrimaryText}; border: 1px solid #bac4d6; border-radius: 8px; - padding: 9px 15px; - min-width: 80px; - @media (max-width: 768px) { - padding: 9px 30px; - } + padding: 0px 8px 0px 16px; + gap: 8px; + min-height: 36px; + min-width: 108px; `; const OwnerButton = styled(ChannelActionButton)` diff --git a/src/components/dropdowns/DropdownBtnHandler.tsx b/src/components/dropdowns/DropdownBtnHandler.tsx index 1fa9d67650..4fe307d74a 100644 --- a/src/components/dropdowns/DropdownBtnHandler.tsx +++ b/src/components/dropdowns/DropdownBtnHandler.tsx @@ -13,6 +13,7 @@ interface DropdownBtnHandlerProps { showDropdown: boolean; toggleDropdown: () => void; closeDropdown: () => void; + containerPadding?: string; } export const DropdownBtnHandler: React.FC = ({ @@ -20,7 +21,8 @@ export const DropdownBtnHandler: React.FC = ({ renderDropdownContainer, showDropdown, toggleDropdown, - closeDropdown + closeDropdown, + containerPadding }) => { const dropdownRef = useRef(null); const renderDropdownContainerRef = useRef(null); @@ -31,7 +33,7 @@ export const DropdownBtnHandler: React.FC = ({ {children} {showDropdown && ( - +
e.stopPropagation()}> {renderDropdownContainer}
@@ -50,13 +52,13 @@ const Container = styled.button` outline: 0; ` -const DropdownContainer = styled(ItemHV2)` - background: ${(props)=>props.theme.default.secondaryBg}; +const DropdownContainer = styled(ItemHV2)<{ containerPadding?: string}>` + background: ${(props)=>props.theme.settingsModalBackground}; border:1px solid; - border-color:${(props)=>props.theme.snackbarBorderColor}; + border-color:${(props)=>props.theme.settingsModalBorderColor}; border-radius: 8px; align-items:flex-start; - padding:7px 14px 7px 14px; + padding: ${props => props.containerPadding ? props.containerPadding : '7px 14px'}; position:absolute; top:0rem; z-index:10; diff --git a/src/components/dropdowns/ManageNotifSettingDropdown.tsx b/src/components/dropdowns/ManageNotifSettingDropdown.tsx index 5293ac4f61..5ba7316944 100644 --- a/src/components/dropdowns/ManageNotifSettingDropdown.tsx +++ b/src/components/dropdowns/ManageNotifSettingDropdown.tsx @@ -30,18 +30,18 @@ const ManageNotifSettingDropdownContainer: React.FC = () => { src="svg/manageSettings.svg" alt="Settings Logo" /> - Manage Settings + Manage Settings - Opt-out + Opt-out ); @@ -65,6 +65,7 @@ const ManageNotifSettingDropdown: React.FC = ({ toggleDropdown={toggleDropdown} closeDropdown={closeDropdown} renderDropdownContainer={} + containerPadding="12px 16px" > {children} @@ -76,7 +77,7 @@ export default ManageNotifSettingDropdown; const DropdownOuterContainer = styled.div` min-width: max-content; - gap: 5px; + gap: 16px; display: flex; flex-direction: column; `; @@ -93,5 +94,5 @@ const DropdownBtn = styled.button` justify-content: flex-start; background: transparent; cursor: pointer; - gap: 5px; + gap: 8px; `; diff --git a/src/components/dropdowns/OptinNotifSettingDropdown.tsx b/src/components/dropdowns/OptinNotifSettingDropdown.tsx index 1f886e5799..c3b25a4ef9 100644 --- a/src/components/dropdowns/OptinNotifSettingDropdown.tsx +++ b/src/components/dropdowns/OptinNotifSettingDropdown.tsx @@ -64,7 +64,7 @@ const OptinNotifSettingDropdownContainer: React.FC<{ settings: any[] }> = ({ set }, track: { height: 4, - width: 220, + width: 250, backgroundColor: theme.sliderTrackColor }, thumb: { @@ -79,7 +79,7 @@ const OptinNotifSettingDropdownContainer: React.FC<{ settings: any[] }> = ({ set xmin={setting.lowerLimit} xmax={setting.upperLimit} /> - {setting.defaultValue} + {setting.defaultValue} )} @@ -144,6 +144,7 @@ const OptinNotifSettingDropdown: React.FC = ({ c toggleDropdown={toggleDropdown} closeDropdown={closeDropdown} renderDropdownContainer={} + containerPadding="0px 16px 16px 16px" > {children} @@ -154,7 +155,7 @@ const OptinNotifSettingDropdown: React.FC = ({ c export default OptinNotifSettingDropdown; const DropdownOuterContainer = styled.div` - min-width: 280px; + min-width: 300px; `; const DropdownInnerContainer = styled.div<{ hasBottomBorder: boolean }>` @@ -197,10 +198,7 @@ const DropdownSubmitButton = styled.button` position: relative; background: #e20880; border-radius: 8px; - padding: 9px 15px; - @media (max-width: 768px) { - padding: 9px 30px; - } + padding: 9px 20px; &:hover { opacity: 0.9; cursor: pointer; diff --git a/src/components/dropdowns/UpdateNotifSettingDropdown.tsx b/src/components/dropdowns/UpdateNotifSettingDropdown.tsx index e64de1a4e6..2a9c3d85ab 100644 --- a/src/components/dropdowns/UpdateNotifSettingDropdown.tsx +++ b/src/components/dropdowns/UpdateNotifSettingDropdown.tsx @@ -64,7 +64,7 @@ const UpdateNotifSettingDropdownContainer: React.FC<{ settings: any[] }> = ({ se }, track: { height: 4, - width: 220, + width: 250, backgroundColor: theme.sliderTrackColor }, thumb: { @@ -79,7 +79,7 @@ const UpdateNotifSettingDropdownContainer: React.FC<{ settings: any[] }> = ({ se xmin={setting.lowerLimit} xmax={setting.upperLimit} /> - {setting.defaultValue} + {setting.defaultValue} )} @@ -144,6 +144,7 @@ const UpdateNotifSettingDropdown: React.FC = ({ toggleDropdown={toggleDropdown} closeDropdown={closeDropdown} renderDropdownContainer={} + containerPadding="0px 16px 16px 16px" > {children} @@ -154,7 +155,7 @@ const UpdateNotifSettingDropdown: React.FC = ({ export default UpdateNotifSettingDropdown; const DropdownOuterContainer = styled.div` - min-width: 280px; + min-width: 300px; `; const DropdownInnerContainer = styled.div<{ hasBottomBorder: boolean }>` @@ -197,10 +198,7 @@ const DropdownSubmitButton = styled.button` position: relative; background: #e20880; border-radius: 8px; - padding: 9px 15px; - @media (max-width: 768px) { - padding: 9px 30px; - } + padding: 9px 20px; &:hover { opacity: 0.9; cursor: pointer; diff --git a/src/config/Themization.js b/src/config/Themization.js index df9ef04a4d..9035187acf 100644 --- a/src/config/Themization.js +++ b/src/config/Themization.js @@ -245,6 +245,8 @@ const themeLight = { sliderActiveColor: '#CF1C84', sliderTrackColor: '#BAC4D6', settingsModalPrimaryTextColor: '#1E1E1E', + settingsModalBorderColor: '#D4DFF2', + settingsModalBackground: '#FFF', //spaces spaceHostTextColor: '#1e1e1e', @@ -526,6 +528,8 @@ const themeDark = { sliderActiveColor: '#CF1C84', sliderTrackColor: '#4A4F67', settingsModalPrimaryTextColor: '#fff', + settingsModalBorderColor: '#4A4F67', + settingsModalBackground: '#2F3137', //spaces spaceHostTextColor: '#ffff', From 0836fd9769a98a55f56efe8ce1602903cc1acf06 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Tue, 3 Oct 2023 15:40:13 +0530 Subject: [PATCH 07/18] UI changes --- src/components/dropdowns/OptinNotifSettingDropdown.tsx | 2 +- src/components/dropdowns/UpdateNotifSettingDropdown.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/dropdowns/OptinNotifSettingDropdown.tsx b/src/components/dropdowns/OptinNotifSettingDropdown.tsx index c3b25a4ef9..3e231a9579 100644 --- a/src/components/dropdowns/OptinNotifSettingDropdown.tsx +++ b/src/components/dropdowns/OptinNotifSettingDropdown.tsx @@ -79,7 +79,7 @@ const OptinNotifSettingDropdownContainer: React.FC<{ settings: any[] }> = ({ set xmin={setting.lowerLimit} xmax={setting.upperLimit} /> - {setting.defaultValue} + {setting.defaultValue} )} diff --git a/src/components/dropdowns/UpdateNotifSettingDropdown.tsx b/src/components/dropdowns/UpdateNotifSettingDropdown.tsx index 2a9c3d85ab..e80450c3ce 100644 --- a/src/components/dropdowns/UpdateNotifSettingDropdown.tsx +++ b/src/components/dropdowns/UpdateNotifSettingDropdown.tsx @@ -79,7 +79,7 @@ const UpdateNotifSettingDropdownContainer: React.FC<{ settings: any[] }> = ({ se xmin={setting.lowerLimit} xmax={setting.upperLimit} /> - {setting.defaultValue} + {setting.defaultValue} )} From 3936616d8a93271fd7d6d8a729f3143f003ba7d5 Mon Sep 17 00:00:00 2001 From: Kalash Shah <81062983+kalashshah@users.noreply.github.com> Date: Thu, 5 Oct 2023 15:10:07 +0530 Subject: [PATCH 08/18] Notification Settings for channel creator (#1263) * chore: upgrade react icons * feat: components for notif setting * chore: remove add/delete delegate from modal * feat: notif settings page structure * chore: update modal confirm button * feat: channel details page * chore: ui fixes * chore: update url to notif settings page * feat: get channel service * chore: fix styling and add fixed price for nfs --- package.json | 2 +- src/components/ChannelDetails.js | 368 +++++++++++++---- .../ChannelReactivateModalContent.tsx | 2 +- src/components/ChannelSettingsDropdown.tsx | 73 +--- src/components/DelegateInfo.js | 59 ++- src/components/FaucetInfo.tsx | 5 +- src/components/RemoveDelegateModalContent.tsx | 112 ------ src/components/SendNotifications.tsx | 2 +- src/components/SharedStyling.js | 2 - src/components/ShowDelegates.jsx | 280 ------------- src/components/StakingInfo.tsx | 4 +- .../channel/AddSettingModalContent.tsx | 369 ++++++++++++++++++ src/components/channel/ChannelButtons.tsx | 86 ++++ src/components/channel/ChannelInfoHeader.tsx | 69 ++++ src/components/channel/ChannelInfoList.tsx | 207 ++++++++++ .../channel/DelegateSettingsDropdown.tsx | 85 ++++ src/components/channel/DepositFeeFooter.tsx | 309 +++++++++++++++ .../channel/NotificationSettings.tsx | 286 ++++++++++++++ src/config/Themization.js | 8 + src/helpers/channel/InputValidation.ts | 96 +++++ src/helpers/channel/types.ts | 15 + .../channelDashboardModule.tsx | 2 +- src/modules/editChannel/EditChannel.tsx | 1 + .../notifSettings/NotifSettingsModule.tsx | 86 ++++ src/pages/NotifSettingsPage.tsx | 29 ++ .../ModalConfirmButton.tsx | 8 +- src/services/channels/getChannel.ts | 19 + src/services/channels/index.ts | 3 +- src/structure/MasterInterfacePage.tsx | 2 + yarn.lock | 26 +- 30 files changed, 2014 insertions(+), 601 deletions(-) delete mode 100644 src/components/RemoveDelegateModalContent.tsx delete mode 100644 src/components/ShowDelegates.jsx create mode 100644 src/components/channel/AddSettingModalContent.tsx create mode 100644 src/components/channel/ChannelButtons.tsx create mode 100644 src/components/channel/ChannelInfoHeader.tsx create mode 100644 src/components/channel/ChannelInfoList.tsx create mode 100644 src/components/channel/DelegateSettingsDropdown.tsx create mode 100644 src/components/channel/DepositFeeFooter.tsx create mode 100644 src/components/channel/NotificationSettings.tsx create mode 100644 src/helpers/channel/InputValidation.ts create mode 100644 src/helpers/channel/types.ts create mode 100644 src/modules/notifSettings/NotifSettingsModule.tsx create mode 100644 src/pages/NotifSettingsPage.tsx create mode 100644 src/services/channels/getChannel.ts diff --git a/package.json b/package.json index 1347c86350..5085ea406d 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "react-dropzone-uploader": "2.11.0", "react-easy-crop": "^4.1.4", "react-ga": "2.7.0", - "react-icons": "^4.7.1", + "react-icons": "^4.11.0", "react-image-file-resizer": "^0.4.7", "react-images-upload": "^1.2.8", "react-input-slider": "^6.0.1", diff --git a/src/components/ChannelDetails.js b/src/components/ChannelDetails.js index 5378c6e53a..9f02e70b8d 100644 --- a/src/components/ChannelDetails.js +++ b/src/components/ChannelDetails.js @@ -1,38 +1,45 @@ // React + Web3 Essentials import { ethers } from 'ethers'; -import React, { useEffect } from 'react'; +import React, { useEffect, useMemo } from 'react'; // External Packages import moment from 'moment'; import { useSelector } from 'react-redux'; import styled from 'styled-components'; +import { useNavigate } from 'react-router-dom'; +import { MdRemoveCircleOutline } from 'react-icons/md'; // Internal Compoonents -import { getReq, postReq } from 'api'; -import { ButtonV2, ImageV2, ItemHV2, ItemVV2, SpanV2 } from "components/reusables/SharedStylingV2"; -import { convertAddressToAddrCaip } from 'helpers/CaipHelper'; +import { ButtonV2, ImageV2, ItemHV2, ItemVV2, SpanV2 } from 'components/reusables/SharedStylingV2'; import { useAccount, useDeviceWidthCheck } from 'hooks'; import ChannelsDataStore from 'singletons/ChannelsDataStore'; import ChannelSettings from './ChannelSettings'; -import ShowDelegates from './ShowDelegates'; +import { Section } from 'primaries/SharedStyling'; +import useModalBlur, { MODAL_POSITION } from 'hooks/useModalBlur'; +import useToast from 'hooks/useToast'; +import AddDelegateModalContent from './AddDelegateModalContent'; +import { AddDelegateButton, ManageSettingsButton } from './channel/ChannelButtons'; +import { Button, Item } from 'components/SharedStyling'; +import ChannelInfoHeader from './channel/ChannelInfoHeader'; +import ChannelInfoList from './channel/ChannelInfoList'; +import RedCircleSvg from '../assets/RedCircle.svg'; // Internal Configs -import { appConfig } from "config"; -import { device } from "config/Globals"; +import { appConfig } from 'config'; +import { device } from 'config/Globals'; import { CHANNEL_TYPE } from 'helpers/UtilityHelper'; import { getDateFromTimestamp, nextDaysDateFromTimestamp, timeRemaining } from 'helpers/TimerHelper'; -import RedCircleSvg from "../assets/RedCircle.svg" -import { Button } from "components/SharedStyling"; + const DATE_FORMAT = 'DD MMM, YYYY'; -export default function ChannelDetails({ isChannelExpired, setIsChannelExpired, showEditChannel, destroyChannel -}) { - const { chainId } = useAccount(); +export default function ChannelDetails({ isChannelExpired, setIsChannelExpired, showEditChannel, destroyChannel }) { + const { account, chainId } = useAccount(); const { + delegatees, channelDetails, canVerify, - aliasDetails: { isAliasVerified, aliasAddrFromContract } + aliasDetails: { isAliasVerified, aliasAddrFromContract }, } = useSelector((state) => state.admin); const { CHANNEL_ACTIVE_STATE, CHANNNEL_DEACTIVATED_STATE } = useSelector((state) => state.channels); @@ -40,7 +47,7 @@ export default function ChannelDetails({ isChannelExpired, setIsChannelExpired, const [verifyingChannel, setVerifyingChannel] = React.useState([]); const [creationDate, setCreationDate] = React.useState(''); let { channelState } = channelDetails; - if(!channelState) channelState = channelDetails['activation_status']; + if (!channelState) channelState = channelDetails['activation_status']; const channelIsActive = channelState === CHANNEL_ACTIVE_STATE; const channelIsDeactivated = channelState === CHANNNEL_DEACTIVATED_STATE; @@ -48,21 +55,43 @@ export default function ChannelDetails({ isChannelExpired, setIsChannelExpired, const onCoreNetwork = CORE_CHAIN_ID === chainId; const isMobile = useDeviceWidthCheck(600); + const [channelAddress, setChannelAddress] = React.useState(undefined); + const { epnsCommWriteProvider } = useSelector((state) => state.contracts); + + const navigate = useNavigate(); + + const { + isModalOpen: isAddDelegateModalOpen, + showModal: showAddDelegateModal, + ModalComponent: AddDelegateModalComponent, + } = useModalBlur(); + + const addDelegateToast = useToast(); + const addDelegate = async (walletAddress) => { + return epnsCommWriteProvider.addDelegate(walletAddress); + }; + // BEGIN CHANGE // Added this inline if-else condition because of a bug that when connecting to Mumbai, the channelDetails.expiryType is undefined, so the toString() is throwing an exception - const channelExpiryDate = channelDetails.expiryTime ? getDateFromTimestamp(channelDetails.expiryTime?.toString() * 1000) : '' - const isChannelNotExpired = channelDetails.expiryTime ? timeRemaining(channelDetails.expiryTime?.toString() * 1000) : true - const channelAutomaticExpiryDate = channelDetails.expiryTime ? nextDaysDateFromTimestamp(channelDetails.expiryTime?.toString() * 1000, 14) : '' + const channelExpiryDate = channelDetails.expiryTime + ? getDateFromTimestamp(channelDetails.expiryTime?.toString() * 1000) + : ''; + const isChannelNotExpired = channelDetails.expiryTime + ? timeRemaining(channelDetails.expiryTime?.toString() * 1000) + : true; + const channelAutomaticExpiryDate = channelDetails.expiryTime + ? nextDaysDateFromTimestamp(channelDetails.expiryTime?.toString() * 1000, 14) + : ''; // END CHANGE useEffect(() => { - if(channelDetails.channelType != CHANNEL_TYPE["TIMEBOUND"]) return; - if(!isChannelNotExpired) setIsChannelExpired(true); + if (channelDetails.channelType != CHANNEL_TYPE['TIMEBOUND']) return; + if (!isChannelNotExpired) setIsChannelExpired(true); }, [isChannelNotExpired]); React.useEffect(() => { if (!channelDetails || !canVerify) return; - (async function() { + (async function () { let channelJson = await ChannelsDataStore.instance.getChannelJsonAsync(channelDetails.verifiedBy); setVerifyingChannel(channelJson); })(); @@ -70,7 +99,7 @@ export default function ChannelDetails({ isChannelExpired, setIsChannelExpired, React.useEffect(() => { if (!channelDetails || !onCoreNetwork) return; - (async function() { + (async function () { const bn = channelDetails.channelStartBlock.toString(); // using ethers jsonRpcProvider instead of library bcz channels are created on only core chain, that's why block can be fetched from that only @@ -80,45 +109,138 @@ export default function ChannelDetails({ isChannelExpired, setIsChannelExpired, })(); }, [channelDetails]); + React.useEffect(() => { + if (!account) return; + if (!delegatees || !delegatees.length) { + setChannelAddress(account); + } else { + // default the channel address to the first one on the list which should be that of the user if they have a channel + if (onCoreNetwork) setChannelAddress(delegatees[0].channel); + else setChannelAddress(delegatees[0].alias_address); + } + }, [delegatees, account]); + + const channelSettings = useMemo(() => { + if (delegatees) { + const delegatee = delegatees.find(({ channel }) => channel === channelAddress); + if (delegatee) { + const { channel_settings } = delegatee; + if (channel_settings !== null) { + return JSON.parse(channel_settings); + } + } + } + return []; + }, [delegatees, channelAddress]); + + const delegateeList = useMemo(() => { + if (delegatees) { + return delegatees.map(({ channel, alias_address }) => { + return onCoreNetwork ? channel : alias_address; + }); + } + return []; + }); + + const removeDelegate = (walletAddress) => { + return epnsCommWriteProvider.removeDelegate(walletAddress); + }; + + const navigateToNotifSettings = () => { + navigate('/channel/settings'); + }; return ( - + - + {channelDetails.name} {canVerify && } - + {(onCoreNetwork && aliasAddrFromContract && !isAliasVerified) || (!onCoreNetwork && !isAliasVerified) ? ( Alias Network Setup Pending ) : ( - subscount + subscount {channelDetails.subscriber_count} - {channelIsDeactivated && } + {channelIsDeactivated && ( + + )} {channelIsActive ? 'Active' : channelIsDeactivated ? 'Deactivated' : 'Blocked'} - { - channelDetails.channelType == CHANNEL_TYPE["TIMEBOUND"] && !isChannelExpired && - - - Expires on {channelExpiryDate} - - } - { - channelDetails.channelType == CHANNEL_TYPE["TIMEBOUND"] && isChannelExpired && - - - Expired on {channelExpiryDate} - - } + {channelDetails.channelType == CHANNEL_TYPE['TIMEBOUND'] && !isChannelExpired && ( + + + + Expires on {channelExpiryDate} + + + )} + {channelDetails.channelType == CHANNEL_TYPE['TIMEBOUND'] && isChannelExpired && ( + + + + Expired on {channelExpiryDate} + + + )} )} @@ -127,40 +249,49 @@ export default function ChannelDetails({ isChannelExpired, setIsChannelExpired, - {isMobile && - + {isMobile && ( + {!isChannelExpired && onCoreNetwork && Edit Channel} {!isChannelExpired && } - {isChannelExpired && onCoreNetwork && - Delete Channel - } + )} - } + )} - {isChannelExpired && + {isChannelExpired && ( - - Note:{" "} - Channel will auto delete on {" "} - {channelAutomaticExpiryDate} - + + Note: Channel will auto delete on{' '} + {channelAutomaticExpiryDate} + - } + )} + + + {channelDetails.info} + - {channelDetails.info} - - {canVerify && - + {canVerify && ( + verified by: @@ -169,41 +300,99 @@ export default function ChannelDetails({ isChannelExpired, setIsChannelExpired, - } - - {processingState === 0 && + )} + + {processingState === 0 && ( - +
+ + navigateToNotifSettings()} />} + /> + + +
+
+ + showAddDelegateModal()} />} + /> + , + }, + ]} + /> + +
- } + )} + {/* modal to add a delegate */} +
); } const AdaptiveMobileItemVV2 = styled(ItemVV2)` - @media (max-width: 767px) { align-items: center; } -` +`; const DestroyChannelBtn = styled(ButtonV2)` - height: ${props => (props.height || "100%")}; - width: ${props => (props.width || "100%")}; + height: ${(props) => props.height || '100%'}; + width: ${(props) => props.width || '100%'}; `; const AdaptiveMobileItemHV2 = styled(ItemHV2)` @media (max-width: 767px) { justify-content: center; } -` +`; const AdaptiveMobileItemHV22 = styled(ItemHV2)` @media (max-width: 767px) { justify-content: center; flex-direction: column; } -` +`; const ImageSection = styled.img` width: 128px; @@ -239,7 +428,6 @@ const VerifyingName = styled.div``; const Subscribers = styled.div` width: 58px; height: 26px; - margin-bottom: 10px; background: #ffdbf0; color: #cf1c84; border-radius: 25px; @@ -269,7 +457,6 @@ const ChanneStateText = styled(StateText)` color: ${(props) => (props.active ? '#2DBD81' : '#E93636')}; background-color: ${(props) => (props.active ? '#c6efd1' : '#FFD8D8')}; margin-left: 10px; - margin-bottom: 10px; ${(props) => props.active && ` @@ -321,8 +508,8 @@ const Date = styled.div` align-items: flex-start; width: 340px; // color: #657795; - color: ${(props)=>props.theme.default.secondaryColor}; - margin: 10px 0; + color: ${(props) => props.theme.default.secondaryColor}; + margin-top: 10px; text-transform: none; font-weight: 500; font-size: 15px; @@ -351,11 +538,11 @@ const ChannelName = styled.div` font-family: Strawford, Source Sans Pro; flex-direction: row; margin-right: 8px; - margin-top: 40px; + margin-top: 12px; font-weight: 500; font-size: 30px; line-height: 141%; - text-align:center; + text-align: center; color: ${(props) => props.theme.color}; @media (max-width: 767px) { flex-direction: column; @@ -382,18 +569,17 @@ const SectionDes = styled.div` text-transform: none; font-family: Strawford, Source Sans Pro; // color: #657795; - color: ${(props)=>props.theme.default.secondaryColor}; - margin: ${(props) => (props.margin ? props.margin : '25px 0px 40px 0px')}; + color: ${(props) => props.theme.default.secondaryColor}; + margin: ${(props) => (props.margin ? props.margin : '24px 0px')}; font-weight: 400; font-size: 15px; line-height: 140%; - padding: 0px 20px 0px 10px; text-align: left; @media (max-width: 767px) { text-align: center; font-weight: 300; margin-top: 10px; - width:100%; + width: 100%; margin: 10px 0px 10px 0px; padding: 0 0 0 0; } @@ -401,9 +587,9 @@ const SectionDes = styled.div` const SubmitButton = styled(Button)` width: fit-content; - background: #D53A94; + background: #d53a94; color: #fff; - z-Index:0; + z-index: 0; font-family: 'Strawford'; font-style: normal; font-weight: 500; @@ -412,5 +598,15 @@ const SubmitButton = styled(Button)` margin-right: 9px; border-radius: 8px; padding: 10px 16px; - -`; \ No newline at end of file +`; + +const DelegateContainer = styled(Item)` + flex: 5; + min-width: 280px; + align-self: stretch; + align-items: stretch; + margin: 10px 0px 30px 0px; + border-radius: 20px; + border: 1px solid; + border-color: ${(props) => props.theme.default.borderColor}; +`; diff --git a/src/components/ChannelReactivateModalContent.tsx b/src/components/ChannelReactivateModalContent.tsx index 782dd79799..c44c8db9f8 100644 --- a/src/components/ChannelReactivateModalContent.tsx +++ b/src/components/ChannelReactivateModalContent.tsx @@ -530,7 +530,7 @@ const Footer = styled(ItemHV2)` align-content: space-between; justify-content: space-between; grid-gap: 40px; - transform: translateY(40px); + z-index: 1; @media (max-width: 600px) { padding: 16px; diff --git a/src/components/ChannelSettingsDropdown.tsx b/src/components/ChannelSettingsDropdown.tsx index f2f4c315eb..2cf19c86f6 100644 --- a/src/components/ChannelSettingsDropdown.tsx +++ b/src/components/ChannelSettingsDropdown.tsx @@ -13,19 +13,15 @@ import styled, { useTheme } from 'styled-components'; import { postReq } from 'api'; import LoaderSpinner, { LOADER_TYPE } from 'components/reusables/loaders/LoaderSpinner'; import EPNSCoreHelper from 'helpers/EPNSCoreHelper'; -import useModalBlur, {MODAL_POSITION} from 'hooks/useModalBlur'; +import useModalBlur, { MODAL_POSITION } from 'hooks/useModalBlur'; import useToast from 'hooks/useToast'; import { setUserChannelDetails } from 'redux/slices/adminSlice'; import cubeIcon from '../assets/icons/cube.png'; import redBellIcon from '../assets/icons/redBellSlash.png'; import greenBellIcon from '../assets/icons/greenBell.svg'; -import userMinusIcon from '../assets/icons/userCircleMinus.png'; -import userPlusIcon from '../assets/icons/userCirclePlus.png'; -import AddDelegateModalContent from './AddDelegateModalContent'; import AddSubgraphModalContent from './AddSubgraphModalContent'; import ChannelDeactivateModalContent from './ChannelDeactivateModalContent'; import ChannelReactivateModalContent from './ChannelReactivateModalContent'; -import RemoveDelegateModalContent from './RemoveDelegateModalContent'; // Internal Configs import { abis, addresses, appConfig } from 'config'; @@ -66,16 +62,6 @@ function ChannelSettings({ DropdownRef, isDropdownOpen, closeDropdown }: Channel showModal: showReactivateChannelModal, ModalComponent: ReactivateChannelModalComponent, } = useModalBlur(); - const { - isModalOpen: isAddDelegateModalOpen, - showModal: showAddDelegateModal, - ModalComponent: AddDelegateModalComponent, - } = useModalBlur(); - const { - isModalOpen: isRemoveDelegateModalOpen, - showModal: showRemoveDelegateModal, - ModalComponent: RemoveDelegateModalComponent, - } = useModalBlur(); const { isModalOpen: isAddSubgraphModalOpen, showModal: showAddSubgraphModal, @@ -87,8 +73,6 @@ function ChannelSettings({ DropdownRef, isDropdownOpen, closeDropdown }: Channel isDropdownOpen && !isDeactivateChannelModalOpen && !isReactivateChannelModalOpen && - !isAddDelegateModalOpen && - !isRemoveDelegateModalOpen && !isAddSubgraphModalOpen; useClickAway(DropdownRef, () => closeDropdownCondition && closeDropdown()); @@ -156,16 +140,6 @@ function ChannelSettings({ DropdownRef, isDropdownOpen, closeDropdown }: Channel */ const deactivateChannel = () => epnsWriteProvider.deactivateChannel(); - const addDelegateToast = useToast(); - const addDelegate = async (walletAddress: string) => { - return epnsCommWriteProvider.addDelegate(walletAddress); - }; - - const removeDelegateToast = useToast(); - const removeDelegate = (walletAddress: string) => { - return epnsCommWriteProvider.removeDelegate(walletAddress); - }; - const addSubgraphToast = useToast(); const addSubgraphDetails = async (pollTime, subGraphId) => { // design not present to show below cases @@ -220,34 +194,6 @@ function ChannelSettings({ DropdownRef, isDropdownOpen, closeDropdown }: Channel )} - !channelInactive && showAddDelegateModal()} - > -
- -
- Add Delegate -
- - - !channelInactive && showRemoveDelegateModal()} - > -
- -
- Remove Delegate -
- - {onCoreNetwork && ( - {/* modal to add a delegate */} - - - {/* modal to remove a delegate */} - - {/* modal to add a subgraph */} { +const DelegateInfo = ({ delegateAddress, maxWidth }) => { const [addressText, setAddressText] = useState(delegateAddress); const [isCopied, setIsCopied] = useState(false); const isMobile = useDeviceWidthCheck(1200); @@ -26,27 +25,14 @@ const DelegateInfo = ({ delegateAddress, isDelegate, maxWidth }) => { }, [isMobile]); return ( - <> - {!isDelegate ? ( - setIsCopied(false)} - minWidth={!isMobile ? "350px" : "120px"} - > - - - ) : ( - setIsCopied(false)} - minWidth={!isMobile ? "350px" : "120px"} - > - - - )} - + setIsCopied(false)} + minWidth={!isMobile ? "350px" : "120px"} + > + + ); }; @@ -57,6 +43,14 @@ const WalletInfoContent = ({ delegateAddress, }) => { const isMobile = useDeviceWidthCheck(1000); + const [isHovered, setIsHovered] = useState(false); + + const handleMouseOut = (e) => { + setIsHovered(false); + } + const handleMouseOver = (e) => { + setIsHovered(true); + } return (
-
{addressText}
- {shortenText(addressText, 7, 7)} + {isHovered && { navigator.clipboard.writeText(delegateAddress); @@ -80,7 +76,7 @@ const WalletInfoContent = ({ ) : ( )} - + }
); }; @@ -94,7 +90,7 @@ const WalletAddressDisplay = styled.span` flex: 3; // margin-right: 30px; // margin-left: 10px; - padding: 6px 25px; + padding: 0px 15px; max-height: 30px; display: flex; align-items: baseline; @@ -134,10 +130,11 @@ const HoverWallet = styled(WalletAddressDisplay)` } `; -const Wallet = styled(WalletAddressDisplay)` - color: #fff; - background: rgb(226, 8, 128); - background: linear-gradient(87.17deg, #B6A0F5 0%, #F46EF7 57.29%, #FF95D5 100%); +const Address = styled.div` + padding-top: 3px; + font-size: 15px; + font-weight: 400; + &:hover { opacity: 0.9; cursor: pointer; diff --git a/src/components/FaucetInfo.tsx b/src/components/FaucetInfo.tsx index 0f7f91be66..d511bbb399 100644 --- a/src/components/FaucetInfo.tsx +++ b/src/components/FaucetInfo.tsx @@ -110,7 +110,8 @@ const FaucetInfo = ({ onMintPushToken, noOfPushTokensToCheck, containerProps }: }; const Container = styled.div` -width:100%; + width:100%; + transform: translateY(-40px); ` const TextSpace = styled.div` @@ -125,6 +126,8 @@ const TextSpace = styled.div` border-radius: 0px 0px 28px 28px; padding: 32px 32px 20px 32px; margin-top:24px; + margin-bottom: -40px; + @media ${device.tablet} { width: 100%; flex-direction: column; diff --git a/src/components/RemoveDelegateModalContent.tsx b/src/components/RemoveDelegateModalContent.tsx deleted file mode 100644 index 0c3d061908..0000000000 --- a/src/components/RemoveDelegateModalContent.tsx +++ /dev/null @@ -1,112 +0,0 @@ -// React + Web3 Essentials -import React from 'react'; - -// External Packages -import styled, { useTheme } from 'styled-components'; -import { useClickAway } from 'react-use'; -import { MdCheckCircle, MdError } from 'react-icons/md'; - -// Internal Components -import ModalHeader from 'primaries/SharedModalComponents/ModalHeader'; -import ModalInput from 'primaries/SharedModalComponents/ModalInput'; -import ModalConfirmButton from 'primaries/SharedModalComponents/ModalConfirmButton'; -import { ModalInnerComponentType } from 'hooks/useModalBlur'; - -// Internal Configs -import { device } from 'config/Globals'; - -const RemoveDelegateModalContent = ({ - onConfirm: removeDelegate, - onClose, - toastObject, -}: ModalInnerComponentType) => { - const delegateAddressInputRef = React.useRef(); - - const [isLoading, setIsLoading] = React.useState(false); - - const theme = useTheme(); - - const handleClose = () => !isLoading && onClose(); - - // to close the modal upon a click on backdrop - const containerRef = React.useRef(null); - useClickAway(containerRef, () => handleClose()); - - const removeDelegateHandler = () => { - const delegateAddress = delegateAddressInputRef?.current?.value; - - setIsLoading(true); - - removeDelegate(delegateAddress) - .then(async (tx) => { - console.log(tx); - - toastObject.showMessageToast({ - toastTitle: 'Delegate Removed', - toastMessage: 'Delegate has been removed successfully', - toastType: 'SUCCESS', - getToastIcon: (size) => ( - - ), - }); - onClose(); - }) - .catch((err) => { - console.log({ err }); - - toastObject.showMessageToast({ - toastTitle: 'Transaction Failed', - toastMessage: 'Removing a delegate failed.', - toastType: 'ERROR', - getToastIcon: (size) => ( - - ), - }); - }) - .finally(() => { - setIsLoading(false); - }); - }; - - return ( - - - - - - ); -}; - -const ModalContainer = styled.div` - width: 30vw; - display: flex; - flex-direction: column; - margin: 6% 1%; - background: ${(props) => props.theme.modalContentBackground}; - border-radius: 1rem; - padding: 1.2% 2%; - @media (${device.laptop}) { - width: 50vw; - } - @media (${device.mobileL}) { - width: 95vw; - } -`; - -export default RemoveDelegateModalContent; diff --git a/src/components/SendNotifications.tsx b/src/components/SendNotifications.tsx index 676cd842ba..9c2be83280 100644 --- a/src/components/SendNotifications.tsx +++ b/src/components/SendNotifications.tsx @@ -17,7 +17,7 @@ import styled, { useTheme } from 'styled-components'; import * as PushAPI from '@pushprotocol/restapi'; import { postReq } from 'api'; import LoaderSpinner, { LOADER_TYPE } from 'components/reusables/loaders/LoaderSpinner'; -import { SectionV2, AInlineV2 } from 'components/reusables/SharedStylingV2'; +import { AInlineV2, SectionV2 } from 'components/reusables/SharedStylingV2'; import { convertAddressToAddrCaip } from 'helpers/CaipHelper'; import CryptoHelper from 'helpers/CryptoHelper'; import { IPFSupload } from 'helpers/IpfsHelper'; diff --git a/src/components/SharedStyling.js b/src/components/SharedStyling.js index a01622ec83..81577a3695 100644 --- a/src/components/SharedStyling.js +++ b/src/components/SharedStyling.js @@ -57,12 +57,10 @@ export const Item = styled.div` flex: ${props => props.flex || '1'}; flex-basis: ${props => props.flexBasis || 'auto'}; flex-direction: ${props => props.direction || 'column'}; - flex-direction: column; flex-wrap: ${props => props.wrap || 'wrap'}; font-size: ${props => props.size || 'inherit'}; height: ${props => props.height || 'auto'}; justify-content: ${props => props.justify || 'center'}; - justify-content: center; left: ${props => props.left || 'auto'}; margin: ${props => props.margin || '0px'}; diff --git a/src/components/ShowDelegates.jsx b/src/components/ShowDelegates.jsx deleted file mode 100644 index e00fb44480..0000000000 --- a/src/components/ShowDelegates.jsx +++ /dev/null @@ -1,280 +0,0 @@ -// React + Web3 Essentials -import React, { useEffect, useState } from 'react'; - -// External Packages -import { AiOutlineUserDelete } from 'react-icons/ai'; -import { GoTriangleDown, GoTriangleUp } from 'react-icons/go'; -import { useSelector } from 'react-redux'; -import styled, { css, useTheme } from 'styled-components'; - -// Internal Compoonents -import { getReq } from 'api'; -import { ButtonV2 } from 'components/reusables/SharedStylingV2'; -import { convertAddressToAddrCaip } from 'helpers/CaipHelper'; -import { useAccount, useDeviceWidthCheck } from 'hooks'; -import useModalBlur, {MODAL_POSITION} from 'hooks/useModalBlur'; -import useToast from 'hooks/useToast'; -import { Button, Content, H2, H3, Item, Section, Span } from 'primaries/SharedStyling'; -import { getChannelDelegates } from 'services'; -import DelegateInfo from './DelegateInfo'; -import RemoveDelegateModalContent from './RemoveDelegateModalContent'; - -const isOwner = (account, delegate) => { - return account?.toLowerCase() !== delegate?.toLowerCase(); -}; - -const ShowDelegates = () => { - const { account, chainId } = useAccount(); - const [delegatees, setDelegatees] = React.useState([account]); - const theme = useTheme(); - const [isActiveDelegateDropdown, setIsActiveDelegateDropdown] = React.useState(true); - const [removeModalOpen, setRemoveModalOpen] = React.useState(false); - const [delegateToBeRemoved, setDelegateToBeRemoved] = React.useState(''); - const { epnsCommWriteProvider } = useSelector((state) => state.contracts); - const isMobile = useDeviceWidthCheck(700); - - const { - isModalOpen: isRemoveDelegateModalOpen, - showModal: showRemoveDelegateModal, - ModalComponent: RemoveDelegateModalComponent, - } = useModalBlur(); - - const removeDelegateToast = useToast(); - const removeDelegate = (walletAddress) => { - return epnsCommWriteProvider.removeDelegate(walletAddress); - }; - - useEffect(() => { - if(account) fetchDelegatees(); - }, [account]); - - const fetchDelegatees = async () => { - try { - const channelAddressinCAIP = convertAddressToAddrCaip(account, chainId); - const channelDelegates = await getChannelDelegates({ channelCaipAddress: channelAddressinCAIP }); - if (channelDelegates) { - const delegateeList = channelDelegates.map((delegate) => delegate); - delegateeList.unshift(account); - setDelegatees(delegateeList); - } - } catch (err) { - console.error(err); - } - }; - - const removeDelegateModalOpen = (delegateAddress) => { - setDelegateToBeRemoved(delegateAddress); - setRemoveModalOpen(true); - }; - - return ( - <> -
- - - Channel Delegates -
- Delegates that can send notifications on behalf of this channel. - - -
- - - {isActiveDelegateDropdown && delegatees && ( - - {delegatees.map((delegate, idx) => { - return ( - - - {isOwner(account, delegate) ? ( - - ) : ( - Channel Creator - )} - - ); - })} - - )} - - - - ); -}; - -const RemoveButton = ({ delegateAddress, removeDelegateModalOpen, showRemoveDelegateModal }) => { - const theme = useTheme(); - const [isHovered, setIsHovered] = useState(false); - - const handleMouseOver = () => { - setIsHovered(true); - }; - - const handleMouseOut = () => { - setIsHovered(false); - }; - - return ( - showRemoveDelegateModal()} - > - {isHovered ? ( -
- -
-
Remove Delegate
-
- ) : ( - Delegate - )} - - ); -}; - -const TextStyle = styled.div` - color: ${(props) => props.theme.default.secondaryColor}; - text-align: right; - width: 100%; -`; - -const ChannelActionButton = styled.button` - border: 0; - outline: 0; - display: flex; - align-items: center; - justify-content: center; - padding: 8px 15px; - 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 DelegateContainer = styled(Item)` - flex: 5; - min-width: 280px; - align-self: stretch; - align-items: stretch; - margin: 10px 0px 30px 0px; - border-radius: 20px; - border: 1px solid; - border-color: ${(props) => props.theme.default.borderColor}; -`; - -const RemoveButtonUI = styled(ChannelActionButton)` - background: transparent; - color: ${(props) => props.theme.color}; - height: 36px; - max-width: 164px; - flex: 1; - font-style: normal; - font-weight: 600; - font-size: 14px; - line-height: 141%; - display: flex; - align-items: center; - text-align: right; - padding: 6px 10px 6px 9px; - gap: 5px; - - &:hover { - opacity: 0.9; - background: #e93636; - border-radius: 8px; - color: #fff; - } - cursor: pointer; -`; - -const OwnerButton = styled(Button)` - all: unset; - background: transparent; - font-weight: 500; - font-size: 16px; - color: #cf1c84; - cursor: auto; - - @media (max-width: 425px) { - font-weight: 400; - font-size: 14px; - } -`; - -const DelegatesInfoHeader = styled.div` - font-weight: 600; - font-size: 18px; - line-height: 150%; - display: flex; - align-items: center; - color: ${(props) => props.theme.color}; -`; - -const DelegatesInfoLabel = styled.div` - font-weight: 400; - font-size: 15px; - line-height: 140%; - // color: #657795; - color: ${(props) => props.theme.default.secondaryColor}; -`; - -export default ShowDelegates; diff --git a/src/components/StakingInfo.tsx b/src/components/StakingInfo.tsx index 40f7666d3e..ec6405f900 100644 --- a/src/components/StakingInfo.tsx +++ b/src/components/StakingInfo.tsx @@ -56,7 +56,7 @@ const StakingInfo = ({channelStakeFees, setStakeFeesChoosen, setProcessingInfo, {/* */} - +

Amount for Staking @@ -115,7 +115,7 @@ const TabSpace = styled.div` border-radius: 20px; background-color: #f4f5fa; align-items: center; - transform: translateY(40px); + z-index: 1; @media ${device.tablet} { width: 100%; diff --git a/src/components/channel/AddSettingModalContent.tsx b/src/components/channel/AddSettingModalContent.tsx new file mode 100644 index 0000000000..bebae1afd3 --- /dev/null +++ b/src/components/channel/AddSettingModalContent.tsx @@ -0,0 +1,369 @@ +// React + Web3 Essentials +import React, { useState } from 'react'; + +// External Packages +import styled, { useTheme } from 'styled-components'; +import { useClickAway } from 'react-use'; +import { MdClose } from 'react-icons/md'; + +// Internal Components +import ModalConfirmButton from 'primaries/SharedModalComponents/ModalConfirmButton'; +import { ModalInnerComponentType } from 'hooks/useModalBlur'; +import type { ChannelSetting } from '../../helpers/channel/types'; + +// Internal Configs +import { device } from 'config/Globals'; +import { Item } from 'components/SharedStyling'; +import { FormSubmision, Input, Span } from 'primaries/SharedStyling'; +import { IOSSwitch } from 'components/SendNotifications'; +import { isAllFilledAndValid } from 'helpers/channel/InputValidation'; + +const ToggleItem = ({ checked, onChange, label, description }) => { + return ( + + + + + {description} + + + + + ); +}; + +interface AddSettingModalProps extends Omit { + InnerComponentProps?: { + settingToEdit?: ChannelSetting; + }; +} + +const AddSettingModalContent = ({ + onConfirm: onSubmit, + onClose, + toastObject, + InnerComponentProps, +}: AddSettingModalProps) => { + const settingToEdit = InnerComponentProps?.settingToEdit || undefined; + const [isLoading, setIsLoading] = useState(false); + const [settingName, setSettingName] = useState(settingToEdit ? settingToEdit.description : ''); + const [isDefault, setIsDefault] = useState(settingToEdit && settingToEdit.default === true); + const [isRange, setIsRange] = useState(settingToEdit && settingToEdit.type === 2 ? true : false); + const [lowerLimit, setLowerLimit] = useState( + settingToEdit && settingToEdit.type === 2 ? settingToEdit.lowerLimit.toString() : '' + ); + const [upperLimit, setUpperLimit] = useState( + settingToEdit && settingToEdit.type === 2 ? settingToEdit.upperLimit.toString() : '' + ); + const [defaultValue, setDefaultValue] = useState( + settingToEdit && settingToEdit.default ? settingToEdit.default.toString() : '' + ); + const [errorInfo, setErrorInfo] = useState(); + + const theme = useTheme(); + + const handleClose = () => !isLoading && onClose(); + + const containerRef = React.useRef(null); + useClickAway(containerRef, () => handleClose()); + + const onConfirm = (event) => { + event.preventDefault(); + setIsLoading(true); + if ( + isAllFilledAndValid({ + setErrorInfo, + defaultValue, + settingName, + lowerLimit, + type: isRange ? 2 : 1, + upperLimit, + }) + ) { + const index = settingToEdit ? settingToEdit.index : Math.floor(Math.random() * 1000000); + const settingData: ChannelSetting = isRange + ? { + type: 2, + default: Number(defaultValue), + description: settingName, + index: index, + lowerLimit: Number(lowerLimit), + upperLimit: Number(upperLimit), + } + : { + type: 1, + default: isDefault, + description: settingName, + index: index, + }; + onSubmit(settingData); + onClose(); + } + setIsLoading(false); + }; + + return ( + + + + {settingToEdit ? 'Edit ' : 'Add a '} Setting + + + + + {50 - settingName.length} + + + { + setSettingName(e.target.value.slice(0, 50)); + setErrorInfo((prev) => ({ ...prev, settingName: undefined })); + }} + autocomplete="off" + hasError={errorInfo?.settingName ? true : false} + /> + {errorInfo?.settingName} + + setIsDefault((prev) => !prev)} + label="Set as default" + description="Setting turned on for users by default" + /> + setIsRange((prev) => !prev)} + label="Range" + description="Set a range for this setting e.g. 1-10" + /> + {isRange && ( + <> + + + + { + setLowerLimit(e.target.value); + setErrorInfo((prev) => ({ ...prev, lowerLimit: undefined })); + }} + autocomplete="off" + hasError={errorInfo?.lowerLimit ? true : false} + /> + + { + setUpperLimit(e.target.value); + setErrorInfo((prev) => ({ ...prev, upperLimit: undefined })); + }} + autocomplete="off" + hasError={errorInfo?.upperLimit ? true : false} + /> + + {errorInfo?.lowerLimit} + {errorInfo?.upperLimit} + + + + { + setDefaultValue(e.target.value); + setErrorInfo((prev) => ({ ...prev, default: undefined })); + }} + autocomplete="off" + hasError={errorInfo?.default ? true : false} + /> + {errorInfo?.default} + + + )} + + + + ); +}; + +const CloseButton = styled(MdClose)` + align-self: flex-end; + color: ${(props) => props.theme.default.secondaryColor}; + font-size: 20px; +`; + +const ModalTitle = styled.div` + font-size: 24px; + font-weight: 500; + line-height: 29px; + letter-spacing: -0.02em; + text-align: center; + color: ${(props) => props.theme.default.color}; +`; + +const ModalContainer = styled.div` + width: 30vw; + display: flex; + flex-direction: column; + margin: 6% 1%; + background: ${(props) => props.theme.modalContentBackground}; + border-radius: 1rem; + padding: 1.2% 2%; + @media (${device.laptop}) { + width: 50vw; + } + @media (${device.mobileL}) { + width: 95vw; + } +`; + +const Label = styled.div<{ padding?: string }>` + font-style: normal; + font-weight: 500; + font-size: 16px; + line-height: 150%; + letter-spacing: -0.011em; + color: ${(props) => props.theme.default.color}; + padding: ${(props) => props.padding || '0px'}; +`; + +const Description = styled.div` + font-size: 12px; + font-weight: 400; + line-height: 16px; + letter-spacing: 0em; + text-align: left; + color: ${(props) => props.theme.default.secondaryColor}; +`; + +const MaxWidthInput = styled(Input)<{ hasError: boolean }>` + max-width: 108px; + flex: 1; + border: ${(props) => + props.hasError ? `1px solid ${props.theme.nfsError}` : `1px solid ${props.theme.default.borderColor}`}; +`; + +const InputWithError = styled(Input)<{ hasError: boolean }>` + flex: 1; + border: ${(props) => + props.hasError ? `1px solid ${props.theme.nfsError}` : `1px solid ${props.theme.default.borderColor}`}; +`; + +const ErrorInfo = styled.span` + font-size: 12px; + font-weight: 500; + line-height: 18px; + letter-spacing: 0em; + text-align: left; + color: ${(props) => props.theme.nfsError}; + margin-top: 4px; +`; + +export default AddSettingModalContent; diff --git a/src/components/channel/ChannelButtons.tsx b/src/components/channel/ChannelButtons.tsx new file mode 100644 index 0000000000..53b2a2602b --- /dev/null +++ b/src/components/channel/ChannelButtons.tsx @@ -0,0 +1,86 @@ +// External Packages +import { AiOutlinePlus } from 'react-icons/ai'; +import { FiSettings } from 'react-icons/fi'; +import styled from 'styled-components'; + +// Internal Components +import { Button } from 'components/SharedStyling'; + +interface ChannelButtonProps { + onClick: () => void; +} + +export const AddDelegateButton = ({ onClick }: ChannelButtonProps) => { + return ( + + + Add Delegate + + ); +}; + +export const ManageSettingsButton = ({ onClick }: ChannelButtonProps) => { + return ( + + + Manage Settings + + ); +}; + +export const ModifySettingsButton = ({ onClick }: ChannelButtonProps) => { + return ( + + Modify Settings + + ); +}; + +export const AddSettingButton = ({ onClick }: ChannelButtonProps) => { + return ( + + + Add Setting + + ); +}; + +const ChannelButton = styled(Button)` + height: 36px; + background: ${(props) => props.theme.default.primaryPushThemeTextColor}; + color: #fff; + z-index: 0; + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 17px; + border-radius: 8px; + padding: 4px 12px 4px 12px; +`; + +const ChannelButtonWhite = styled.button` + height: 36px; + border: 1px solid ${(props) => props.theme.default.borderColor}; + background: transparent; + color: white; + z-index: 0; + font-style: normal; + font-weight: 500; + font-size: 14px; + line-height: 17px; + border-radius: 8px; + padding: 4px 12px 4px 12px; + cursor: pointer; +`; + +const ButtonText = styled.span` + margin-left: 8px; +`; + +const TransparentButtonText = styled.span` + color: ${(props) => props.theme.default.color}; +`; + +const AddButtonIcon = styled(AiOutlinePlus)` + font-size: 16px; +`; diff --git a/src/components/channel/ChannelInfoHeader.tsx b/src/components/channel/ChannelInfoHeader.tsx new file mode 100644 index 0000000000..c4aec9368e --- /dev/null +++ b/src/components/channel/ChannelInfoHeader.tsx @@ -0,0 +1,69 @@ +// React + Web3 Essentials +import React, { CSSProperties } from 'react'; + +// External Packages +import styled, { useTheme } from 'styled-components'; + +// Internal Compoonents +import { useDeviceWidthCheck } from 'hooks'; +import { Item } from 'primaries/SharedStyling'; +import { Section } from 'components/SharedStyling'; + +// Internal Configs +import { device } from 'config/Globals'; + +interface ChannelInfoHeaderProps { + title: string; + description: string; + Button?: React.ReactNode; + style?: CSSProperties; +} + +const ChannelInfoHeader = ({ title, description, Button, style }: ChannelInfoHeaderProps) => { + const theme = useTheme(); + const isMobile = useDeviceWidthCheck(700); + + return ( + + + {title} + {!isMobile && ( + <> +

+ {description} + + )} + + {Button} + + ); +}; + +export default ChannelInfoHeader; + +const DelegatesInfoHeader = styled.div` + font-weight: 600; + font-size: 18px; + line-height: 150%; + display: flex; + align-items: center; + color: ${(props) => props.theme.color}; +`; + +const DelegatesInfoLabel = styled.div` + font-weight: 400; + font-size: 15px; + line-height: 140%; + color: ${(props) => props.theme.default.secondaryColor}; +`; + +const HeaderSection = styled(Section)` + flex-direction: row; + align-items: center; + padding: 24px 24px 20px 24px; + + @media ${device.tablet} { + padding: 20px 12px; + flex: 0; + } +`; diff --git a/src/components/channel/ChannelInfoList.tsx b/src/components/channel/ChannelInfoList.tsx new file mode 100644 index 0000000000..84210415cf --- /dev/null +++ b/src/components/channel/ChannelInfoList.tsx @@ -0,0 +1,207 @@ +// React + Web3 Essentials +import React, { CSSProperties } from 'react'; + +// External Packages +import styled from 'styled-components'; +import { useNavigate } from 'react-router-dom'; + +// Internal Compoonents +import { Item } from 'primaries/SharedStyling'; +import DelegateInfo from 'components/DelegateInfo'; +import LoaderSpinner, { LOADER_TYPE } from 'components/reusables/loaders/LoaderSpinner'; +import Icon from 'assets/navigation/receiveNotifOffIcon.svg'; +import { ImageV2 } from 'components/reusables/SharedStylingV2'; +import { ModifySettingsButton } from './ChannelButtons'; +import DelegateSettingsDropdown, { ChannelDropdownOption } from './DelegateSettingsDropdown'; + +// Internal Configs +import { device } from 'config/Globals'; +import { ChannelSetting } from 'helpers/channel/types'; + +const isOwner = (account: string, delegate: string) => { + return account.toLowerCase() === delegate.toLowerCase(); +}; + +type ChannelInfoListProps = + | { + isAddress: true; + items: string[]; + isLoading: boolean; + account: string; + style?: CSSProperties; + addressDropdownOptions: Array; + } + | { + isAddress: false; + items: Array; + isLoading: boolean; + account: string; + style?: CSSProperties; + settingsDropdownOptions?: Array; + }; + +const ChannelInfoList = (props: ChannelInfoListProps) => { + const navigate = useNavigate(); + + const handleNavigateToModifySettings = () => { + navigate(`/channel/settings`); + }; + + return ( + + + {props.isLoading ? ( + + + + ) : ( + <> + {props.items && + props.items.length > 0 && + props.items.map((item) => { + return ( +
+ + + + {props.isAddress ? ( + + + + ) : ( + <> + {item.description} + {item.lowerLimit !== undefined && Range} + + )} + {props.isAddress && isOwner(props.account, item) && Creator} + + {props.isAddress === true && + props.addressDropdownOptions?.length > 0 && + !isOwner(props.account, item) && ( + + )} + {props.isAddress === false && props.settingsDropdownOptions?.length > 0 && ( + + )} + +
+ ); + })} + {props.items && props.items.length === 0 && !props.isAddress && ( + + + No settings added + + Add settings for users to customize their notification preferences. + + + + )} + + )} +
+
+ ); +}; + +export default ChannelInfoList; + +const DelegatesList = styled.div<{ isLoading: boolean }>` + padding: ${(props) => (props.isLoading ? '0px' : '0px 24px 16px')}; + flex: 1; + + @media ${device.tablet} { + flex: 0; + padding: ${(props) => (props.isLoading ? '0px' : '0px 16px 10px')}; + } +`; + +const Tag = styled.div` + padding: 4px 8px 4px 8px; + border-radius: 4px; + background-color: ${(props) => props.theme.default.secondaryBg}; + color: ${(props) => props.theme.tooltipContentDesc}; + font-size: 10px; + margin-left: 8px; +`; + +const NotificationSettingName = styled.span` + margin-left: 15px; + color: ${(props) => + props.theme.scheme === 'light' ? props.theme.default.color : props.theme.default.secondaryColor}; +`; + +const EmptyNotificationSetting = styled.div` + border-top: ${(props) => `1px solid ${props.theme.default.borderColor}`}; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + margin-bottom: 16px; +`; + +const EmptyNotificationTitle = styled.div` + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0em; + text-align: left; + color: ${(props) => props.theme.default.color}; +`; + +const EmptyNotificationDesc = styled.div` + margin-top: 1px; + margin-bottom: 16px; + color: ${(props) => props.theme.default.secondaryColor}; +`; + +const NotifIcon = styled(ImageV2)` + color: ${(props) => props.theme.default.color}; + margin-top: 32px; + margin-bottom: 12px; +`; + +const Divider = styled.div` + background-color: ${(props) => props.theme.default.border}; + height: 1px; +`; + +const SpinnerContainer = styled.div` + height: 100px; +`; + +const DelegateInfoContainer = styled.div` + @media ${device.tablet} { + margin: 0px 0px 0px 5px; + } +`; diff --git a/src/components/channel/DelegateSettingsDropdown.tsx b/src/components/channel/DelegateSettingsDropdown.tsx new file mode 100644 index 0000000000..dfea1b26a3 --- /dev/null +++ b/src/components/channel/DelegateSettingsDropdown.tsx @@ -0,0 +1,85 @@ +// React + Web3 Essentials +import React, { useRef, useState } from 'react'; + +// External Packages +import { AiOutlineMore } from 'react-icons/ai'; +import styled from 'styled-components'; +import { useClickAway } from 'react-use'; + +export interface ChannelDropdownOption { + icon: React.ReactNode; + text: string; + onClick: (item) => void; +} + +interface DelegateSettingsDropdownProps { + options: Array; + item: string; +} + +const DelegateSettingsDropdown = ({ options, item }: DelegateSettingsDropdownProps) => { + const [isOpen, setIsOpen] = useState(false); + const dropdownRef = useRef(); + + useClickAway(dropdownRef, () => setIsOpen(false)); + + return ( +
+ setIsOpen(true)} /> + {isOpen && ( + setIsOpen(false)} ref={dropdownRef}> + {options.map(({ icon, onClick, text }, index) => { + return ( + onClick(item)} + key={index} + index={index} + > + {icon} + {text} + + ); + })} + + )} +
+ ); +}; + +export default DelegateSettingsDropdown; + +const MoreButtonUI = styled(AiOutlineMore)` + background: transparent; + display: flex; + cursor: pointer; + width: 24px; + height: 24px; + padding: 0px; + position: relative; + width: 24px; + height: 24px; + color: ${(props) => props.theme.default.color}; +`; + +const ListContainer = styled.div` + padding: 10px 6px; + width: 119px; + border-radius: 8px; + border: 1px solid ${(props) => props.theme.default.border}; + position: absolute; + top: 3px; + right: 0px; + background-color: ${(props) => props.theme.default.bg}; + z-index: 2; +`; + +const OptionButton = styled.div<{ index: number }>` + cursor: pointer; + display: flex; + flex-direction: row; + margin-top: ${(props) => (props.index === 0 ? '0px' : '16px')}; +`; + +const OptionText = styled.span` + margin-left: 8px; +`; diff --git a/src/components/channel/DepositFeeFooter.tsx b/src/components/channel/DepositFeeFooter.tsx new file mode 100644 index 0000000000..30f49a8480 --- /dev/null +++ b/src/components/channel/DepositFeeFooter.tsx @@ -0,0 +1,309 @@ +// React + Web3 Essentials +import React, { useEffect, useState } from 'react'; + +// External Packages +import styled from 'styled-components'; +import { useSelector } from 'react-redux'; +import { MdCheckCircle, MdError } from 'react-icons/md'; + +// Internal Compoonents +import { ItemHV2, ItemVV2 } from 'components/reusables/SharedStylingV2'; +import FaucetInfo from 'components/FaucetInfo'; +import useToast from 'hooks/useToast'; +import { useAccount } from 'hooks'; + +// Internal Configs +import GLOBALS, { device } from 'config/Globals'; +import { Button } from '../SharedStyling'; +import { LOADER_SPINNER_TYPE } from 'components/reusables/loaders/LoaderSpinner'; +import Spinner from 'components/reusables/spinners/SpinnerUnit'; +import VerifyLogo from '../../assets/Vector.svg'; +import { approvePushToken, getPushTokenApprovalAmount, mintPushToken } from 'helpers'; +import { addresses } from 'config'; + +interface DepositFeeFooterProps { + title: string; + description: string; + onCancel: () => void; + disabled: boolean; + onClick: () => void; + feeRequired: number; +} + +const DepositFeeFooter = ({ title, description, onCancel, disabled, onClick, feeRequired }: DepositFeeFooterProps) => { + const { account, provider } = useAccount(); + const [pushApprovalAmount, setPushApprovalAmount] = useState(0); + const [pushDeposited, setPushDeposited] = useState(false); + const [isLoading, setIsLoading] = useState(false); + + const depositFeeToast = useToast(); + + useEffect(() => { + if (!account || !provider) return; + + (async function () { + const pushTokenApprovalAmount = await getPushTokenApprovalAmount({ + address: account, + provider: provider, + contractAddress: addresses.epnscore, + }); + setPushApprovalAmount(parseInt(pushTokenApprovalAmount)); + const amountToBeDeposit = parseInt(pushTokenApprovalAmount); + + if (amountToBeDeposit >= feeRequired && amountToBeDeposit != 0) { + setPushDeposited(true); + } else { + setPushDeposited(false); + } + })(); + }, [account, provider]); + + const depositPush = async () => { + setIsLoading(true); + if (!provider) return; + const signer = provider.getSigner(account); + depositFeeToast.showLoaderToast({ loaderMessage: 'Waiting for Confirmation...' }); + try { + const response = await approvePushToken({ + signer, + contractAddress: addresses.epnscore, + amount: feeRequired - pushApprovalAmount, + }); + console.log('response', response); + if (response) { + setIsLoading(false); + setPushApprovalAmount(feeRequired); + setPushDeposited(true); + depositFeeToast.showMessageToast({ + toastTitle: 'Success', + toastMessage: 'Successfully approved Push!', + toastType: 'SUCCESS', + getToastIcon: (size) => ( + + ), + }); + } + } catch (err) { + console.log(err); + if (err.code == 'ACTION_REJECTED') { + // EIP-1193 userRejectedRequest error + depositFeeToast.showMessageToast({ + toastTitle: 'Error', + toastMessage: `User denied message signature.`, + toastType: 'ERROR', + getToastIcon: (size) => ( + + ), + }); + } else { + depositFeeToast.showMessageToast({ + toastTitle: 'Error', + toastMessage: `There was an error in approving PUSH Token`, + toastType: 'ERROR', + getToastIcon: (size) => ( + + ), + }); + + console.log('Error --> %o', err); + console.log({ err }); + } + } + setIsLoading(false); + }; + + return ( + <> +
+
+ {title} + {description} +
+ + {pushDeposited ? : null} + {feeRequired} PUSH + +
+ { + await mintPushToken({ noOfTokens, provider, account }); + }} + /> + + {isLoading ? ( + <> + {/* Verifying Spinner and Text */} + + + Verifying Transaction + + + ) : ( + <> + {/* This below is Footer Buttons i.e, Cancel and save changes */} + + + Cancel + + + {pushApprovalAmount >= feeRequired ? ( + + Save Changes + + ) : ( + + Approve PUSH + + )} + + + )} + + ); +}; + +export default DepositFeeFooter; + +const TickImage = styled.img``; + +const Footer = styled(ItemVV2)` + background: ${(props) => props.theme.editFooterBg}; + border-radius: 20px; + padding: 23px 32px; + display: grid; + grid-auto-flow: column; + align-content: space-between; + justify-content: space-between; + grid-gap: 40px; + height: 100px; + align-items: center; + z-index: 1; + + @media ${device.tablet} { + padding: 16px; + flex: 0; + } + + @media ${device.mobileL} { + margin: 0px; + } +`; + +const FooterPrimaryText = styled.p` + margin: 0px; + color: ${(props) => props.theme.editChannelPrimaryText}; + font-style: normal; + font-weight: 500; + font-size: 20px; + line-height: 24px; +`; + +const FooterSecondaryText = styled.p` + font-size: 12px; + margin: 0px; + font-weight: 400; + line-height: 130%; + color: ${(props) => props.theme.editChannelSecondaryText}; +`; + +const EditFee = styled.p` + margin: 0px 0px 0px 5px; + color: ${(props) => props.theme.viewChannelSecondaryText}; + font-style: normal; + font-weight: 500; + font-size: 20px; + line-height: 24px; +`; + +const VerifyingContainer = styled(ItemVV2)` + flex-direction: row; + margin-top: 33px; + + @media ${device.tablet} { + flex: 0; + } +`; + +const TransactionText = styled.p` + font-style: normal; + font-weight: 500; + font-size: 18px; + line-height: 22px; + display: flex; + align-items: center; + margin-left: 12px; + color: ${(props) => props.theme.editChannelPrimaryText}; +`; + +const ButtonContainer = styled(ItemHV2)` + justify-content: end; + margin-top: 24px; + @media ${device.mobileL} { + flex-direction: column-reverse; + flex: 0; + } +`; + +const FooterButtons = styled(Button)<{ disabled: boolean }>` + font-style: normal; + font-weight: 500; + font-size: 18px; + line-height: 22px; + display: flex; + border-radius: 15px; + align-items: center; + text-align: center; + background: ${(props) => (props.disabled ? props.theme.nfsDisabled : props.theme.default.primaryPushThemeTextColor)}; + color: ${(props) => (props.disabled ? props.theme.nfsDisabledText : 'white')}; + padding: 16px 27px; + width: 12rem; + + @media ${device.tablet} { + font-size: 15px; + padding: 12px 12px; + width: 8rem; + } + + @media ${device.mobileL} { + width: -webkit-fill-available; + } +`; + +const CancelButtons = styled(FooterButtons)` + margin-right: 14px; + background: ${(props) => props.theme.default.bg}; + color: ${(props) => props.theme.logoBtnColor}; + border: 1px solid ${(props) => + props.theme.scheme === 'light' + ? props.theme.default.primaryPushThemeTextColor + : props.theme.default.borderColor}; + + @media ${device.mobileL} { + margin-right: 0px; + margin-top: 10px; + } +`; diff --git a/src/components/channel/NotificationSettings.tsx b/src/components/channel/NotificationSettings.tsx new file mode 100644 index 0000000000..cd68c06845 --- /dev/null +++ b/src/components/channel/NotificationSettings.tsx @@ -0,0 +1,286 @@ +// React + Web3 Essentials +import React, { useEffect, useMemo } from 'react'; +import { ethers } from 'ethers'; + +// External Packages +import 'react-dropdown/style.css'; +import { useSelector } from 'react-redux'; +import 'react-toastify/dist/ReactToastify.min.css'; +import { useNavigate } from 'react-router-dom'; +import { PiPencilSimpleBold } from 'react-icons/pi'; +import { IoMdRemoveCircleOutline } from 'react-icons/io'; +import { MdCheckCircle, MdError } from 'react-icons/md'; + +// Internal Compoonents +import useToast from '../../hooks/useToast'; +import AddSettingModalContent from './AddSettingModalContent'; +import ChannelInfoHeader from './ChannelInfoHeader'; +import { AddSettingButton } from './ChannelButtons'; +import ChannelInfoList from './ChannelInfoList'; +import DepositFeeFooter from './DepositFeeFooter'; +import { useAccount } from 'hooks'; + +// Internal Configs +import { appConfig } from 'config'; +import useModalBlur, { MODAL_POSITION } from 'hooks/useModalBlur'; +import { ChannelSetting } from 'helpers/channel/types'; +import { getChannel } from 'services'; + +// Constants +const CORE_CHAIN_ID = appConfig.coreContractChain; + +function NotificationSettings() { + const { account, chainId } = useAccount(); + const { coreChannelAdmin, delegatees } = useSelector((state: any) => state.admin); + const { epnsWriteProvider } = useSelector((state: any) => state.contracts); + + const onCoreNetwork = CORE_CHAIN_ID === chainId; + const EDIT_SETTING_FEE = 50; + + const [channelAddress, setChannelAddress] = React.useState(''); + const [settings, setSettings] = React.useState([]); + const [settingToEdit, setSettingToEdit] = React.useState(undefined); + const [isLoading, setIsLoading] = React.useState(false); + const [currentSettings, setCurrentSettings] = React.useState([]); + const [isLoadingSettings, setIsLoadingSettings] = React.useState(true); + + const { + isModalOpen: isAddSettingModalOpen, + showModal: showAddSettingModal, + ModalComponent: AddSettingModal, + } = useModalBlur(); + + const redirectBack = () => { + const url = window.location.origin; + window.location.replace(`${url}/channels`); + }; + + useEffect(() => { + // Is not the channel admin so cannot edit settings + (async () => { + setIsLoading(true); + if (!account) return; + try { + const channelDetails = await getChannel({ channel: account }); + if (!channelDetails) redirectBack(); + } catch { + redirectBack(); + } + if (coreChannelAdmin && coreChannelAdmin !== account) redirectBack(); + setIsLoading(false); + })(); + }, [account, coreChannelAdmin]); + + useEffect(() => { + if (isAddSettingModalOpen === false) setSettingToEdit(undefined); + }, [isAddSettingModalOpen]); + + React.useEffect(() => { + if (!account) return; + if (!delegatees || !delegatees.length) { + setChannelAddress(account); + } else { + // default the channel address to the first one on the list which should be that of the user if they have a channel + if (onCoreNetwork) setChannelAddress(delegatees[0].channel); + else setChannelAddress(delegatees[0].alias_address); + } + }, [delegatees, account]); + + useEffect(() => { + if (delegatees) { + const delegatee = delegatees.find(({ channel }) => channel === channelAddress); + if (delegatee) { + const { channel_settings } = delegatee; + if (channel_settings !== null) { + const settings = JSON.parse(channel_settings); + setSettings(settings); + setCurrentSettings(settings); + setIsLoadingSettings(false); + } + } + } + return null; + }, [delegatees, channelAddress]); + + // Notification Toast + const notificationToast = useToast(5000); + + const navigate = useNavigate(); + + const goBack = () => { + navigate('/dashboard', { replace: true }); + }; + + const addSetting = (newSetting: ChannelSetting) => { + const index = settings.findIndex((setting) => setting.index === newSetting.index); + if (index === -1) setSettings([...settings, newSetting]); + else { + const updatedSetting = [...settings]; + updatedSetting[index] = newSetting; + setSettings(updatedSetting); + } + }; + + const editSetting = (settingToEdit: ChannelSetting) => { + setSettingToEdit(settingToEdit); + showAddSettingModal(); + }; + + const deleteSetting = (settingToDelete: ChannelSetting) => { + setSettings((settings) => settings.filter((setting) => setting.index !== settingToDelete.index)); + }; + + const saveSettings = async () => { + try { + setIsLoading(true); + + const feesRequiredForEdit = 50; + const parsedFees = ethers.utils.parseUnits(feesRequiredForEdit.toString(), 18); + + notificationToast.showLoaderToast({ loaderMessage: 'Waiting for Confirmation...' }); + const notifOptions = ethers.utils.parseUnits('2', 18); + let _notifSettings = ''; + let _notifDescription = ''; + settings.forEach((setting) => { + if (_notifSettings !== '') _notifSettings += '+'; + if (_notifDescription !== '') _notifDescription += '+'; + if (setting.type === 1) { + _notifSettings += `${setting.type}-${setting.default ? '1' : '0'}`; + } else if (setting.type === 2) { + _notifSettings += `${setting.type}-${setting.default}-${setting.lowerLimit}-${setting.upperLimit}`; + } + _notifDescription += setting.description; + }); + + const tx = await epnsWriteProvider.createChannelSettings( + notifOptions, + _notifSettings, + _notifDescription, + parsedFees, + { gasLimit: 1000000 } + ); + + console.log(tx); + await tx.wait(); + setCurrentSettings(settings); + setIsLoading(false); + + notificationToast.showMessageToast({ + toastTitle: 'Success', + toastMessage: `Channel Settings Updated Successfully`, + toastType: 'SUCCESS', + getToastIcon: (size) => ( + + ), + }); + } catch (err) { + setIsLoading(false); + console.log(err.message); + if (err.code == 'ACTION_REJECTED') { + // EIP-1193 userRejectedRequest error + notificationToast.showMessageToast({ + toastTitle: 'Error', + toastMessage: `User denied message signature.`, + toastType: 'ERROR', + getToastIcon: (size) => ( + + ), + }); + } else { + notificationToast.showMessageToast({ + toastTitle: 'Error', + toastMessage: `There was an error in updating channel settings`, + toastType: 'ERROR', + getToastIcon: (size) => ( + + ), + }); + console.log('Error --> %o', err); + console.log({ err }); + } + } + }; + + const settingsChanged = useMemo(() => { + if (!settings || !currentSettings) return false; + console.log('Settings changed bro', settings, currentSettings); + if (settings.length !== currentSettings.length) return true; + let isUnchanged = true; + for (let i = 0; i < settings.length; i++) { + const setting1 = settings[i]; + const setting2 = currentSettings[i]; + if (setting1.type === 1) { + isUnchanged = + isUnchanged && + setting1.type === setting2.type && + setting1.description === setting2.description && + setting1.default === setting2.default; + } else if (setting1.type === 2) { + isUnchanged = + isUnchanged && + setting1.type === setting2.type && + setting1.description === setting2.description && + setting1.default === setting2.default && + setting1.lowerLimit === setting2.lowerLimit && + setting1.upperLimit === setting2.upperLimit; + } + } + return isUnchanged === false; + }, [settings, currentSettings]); + + return ( + <> + } + /> + , + onClick: editSetting, + text: 'Edit', + }, + { + icon: , + onClick: deleteSetting, + text: 'Delete', + }, + ]} + /> + + + + ); +} + +// Export Default +export default NotificationSettings; diff --git a/src/config/Themization.js b/src/config/Themization.js index c9f7012a7e..4abc8d45cb 100644 --- a/src/config/Themization.js +++ b/src/config/Themization.js @@ -276,6 +276,10 @@ const themeLight = { snapSecondaryText:'#62626a', snapBackground:'#F2F2F2', + // Notification Settings + nfsError: '#ED5858', + nfsDisabled: '#DFDEE9', + nfsDisabledText: '#AFB3BF', }; const themeDark = { @@ -560,6 +564,10 @@ const themeDark = { snapSecondaryText:'#787E99', snapBackground:'#404650', + // Notification Settings + nfsError: '#ED5858', + nfsDisabled: '#AFB3BF', + nfsDisabledText: '#787E99', }; module.exports = { diff --git a/src/helpers/channel/InputValidation.ts b/src/helpers/channel/InputValidation.ts new file mode 100644 index 0000000000..eaa0d7843e --- /dev/null +++ b/src/helpers/channel/InputValidation.ts @@ -0,0 +1,96 @@ +import { ChannelSetting } from './types'; + +const isEmpty = (field: string) => { + return field.trim().length == 0; +}; + +export const isAllFilledAndValid = ({ + setErrorInfo, + lowerLimit, + upperLimit, + type, + settingName, + defaultValue, +}: { + setErrorInfo: React.Dispatch< + React.SetStateAction<{ + settingName: string; + lowerLimit: string; + upperLimit: string; + default: string; + }> + >; + upperLimit: string; + lowerLimit: string; + type: ChannelSetting['type']; + settingName: string; + defaultValue: string; +}): boolean => { + setErrorInfo(undefined); + + let hasError = false; + + if (isEmpty(settingName)) { + setErrorInfo((x) => ({ + ...x, + settingName: 'Setting Name is required', + })); + hasError = true; + } + + if (type === 2) { + if (isEmpty(lowerLimit)) { + setErrorInfo((x) => ({ + ...x, + lowerLimit: 'Minimum range is required', + })); + hasError = true; + } + if (isEmpty(upperLimit)) { + setErrorInfo((x) => ({ + ...x, + upperLimit: 'Maximum range is required', + })); + hasError = true; + } + if (isEmpty(defaultValue)) { + setErrorInfo((x) => ({ + ...x, + default: 'Default value is required', + })); + hasError = true; + } + if (!isEmpty(lowerLimit) && !isEmpty(upperLimit) && !isEmpty(defaultValue)) { + if (Number(lowerLimit) < 0) { + setErrorInfo((x) => ({ + ...x, + lowerLimit: 'Minimum range should be greater than 0', + })); + hasError = true; + } + if (Number(upperLimit) < 0) { + setErrorInfo((x) => ({ + ...x, + upperLimit: 'Maximum range should be greater than 0', + })); + hasError = true; + } + if (Number(lowerLimit) > Number(upperLimit)) { + setErrorInfo((x) => ({ + ...x, + lowerLimit: 'Minimum range should be less than maximum range', + })); + hasError = true; + } + if (Number(defaultValue) < Number(lowerLimit) || Number(defaultValue) > Number(upperLimit)) { + setErrorInfo((x) => ({ + ...x, + default: 'Default value not in range', + })); + hasError = true; + } + } + } + console.log('Has error', hasError); + return !hasError; +}; diff --git a/src/helpers/channel/types.ts b/src/helpers/channel/types.ts new file mode 100644 index 0000000000..09616ad0bc --- /dev/null +++ b/src/helpers/channel/types.ts @@ -0,0 +1,15 @@ +export type ChannelSetting = + | { + type: 1; // Boolean + default: boolean; + description: string; + index: number; + } + | { + type: 2; // Range + default: number; + description: string; + index: number; + lowerLimit: number; + upperLimit: number; + }; diff --git a/src/modules/channelDashboard/channelDashboardModule.tsx b/src/modules/channelDashboard/channelDashboardModule.tsx index 7b09a56982..fa0c72da2d 100644 --- a/src/modules/channelDashboard/channelDashboardModule.tsx +++ b/src/modules/channelDashboard/channelDashboardModule.tsx @@ -60,7 +60,7 @@ const Container = styled(Section)` 100% - ${globalsMargin.MINI_MODULES.DESKTOP.RIGHT} - ${globalsMargin.MINI_MODULES.DESKTOP.LEFT} - ${GLOBALS.ADJUSTMENTS.PADDING.HUGE} - ${GLOBALS.ADJUSTMENTS.PADDING.HUGE} ); - padding: ${GLOBALS.ADJUSTMENTS.PADDING.HUGE}; + padding: ${GLOBALS.ADJUSTMENTS.PADDING.DEFAULT}; position: relative; margin: ${GLOBALS.ADJUSTMENTS.MARGIN.MINI_MODULES.DESKTOP}; diff --git a/src/modules/editChannel/EditChannel.tsx b/src/modules/editChannel/EditChannel.tsx index ea558e4e05..411d40530c 100644 --- a/src/modules/editChannel/EditChannel.tsx +++ b/src/modules/editChannel/EditChannel.tsx @@ -533,6 +533,7 @@ const Footer = styled(ItemVV2)` justify-content: space-between; grid-gap: 40px; margin-top:35px; + z-index: 1; @media (max-width:600px){ padding: 16px; diff --git a/src/modules/notifSettings/NotifSettingsModule.tsx b/src/modules/notifSettings/NotifSettingsModule.tsx new file mode 100644 index 0000000000..59254f1418 --- /dev/null +++ b/src/modules/notifSettings/NotifSettingsModule.tsx @@ -0,0 +1,86 @@ +// React + Web3 Essentials +import React from 'react'; + +// External Packages +import ReactGA from 'react-ga'; +import styled from 'styled-components'; +import { Navigate } from 'react-router-dom'; + +// Internal Components +import NotificationSettings from 'components/channel/NotificationSettings'; +import { Section } from 'primaries/SharedStyling'; + +// Internal Configs +import { appConfig } from 'config'; +import GLOBALS, { device, globalsMargin } from 'config/Globals'; + +// Constants +export const ALLOWED_CORE_NETWORK = appConfig.coreContractChain; //chainId of network which we have deployed the core contract on + +// Create Header +function NotifSettingsPage() { + ReactGA.pageview('/channel/settings'); + + // toast related section + const [toast, showToast] = React.useState(null); + const clearToast = () => showToast(null); + + //clear toast variable after it is shown + React.useEffect(() => { + if (toast) { + clearToast(); + } + }, [toast]); + // toast related section + + // Render + return ( + + + + ); +} + +// Define how the module is fitted, define it align-self to strect to fill entire bounds +// Define height: inherit to cover entire height +const Container = styled(Section)` + align-items: center; + align-self: center; + background: ${(props) => props.theme.default.bg}; + border-radius: ${GLOBALS.ADJUSTMENTS.RADIUS.LARGE} ${GLOBALS.ADJUSTMENTS.RADIUS.LARGE} + ${GLOBALS.ADJUSTMENTS.RADIUS.LARGE} ${GLOBALS.ADJUSTMENTS.RADIUS.LARGE}; + box-shadow: ${GLOBALS.ADJUSTMENTS.MODULE_BOX_SHADOW}; + display: flex; + flex-direction: column; + flex: initial; + justify-content: center; + max-width: 1200px; + width: calc( + 100% - ${globalsMargin.MINI_MODULES.DESKTOP.RIGHT} - ${globalsMargin.MINI_MODULES.DESKTOP.LEFT} - + ${GLOBALS.ADJUSTMENTS.PADDING.BIG} - ${GLOBALS.ADJUSTMENTS.PADDING.BIG} + ); + position: relative; + margin: ${GLOBALS.ADJUSTMENTS.MARGIN.MINI_MODULES.DESKTOP}; + padding: ${GLOBALS.ADJUSTMENTS.PADDING.BIG}; + + @media ${device.laptop} { + margin: ${GLOBALS.ADJUSTMENTS.MARGIN.MINI_MODULES.TABLET}; + padding: ${GLOBALS.ADJUSTMENTS.PADDING.DEFAULT}; + justify-content: flex-start; + } + + @media ${device.mobileL} { + margin: ${GLOBALS.ADJUSTMENTS.MARGIN.BIG_MODULES.MOBILE}; + padding: ${GLOBALS.ADJUSTMENTS.PADDING.DEFAULT}; + width: calc( + 100% - ${globalsMargin.MINI_MODULES.MOBILE.RIGHT} - ${globalsMargin.MINI_MODULES.MOBILE.LEFT} - + ${GLOBALS.ADJUSTMENTS.PADDING.DEFAULT} - ${GLOBALS.ADJUSTMENTS.PADDING.DEFAULT} + ); + min-height: calc(100vh - ${GLOBALS.CONSTANTS.HEADER_HEIGHT}px - ${globalsMargin.BIG_MODULES.MOBILE.TOP}); + overflow-y: scroll; + border-radius: ${GLOBALS.ADJUSTMENTS.RADIUS.LARGE} ${GLOBALS.ADJUSTMENTS.RADIUS.LARGE} 0 0; + } +`; + +// Export Default +export default NotifSettingsPage; diff --git a/src/pages/NotifSettingsPage.tsx b/src/pages/NotifSettingsPage.tsx new file mode 100644 index 0000000000..1027f064f3 --- /dev/null +++ b/src/pages/NotifSettingsPage.tsx @@ -0,0 +1,29 @@ +// React + Web3 Essentials +import React from "react"; + +// External Packages +import styled from 'styled-components'; + +// Internal Components +import { SectionV2 } from 'components/reusables/SharedStylingV2'; +import NotificationSettings from "modules/notifSettings/NotifSettingsModule"; + +// Page structure +const SendNotifsPage = () => { + // RENDER + return ( + + + + ); +} +export default SendNotifsPage; + +// This defines the page settings, toggle align-self to center if not covering entire stuff, align-items to place them at center +// justify content flex start to start from top, height is defined by module as well as amount of margin, padding +const Container = styled(SectionV2)` + flex: 1; + flex-direction: column; + align-self: stretch; + justify-content: flex-start; +`; \ No newline at end of file diff --git a/src/primaries/SharedModalComponents/ModalConfirmButton.tsx b/src/primaries/SharedModalComponents/ModalConfirmButton.tsx index b08ca2fd2f..adb907c004 100644 --- a/src/primaries/SharedModalComponents/ModalConfirmButton.tsx +++ b/src/primaries/SharedModalComponents/ModalConfirmButton.tsx @@ -10,16 +10,17 @@ import LoaderSpinner, { LOADER_TYPE } from 'components/reusables/loaders/LoaderS // Types type ModalConfirmButtonType = { text:string, - onClick: ()=>void, + onClick?: ()=>void, isLoading: boolean, color?:string, backgroundColor?:string, border?:string, topMargin?:string, loaderTitle?: string, + padding?:string, } -const ModalConfirmButton = ({text, onClick, isLoading,color,backgroundColor,border,topMargin,loaderTitle}:ModalConfirmButtonType)=>{ +const ModalConfirmButton = ({text, onClick, isLoading,color,backgroundColor,border,topMargin,loaderTitle,padding}:ModalConfirmButtonType)=>{ const themes = useTheme(); return( @@ -46,6 +47,7 @@ const ModalConfirmButton = ({text, onClick, isLoading,color,backgroundColor,bord color={color} backgroundColor={backgroundColor} border={border} + style={{ padding: padding ? padding : "16px" }} > {text} @@ -87,8 +89,6 @@ const CustomButton = styled.button` background-color:${props => props.backgroundColor || '#CF1C84'}; border:${props=>props.border || '1px solid transparent'}; border-radius:15px; - // padding: 5% 12%; - padding:16px; `; export default ModalConfirmButton \ No newline at end of file diff --git a/src/services/channels/getChannel.ts b/src/services/channels/getChannel.ts new file mode 100644 index 0000000000..ce3ebd2ebc --- /dev/null +++ b/src/services/channels/getChannel.ts @@ -0,0 +1,19 @@ +// External Packages +import * as PushAPI from '@pushprotocol/restapi'; + +// Internal Configs +import { appConfig } from 'config'; + +// Types +type Props = { + channel: string; +}; + +export const getChannel = async ({ channel }: Props) => { + try { + return await PushAPI.channels.getChannel({ channel, env: appConfig.appEnv }); + } catch (err) { + console.error(err); + throw new Error(err.message); + } +}; diff --git a/src/services/channels/index.ts b/src/services/channels/index.ts index 9073c8ed58..03752f1c6c 100644 --- a/src/services/channels/index.ts +++ b/src/services/channels/index.ts @@ -1,3 +1,4 @@ export * from "./getChannelDelegates"; export * from "./getChannels"; -export * from "./getChannelsSearch"; \ No newline at end of file +export * from "./getChannelsSearch"; +export * from "./getChannel"; diff --git a/src/structure/MasterInterfacePage.tsx b/src/structure/MasterInterfacePage.tsx index b34e610825..56f0ead8df 100644 --- a/src/structure/MasterInterfacePage.tsx +++ b/src/structure/MasterInterfacePage.tsx @@ -23,6 +23,7 @@ const InternalDevPage = lazy(() => import('pages/InternalDevPage')); const NFTPage = lazy(() => import('pages/NFTPage')); const NotAvailablePage = lazy(() => import('pages/NotAvailablePage')); const ReceiveNotifsPage = lazy(() => import('pages/ReceiveNotifsPage')); +const NotifSettingsPage = lazy(() => import('pages/NotifSettingsPage')); const SendNotifsPage = lazy(() => import('pages/SendNotifsPage')); const SpacePage = lazy(() => import('pages/SpacePage')); const SpamPage = lazy(() => import('pages/SpamPage')); @@ -97,6 +98,7 @@ function MasterInterfacePage() { } /> } /> } /> + } /> } /> } /> diff --git a/yarn.lock b/yarn.lock index b10c26aed1..e66c89183d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2166,7 +2166,7 @@ __metadata: languageName: node linkType: hard -"@emotion/core@npm:^10.0.0, @emotion/core@npm:^10.0.15": +"@emotion/core@npm:^10.0.0, @emotion/core@npm:^10.0.14, @emotion/core@npm:^10.0.15": version: 10.3.1 resolution: "@emotion/core@npm:10.3.1" dependencies: @@ -5424,9 +5424,10 @@ __metadata: react-dropzone-uploader: 2.11.0 react-easy-crop: ^4.1.4 react-ga: 2.7.0 - react-icons: ^4.7.1 + react-icons: ^4.11.0 react-image-file-resizer: ^0.4.7 react-images-upload: ^1.2.8 + react-input-slider: ^6.0.1 react-joyride: ^2.4.0 react-loader-spinner: ^5.3.4 react-multi-select-component: ^4.2.3 @@ -24812,12 +24813,12 @@ __metadata: languageName: node linkType: hard -"react-icons@npm:^4.7.1": - version: 4.8.0 - resolution: "react-icons@npm:4.8.0" +"react-icons@npm:^4.11.0": + version: 4.11.0 + resolution: "react-icons@npm:4.11.0" peerDependencies: react: "*" - checksum: 4dbba7ad989c295b410e19b2a702722dae44368cb04b6515f9471353552f31ac80bd350f121d5bff79f81504b84039ede44d09e9f035f48bb1032e6eace126c4 + checksum: 7b8b80bbe2dabcc54b6c2129b7761a04b19caebe24389adc7683478ef41212b9ca0b53c63abcc06b3c01b94c84855ec5142b4c357e19c4aaaad9a4db23a3c485 languageName: node linkType: hard @@ -24841,6 +24842,19 @@ __metadata: languageName: node linkType: hard +"react-input-slider@npm:^6.0.1": + version: 6.0.1 + resolution: "react-input-slider@npm:6.0.1" + dependencies: + "@babel/runtime": ^7.9.2 + "@emotion/core": ^10.0.14 + peerDependencies: + react: ">=16" + react-dom: ">=16" + checksum: 8611d1309bc8a10c7181f30c072840548f0911511d10b7c98928eef461297a9c9b645956cadbe0d8e586db4c958cb5e422fbe440d2b422e4cb79e841e4109ef6 + languageName: node + linkType: hard + "react-is@npm:^16.12.0, react-is@npm:^16.13.1, react-is@npm:^16.6.3, react-is@npm:^16.7.0, react-is@npm:^16.8.0, react-is@npm:^16.8.1, react-is@npm:^16.8.4": version: 16.13.1 resolution: "react-is@npm:16.13.1" From cb6daa1930ea6c8bbef7dac241a69c0286a0b8fe Mon Sep 17 00:00:00 2001 From: Kalash Shah <81062983+kalashshah@users.noreply.github.com> Date: Thu, 5 Oct 2023 17:47:24 +0530 Subject: [PATCH 09/18] User settings page (#1264) * Optimized app paths for pages and created a page for user settings * Updated userSettings from APP_PATHS * chore: add channel settings page to app paths * chore: user settings page * chore: add notif settings page title * chore: ui fixes --------- Co-authored-by: Nilesh Gupta --- public/svg/setting.svg | 11 + src/components/ChannelDetails.js | 7 +- src/components/Dropdown.tsx | 19 +- src/components/InboxComponent.js | 5 +- src/components/SendNotifications.tsx | 5 +- src/components/ViewChannelItem.js | 2 +- src/components/channel/ChannelButtons.tsx | 8 +- src/components/channel/ChannelInfoList.tsx | 105 ++---- .../channel/EmptyNotificationSettings.tsx | 76 ++++ .../channel/NotificationSettings.tsx | 2 + src/components/channel/UserSettings.tsx | 354 ++++++++++++++++++ .../dropdowns/DropdownBtnHandler.tsx | 18 +- .../dropdowns/ManageNotifSettingDropdown.tsx | 10 +- .../dropdowns/OptinNotifSettingDropdown.tsx | 3 +- .../dropdowns/UpdateNotifSettingDropdown.tsx | 4 +- src/config/AppPaths.ts | 26 ++ src/config/NavigationList.js | 21 +- src/modules/channels/ChannelsModule.tsx | 3 +- src/modules/faq/FaqModule.tsx | 2 +- src/modules/inbox/InboxModule.tsx | 3 +- .../userSettings/UserSettingsModule.tsx | 58 +++ src/pages/NotAvailablePage.tsx | 3 +- src/pages/SpamPage.js | 3 +- src/pages/UserSettingsPage.tsx | 29 ++ src/primaries/Profile.tsx | 13 +- src/structure/Header.tsx | 31 ++ src/structure/MasterInterfacePage.tsx | 49 +-- 27 files changed, 736 insertions(+), 134 deletions(-) create mode 100644 public/svg/setting.svg create mode 100644 src/components/channel/EmptyNotificationSettings.tsx create mode 100644 src/components/channel/UserSettings.tsx create mode 100644 src/config/AppPaths.ts create mode 100644 src/modules/userSettings/UserSettingsModule.tsx create mode 100644 src/pages/UserSettingsPage.tsx diff --git a/public/svg/setting.svg b/public/svg/setting.svg new file mode 100644 index 0000000000..ac18686681 --- /dev/null +++ b/public/svg/setting.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/components/ChannelDetails.js b/src/components/ChannelDetails.js index 9f02e70b8d..8ad47bc6cd 100644 --- a/src/components/ChannelDetails.js +++ b/src/components/ChannelDetails.js @@ -29,6 +29,7 @@ import { appConfig } from 'config'; import { device } from 'config/Globals'; import { CHANNEL_TYPE } from 'helpers/UtilityHelper'; import { getDateFromTimestamp, nextDaysDateFromTimestamp, timeRemaining } from 'helpers/TimerHelper'; +import APP_PATHS from 'config/AppPaths'; const DATE_FORMAT = 'DD MMM, YYYY'; @@ -147,7 +148,7 @@ export default function ChannelDetails({ isChannelExpired, setIsChannelExpired, }; const navigateToNotifSettings = () => { - navigate('/channel/settings'); + navigate(APP_PATHS.ChannelSettings); }; return ( @@ -317,13 +318,15 @@ export default function ChannelDetails({ isChannelExpired, setIsChannelExpired, navigateToNotifSettings()} />} + Button={} /> diff --git a/src/components/Dropdown.tsx b/src/components/Dropdown.tsx index f90cd063d9..9aeda79d88 100644 --- a/src/components/Dropdown.tsx +++ b/src/components/Dropdown.tsx @@ -1,6 +1,7 @@ // React + Web3 Essentials import { shortenText } from 'helpers/UtilityHelper'; import React from 'react'; +import { Link } from 'react-router-dom'; // External Packages import styled, { useTheme } from 'styled-components'; @@ -15,6 +16,7 @@ export type DropdownValueType = { icon: string, textColor?: string, function: () => void, + to?: string; // Add the 'to' property for navigation } type DropdownProps = { @@ -118,7 +120,7 @@ function Dropdown({ dropdownValues, textColor, iconFilter, hoverBGColor }: Dropd cursor="pointer" /> )} - {!dropdownValue?.link && dropdownValue?.function && ( + {!dropdownValue?.to && !dropdownValue?.link && dropdownValue?.function && ( )} + {dropdownValue?.to && ( + + {/* You can customize the Link as needed */} + + {dropdownValue.title} + + + )} {dropdownValue?.link && ( { const [showInbox, setShowInbox] = useState(!isSpam); @@ -34,10 +35,10 @@ const InboxComponent = ({isSpam}) => { - handleToggle('/inbox')}> + handleToggle(APP_PATHS.Inbox)}> Inbox - handleToggle('/spam')}> + handleToggle(APP_PATHS.Spam)}> Spam diff --git a/src/components/SendNotifications.tsx b/src/components/SendNotifications.tsx index 9c2be83280..03ac3bf29e 100644 --- a/src/components/SendNotifications.tsx +++ b/src/components/SendNotifications.tsx @@ -40,6 +40,7 @@ import PreviewNotif from './PreviewNotif'; // Internal Configs import { appConfig } from 'config'; import { useAccount, useDeviceWidthCheck } from 'hooks'; +import APP_PATHS from 'config/AppPaths'; // Constants const CORE_CHAIN_ID = appConfig.coreContractChain; @@ -180,7 +181,7 @@ function SendNotifications() { }, [channelSettings]); const openManageSettings = () => { - const newPageUrl = '/channels'; // Replace with the URL of the manage settings later + const newPageUrl = APP_PATHS.Channels; // Replace with the URL of the manage settings later // Use window.open() to open the URL in a new tab window.open(newPageUrl, '_blank'); @@ -189,7 +190,7 @@ function SendNotifications() { useEffect(() => { if (canSend !== 1) { const url = window.location.origin; - window.location.replace(`${url}/#/channels`); + window.location.replace(`${url}${APP_PATHS.Channels}`); } }); diff --git a/src/components/ViewChannelItem.js b/src/components/ViewChannelItem.js index b73ca4f211..a2e58cbc68 100644 --- a/src/components/ViewChannelItem.js +++ b/src/components/ViewChannelItem.js @@ -1066,7 +1066,7 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { <> {isOwner && Owner} {!isOwner && ( - + {}} disabled={txInProgress} diff --git a/src/components/channel/ChannelButtons.tsx b/src/components/channel/ChannelButtons.tsx index 53b2a2602b..d54b41f9a6 100644 --- a/src/components/channel/ChannelButtons.tsx +++ b/src/components/channel/ChannelButtons.tsx @@ -10,6 +10,10 @@ interface ChannelButtonProps { onClick: () => void; } +interface ModifySettingsButtonProps extends ChannelButtonProps { + title?: string; +} + export const AddDelegateButton = ({ onClick }: ChannelButtonProps) => { return ( @@ -28,10 +32,10 @@ export const ManageSettingsButton = ({ onClick }: ChannelButtonProps) => { ); }; -export const ModifySettingsButton = ({ onClick }: ChannelButtonProps) => { +export const ModifySettingsButton = ({ onClick, title }: ModifySettingsButtonProps) => { return ( - Modify Settings + {title ? title : 'Modify Settings'} ); }; diff --git a/src/components/channel/ChannelInfoList.tsx b/src/components/channel/ChannelInfoList.tsx index 84210415cf..1659924bca 100644 --- a/src/components/channel/ChannelInfoList.tsx +++ b/src/components/channel/ChannelInfoList.tsx @@ -9,42 +9,40 @@ import { useNavigate } from 'react-router-dom'; import { Item } from 'primaries/SharedStyling'; import DelegateInfo from 'components/DelegateInfo'; import LoaderSpinner, { LOADER_TYPE } from 'components/reusables/loaders/LoaderSpinner'; -import Icon from 'assets/navigation/receiveNotifOffIcon.svg'; -import { ImageV2 } from 'components/reusables/SharedStylingV2'; -import { ModifySettingsButton } from './ChannelButtons'; import DelegateSettingsDropdown, { ChannelDropdownOption } from './DelegateSettingsDropdown'; +import EmptyNotificationSettings from './EmptyNotificationSettings'; // Internal Configs import { device } from 'config/Globals'; import { ChannelSetting } from 'helpers/channel/types'; -const isOwner = (account: string, delegate: string) => { - return account.toLowerCase() === delegate.toLowerCase(); -}; - -type ChannelInfoListProps = - | { - isAddress: true; - items: string[]; - isLoading: boolean; - account: string; - style?: CSSProperties; - addressDropdownOptions: Array; - } - | { - isAddress: false; - items: Array; - isLoading: boolean; - account: string; - style?: CSSProperties; - settingsDropdownOptions?: Array; - }; +// Types +interface ChannelInfoListCommonProps { + isLoading: boolean; + account: string; + style?: CSSProperties; +} + +interface AddressListOptions extends ChannelInfoListCommonProps { + isAddress: true; + items: string[]; + addressDropdownOptions: Array; +} + +interface SettingListOptions extends ChannelInfoListCommonProps { + isAddress: false; + items: Array; + isLoading: boolean; + settingsDropdownOptions?: Array; + onClickEmptyListButton: () => void; + emptyListButtonTitle: string; +} + +type ChannelInfoListProps = AddressListOptions | SettingListOptions; const ChannelInfoList = (props: ChannelInfoListProps) => { - const navigate = useNavigate(); - - const handleNavigateToModifySettings = () => { - navigate(`/channel/settings`); + const isOwner = (account: string, delegate: string) => { + return account.toLowerCase() === delegate.toLowerCase(); }; return ( @@ -112,20 +110,13 @@ const ChannelInfoList = (props: ChannelInfoListProps) => {
); })} - {props.items && props.items.length === 0 && !props.isAddress && ( - - - No settings added - - Add settings for users to customize their notification preferences. - - - + {props.items && props.items.length === 0 && props.isAddress === false && ( + )} )} @@ -161,36 +152,6 @@ const NotificationSettingName = styled.span` props.theme.scheme === 'light' ? props.theme.default.color : props.theme.default.secondaryColor}; `; -const EmptyNotificationSetting = styled.div` - border-top: ${(props) => `1px solid ${props.theme.default.borderColor}`}; - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - margin-bottom: 16px; -`; - -const EmptyNotificationTitle = styled.div` - font-size: 16px; - font-weight: 500; - line-height: 24px; - letter-spacing: 0em; - text-align: left; - color: ${(props) => props.theme.default.color}; -`; - -const EmptyNotificationDesc = styled.div` - margin-top: 1px; - margin-bottom: 16px; - color: ${(props) => props.theme.default.secondaryColor}; -`; - -const NotifIcon = styled(ImageV2)` - color: ${(props) => props.theme.default.color}; - margin-top: 32px; - margin-bottom: 12px; -`; - const Divider = styled.div` background-color: ${(props) => props.theme.default.border}; height: 1px; diff --git a/src/components/channel/EmptyNotificationSettings.tsx b/src/components/channel/EmptyNotificationSettings.tsx new file mode 100644 index 0000000000..637c115f0b --- /dev/null +++ b/src/components/channel/EmptyNotificationSettings.tsx @@ -0,0 +1,76 @@ +// React + Web3 Essentials +import React from 'react'; + +// External Packages +import styled from 'styled-components'; + +// Internal Components +import Icon from 'assets/navigation/receiveNotifOffIcon.svg'; +import { ImageV2 } from 'components/reusables/SharedStylingV2'; +import { ModifySettingsButton } from './ChannelButtons'; + +// Types +interface EmptyNotificationSettingsProps { + onClick: () => void; + title: string; + description: string; + buttonTitle?: string; + showTopBorder?: boolean; +} + +const EmptyNotificationSettings = ({ + description, + onClick, + title, + buttonTitle, + showTopBorder = true, +}: EmptyNotificationSettingsProps) => { + return ( + + + {title} + {description} + + + ); +}; + +export default EmptyNotificationSettings; + +const EmptyNotificationSetting = styled.div<{ showTopBorder: boolean }>` + border-top: ${(props) => props.showTopBorder && `1px solid ${props.theme.default.borderColor}`}; + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + margin-bottom: 16px; +`; + +const EmptyNotificationTitle = styled.div` + font-size: 16px; + font-weight: 500; + line-height: 24px; + letter-spacing: 0em; + text-align: left; + color: ${(props) => props.theme.default.color}; +`; + +const EmptyNotificationDesc = styled.div` + margin-top: 1px; + margin-bottom: 16px; + color: ${(props) => props.theme.default.secondaryColor}; +`; + +const NotifIcon = styled(ImageV2)` + color: ${(props) => props.theme.default.color}; + margin-top: 32px; + margin-bottom: 12px; +`; diff --git a/src/components/channel/NotificationSettings.tsx b/src/components/channel/NotificationSettings.tsx index cd68c06845..0cb4cdf0a5 100644 --- a/src/components/channel/NotificationSettings.tsx +++ b/src/components/channel/NotificationSettings.tsx @@ -251,6 +251,8 @@ function NotificationSettings() { isAddress={false} isLoading={isLoadingSettings} items={settings} + onClickEmptyListButton={showAddSettingModal} + emptyListButtonTitle='Add Setting' settingsDropdownOptions={[ { icon: , diff --git a/src/components/channel/UserSettings.tsx b/src/components/channel/UserSettings.tsx new file mode 100644 index 0000000000..45209eaa74 --- /dev/null +++ b/src/components/channel/UserSettings.tsx @@ -0,0 +1,354 @@ +// React + Web3 Essentials +import React, { useEffect, useState } from 'react'; + +// External Packages +import styled from 'styled-components'; +import { useDispatch, useSelector } from 'react-redux'; +import { useNavigate } from 'react-router-dom'; +import { AiOutlineMore } from 'react-icons/ai'; + +// Internal Components +import { useAccount } from 'hooks'; +import { Button } from 'primaries/SharedStyling'; +import { ImageV2 } from 'components/reusables/SharedStylingV2'; +import { getChannel, getUserSubscriptions } from 'services'; +import LoaderSpinner from 'primaries/LoaderSpinner'; +import EmptyNotificationSettings from './EmptyNotificationSettings'; +import { updateBulkSubscriptions } from 'redux/slices/channelSlice'; +import { convertAddressToAddrCaip } from 'helpers/CaipHelper'; +import ManageNotifSettingDropdown from 'components/dropdowns/ManageNotifSettingDropdown'; + +// Internal Configs +import { device } from 'config/Globals'; + +interface ChannelListItem { + channel: string; + icon: string; + name: string; + id: number; +} + +function UserSettings() { + const { account, chainId } = useAccount(); + const [selectedOption, setSelectedOption] = useState(0); + const [channelList, setChannelList] = useState([]); + const [isLoading, setIsLoading] = useState(true); + + const navigate = useNavigate(); + + const { subscriptionStatus } = useSelector((state: any) => state.channels); + const dispatch = useDispatch(); + + const fetchChannelDetails = async (channel: string) => { + const details = await getChannel({ channel }); + if (details) { + const updatedChannelItem: ChannelListItem = { + channel, + id: details.id, + icon: details.icon, + name: details.name, + }; + return updatedChannelItem; + } else return undefined; + }; + + const fillData = async (details: any) => { + const data = await Promise.all( + Object.keys(details).map(async (channel) => { + const channelData = await fetchChannelDetails(channel); + if (channelData) return channelData; + }) + ); + setChannelList(data); + }; + + useEffect(() => { + if (!account) return; + (async function () { + setIsLoading(true); + if (Object.keys(subscriptionStatus).length === 0) { + const userCaipAddress = convertAddressToAddrCaip(account, chainId); + const subscriptionsArr = await getUserSubscriptions({ userCaipAddress }); + const subscriptionsMapping = {}; + subscriptionsArr.map(({ channel }) => (subscriptionsMapping[channel] = true)); + dispatch(updateBulkSubscriptions(subscriptionsMapping)); + await fillData(subscriptionsMapping); + } else { + await fillData(subscriptionStatus); + } + setIsLoading(false); + })(); + }, [account]); + + const navigateToChannels = () => { + navigate('/channels'); + }; + + const selectOptions = [ + { + value: 0, + label: 'Notification Settings', + }, + ]; + + return ( + + Settings + Customize your Push profile or manage your notification preferences + + + {selectOptions.map((selectOptions) => ( + setSelectedOption(selectOptions.value)} + key={selectOptions.value} + isSelected={selectOptions.value === selectedOption} + > + {selectOptions.label} + + ))} + + + + {selectOptions[selectedOption].label} + <> + {isLoading ? ( + + + + ) : ( + <> + {channelList.length > 0 ? ( + channelList.map((channel, index) => ( + <> + {channel && ( + <> + + + + {channel.name} + + + + + + {index !== channelList.length - 1 &&
} + + )} + + )) + ) : ( + + + + )} + + )} + +
+
+
+
+ ); +} + +// Export Default +export default UserSettings; + +const Container = styled.div` + padding: 32px 24px; + flex: 1; +`; + +const PageTitle = styled.div` + font-size: 32px; + font-weight: 500; + line-height: 45px; + letter-spacing: 0em; + text-align: left; + color: ${(props) => props.theme.default.color}; + + @media ${device.tablet} { + text-align: center; + margin-top: 24px; + } +`; + +const PageDescription = styled.div` + font-size: 15px; + font-weight: 400; + line-height: 21px; + letter-spacing: 0em; + text-align: left; + color: ${(props) => props.theme.default.secondaryColor}; + margin-bottom: 40px; + + @media ${device.tablet} { + text-align: center; + margin-bottom: 8px; + } +`; + +const Wrapper = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + + @media ${device.tablet} { + flex-direction: column; + } +`; + +const SelectSection = styled.div` + display: flex; + flex-direction: column; + margin-right: 42px; + + @media ${device.tablet} { + margin-right: 0px; + flex-direction: row; + overflow-x: scroll; + justify-content: center; + } +`; + +const SelectListOption = styled(Button)<{ isSelected: boolean }>` + background-color: ${(props) => (props.isSelected ? props.theme.default.secondaryBg : 'transparent')}; + color: ${(props) => props.theme.default.secondaryColor}; + border-radius: 12px; + width: 100%; + padding: 14px; + margin: 10px 0px; + justify-content: flex-start; + + &:hover:after { + background-color: ${(props) => props.theme.default.secondaryBg}; + } + + @media ${device.tablet} { + padding: 12px; + max-width: fit-content; + } +`; + +const ChannelWrapper = styled.div` + border: 1px solid ${(props) => props.theme.default.borderColor}; + padding: 12px; + border-radius: 16px; + flex-grow: 1; + + @media ${device.tablet} { + margin: 8px 0px; + padding: 12px 6px; + } +`; + +const ChannelContainer = styled.div` + overflow: hidden; + overflow-y: scroll; + height: 55vh; + padding: 12px; + + &::-webkit-scrollbar-track { + background-color: transparent; + position: absolute; + right: 10px; + } + + &::-webkit-scrollbar { + background-color: transparent; + width: 4px; + position: absolute; + right: 10px; + } + + &::-webkit-scrollbar-thumb { + background-color: #d53a94; + border-radius: 99px; + width: 4px; + position: absolute; + right: 10px; + } + + @media ${device.tablet} { + margin: 8px 0px; + padding: 12px 6px; + } +`; + +const SectionTitle = styled.div` + font-size: 22px; + font-weight: 500; + line-height: 33px; + letter-spacing: -0.019em; + text-align: left; + margin-bottom: 20px; + color: ${(props) => props.theme.default.color}; + + @media ${device.tablet} { + text-align: center; + } +`; + +const SettingsListItem = styled.div` + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + margin: 12px 0px; +`; + +const Icon = styled(ImageV2)` + border: 1px solid ${(props) => props.theme.default.borderColor}; + border-radius: 8px; + overflow: hidden; + margin-right: 16px; + width: 28px; + height: 28px; +`; + +const ChannelName = styled.span` + font-size: 15px; + font-weight: 400; + line-height: 23px; + letter-spacing: 0em; + color: ${(props) => props.theme.default.color}; +`; + +const SettingsListRow = styled.div` + display: flex; + flex-direction: row; + align-items: center; +`; + +const HR = styled.span` + background-color: ${(props) => props.theme.default.borderColor}; + width: 100%; + display: flex; + height: 1px; +`; + +const MoreButtonUI = styled(AiOutlineMore)` + background: transparent; + display: flex; + cursor: pointer; + width: 24px; + height: 24px; + padding: 0px; + position: relative; + width: 24px; + height: 24px; + color: ${(props) => props.theme.default.color}; +`; + +const CenterContainer = styled.div` + display: flex; + justify-content: center; + align-items: center; + height: 80%; +`; diff --git a/src/components/dropdowns/DropdownBtnHandler.tsx b/src/components/dropdowns/DropdownBtnHandler.tsx index 4fe307d74a..3e9cccca67 100644 --- a/src/components/dropdowns/DropdownBtnHandler.tsx +++ b/src/components/dropdowns/DropdownBtnHandler.tsx @@ -1,7 +1,7 @@ import React, { useRef } from 'react'; // External Packages -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; // Internal Configs import { useClickAway } from 'hooks/useClickAway'; @@ -14,6 +14,7 @@ interface DropdownBtnHandlerProps { toggleDropdown: () => void; closeDropdown: () => void; containerPadding?: string; + centerOnMobile: boolean; } export const DropdownBtnHandler: React.FC = ({ @@ -22,7 +23,8 @@ export const DropdownBtnHandler: React.FC = ({ showDropdown, toggleDropdown, closeDropdown, - containerPadding + containerPadding, + centerOnMobile, }) => { const dropdownRef = useRef(null); const renderDropdownContainerRef = useRef(null); @@ -33,7 +35,7 @@ export const DropdownBtnHandler: React.FC = ({ {children} {showDropdown && ( - +
e.stopPropagation()}> {renderDropdownContainer}
@@ -52,7 +54,7 @@ const Container = styled.button` outline: 0; ` -const DropdownContainer = styled(ItemHV2)<{ containerPadding?: string}>` +const DropdownContainer = styled(ItemHV2)<{ containerPadding?: string, centerOnMobile: boolean }>` background: ${(props)=>props.theme.settingsModalBackground}; border:1px solid; border-color:${(props)=>props.theme.settingsModalBorderColor}; @@ -65,8 +67,10 @@ const DropdownContainer = styled(ItemHV2)<{ containerPadding?: string}>` right:-0.5rem; @media (max-width:768px){ - left: 50%; - transform: translateX(-50%); + ${(props) => props.centerOnMobile && css` + left: 50%; + transform: translateX(-50%); + `} width: fit-content; } -`; \ No newline at end of file +`; diff --git a/src/components/dropdowns/ManageNotifSettingDropdown.tsx b/src/components/dropdowns/ManageNotifSettingDropdown.tsx index 5ba7316944..931c85392d 100644 --- a/src/components/dropdowns/ManageNotifSettingDropdown.tsx +++ b/src/components/dropdowns/ManageNotifSettingDropdown.tsx @@ -13,16 +13,17 @@ import { ImageV2, SpanV2 } from "components/reusables/SharedStylingV2"; interface ManageNotifSettingDropdownProps { children: React.ReactNode; + centerOnMobile: boolean; } -const ManageNotifSettingDropdownContainer: React.FC = () => { +const ManageNotifSettingDropdownContainer: React.FC<{centerOnMobile: boolean}> = ({centerOnMobile}) => { const theme = useTheme(); return ( - + { ); }; -const ManageNotifSettingDropdown: React.FC = ({ children }) => { +const ManageNotifSettingDropdown: React.FC = ({ children, centerOnMobile }) => { const [isOpen, setIsOpen] = useState(false); const toggleDropdown = () => { @@ -64,8 +65,9 @@ const ManageNotifSettingDropdown: React.FC = ({ showDropdown={isOpen} toggleDropdown={toggleDropdown} closeDropdown={closeDropdown} - renderDropdownContainer={} + renderDropdownContainer={} containerPadding="12px 16px" + centerOnMobile={centerOnMobile} > {children} diff --git a/src/components/dropdowns/OptinNotifSettingDropdown.tsx b/src/components/dropdowns/OptinNotifSettingDropdown.tsx index 3e231a9579..f0bb905bee 100644 --- a/src/components/dropdowns/OptinNotifSettingDropdown.tsx +++ b/src/components/dropdowns/OptinNotifSettingDropdown.tsx @@ -139,7 +139,8 @@ const OptinNotifSettingDropdown: React.FC = ({ c // render return ( - = ({ settings }) => { @@ -93,7 +94,7 @@ const UpdateNotifSettingDropdownContainer: React.FC<{ settings: any[] }> = ({ se }; // Faucet URLs -const UpdateNotifSettingDropdown: React.FC = ({ children }) => { +const UpdateNotifSettingDropdown: React.FC = ({ children, centerOnMobile }) => { const [isOpen, setIsOpen] = useState(false); const toggleDropdown = () => { @@ -140,6 +141,7 @@ const UpdateNotifSettingDropdown: React.FC = ({ // render return ( { - ReactGA.pageview("/channels"); + ReactGA.pageview(APP_PATHS.Channels); // Render return ( diff --git a/src/modules/faq/FaqModule.tsx b/src/modules/faq/FaqModule.tsx index 98136d2482..d84f3ba6f6 100644 --- a/src/modules/faq/FaqModule.tsx +++ b/src/modules/faq/FaqModule.tsx @@ -237,7 +237,7 @@ function FaqModule() { Easiest way to create a channel is from our{' '} - + Push (EPNS) Dapp {' '} itself. Find the entire channel creation process{' '} diff --git a/src/modules/inbox/InboxModule.tsx b/src/modules/inbox/InboxModule.tsx index a38666151c..3fa6a3c4ab 100644 --- a/src/modules/inbox/InboxModule.tsx +++ b/src/modules/inbox/InboxModule.tsx @@ -23,6 +23,7 @@ import { useAccount } from 'hooks'; // Internal Configs import { abis, addresses, appConfig, CHAIN_DETAILS } from 'config'; import GLOBALS, { device, globalsMargin } from 'config/Globals'; +import APP_PATHS from 'config/AppPaths'; // Constants export const ALLOWED_CORE_NETWORK = appConfig.coreContractChain; @@ -30,7 +31,7 @@ export const ALLOWED_CORE_NETWORK = appConfig.coreContractChain; // Create Inbox Module const InboxModule = ({isSpam}) => { // React GA Analytics - ReactGA.pageview('/inbox'); + ReactGA.pageview(APP_PATHS.Inbox) const dispatch = useDispatch(); const { account, chainId, provider } = useAccount(); diff --git a/src/modules/userSettings/UserSettingsModule.tsx b/src/modules/userSettings/UserSettingsModule.tsx new file mode 100644 index 0000000000..bfb1bcba4e --- /dev/null +++ b/src/modules/userSettings/UserSettingsModule.tsx @@ -0,0 +1,58 @@ +// React + Web3 Essentials +import React from 'react'; + +// External Packages +import styled from 'styled-components'; + +// Internal Compoonents +import UserSettings from 'components/channel/UserSettings'; + +// Internal Configs +import GLOBALS, { device, globalsMargin } from 'config/Globals'; + +// Create Header +const UserSettingsModule = () => { + return ( + + + + ); +} + +// css styles +const Container = styled.div` + align-items: stretch; + align-self: stretch; + flex: 1; + background: ${(props) => props.theme.default.bg}; + border-radius: ${GLOBALS.ADJUSTMENTS.RADIUS.LARGE}; + box-shadow: ${GLOBALS.ADJUSTMENTS.MODULE_BOX_SHADOW}; + display: flex; + flex-direction: column; + flex: initial; + justify-content: center; + position: relative; + overflow: hidden; + box-sizing: border-box; + + margin: ${GLOBALS.ADJUSTMENTS.MARGIN.MINI_MODULES.DESKTOP}; + height: calc(100vh - ${GLOBALS.CONSTANTS.HEADER_HEIGHT}px - ${globalsMargin.MINI_MODULES.DESKTOP.TOP} - ${ + globalsMargin.MINI_MODULES.DESKTOP.BOTTOM +}); + + @media ${device.laptop} { + margin: ${GLOBALS.ADJUSTMENTS.MARGIN.MINI_MODULES.TABLET}; + height: calc(100vh - ${GLOBALS.CONSTANTS.HEADER_HEIGHT}px - ${globalsMargin.MINI_MODULES.TABLET.TOP} - ${ + globalsMargin.MINI_MODULES.TABLET.BOTTOM + }); + } + + @media ${device.mobileL} { + margin: ${GLOBALS.ADJUSTMENTS.MARGIN.MINI_MODULES.MOBILE}; + height: calc(100vh - ${GLOBALS.CONSTANTS.HEADER_HEIGHT}px - ${globalsMargin.MINI_MODULES.MOBILE.TOP}); + border: ${GLOBALS.ADJUSTMENTS.RADIUS.LARGE}; + border-radius: ${GLOBALS.ADJUSTMENTS.RADIUS.LARGE} ${GLOBALS.ADJUSTMENTS.RADIUS.LARGE} 0 0; +`; + +// Export Default +export default UserSettingsModule; diff --git a/src/pages/NotAvailablePage.tsx b/src/pages/NotAvailablePage.tsx index 6b9ad9cdfc..7e8e1982a7 100644 --- a/src/pages/NotAvailablePage.tsx +++ b/src/pages/NotAvailablePage.tsx @@ -11,6 +11,7 @@ import { useAccount } from 'hooks'; // Internal Configs import { appConfig } from "config"; +import APP_PATHS from "config/AppPaths"; function NotAvailablePage(props) { const themes = useTheme(); @@ -20,7 +21,7 @@ function NotAvailablePage(props) { React.useEffect(() => { if (onCoreNetwork) { const url = window.location.origin; - window.location.replace(`${url}/#/channels`); + window.location.replace(`${url}${APP_PATHS.Channels}`); } }) diff --git a/src/pages/SpamPage.js b/src/pages/SpamPage.js index 3765ceb60f..3b82ea5913 100644 --- a/src/pages/SpamPage.js +++ b/src/pages/SpamPage.js @@ -7,10 +7,11 @@ import styled from "styled-components"; // Internal Components import Spambox from "segments/Spambox"; +import APP_PATHS from "config/AppPaths"; // Create Header function InboxPage() { - ReactGA.pageview("/spam"); + ReactGA.pageview(APP_PATHS.Spam); // Render return ( diff --git a/src/pages/UserSettingsPage.tsx b/src/pages/UserSettingsPage.tsx new file mode 100644 index 0000000000..435fa15143 --- /dev/null +++ b/src/pages/UserSettingsPage.tsx @@ -0,0 +1,29 @@ +// React + Web3 Essentials +import React from 'react'; + +// External Packages +import styled from 'styled-components'; + +// Internal Components +import { SectionV2 } from 'components/reusables/SharedStylingV2'; +import UserSettingsModule from 'modules/userSettings/UserSettingsModule'; + +// Chat page +const UserSettingsPage = () => { + + // RENDER + return ( + + + + ); +} +export default UserSettingsPage; + +// This defines the page settings, toggle align-self to center if not covering entire stuff, align-items to place them at center +// justify content flex start to start from top, height is defined by module as well as amount of margin, padding +const Container = styled(SectionV2)` + flex: 1; + flex-direction: column; + align-self: stretch; +`; \ No newline at end of file diff --git a/src/primaries/Profile.tsx b/src/primaries/Profile.tsx index ff7f644265..05b4bcb557 100644 --- a/src/primaries/Profile.tsx +++ b/src/primaries/Profile.tsx @@ -12,10 +12,13 @@ import ProfileModal from 'components/ProfileModal'; import Dropdown from '../components/Dropdown'; import { useClickAway } from 'hooks/useClickAway'; import { useResolveWeb3Name } from 'hooks/useResolveWeb3Name'; +import { useAccount } from 'hooks'; + +// Internal Configs +import APP_PATHS from 'config/AppPaths'; import { AppContext } from 'contexts/AppContext'; import { ErrorContext } from 'contexts/ErrorContext'; import { AppContextType } from 'types/context'; -import { useAccount } from 'hooks'; // Create Header const Profile = ({ isDarkMode }) => { @@ -43,6 +46,14 @@ const Profile = ({ isDarkMode }) => { function: ()=>{}, invertedIcon: './copy.svg', }, + { + id: 'userSettings', + value: '', + title: 'Settings', + function: ()=>{}, + to: APP_PATHS.UserSettings, + invertedIcon: 'svg/setting.svg' + }, { id: 'prodDapp', value: '', diff --git a/src/structure/Header.tsx b/src/structure/Header.tsx index 50f7298d29..af72e0469c 100644 --- a/src/structure/Header.tsx +++ b/src/structure/Header.tsx @@ -28,6 +28,34 @@ import MobileNavigation from './MobileNavigation'; import { useAccount, useDeviceWidthCheck } from 'hooks'; import ChainIndicator from 'components/ChainIndicator'; import { UnsupportedChainIdError } from 'connectors/error'; +import APP_PATHS from 'config/AppPaths'; +import { themeDark, themeLight } from 'config/Themization'; + +// header tags for pages that are not there in navigationList (Sidebar) +const EXTRA_HEADER_TAGS = { + [APP_PATHS.UserSettings]: { + title: 'Settings', + light: { + bg: GLOBALS.COLORS.GRADIENT_PRIMARY, + fg: themeLight.headerTagFg, + }, + dark: { + bg: themeDark.headerTagBg, + fg: themeDark.headerTagFg, + }, + }, + [APP_PATHS.ChannelSettings]: { + title: 'Notification Settings', + light: { + bg: GLOBALS.COLORS.GRADIENT_PRIMARY, + fg: themeLight.headerTagFg, + }, + dark: { + bg: themeDark.headerTagBg, + fg: themeDark.headerTagFg, + }, + } +} // Create Header function Header({ isDarkMode, darkModeToggle }) { @@ -71,6 +99,9 @@ function Header({ isDarkMode, darkModeToggle }) { const item = navigationSetup.navigation[key]; if (location.pathname === item.data.href) { setHeaderTag(item.data.headerTag); + } else { + if(EXTRA_HEADER_TAGS[location.pathname]) + setHeaderTag(EXTRA_HEADER_TAGS[location.pathname]); } }); } diff --git a/src/structure/MasterInterfacePage.tsx b/src/structure/MasterInterfacePage.tsx index 56f0ead8df..f98c251d8f 100644 --- a/src/structure/MasterInterfacePage.tsx +++ b/src/structure/MasterInterfacePage.tsx @@ -31,6 +31,7 @@ const SupportPage = lazy(() => import('pages/SupportPage')); const TutorialPage = lazy(() => import('pages/TutorialPage')); // const YieldFarmingPage = lazy(() => import('pages/YieldFarmingPage')); const YieldFarmingV2Page = lazy(() => import('pages/YieldFarmingPageV2')); +const UserSettingsPage = lazy(() => import('pages/UserSettingsPage')); // import AirdropPage from 'pages/AirdropPage'; // import ChannelDashboardPage from 'pages/ChannelDashboardPage'; @@ -62,6 +63,7 @@ import { AppContext } from 'contexts/AppContext'; import { AppContextType } from 'types/context'; import MetamaskPushSnapModal from 'modules/receiveNotifs/MetamaskPushSnapModal'; import { MODAL_POSITION } from 'hooks/useModalBlur'; +import APP_PATHS from 'config/AppPaths'; // Create Header function MasterInterfacePage() { @@ -83,37 +85,38 @@ function MasterInterfacePage() { } > - } /> - } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } /> {/* } /> */} } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> - } /> - } /> + } /> + } /> {/* } /> */} - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> From 437809d58db3bc0b883909f8a107eaf27cd9d57a Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Tue, 10 Oct 2023 14:00:27 +0530 Subject: [PATCH 10/18] Integrated new api and notif settings backend --- package.json | 4 +- src/components/ViewChannelItem.js | 214 +------ .../channel/NotificationSettings.tsx | 2 +- src/components/channel/UserSettings.tsx | 12 +- .../dropdowns/DropdownBtnHandler.tsx | 1 + .../dropdowns/ManageNotifSettingDropdown.tsx | 182 +++++- .../dropdowns/OptinNotifSettingDropdown.tsx | 186 ++++-- .../dropdowns/UpdateNotifSettingDropdown.tsx | 184 ++++-- src/contexts/AppContext.tsx | 30 +- src/helpers/channel/notifSetting.ts | 30 + src/helpers/channel/types.ts | 49 +- src/types/context.ts | 3 + yarn.lock | 595 ++++++++++++++++-- 13 files changed, 1133 insertions(+), 359 deletions(-) create mode 100644 src/helpers/channel/notifSetting.ts diff --git a/package.json b/package.json index 5085ea406d..cce645b5da 100644 --- a/package.json +++ b/package.json @@ -34,9 +34,9 @@ "@mui/lab": "^5.0.0-alpha.72", "@mui/material": "^5.5.0", "@pushprotocol/ledgerlive": "latest", - "@pushprotocol/restapi": "1.4.18", + "@pushprotocol/restapi": "0.0.1-alpha.48", "@pushprotocol/socket": "latest", - "@pushprotocol/uiweb": "1.1.13", + "@pushprotocol/uiweb": "0.0.1-alpha.17", "@reduxjs/toolkit": "^1.7.1", "@testing-library/dom": "^9.0.1", "@testing-library/jest-dom": "^4.2.4", diff --git a/src/components/ViewChannelItem.js b/src/components/ViewChannelItem.js index a2e58cbc68..66240143ff 100644 --- a/src/components/ViewChannelItem.js +++ b/src/components/ViewChannelItem.js @@ -186,12 +186,6 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { const generalToast = useToast(); - // to subscribe - const subscribe = async () => { - console.log('click executed'); - subscribeAction(false); - }; - const formatAddress = (addressText) => { return addressText.length > 40 ? `${shortenText(addressText, 4, 6)}` : addressText; }; @@ -325,125 +319,6 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { }); }; - const subscribeToast = useToast(); - const subscribeAction = async () => { - setTxInProgress(true); - try { - let channelAddress = channelObject.channel; - if (!onCoreNetwork) { - channelAddress = channelObject.alias_address; - } - - subscribeToast.showLoaderToast({ loaderMessage: 'Waiting for Confirmation...' }); - - if (run) { - const type = { - Subscribe: [ - { name: 'channel', type: 'address' }, - { name: 'subscriber', type: 'address' }, - { name: 'action', type: 'string' }, - ], - }; - - const message = { - channel: channelAddress, - subscriber: account, - action: 'Subscribe', - }; - - await provider.getSigner(account)._signTypedData(EPNS_DOMAIN, type, message); - - console.log('in run'); - subscribeToast.showMessageToast({ - toastTitle: 'Success', - toastMessage: 'Successfully opted into channel !', - toastType: 'SUCCESS', - getToastIcon: (size) => ( - - ), - }); - - dispatch( - addNewWelcomeNotif({ - cta: '', - title: channelObject.info, - message: `Welcome to ${channelObject.name} Channel. From now onwards, you'll be getting notifications from this channel`, - icon: channelIcon, - url: channelObject.url, - sid: '', - app: channelObject.name, - image: '', - }) - ); - setTxInProgress(false); - setSubscribed(true); - if (stepIndex === 5) { - console.log('this is working'); - dispatch(incrementStepIndex()); - } - return; - } - - const _signer = await provider.getSigner(account); - await PushAPI.channels.subscribe({ - signer: _signer, - channelAddress: convertAddressToAddrCaip(channelAddress, chainId), // channel address in CAIP - userAddress: convertAddressToAddrCaip(account, chainId), // user address in CAIP - onSuccess: () => { - dispatch(updateSubscriptionStatus({ channelAddress: channelObject.channel, status: true })); - setSubscribed(true); - setSubscriberCount(subscriberCount + 1); - - subscribeToast.showMessageToast({ - toastTitle: 'Success', - toastMessage: 'Successfully opted into channel !', - toastType: 'SUCCESS', - getToastIcon: (size) => ( - - ), - }); - }, - onError: () => { - console.error('opt in error'); - subscribeToast.showMessageToast({ - toastTitle: 'Error', - toastMessage: `There was an error opting into channel`, - toastType: 'ERROR', - getToastIcon: (size) => ( - - ), - }); - }, - env: appConfig.pushNodesEnv, - }); - } catch (err) { - subscribeToast.showMessageToast({ - toastTitle: 'Error', - toastMessage: `There was an error opting into channel ( ${err.message} )`, - toastType: 'ERROR', - getToastIcon: (size) => ( - - ), - }); - - console.log(err); - } finally { - setTxInProgress(false); - } - }; - const copyToClipboard = (address) => { let hostname = window.location.hostname; // if we are on localhost, attach the port @@ -464,73 +339,6 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { } }; - const unsubscribeToast = useToast(); - const unsubscribeAction = async () => { - try { - let channelAddress = channelObject.channel; - if (!onCoreNetwork) { - channelAddress = channelObject.alias_address; - } - - unsubscribeToast.showLoaderToast({ loaderMessage: 'Waiting for Confirmation...' }); - - const _signer = await provider.getSigner(account); - await PushAPI.channels.unsubscribe({ - signer: _signer, - channelAddress: convertAddressToAddrCaip(channelAddress, chainId), // channel address in CAIP - userAddress: convertAddressToAddrCaip(account, chainId), // user address in CAIP - onSuccess: () => { - dispatch(updateSubscriptionStatus({ channelAddress: channelObject.channel, status: false })); - setSubscribed(false); - setSubscriberCount(subscriberCount - 1); - - unsubscribeToast.showMessageToast({ - toastTitle: 'Success', - toastMessage: 'Successfully opted out of channel !', - toastType: 'SUCCESS', - getToastIcon: (size) => ( - - ), - }); - }, - onError: () => { - console.error('opt out error'); - unsubscribeToast.showMessageToast({ - toastTitle: 'Error', - toastMessage: `There was an error opting out of channel`, - toastType: 'ERROR', - getToastIcon: (size) => ( - - ), - }); - }, - env: appConfig.pushNodesEnv, - }); - } catch (err) { - unsubscribeToast.showMessageToast({ - toastTitle: 'Error', - toastMessage: `There was an error opting out of channel ( ${err.message} )`, - toastType: 'ERROR', - getToastIcon: (size) => ( - - ), - }); - - console.log(err); - } finally { - setTxInProgress(false); - } - }; - const correctChannelTitleLink = () => { const channelLink = CTA_OVERRIDE_CACHE[channelObject.channel] || channelObject.url; if (/(?:http|https):\/\//i.test(channelLink)) { @@ -1041,7 +849,15 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { <> {isOwner && Owner} {!isOwner && ( - + { + dispatch(updateSubscriptionStatus({ channelAddress: channelObject.channel, status: true })); + setSubscribed(true); + setSubscriberCount((prevSubscriberCount) => prevSubscriberCount + 1) + }} + > {}} disabled={txInProgress} @@ -1066,7 +882,17 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { <> {isOwner && Owner} {!isOwner && ( - + { + dispatch(updateSubscriptionStatus({ channelAddress: channelObject.channel, status: false })); + setSubscribed(false); + setSubscriberCount((prevSubscriberCount) => prevSubscriberCount - 1) + }} + > {}} disabled={txInProgress} diff --git a/src/components/channel/NotificationSettings.tsx b/src/components/channel/NotificationSettings.tsx index 0cb4cdf0a5..4e7a4c0d94 100644 --- a/src/components/channel/NotificationSettings.tsx +++ b/src/components/channel/NotificationSettings.tsx @@ -138,7 +138,7 @@ function NotificationSettings() { const parsedFees = ethers.utils.parseUnits(feesRequiredForEdit.toString(), 18); notificationToast.showLoaderToast({ loaderMessage: 'Waiting for Confirmation...' }); - const notifOptions = ethers.utils.parseUnits('2', 18); + const notifOptions = settings.length; let _notifSettings = ''; let _notifDescription = ''; settings.forEach((setting) => { diff --git a/src/components/channel/UserSettings.tsx b/src/components/channel/UserSettings.tsx index 45209eaa74..bbd6c2a41d 100644 --- a/src/components/channel/UserSettings.tsx +++ b/src/components/channel/UserSettings.tsx @@ -26,6 +26,7 @@ interface ChannelListItem { icon: string; name: string; id: number; + channel_settings: string; } function UserSettings() { @@ -47,6 +48,7 @@ function UserSettings() { id: details.id, icon: details.icon, name: details.name, + channel_settings: details.channel_settings }; return updatedChannelItem; } else return undefined; @@ -127,7 +129,15 @@ function UserSettings() { {channel.name} - + { + setChannelList((prevChannelList) => + prevChannelList.filter((item) => item?.id !== channel.id) + ); + }} + > diff --git a/src/components/dropdowns/DropdownBtnHandler.tsx b/src/components/dropdowns/DropdownBtnHandler.tsx index 3e9cccca67..0841041295 100644 --- a/src/components/dropdowns/DropdownBtnHandler.tsx +++ b/src/components/dropdowns/DropdownBtnHandler.tsx @@ -59,6 +59,7 @@ const DropdownContainer = styled(ItemHV2)<{ containerPadding?: string, centerOnM border:1px solid; border-color:${(props)=>props.theme.settingsModalBorderColor}; border-radius: 8px; + box-shadow: 0px 4px 8px 0px rgba(0, 0, 0, 0.05); align-items:flex-start; padding: ${props => props.containerPadding ? props.containerPadding : '7px 14px'}; position:absolute; diff --git a/src/components/dropdowns/ManageNotifSettingDropdown.tsx b/src/components/dropdowns/ManageNotifSettingDropdown.tsx index 931c85392d..b9dc820fd7 100644 --- a/src/components/dropdowns/ManageNotifSettingDropdown.tsx +++ b/src/components/dropdowns/ManageNotifSettingDropdown.tsx @@ -1,8 +1,8 @@ // React + Web3 Essentials -import React, { useState } from "react"; +import React, { useContext, useMemo, useState } from "react"; // External Packages -import styled, { useTheme } from "styled-components"; +import styled, { css, useTheme } from "styled-components"; // Internal Components import { DropdownBtnHandler } from "./DropdownBtnHandler"; @@ -10,46 +10,109 @@ import UpdateNotifSettingDropdown from "./UpdateNotifSettingDropdown"; // Internal Configs import { ImageV2, SpanV2 } from "components/reusables/SharedStylingV2"; +import { useAccount } from "hooks"; +import { AppContext } from "contexts/AppContext"; +import useToast from "hooks/useToast"; +import { appConfig } from "config"; +import { MdCheckCircle, MdError } from "react-icons/md"; +import LoaderSpinner, { LOADER_TYPE } from "components/reusables/loaders/LoaderSpinner"; +import { convertAddressToAddrCaip } from "helpers/CaipHelper"; +import { ChannelSetting, UserSetting } from "helpers/channel/types"; interface ManageNotifSettingDropdownProps { children: React.ReactNode; centerOnMobile: boolean; + channelDetail: any; + userSetting?: UserSetting[]; + onSuccessOptout: () => void; } -const ManageNotifSettingDropdownContainer: React.FC<{centerOnMobile: boolean}> = ({centerOnMobile}) => { +interface ManageNotifSettingDropdownContainerProps { + centerOnMobile: boolean; + userSetting?: UserSetting[]; + channelSetting?: ChannelSetting[]; + channelDetail: any; + optOutHandler: (options: { setLoading?: React.Dispatch> }) => Promise; + closeDropdown: () => void; +} + +const ManageNotifSettingDropdownContainer: React.FC = ({ + centerOnMobile, + optOutHandler, + channelSetting, + channelDetail, + userSetting, + closeDropdown +}) => { + const [txInProgress, setTxInProgress] = useState(false); const theme = useTheme(); return ( + {channelSetting && - - - - Manage Settings - - + + + + + Manage Settings + + + - +} + optOutHandler({ setLoading: setTxInProgress })}> - Opt-out + + {txInProgress && + + } + {!txInProgress && Opt-out} + ); }; -const ManageNotifSettingDropdown: React.FC = ({ children, centerOnMobile }) => { +const ManageNotifSettingDropdown: React.FC = (options) => { + const { + children, + centerOnMobile, + userSetting, + channelDetail, + onSuccessOptout + } = options; const [isOpen, setIsOpen] = useState(false); + const { chainId } = useAccount(); + const { userPushSDKInstance } = useContext(AppContext); + + const channelSetting = useMemo(() => { + if(channelDetail && channelDetail?.channel_settings) { + return JSON.parse(channelDetail?.channel_settings); + } + return null; + }, [channelDetail]); const toggleDropdown = () => { setIsOpen(!isOpen); @@ -59,13 +122,88 @@ const ManageNotifSettingDropdown: React.FC = ({ setIsOpen(false); }; + const onCoreNetwork = chainId === appConfig.coreContractChain; + + const unsubscribeToast = useToast(); + const optOutHandler = async ({ setLoading }: { setLoading?: React.Dispatch> }) => { + const setLoadingFunc = setLoading || (() => {}); + setLoadingFunc(true); + + try { + let channelAddress = channelDetail.channel; + if (!onCoreNetwork) { + channelAddress = channelDetail.alias_address; + } + + unsubscribeToast.showLoaderToast({ loaderMessage: 'Waiting for Confirmation...' }); + + await userPushSDKInstance.notification.unsubscribe(convertAddressToAddrCaip(channelAddress, chainId), { + onSuccess: () => { + onSuccessOptout(); + + unsubscribeToast.showMessageToast({ + toastTitle: 'Success', + toastMessage: 'Successfully opted out of channel !', + toastType: 'SUCCESS', + getToastIcon: (size) => ( + + ), + }); + + closeDropdown(); + }, + onError: () => { + console.error('opt in error'); + unsubscribeToast.showMessageToast({ + toastTitle: 'Error', + toastMessage: `There was an error opting out of channel`, + toastType: 'ERROR', + getToastIcon: (size) => ( + + ), + }); + }, + }); + } catch (err) { + unsubscribeToast.showMessageToast({ + toastTitle: 'Error', + toastMessage: `There was an error opting into channel ( ${err.message} )`, + toastType: 'ERROR', + getToastIcon: (size) => ( + + ), + }); + + console.log(err); + } finally { + setLoadingFunc(false); + } + }; + // render return ( } + renderDropdownContainer={ + } containerPadding="12px 16px" centerOnMobile={centerOnMobile} > @@ -98,3 +236,11 @@ const DropdownBtn = styled.button` cursor: pointer; gap: 8px; `; + +const ActionTitle = styled.span<{ hideIt: boolean }>` + ${(props) => + props.hideIt && + css` + visibility: hidden; + `}; +`; \ No newline at end of file diff --git a/src/components/dropdowns/OptinNotifSettingDropdown.tsx b/src/components/dropdowns/OptinNotifSettingDropdown.tsx index f0bb905bee..a687981a91 100644 --- a/src/components/dropdowns/OptinNotifSettingDropdown.tsx +++ b/src/components/dropdowns/OptinNotifSettingDropdown.tsx @@ -1,5 +1,5 @@ // React + Web3 Essentials -import React, { useState } from "react"; +import React, { useContext, useMemo, useState } from "react"; // External Packages import Switch from 'react-switch'; @@ -11,27 +11,53 @@ import { DropdownBtnHandler } from "./DropdownBtnHandler"; // Internal Configs import { SpanV2 } from "components/reusables/SharedStylingV2"; +import { useAccount } from "hooks"; +import { appConfig } from "config"; +import { convertAddressToAddrCaip } from "helpers/CaipHelper"; +import useToast from "hooks/useToast"; +import { MdCheckCircle, MdError } from "react-icons/md"; +import { ChannelSetting } from "helpers/channel/types"; +import { notifChannelSettingFormatString } from "helpers/channel/notifSetting"; +import { AppContext } from "contexts/AppContext"; +import LoaderSpinner, { LOADER_TYPE } from "components/reusables/loaders/LoaderSpinner"; interface OptinNotifSettingDropdownProps { children: React.ReactNode; + channelDetail: any; + setLoading: (loading: boolean) => {}; + onSuccessOptin: () => {}; } -const OptinNotifSettingDropdownContainer: React.FC<{ settings: any[] }> = ({ settings }) => { +interface OptinNotifSettingDropdownContainerProps { + settings: ChannelSetting[]; + optInHandler: (options: { channelSettings?: ChannelSetting[], setLoading?: React.Dispatch> }) => Promise; +} + +const OptinNotifSettingDropdownContainer: React.FC = ({ settings, optInHandler }) => { const [modifiedSettings, setModifiedSettings] = useState([...settings]); + const [txInProgress, setTxInProgress] = useState(false); const theme = useTheme(); - console.log(modifiedSettings); - const handleSliderChange = (index: number, value: number) => { const updatedSettings = [...modifiedSettings]; - updatedSettings[index].defaultValue = value; + updatedSettings[index].default = value; setModifiedSettings(updatedSettings); }; const handleSwitchChange = (index: number) => { const updatedSettings = [...modifiedSettings]; - updatedSettings[index].default = !updatedSettings[index].default; + if(updatedSettings[index].type === 1) { + // Type 1 + // Use a type guard to narrow the type to ChannelSetting of type 1 + const setting = updatedSettings[index] as ChannelSetting & { type: 1 }; + setting.default = !setting.default; + } else { + // Type 2 + // Use a type guard to narrow the type to ChannelSetting of type 2 + const setting = updatedSettings[index] as ChannelSetting & { type: 2 }; + setting.enabled = !setting.enabled; + } setModifiedSettings(updatedSettings); }; @@ -45,7 +71,7 @@ const OptinNotifSettingDropdownContainer: React.FC<{ settings: any[] }> = ({ set {setting.description} handleSwitchChange(index)} checked={setting.default} + onChange={() => handleSwitchChange(index)} checked={setting.type === 1 ? setting.default : setting.enabled} checkedIcon={false} uncheckedIcon={false} onColor="#D53A94" @@ -55,7 +81,7 @@ const OptinNotifSettingDropdownContainer: React.FC<{ settings: any[] }> = ({ set handleDiameter={12} /> - {setting.type === 2 && setting.default && ( + {setting.type === 2 && setting.enabled && ( = ({ set } }} axis="x" - x={setting.defaultValue} + x={setting.default} onChange={({ x }) => handleSliderChange(index, x)} xstep={1} xmin={setting.lowerLimit} xmax={setting.upperLimit} /> - {setting.defaultValue} + {setting.default} )} ))} You will receive all important updates from this channel. - Opt-in + optInHandler({ channelSettings: modifiedSettings, setLoading: setTxInProgress })} + > + {txInProgress && + + } + {!txInProgress && Opt-in} + ); }; // Faucet URLs -const OptinNotifSettingDropdown: React.FC = ({ children }) => { +const OptinNotifSettingDropdown: React.FC = (options) => { + const { children, channelDetail, setLoading, onSuccessOptin } = options; + + const { chainId } = useAccount(); + const { userPushSDKInstance } = useContext(AppContext); const [isOpen, setIsOpen] = useState(false); + const onCoreNetwork = chainId === appConfig.coreContractChain; + + const channelSetting = useMemo(() => { + if(channelDetail && channelDetail?.channel_settings) { + return JSON.parse(channelDetail?.channel_settings); + } + return null; + }, [channelDetail]); + const toggleDropdown = () => { setIsOpen(!isOpen); }; @@ -104,51 +154,89 @@ const OptinNotifSettingDropdown: React.FC = ({ c setIsOpen(false); }; - const settings = [ - { - "type": 1, - "index": 1, - "default": false, - "description": "Giveaway Alerts" - }, - { - "type": 2, - "index": 2, - "default": true, - "defaultValue": 50, - "lowerLimit": 20, - "upperLimit": 100, - "description": "ETH Price Drop Alert" - }, - { - "type": 1, - "index": 3, - "default": true, - "description": "Partnership Announcements" - }, - { - "type": 2, - "index": 4, - "default": false, - "defaultValue": 45, - "lowerLimit": 10, - "upperLimit": 150, - "description": "Marketing" + const subscribeToast = useToast(); + const optInHandler = async ({ channelSettings, setLoading }: { channelSettings?: ChannelSetting[], setLoading?: React.Dispatch> }) => { + const setLoadingFunc = setLoading || (options && options.setLoading) || (() => {}); + setLoadingFunc(true); + + try { + let channelAddress = channelDetail.channel; + if (!onCoreNetwork) { + channelAddress = channelDetail.alias_address; + } + + subscribeToast.showLoaderToast({ loaderMessage: 'Waiting for Confirmation...' }); + + await userPushSDKInstance.notification.subscribe(convertAddressToAddrCaip(channelAddress, chainId), { + settings: notifChannelSettingFormatString({ settings: channelSettings }), + // settings: [], + onSuccess: () => { + onSuccessOptin(); + + subscribeToast.showMessageToast({ + toastTitle: 'Success', + toastMessage: 'Successfully opted into channel !', + toastType: 'SUCCESS', + getToastIcon: (size) => ( + + ), + }); + }, + onError: () => { + console.error('opt in error'); + subscribeToast.showMessageToast({ + toastTitle: 'Error', + toastMessage: `There was an error opting into channel`, + toastType: 'ERROR', + getToastIcon: (size) => ( + + ), + }); + }, + }); + } catch (err) { + subscribeToast.showMessageToast({ + toastTitle: 'Error', + toastMessage: `There was an error opting into channel ( ${err.message} )`, + toastType: 'ERROR', + getToastIcon: (size) => ( + + ), + }); + + console.log(err); + } finally { + setLoadingFunc(false); } -] + }; // render return ( + channelSetting ? } + renderDropdownContainer={} containerPadding="0px 16px 16px 16px" > {children} + : + + {children} + + ); } @@ -190,7 +278,7 @@ const DropdownSubmitButton = styled.button` outline: 0; display: flex; align-items: center; - min-width: max-content; + min-width: 90px; justify-content: center; margin: 0px 0px 0px 10px; color: #fff; @@ -232,3 +320,11 @@ const DropdownSliderItem = styled.div` align-items: center; padding-bottom: 10px; `; + +const ActionTitle = styled.span<{ hideIt: boolean }>` + ${(props) => + props.hideIt && + css` + visibility: hidden; + `}; +`; diff --git a/src/components/dropdowns/UpdateNotifSettingDropdown.tsx b/src/components/dropdowns/UpdateNotifSettingDropdown.tsx index 60ee8a02f8..299a2bbd15 100644 --- a/src/components/dropdowns/UpdateNotifSettingDropdown.tsx +++ b/src/components/dropdowns/UpdateNotifSettingDropdown.tsx @@ -1,5 +1,5 @@ // React + Web3 Essentials -import React, { useState } from "react"; +import React, { useContext, useState } from "react"; // External Packages import Switch from 'react-switch'; @@ -11,28 +11,55 @@ import { DropdownBtnHandler } from "./DropdownBtnHandler"; // Internal Configs import { SpanV2 } from "components/reusables/SharedStylingV2"; +import useToast from "hooks/useToast"; +import { useAccount } from "hooks"; +import { AppContext } from "contexts/AppContext"; +import { appConfig } from "config"; +import { ChannelSetting, UserSetting } from "helpers/channel/types"; +import { convertAddressToAddrCaip } from "helpers/CaipHelper"; +import { notifUserSettingFormatString, userSettingsFromDefaultChannelSetting } from "helpers/channel/notifSetting"; +import { MdCheckCircle, MdError } from "react-icons/md"; +import LoaderSpinner, { LOADER_TYPE } from "components/reusables/loaders/LoaderSpinner"; interface UpdateNotifSettingDropdownProps { children: React.ReactNode; centerOnMobile: boolean; + channelDetail: any; + channelSetting?: ChannelSetting[]; + userSetting?: UserSetting[]; + onSuccessSave?: () => void; } -const UpdateNotifSettingDropdownContainer: React.FC<{ settings: any[] }> = ({ settings }) => { +interface UpdateNotifSettingDropdownContainerProps { + settings: UserSetting[]; + saveUserSettingHandler: (options: { userSettings?: UserSetting[], setLoading?: React.Dispatch> }) => Promise; +} + +const UpdateNotifSettingDropdownContainer: React.FC = ({ settings, saveUserSettingHandler }) => { const [modifiedSettings, setModifiedSettings] = useState([...settings]); + const [txInProgress, setTxInProgress] = useState(false); const theme = useTheme(); - console.log(modifiedSettings); - const handleSliderChange = (index: number, value: number) => { const updatedSettings = [...modifiedSettings]; - updatedSettings[index].defaultValue = value; + updatedSettings[index].user = value; setModifiedSettings(updatedSettings); }; const handleSwitchChange = (index: number) => { const updatedSettings = [...modifiedSettings]; - updatedSettings[index].default = !updatedSettings[index].default; + if(updatedSettings[index].type === 1) { + // Type 1 + // Use a type guard to narrow the type to UserSetting of type 1 + const setting = updatedSettings[index] as UserSetting & { type: 1 }; + setting.user = !setting.user; + } else { + // Type 2 + // Use a type guard to narrow the type to UserSetting of type 2 + const setting = updatedSettings[index] as UserSetting & { type: 2 }; + setting.enabled = !setting.enabled; + } setModifiedSettings(updatedSettings); }; @@ -46,7 +73,7 @@ const UpdateNotifSettingDropdownContainer: React.FC<{ settings: any[] }> = ({ se {setting.description} handleSwitchChange(index)} checked={setting.default} + onChange={() => handleSwitchChange(index)} checked={setting.type === 1 ? setting.user : setting.enabled} checkedIcon={false} uncheckedIcon={false} onColor="#D53A94" @@ -56,7 +83,7 @@ const UpdateNotifSettingDropdownContainer: React.FC<{ settings: any[] }> = ({ se handleDiameter={12} /> - {setting.type === 2 && setting.default && ( + {setting.type === 2 && setting.enabled && ( = ({ se } }} axis="x" - x={setting.defaultValue} + x={setting.user} onChange={({ x }) => handleSliderChange(index, x)} xstep={1} xmin={setting.lowerLimit} xmax={setting.upperLimit} /> - {setting.defaultValue} + {setting.user} )} ))} You will receive all important updates from this channel. - Save + saveUserSettingHandler({ userSettings: modifiedSettings, setLoading: setTxInProgress })} + > + {txInProgress && + + } + {!txInProgress && Save} + ); }; // Faucet URLs -const UpdateNotifSettingDropdown: React.FC = ({ children, centerOnMobile }) => { +const UpdateNotifSettingDropdown: React.FC = ({ + children, + centerOnMobile, + channelDetail, + channelSetting, + userSetting, + onSuccessSave +}) => { const [isOpen, setIsOpen] = useState(false); + const { chainId } = useAccount(); + const { userPushSDKInstance } = useContext(AppContext); + + const onCoreNetwork = chainId === appConfig.coreContractChain; + const toggleDropdown = () => { setIsOpen(!isOpen); }; @@ -105,38 +155,72 @@ const UpdateNotifSettingDropdown: React.FC = ({ setIsOpen(false); }; - const settings = [ - { - "type": 1, - "index": 1, - "default": false, - "description": "Giveaway Alerts" - }, - { - "type": 2, - "index": 2, - "default": true, - "defaultValue": 50, - "lowerLimit": 20, - "upperLimit": 100, - "description": "ETH Price Drop Alert" - }, - { - "type": 1, - "index": 3, - "default": true, - "description": "Partnership Announcements" - }, - { - "type": 2, - "index": 4, - "default": false, - "defaultValue": 45, - "lowerLimit": 10, - "upperLimit": 150, - "description": "Marketing" + const subscribeToast = useToast(); + const saveUserSettingHandler = async ({ userSettings, setLoading }: { userSettings?: UserSetting[], setLoading?: React.Dispatch> }) => { + const setLoadingFunc = setLoading || (() => {}); + const saveOnSuccessSettingFunc = onSuccessSave || (() => {}); + setLoadingFunc(true); + + try { + let channelAddress = channelDetail.channel; + if (!onCoreNetwork) { + channelAddress = channelDetail.alias_address; + } + + subscribeToast.showLoaderToast({ loaderMessage: 'Waiting for Confirmation...' }); + + await userPushSDKInstance.notification.subscribe(convertAddressToAddrCaip(channelAddress, chainId), { + settings: notifUserSettingFormatString({ settings: userSettings }), + // settings: [], + onSuccess: () => { + saveOnSuccessSettingFunc(); + closeDropdown(); + + subscribeToast.showMessageToast({ + toastTitle: 'Success', + toastMessage: 'Successfully saved the user settings!', + toastType: 'SUCCESS', + getToastIcon: (size) => ( + + ), + }); + }, + onError: () => { + console.error('opt in error'); + subscribeToast.showMessageToast({ + toastTitle: 'Error', + toastMessage: `There was an error in saving the settings`, + toastType: 'ERROR', + getToastIcon: (size) => ( + + ), + }); + }, + }); + } catch (err) { + subscribeToast.showMessageToast({ + toastTitle: 'Error', + toastMessage: `There was an error in saving the settings ( ${err.message} )`, + toastType: 'ERROR', + getToastIcon: (size) => ( + + ), + }); + + console.log(err); + } finally { + setLoadingFunc(false); } -] + }; // render return ( @@ -145,7 +229,11 @@ const UpdateNotifSettingDropdown: React.FC = ({ showDropdown={isOpen} toggleDropdown={toggleDropdown} closeDropdown={closeDropdown} - renderDropdownContainer={} + renderDropdownContainer={ + } containerPadding="0px 16px 16px 16px" > {children} @@ -233,3 +321,11 @@ const DropdownSliderItem = styled.div` align-items: center; padding-bottom: 10px; `; + +const ActionTitle = styled.span<{ hideIt: boolean }>` + ${(props) => + props.hideIt && + css` + visibility: hidden; + `}; +`; diff --git a/src/contexts/AppContext.tsx b/src/contexts/AppContext.tsx index cba96971b4..347afb1a74 100644 --- a/src/contexts/AppContext.tsx +++ b/src/contexts/AppContext.tsx @@ -1,14 +1,21 @@ // React + Web3 Essentials import useModalBlur from "hooks/useModalBlur"; -import React,{createContext,useState} from "react" +import React,{createContext,useEffect,useMemo,useState} from "react"; + +// External Packages +import { PushAPI } from "@pushprotocol/restapi"; // Internal Components import { AppContextType, Web3NameListType } from "types/context" +import { appConfig } from "config"; +import { useAccount } from "hooks"; export const AppContext = createContext(null); const AppContextProvider=({children})=>{ const [web3NameList,setWeb3NameList]=useState({}); + const [userPushSDKInstance, setUserPushSDKInstance] = useState(null); + const {account, provider} = useAccount(); const [SnapState, setSnapState] = useState(1); const { @@ -17,10 +24,31 @@ const AppContextProvider=({children})=>{ ModalComponent: MetamaskPushSnapModalComponent, } = useModalBlur(); + useEffect(() => { + const librarySigner = provider?.getSigner(account); + if(!account || !librarySigner || !appConfig?.appEnv) return; + + const initializePushSDK = async () => { + try { + const userInstance = await PushAPI.initialize(librarySigner, { + env: appConfig.appEnv, // defaults to staging + account: account + }); + + setUserPushSDKInstance(userInstance); + } catch (error) { + // Handle initialization error + } + }; + + initializePushSDK(); + }, [account, appConfig?.appEnv, provider]); + return( setting.type === 1; + +export const notifChannelSettingFormatString = ({ settings }: { settings: ChannelSetting[] }) => { + let _notifSettings = []; + settings && settings.forEach((setting) => + isSettingType1(setting) + ? _notifSettings.push({ enabled: setting.default }) + : _notifSettings.push({ value: setting.default, enabled: (setting as ChannelSetting & { type: 2 }).enabled })); + return _notifSettings; +} + +export const notifUserSettingFormatString = ({ settings }: { settings: UserSetting[] }) => { + let _notifSettings = []; + settings && settings.forEach((setting) => + isSettingType1(setting) + ? _notifSettings.push({ enabled: setting.user }) + : _notifSettings.push({ value: setting.user, enabled: (setting as ChannelSetting & { type: 2 }).enabled })); + return _notifSettings; +} + +export const userSettingsFromDefaultChannelSetting = ({ channelSetting }: { channelSetting: ChannelSetting[] }) => { + let _userSettings = []; + channelSetting && channelSetting.forEach((setting) => + isSettingType1(setting) + ? _userSettings.push({ ...setting, user: setting.default }) + : _userSettings.push({ ...setting, user: setting.default })); + return _userSettings; +}; \ No newline at end of file diff --git a/src/helpers/channel/types.ts b/src/helpers/channel/types.ts index 09616ad0bc..6a0f028f62 100644 --- a/src/helpers/channel/types.ts +++ b/src/helpers/channel/types.ts @@ -1,15 +1,36 @@ export type ChannelSetting = - | { - type: 1; // Boolean - default: boolean; - description: string; - index: number; - } - | { - type: 2; // Range - default: number; - description: string; - index: number; - lowerLimit: number; - upperLimit: number; - }; +| { + type: 1; // Boolean + default: boolean; + description: string; + index: number; + } +| { + type: 2; // Range + default: number; + enabled: boolean; + description: string; + index: number; + lowerLimit: number; + upperLimit: number; + }; + +export type UserSetting = +| { + type: 1; // Boolean + default: boolean; + description: string; + index: number; + user: boolean; + } +| { + type: 2; // Range + default: number; + enabled: boolean; + description: string; + index: number; + lowerLimit: number; + upperLimit: number; + user: number; + }; + \ No newline at end of file diff --git a/src/types/context.ts b/src/types/context.ts index 32e044ac90..576bf0a74a 100644 --- a/src/types/context.ts +++ b/src/types/context.ts @@ -1,3 +1,5 @@ +import { PushAPI } from "@pushprotocol/restapi"; + export interface Web3NameListType { [key: string]: string; } @@ -5,6 +7,7 @@ export interface Web3NameListType { export interface AppContextType { web3NameList: Web3NameListType; setWeb3NameList: (ens: Web3NameListType) => void; + userPushSDKInstance: PushAPI; MetamaskPushSnapModalComponent:any, showMetamaskPushSnap:any, SnapState:number, diff --git a/yarn.lock b/yarn.lock index e66c89183d..0498671339 100644 --- a/yarn.lock +++ b/yarn.lock @@ -74,6 +74,13 @@ __metadata: languageName: node linkType: hard +"@adraffy/ens-normalize@npm:1.9.4": + version: 1.9.4 + resolution: "@adraffy/ens-normalize@npm:1.9.4" + checksum: 7d7fff58ebe2c4961f7e5e61dad123aa6a63fec0df5c84af1fa41079dc05d398599690be4427b3a94d2baa94084544bcfdf2d51cbed7504b9b0583b0960ad550 + languageName: node + linkType: hard + "@ambire/signature-validator@npm:^1.3.1": version: 1.3.1 resolution: "@ambire/signature-validator@npm:1.3.1" @@ -1687,6 +1694,13 @@ __metadata: languageName: node linkType: hard +"@bufbuild/protobuf@npm:^1.3.0": + version: 1.3.1 + resolution: "@bufbuild/protobuf@npm:1.3.1" + checksum: 5ee96282a6259a222ccd0daf56bcab587918253750dda34c1bf2caad0d89bc3796970f7b55b1c7e25bae5d033c8e9dd687638675fe15cac7d9d6558ebfd4dfa8 + languageName: node + linkType: hard + "@ceramicnetwork/3id-did-resolver@npm:^0.4.11": version: 0.4.11 resolution: "@ceramicnetwork/3id-did-resolver@npm:0.4.11" @@ -3663,6 +3677,15 @@ __metadata: languageName: node linkType: hard +"@floating-ui/core@npm:^1.4.2": + version: 1.5.0 + resolution: "@floating-ui/core@npm:1.5.0" + dependencies: + "@floating-ui/utils": ^0.1.3 + checksum: 54b4fe26b3c228746ac5589f97303abf158b80aa5f8b99027259decd68d1c2030c4c637648ebd33dfe78a4212699453bc2bd7537fd5a594d3bd3e63d362f666f + languageName: node + linkType: hard + "@floating-ui/dom@npm:^1.0.1": version: 1.2.5 resolution: "@floating-ui/dom@npm:1.2.5" @@ -3672,6 +3695,16 @@ __metadata: languageName: node linkType: hard +"@floating-ui/dom@npm:^1.1.0": + version: 1.5.3 + resolution: "@floating-ui/dom@npm:1.5.3" + dependencies: + "@floating-ui/core": ^1.4.2 + "@floating-ui/utils": ^0.1.3 + checksum: 00053742064aac70957f0bd5c1542caafb3bfe9716588bfe1d409fef72a67ed5e60450d08eb492a77f78c22ed1ce4f7955873cc72bf9f9caf2b0f43ae3561c21 + languageName: node + linkType: hard + "@floating-ui/dom@npm:^1.3.0": version: 1.5.1 resolution: "@floating-ui/dom@npm:1.5.1" @@ -3701,6 +3734,13 @@ __metadata: languageName: node linkType: hard +"@floating-ui/utils@npm:^0.1.3": + version: 0.1.6 + resolution: "@floating-ui/utils@npm:0.1.6" + checksum: b34d4b5470869727f52e312e08272edef985ba5a450a76de0917ba0a9c6f5df2bdbeb99448e2c60f39b177fb8981c772ff1831424e75123471a27ebd5b52c1eb + languageName: node + linkType: hard + "@fontsource/ibm-plex-mono@npm:^4.5.1": version: 4.5.13 resolution: "@fontsource/ibm-plex-mono@npm:4.5.13" @@ -4496,6 +4536,44 @@ __metadata: languageName: node linkType: hard +"@livekit/components-core@npm:0.7.0": + version: 0.7.0 + resolution: "@livekit/components-core@npm:0.7.0" + dependencies: + "@floating-ui/dom": ^1.1.0 + email-regex: ^5.0.0 + global-tld-list: ^0.0.1139 + loglevel: ^1.8.1 + rxjs: ^7.8.0 + peerDependencies: + livekit-client: ^1.12.0 + checksum: 8d225b6160197fa6fe74ec29226d0a58a4eca4e46978617f632a08a6295cbac49b5f1d8f06df89ea1e1476471ae3a141bb1719e142ac4686781ccf8adff7aa94 + languageName: node + linkType: hard + +"@livekit/components-react@npm:^1.2.2": + version: 1.3.0 + resolution: "@livekit/components-react@npm:1.3.0" + dependencies: + "@livekit/components-core": 0.7.0 + "@react-hook/latest": ^1.0.3 + clsx: ^2.0.0 + usehooks-ts: ^2.9.1 + peerDependencies: + livekit-client: ^1.12.0 + react: ">=18" + react-dom: ">=18" + checksum: 1f71f688b6e5aaa4d221ffda67fceb0a9fbeeffb8b9fa9a282d7912ea5b786c1aef688811ad660acd5f18c8d7ec040be21dd28811988e6359acc7196c3fa047d + languageName: node + linkType: hard + +"@livekit/components-styles@npm:^1.0.6": + version: 1.0.6 + resolution: "@livekit/components-styles@npm:1.0.6" + checksum: 5422458a2b442024b38b8b74313e417fd5feaa0b974e416eed45de4feb05b714175ba03ffb8f3cb04d8efd729f26f8f601d96d94d255805891124a84947f620e + languageName: node + linkType: hard + "@livepeer/core-react@npm:^1.8.0": version: 1.8.0 resolution: "@livepeer/core-react@npm:1.8.0" @@ -5104,7 +5182,7 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:^1.0.0": +"@noble/curves@npm:1.2.0, @noble/curves@npm:^1.0.0, @noble/curves@npm:~1.2.0": version: 1.2.0 resolution: "@noble/curves@npm:1.2.0" dependencies: @@ -5120,7 +5198,7 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.3.2, @noble/hashes@npm:^1.3.1": +"@noble/hashes@npm:1.3.2, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.2": version: 1.3.2 resolution: "@noble/hashes@npm:1.3.2" checksum: fe23536b436539d13f90e4b9be843cc63b1b17666a07634a2b1259dded6f490be3d050249e6af98076ea8f2ea0d56f578773c2197f2aa0eeaa5fba5bc18ba474 @@ -5345,9 +5423,9 @@ __metadata: "@mui/lab": ^5.0.0-alpha.72 "@mui/material": ^5.5.0 "@pushprotocol/ledgerlive": latest - "@pushprotocol/restapi": 1.4.18 + "@pushprotocol/restapi": 0.0.1-alpha.48 "@pushprotocol/socket": latest - "@pushprotocol/uiweb": 1.1.13 + "@pushprotocol/uiweb": 0.0.1-alpha.17 "@reduxjs/toolkit": ^1.7.1 "@testing-library/dom": ^6.12.2 "@testing-library/jest-dom": ^4.2.4 @@ -5547,12 +5625,13 @@ __metadata: languageName: node linkType: hard -"@pushprotocol/restapi@npm:1.4.18": - version: 1.4.18 - resolution: "@pushprotocol/restapi@npm:1.4.18" +"@pushprotocol/restapi@npm:0.0.1-alpha.48": + version: 0.0.1-alpha.48 + resolution: "@pushprotocol/restapi@npm:0.0.1-alpha.48" dependencies: "@ambire/signature-validator": ^1.3.1 "@metamask/eth-sig-util": ^5.0.2 + "@pushprotocol/socket": ^0.5.2 axios: ^0.27.2 buffer: ^6.0.3 crypto-js: ^4.1.1 @@ -5561,13 +5640,15 @@ __metadata: livepeer: ^2.5.8 openpgp: ^5.5.0 simple-peer: ^9.11.1 + socket.io-client: ^4.5.2 tslib: ^2.3.0 unique-names-generator: ^4.7.1 uuid: ^9.0.0 video-stream-merger: ^4.0.1 + viem: ^1.3.0 peerDependencies: ethers: ^5.6.8 - checksum: a350b385db72c5ce106ed940d77acfac11cec1960409b18698d63d3359a84788da503b62fa3fd01f94b7a7d3634614e26346432d833eb0d69b717c00bb627415 + checksum: 49aa8e537574b4fbcefa154dbdde36c66bda285a306794dc259f6cbf7e056fdfe9e2d4a1bd3f4bfa0c0f25370b9687e083fabd0113c309e021a0b702ab42dd40 languageName: node linkType: hard @@ -5583,6 +5664,18 @@ __metadata: languageName: node linkType: hard +"@pushprotocol/socket@npm:^0.5.2": + version: 0.5.2 + resolution: "@pushprotocol/socket@npm:0.5.2" + dependencies: + socket.io-client: ^4.5.2 + tslib: ^2.3.0 + peerDependencies: + ethers: ^5.6.8 + checksum: 14a438269eae87979e10377e5b8a38953e190593648ce64f148def553b66467cf52ec29ce2b70956342c23f42b67ae179f54a1aab6f22d1495a3806ba0146bbc + languageName: node + linkType: hard + "@pushprotocol/socket@npm:latest": version: 0.4.2 resolution: "@pushprotocol/socket@npm:0.4.2" @@ -5595,30 +5688,40 @@ __metadata: languageName: node linkType: hard -"@pushprotocol/uiweb@npm:1.1.13": - version: 1.1.13 - resolution: "@pushprotocol/uiweb@npm:1.1.13" +"@pushprotocol/uiweb@npm:0.0.1-alpha.17": + version: 0.0.1-alpha.17 + resolution: "@pushprotocol/uiweb@npm:0.0.1-alpha.17" dependencies: + "@livekit/components-react": ^1.2.2 + "@livekit/components-styles": ^1.0.6 "@livepeer/react": ^2.6.0 "@pushprotocol/socket": ^0.5.0 "@unstoppabledomains/resolution": ^8.5.0 + "@web3-onboard/coinbase": ^2.2.5 + "@web3-onboard/core": ^2.21.1 + "@web3-onboard/injected-wallets": ^2.10.5 + "@web3-onboard/react": ^2.8.9 + "@web3-onboard/walletconnect": ^2.4.6 "@web3-react/injected-connector": ^6.0.7 date-fns: ^2.28.0 emoji-picker-react: ^4.4.9 + ethers: ^5.6.8 font-awesome: ^4.7.0 gif-picker-react: ^1.1.0 html-react-parser: ^1.4.13 + livekit-client: ^1.13.3 moment: ^2.29.4 react-icons: ^4.10.1 react-toastify: ^9.1.3 react-twitter-embed: ^4.0.4 + uuid: ^9.0.1 peerDependencies: "@pushprotocol/restapi": ^1.2.15 "@pushprotocol/socket": ^0.5.0 - ethers: ^5.7.1 + axios: ^0.27.2 react: ">=16.8.0" styled-components: ^5.3.5 - checksum: a03933a5e2c010435aad2bf36657198276b75025521146a64e66847f9f7f492ff85ec4612cf190758d6fac873634feb7250ccf2f3034f399859f478716a96c8e + checksum: 699b576cc1612b41e83790f3e9845fc7702ecf362fb542f28156dadec039f52e41962ab774b61a910c5c05a9afda4ce6d0b79e7d02d33240054f8df230bdad92 languageName: node linkType: hard @@ -6156,6 +6259,15 @@ __metadata: languageName: node linkType: hard +"@react-hook/latest@npm:^1.0.3": + version: 1.0.3 + resolution: "@react-hook/latest@npm:1.0.3" + peerDependencies: + react: ">=16.8" + checksum: 2408c9cd35c5cfa7697b6da3bc5eebef254a932ade70955074c474f23be7dd3e2f81bbba12edcc9208bd0f89c6ed366d6b11d4f6d7b1052877a0bac8f74afad4 + languageName: node + linkType: hard + "@react-spring/animated@npm:~9.7.1": version: 9.7.1 resolution: "@react-spring/animated@npm:9.7.1" @@ -6386,6 +6498,13 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:~1.1.2": + version: 1.1.3 + resolution: "@scure/base@npm:1.1.3" + checksum: 1606ab8a4db898cb3a1ada16c15437c3bce4e25854fadc8eb03ae93cbbbac1ed90655af4b0be3da37e12056fef11c0374499f69b9e658c9e5b7b3e06353c630c + languageName: node + linkType: hard + "@scure/bip32@npm:1.3.1": version: 1.3.1 resolution: "@scure/bip32@npm:1.3.1" @@ -6397,6 +6516,17 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:1.3.2": + version: 1.3.2 + resolution: "@scure/bip32@npm:1.3.2" + dependencies: + "@noble/curves": ~1.2.0 + "@noble/hashes": ~1.3.2 + "@scure/base": ~1.1.2 + checksum: c5ae84fae43490853693b481531132b89e056d45c945fc8b92b9d032577f753dfd79c5a7bbcbf0a7f035951006ff0311b6cf7a389e26c9ec6335e42b20c53157 + languageName: node + linkType: hard + "@scure/bip39@npm:1.2.1": version: 1.2.1 resolution: "@scure/bip39@npm:1.2.1" @@ -7869,6 +7999,15 @@ __metadata: languageName: node linkType: hard +"@types/ws@npm:^8.5.5": + version: 8.5.6 + resolution: "@types/ws@npm:8.5.6" + dependencies: + "@types/node": "*" + checksum: 7addb0c5fa4e7713d5209afb8a90f1852b12c02cb537395adf7a05fbaf21205dc5f7c110fd5ad6f3dbf147112cbff33fb11d8633059cb344f0c14f595b1ea1fb + languageName: node + linkType: hard + "@types/yargs-parser@npm:*": version: 21.0.0 resolution: "@types/yargs-parser@npm:21.0.0" @@ -8557,6 +8696,30 @@ __metadata: languageName: node linkType: hard +"@walletconnect/core@npm:2.10.1": + version: 2.10.1 + resolution: "@walletconnect/core@npm:2.10.1" + dependencies: + "@walletconnect/heartbeat": 1.2.1 + "@walletconnect/jsonrpc-provider": 1.0.13 + "@walletconnect/jsonrpc-types": 1.0.3 + "@walletconnect/jsonrpc-utils": 1.0.8 + "@walletconnect/jsonrpc-ws-connection": 1.0.13 + "@walletconnect/keyvaluestorage": ^1.0.2 + "@walletconnect/logger": ^2.0.1 + "@walletconnect/relay-api": ^1.0.9 + "@walletconnect/relay-auth": ^1.0.4 + "@walletconnect/safe-json": ^1.0.2 + "@walletconnect/time": ^1.0.2 + "@walletconnect/types": 2.10.1 + "@walletconnect/utils": 2.10.1 + events: ^3.3.0 + lodash.isequal: 4.5.0 + uint8arrays: ^3.1.0 + checksum: d58ae15c53efe1792da8c7aa1b7ba47efb49807cfe0c73f225d59c5cd847a0e50979ce6965b94915812412deba3e5aa2dca13a02bd41c087e85575e99afad223 + languageName: node + linkType: hard + "@walletconnect/core@npm:^1.8.0": version: 1.8.0 resolution: "@walletconnect/core@npm:1.8.0" @@ -8640,6 +8803,28 @@ __metadata: languageName: node linkType: hard +"@walletconnect/ethereum-provider@npm:^2.10.1": + version: 2.10.1 + resolution: "@walletconnect/ethereum-provider@npm:2.10.1" + dependencies: + "@walletconnect/jsonrpc-http-connection": ^1.0.7 + "@walletconnect/jsonrpc-provider": ^1.0.13 + "@walletconnect/jsonrpc-types": ^1.0.3 + "@walletconnect/jsonrpc-utils": ^1.0.8 + "@walletconnect/sign-client": 2.10.1 + "@walletconnect/types": 2.10.1 + "@walletconnect/universal-provider": 2.10.1 + "@walletconnect/utils": 2.10.1 + events: ^3.3.0 + peerDependencies: + "@walletconnect/modal": ">=2" + peerDependenciesMeta: + "@walletconnect/modal": + optional: true + checksum: ec3d88ba101a5d8f193262b5b1e770cccad6457ec56fa1f3d17fa531de4e07e8cf03a1341669122c61956f0d5c3a6eca57d3f12f524e046acddb401cdb76fe7c + languageName: node + linkType: hard + "@walletconnect/events@npm:^1.0.1": version: 1.0.1 resolution: "@walletconnect/events@npm:1.0.1" @@ -8817,6 +9002,15 @@ __metadata: languageName: node linkType: hard +"@walletconnect/modal-core@npm:2.6.2": + version: 2.6.2 + resolution: "@walletconnect/modal-core@npm:2.6.2" + dependencies: + valtio: 1.11.2 + checksum: 94daceba50c323b06ecbeac2968d9f0972f327359c6118887c6526cd64006249b12f64322d71bc6c4a2b928436ecc89cf3d3af706511fcdc264c1f4b34a2dd5d + languageName: node + linkType: hard + "@walletconnect/modal-ui@npm:2.6.1": version: 2.6.1 resolution: "@walletconnect/modal-ui@npm:2.6.1" @@ -8829,6 +9023,18 @@ __metadata: languageName: node linkType: hard +"@walletconnect/modal-ui@npm:2.6.2": + version: 2.6.2 + resolution: "@walletconnect/modal-ui@npm:2.6.2" + dependencies: + "@walletconnect/modal-core": 2.6.2 + lit: 2.8.0 + motion: 10.16.2 + qrcode: 1.5.3 + checksum: cd1ec0205eb491e529670599d3dd26f6782d7c5a99d5594bf6949a8c760c1c5f4eb6ed72b8662450774fe4e2dd47678f2c05145c8f2494bd7153446ddf4bd7ed + languageName: node + linkType: hard + "@walletconnect/modal@npm:2.6.1": version: 2.6.1 resolution: "@walletconnect/modal@npm:2.6.1" @@ -8839,6 +9045,16 @@ __metadata: languageName: node linkType: hard +"@walletconnect/modal@npm:2.6.2": + version: 2.6.2 + resolution: "@walletconnect/modal@npm:2.6.2" + dependencies: + "@walletconnect/modal-core": 2.6.2 + "@walletconnect/modal-ui": 2.6.2 + checksum: 68b354d49960b96d22de0e47a3801df27c01a3e96ec5fbde3ca6df1344ca2b20668b0c4d58fe1803f5670ac7b7b4c6f5b7b405e354f5f9eaff5cca147c13de9c + languageName: node + linkType: hard + "@walletconnect/qrcode-modal@npm:^1.8.0": version: 1.8.0 resolution: "@walletconnect/qrcode-modal@npm:1.8.0" @@ -8922,6 +9138,23 @@ __metadata: languageName: node linkType: hard +"@walletconnect/sign-client@npm:2.10.1": + version: 2.10.1 + resolution: "@walletconnect/sign-client@npm:2.10.1" + dependencies: + "@walletconnect/core": 2.10.1 + "@walletconnect/events": ^1.0.1 + "@walletconnect/heartbeat": 1.2.1 + "@walletconnect/jsonrpc-utils": 1.0.8 + "@walletconnect/logger": ^2.0.1 + "@walletconnect/time": ^1.0.2 + "@walletconnect/types": 2.10.1 + "@walletconnect/utils": 2.10.1 + events: ^3.3.0 + checksum: dbdced8dece73b20ae73df9c0cf0d9e3eee753f6c81e264c87583ca60d1d13d4f7d61944e4b22d1f70c5f32424fd842a7de778838aa7d0ae27195976a86e102f + languageName: node + linkType: hard + "@walletconnect/signer-connection@npm:^1.8.0": version: 1.8.0 resolution: "@walletconnect/signer-connection@npm:1.8.0" @@ -8970,6 +9203,20 @@ __metadata: languageName: node linkType: hard +"@walletconnect/types@npm:2.10.1": + version: 2.10.1 + resolution: "@walletconnect/types@npm:2.10.1" + dependencies: + "@walletconnect/events": ^1.0.1 + "@walletconnect/heartbeat": 1.2.1 + "@walletconnect/jsonrpc-types": 1.0.3 + "@walletconnect/keyvaluestorage": ^1.0.2 + "@walletconnect/logger": ^2.0.1 + events: ^3.3.0 + checksum: b663a236404bb423d3cc5cde656794ce42132f09193da5a51dac815d844f78eebb29c7275ebe10f6134492db21386ffd81b66ce42992332847b72c9128f74990 + languageName: node + linkType: hard + "@walletconnect/types@npm:^1.8.0": version: 1.8.0 resolution: "@walletconnect/types@npm:1.8.0" @@ -8994,6 +9241,23 @@ __metadata: languageName: node linkType: hard +"@walletconnect/universal-provider@npm:2.10.1": + version: 2.10.1 + resolution: "@walletconnect/universal-provider@npm:2.10.1" + dependencies: + "@walletconnect/jsonrpc-http-connection": ^1.0.7 + "@walletconnect/jsonrpc-provider": 1.0.13 + "@walletconnect/jsonrpc-types": ^1.0.2 + "@walletconnect/jsonrpc-utils": ^1.0.7 + "@walletconnect/logger": ^2.0.1 + "@walletconnect/sign-client": 2.10.1 + "@walletconnect/types": 2.10.1 + "@walletconnect/utils": 2.10.1 + events: ^3.3.0 + checksum: a33ad597a7601157cd96bceb7637c3463a5df981e5548c5343ab84f92c542bd7cae577fb2884d549164c9ad8262b097dc5fc0bc7fd9a515ee7c3f30b271cb034 + languageName: node + linkType: hard + "@walletconnect/utils@npm:2.10.0": version: 2.10.0 resolution: "@walletconnect/utils@npm:2.10.0" @@ -9016,6 +9280,28 @@ __metadata: languageName: node linkType: hard +"@walletconnect/utils@npm:2.10.1": + version: 2.10.1 + resolution: "@walletconnect/utils@npm:2.10.1" + dependencies: + "@stablelib/chacha20poly1305": 1.0.1 + "@stablelib/hkdf": 1.0.1 + "@stablelib/random": ^1.0.2 + "@stablelib/sha256": 1.0.1 + "@stablelib/x25519": ^1.0.3 + "@walletconnect/relay-api": ^1.0.9 + "@walletconnect/safe-json": ^1.0.2 + "@walletconnect/time": ^1.0.2 + "@walletconnect/types": 2.10.1 + "@walletconnect/window-getters": ^1.0.1 + "@walletconnect/window-metadata": ^1.0.1 + detect-browser: 5.3.0 + query-string: 7.1.3 + uint8arrays: ^3.1.0 + checksum: 150d1a3c75ce0736ffc8ed8a844e3dc63476e556f7f308154ee6bc9d99e08907bc11a504b7ce3889951293b48d9eef4e32b84de1c7f27b7a84e6731a7bb65189 + languageName: node + linkType: hard + "@walletconnect/utils@npm:^1.8.0": version: 1.8.0 resolution: "@walletconnect/utils@npm:1.8.0" @@ -9108,6 +9394,38 @@ __metadata: languageName: node linkType: hard +"@web3-onboard/core@npm:^2.21.1": + version: 2.21.2 + resolution: "@web3-onboard/core@npm:2.21.2" + dependencies: + "@web3-onboard/common": ^2.3.3 + bignumber.js: ^9.0.0 + bnc-sdk: ^4.6.7 + bowser: ^2.11.0 + ethers: 5.5.3 + eventemitter3: ^4.0.7 + joi: 17.9.1 + lodash.merge: ^4.6.2 + lodash.partition: ^4.6.0 + nanoid: ^4.0.0 + rxjs: ^7.5.5 + svelte: ^3.49.0 + svelte-i18n: ^3.3.13 + checksum: 13fa0df0c5c8b84cd65e363c2f48f1cd2bcb95217ce570faf87d0e6752add80365fc5554cfb706c375124fcd1e10a0e7321b297f330064574ffb8ac80d16d490 + languageName: node + linkType: hard + +"@web3-onboard/injected-wallets@npm:^2.10.5": + version: 2.10.7 + resolution: "@web3-onboard/injected-wallets@npm:2.10.7" + dependencies: + "@web3-onboard/common": ^2.3.3 + joi: 17.9.1 + lodash.uniqby: ^4.7.0 + checksum: f74617456ec6a5eec45c5c484fc0e3b28d3c267657cb015baf2c53fd23fc1da550b6ce2f71964013b869de190e4bc8534e0753e01891a6dd83c9c3e1529d947a + languageName: node + linkType: hard + "@web3-onboard/injected-wallets@npm:^2.9.0": version: 2.10.5 resolution: "@web3-onboard/injected-wallets@npm:2.10.5" @@ -9148,6 +9466,22 @@ __metadata: languageName: node linkType: hard +"@web3-onboard/walletconnect@npm:^2.4.6": + version: 2.4.7 + resolution: "@web3-onboard/walletconnect@npm:2.4.7" + dependencies: + "@ethersproject/providers": 5.5.0 + "@walletconnect/client": ^1.8.0 + "@walletconnect/ethereum-provider": ^2.10.1 + "@walletconnect/modal": 2.6.2 + "@walletconnect/qrcode-modal": ^1.8.0 + "@web3-onboard/common": ^2.3.3 + joi: 17.9.1 + rxjs: ^7.5.2 + checksum: cfeefcf195ca84fac8de2f80ac8684e980d72c17d519185b2e817b218fa5274839d940b52d6866ae7c6b21b3570726950c84fdd4aee4e3af0a944e6223b1eacd + languageName: node + linkType: hard + "@web3-react/abstract-connector@npm:^6.0.7": version: 6.0.7 resolution: "@web3-react/abstract-connector@npm:6.0.7" @@ -9521,6 +9855,21 @@ __metadata: languageName: node linkType: hard +"abitype@npm:0.9.8": + version: 0.9.8 + resolution: "abitype@npm:0.9.8" + peerDependencies: + typescript: ">=5.0.4" + zod: ^3 >=3.19.1 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + checksum: d7d887f29d6821e3f7a400de9620511b80ead3f85c5c87308aaec97965d3493e6687ed816e88722b4f512249bd66dee9e69231b49af0e1db8f69400a62c87cf6 + languageName: node + linkType: hard + "abort-controller@npm:^3.0.0": version: 3.0.0 resolution: "abort-controller@npm:3.0.0" @@ -11923,6 +12272,13 @@ __metadata: languageName: node linkType: hard +"clsx@npm:^2.0.0": + version: 2.0.0 + resolution: "clsx@npm:2.0.0" + checksum: a2cfb2351b254611acf92faa0daf15220f4cd648bdf96ce369d729813b85336993871a4bf6978ddea2b81b5a130478339c20d9d0b5c6fc287e5147f0c059276e + languageName: node + linkType: hard + "co@npm:^4.6.0": version: 4.6.0 resolution: "co@npm:4.6.0" @@ -13879,6 +14235,13 @@ __metadata: languageName: node linkType: hard +"email-regex@npm:^5.0.0": + version: 5.0.0 + resolution: "email-regex@npm:5.0.0" + checksum: 4089b601a0db88363391bb5c93f12eba56451687aadc925359b1408831b4b0f281737c51995a6fdbae5ff56f64b2fc2464189107575ecc0d67f0524f457a0632 + languageName: node + linkType: hard + "emittery@npm:^0.10.2": version: 0.10.2 resolution: "emittery@npm:0.10.2" @@ -15310,7 +15673,7 @@ __metadata: languageName: node linkType: hard -"ethers@npm:^5.3.1, ethers@npm:^5.6.5, ethers@npm:^5.7.2": +"ethers@npm:^5.3.1, ethers@npm:^5.6.5, ethers@npm:^5.6.8, ethers@npm:^5.7.2": version: 5.7.2 resolution: "ethers@npm:5.7.2" dependencies: @@ -16434,6 +16797,13 @@ __metadata: languageName: node linkType: hard +"global-tld-list@npm:^0.0.1139": + version: 0.0.1139 + resolution: "global-tld-list@npm:0.0.1139" + checksum: cfe5e6338059328e8b90ef890274419fb718e0c22f9aa0ec88dea016e1a9f756377ba102e1beffdc2d5c3fee3cf9e605cb76e056104f1d8602c599f447aa7d37 + languageName: node + linkType: hard + "global@npm:~4.4.0": version: 4.4.0 resolution: "global@npm:4.4.0" @@ -18412,6 +18782,15 @@ __metadata: languageName: node linkType: hard +"isomorphic-ws@npm:5.0.0": + version: 5.0.0 + resolution: "isomorphic-ws@npm:5.0.0" + peerDependencies: + ws: "*" + checksum: e20eb2aee09ba96247465fda40c6d22c1153394c0144fa34fe6609f341af4c8c564f60ea3ba762335a7a9c306809349f9b863c8beedf2beea09b299834ad5398 + languageName: node + linkType: hard + "isomorphic-ws@npm:^4.0.1": version: 4.0.1 resolution: "isomorphic-ws@npm:4.0.1" @@ -20182,6 +20561,15 @@ __metadata: languageName: node linkType: hard +"lit-html@npm:^2.8.0": + version: 2.8.0 + resolution: "lit-html@npm:2.8.0" + dependencies: + "@types/trusted-types": ^2.0.2 + checksum: 2d70df07248bcb2f502a3afb1e91d260735024fa669669ffb1417575aa39c3092779725ac1b90f5f39e4ce78c63f431f51176bc67f532389f0285a6991573255 + languageName: node + linkType: hard + "lit@npm:2.7.6": version: 2.7.6 resolution: "lit@npm:2.7.6" @@ -20193,6 +20581,32 @@ __metadata: languageName: node linkType: hard +"lit@npm:2.8.0": + version: 2.8.0 + resolution: "lit@npm:2.8.0" + dependencies: + "@lit/reactive-element": ^1.6.0 + lit-element: ^3.3.0 + lit-html: ^2.8.0 + checksum: 2480e733f7d022d3ecba91abc58a20968f0ca8f5fa30b3341ecf4bcf4845e674ad27b721a5ae53529cafc6ca603c015b80d0979ceb7a711e268ef20bb6bc7527 + languageName: node + linkType: hard + +"livekit-client@npm:^1.13.3": + version: 1.13.4 + resolution: "livekit-client@npm:1.13.4" + dependencies: + "@bufbuild/protobuf": ^1.3.0 + events: ^3.3.0 + loglevel: ^1.8.0 + sdp-transform: ^2.14.1 + ts-debounce: ^4.0.0 + typed-emitter: ^2.1.0 + webrtc-adapter: ^8.1.1 + checksum: 90b54ad3dee69bac2f91d09c3db03928448432f8c27ca456717114c891543fab1e2796547322f9a7e3da6047973a0ba36de4b74ed4af66dba0ada25daac228b8 + languageName: node + linkType: hard + "livepeer@npm:2.8.0, livepeer@npm:^2.5.8": version: 2.8.0 resolution: "livepeer@npm:2.8.0" @@ -20475,7 +20889,7 @@ __metadata: languageName: node linkType: hard -"loglevel@npm:^1.7.0": +"loglevel@npm:^1.7.0, loglevel@npm:^1.8.0, loglevel@npm:^1.8.1": version: 1.8.1 resolution: "loglevel@npm:1.8.1" checksum: a1a62db40291aaeaef2f612334c49e531bff71cc1d01a2acab689ab80d59e092f852ab164a5aedc1a752fdc46b7b162cb097d8a9eb2cf0b299511106c29af61d @@ -26260,6 +26674,15 @@ __metadata: languageName: node linkType: hard +"rxjs@npm:*, rxjs@npm:^7.5.5, rxjs@npm:^7.8.0": + version: 7.8.1 + resolution: "rxjs@npm:7.8.1" + dependencies: + tslib: ^2.1.0 + checksum: de4b53db1063e618ec2eca0f7965d9137cabe98cf6be9272efe6c86b47c17b987383df8574861bcced18ebd590764125a901d5506082be84a8b8e364bf05f119 + languageName: node + linkType: hard + "rxjs@npm:^6.6.3": version: 6.6.7 resolution: "rxjs@npm:6.6.7" @@ -26278,15 +26701,6 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^7.5.5": - version: 7.8.1 - resolution: "rxjs@npm:7.8.1" - dependencies: - tslib: ^2.1.0 - checksum: de4b53db1063e618ec2eca0f7965d9137cabe98cf6be9272efe6c86b47c17b987383df8574861bcced18ebd590764125a901d5506082be84a8b8e364bf05f119 - languageName: node - linkType: hard - "sade@npm:^1.8.1": version: 1.8.1 resolution: "sade@npm:1.8.1" @@ -26522,6 +26936,15 @@ __metadata: languageName: node linkType: hard +"sdp-transform@npm:^2.14.1": + version: 2.14.1 + resolution: "sdp-transform@npm:2.14.1" + bin: + sdp-verify: checker.js + checksum: 8b3179786db1a0f1ebfdacb1ac0dfe2833e63e8c64b638884cec212455061d53beaa8d9c8bf76fdbd5f844b7885f3892adec27e87734cfbc2b3e5c65e18a489b + languageName: node + linkType: hard + "sdp@npm:^2.12.0, sdp@npm:^2.6.0": version: 2.12.0 resolution: "sdp@npm:2.12.0" @@ -26529,6 +26952,13 @@ __metadata: languageName: node linkType: hard +"sdp@npm:^3.2.0": + version: 3.2.0 + resolution: "sdp@npm:3.2.0" + checksum: 227885bddab9a5845e56ae184ff51e43ec7bc155e7f1ed2f17ca1b012e6767011d5bd01b6c4064ded8e3b6f6bf3c9b26b2cf754b9c8662285988ed27b54f37b1 + languageName: node + linkType: hard + "secp256k1-v4@https://github.com/HarshRajat/secp256k1-node": version: 4.0.1 resolution: "secp256k1-v4@https://github.com/HarshRajat/secp256k1-node.git#commit=90a04a2e1127f4c1bfd7015aa5a7b22d08edb811" @@ -28496,6 +28926,13 @@ __metadata: languageName: node linkType: hard +"ts-debounce@npm:^4.0.0": + version: 4.0.0 + resolution: "ts-debounce@npm:4.0.0" + checksum: e1e509632c5aa09c40d3fa315b3a95b2c2e8813ccc706a400aa08e41f691e658061f34b42a1e8a578a043540d6db198e6ecf3ce26a5356a02a0940985fb1e379 + languageName: node + linkType: hard + "ts-easing@npm:^0.2.0": version: 0.2.0 resolution: "ts-easing@npm:0.2.0" @@ -28739,6 +29176,18 @@ __metadata: languageName: node linkType: hard +"typed-emitter@npm:^2.1.0": + version: 2.1.0 + resolution: "typed-emitter@npm:2.1.0" + dependencies: + rxjs: "*" + dependenciesMeta: + rxjs: + optional: true + checksum: 95821a9e05784b972cc9d152891fd12a56cb4b1a7c57e768c02bea6a8984da7aff8f19404a7b69eea11fae2a3b6c0c510a4c510f575f50162c759ae9059f2520 + languageName: node + linkType: hard + "typedarray-to-buffer@npm:3.1.5, typedarray-to-buffer@npm:^3.1.5, typedarray-to-buffer@npm:~3.1.5": version: 3.1.5 resolution: "typedarray-to-buffer@npm:3.1.5" @@ -29132,6 +29581,16 @@ __metadata: languageName: node linkType: hard +"usehooks-ts@npm:^2.9.1": + version: 2.9.1 + resolution: "usehooks-ts@npm:2.9.1" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 36f1e4142ce23bc019b81d2e93aefd7f2c350abcf255598c21627114a69a2f2f116b35dc3a353375f09c6e4c9b704a04f104e3d10e98280545c097feca66c30a + languageName: node + linkType: hard + "utf-8-validate@npm:^5.0.2, utf-8-validate@npm:^5.0.8": version: 5.0.10 resolution: "utf-8-validate@npm:5.0.10" @@ -29238,6 +29697,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^9.0.1": + version: 9.0.1 + resolution: "uuid@npm:9.0.1" + bin: + uuid: dist/bin/uuid + checksum: 39931f6da74e307f51c0fb463dc2462807531dc80760a9bff1e35af4316131b4fc3203d16da60ae33f07fdca5b56f3f1dd662da0c99fea9aaeab2004780cc5f4 + languageName: node + linkType: hard + "v8-compile-cache@npm:^2.0.3": version: 2.3.0 resolution: "v8-compile-cache@npm:2.3.0" @@ -29271,6 +29739,24 @@ __metadata: languageName: node linkType: hard +"valtio@npm:1.11.2": + version: 1.11.2 + resolution: "valtio@npm:1.11.2" + dependencies: + proxy-compare: 2.5.1 + use-sync-external-store: 1.2.0 + peerDependencies: + "@types/react": ">=16.8" + react: ">=16.8" + peerDependenciesMeta: + "@types/react": + optional: true + react: + optional: true + checksum: cce2d9212aac9fc4bdeba2d381188cc831cfe8d2d03039024cfcd58ba1801f2a5b14d01c2bb21a2c9f12046d2ede64f1dd887175185f39bee553677a35592c30 + languageName: node + linkType: hard + "varint@npm:^5.0.0, varint@npm:^5.0.2, varint@npm:~5.0.0": version: 5.0.2 resolution: "varint@npm:5.0.2" @@ -29310,6 +29796,28 @@ __metadata: languageName: node linkType: hard +"viem@npm:^1.3.0": + version: 1.15.3 + resolution: "viem@npm:1.15.3" + dependencies: + "@adraffy/ens-normalize": 1.9.4 + "@noble/curves": 1.2.0 + "@noble/hashes": 1.3.2 + "@scure/bip32": 1.3.2 + "@scure/bip39": 1.2.1 + "@types/ws": ^8.5.5 + abitype: 0.9.8 + isomorphic-ws: 5.0.0 + ws: 8.13.0 + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 61d573158de32896f9f7957ae2ff943953838385f9b04414a55e6041f0e070827e4c5cfc6196485e0b0101c3dcbfdfd262fb54040817b257b38216a7f50e3384 + languageName: node + linkType: hard + "w3c-hr-time@npm:^1.0.2": version: 1.0.2 resolution: "w3c-hr-time@npm:1.0.2" @@ -29857,6 +30365,15 @@ __metadata: languageName: node linkType: hard +"webrtc-adapter@npm:^8.1.1": + version: 8.2.3 + resolution: "webrtc-adapter@npm:8.2.3" + dependencies: + sdp: ^3.2.0 + checksum: 8239c9452c489c9aad2584b5d00af22462c3e0f1b7885c6e4036b518d2b9411d94c00d2ceadbed987459a3647cfc4ce04c0eb75dd5ae7c3d7df9b810525e6a07 + languageName: node + linkType: hard + "websocket-driver@npm:>=0.5.1, websocket-driver@npm:^0.7.4": version: 0.7.4 resolution: "websocket-driver@npm:0.7.4" @@ -30349,6 +30866,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:8.13.0, ws@npm:^8.13.0": + version: 8.13.0 + resolution: "ws@npm:8.13.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 53e991bbf928faf5dc6efac9b8eb9ab6497c69feeb94f963d648b7a3530a720b19ec2e0ec037344257e05a4f35bd9ad04d9de6f289615ffb133282031b18c61c + languageName: node + linkType: hard + "ws@npm:^3.0.0": version: 3.3.3 resolution: "ws@npm:3.3.3" @@ -30375,21 +30907,6 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.13.0": - version: 8.13.0 - resolution: "ws@npm:8.13.0" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 53e991bbf928faf5dc6efac9b8eb9ab6497c69feeb94f963d648b7a3530a720b19ec2e0ec037344257e05a4f35bd9ad04d9de6f289615ffb133282031b18c61c - languageName: node - linkType: hard - "ws@npm:^8.5.0": version: 8.14.2 resolution: "ws@npm:8.14.2" From 9101023657f0e5f5f874cadce71172182779f2d8 Mon Sep 17 00:00:00 2001 From: Kalash Shah <81062983+kalashshah@users.noreply.github.com> Date: Tue, 10 Oct 2023 14:07:53 +0530 Subject: [PATCH 11/18] Refactor/notif settings integration (#1266) * Push snap qa fixes (#1261) * Snap UI fixes from QA-1 * fix: connected snap address detection * fix: snooze toggle * fix: address detection bug * fix: spacing in signature * update: route bug * Snap Dark mode fix and responsiveness added * Added dark mode to Snap QA --------- Co-authored-by: Arch0125 * Added support for arbitrum * Resolved issues * Updated sdk package versions * chore: integrate channel settings and send notif * chore: design update for send notif page and minor fixes * chore: move tag to reusables * fix: rename folder to avoid .gitignore file --------- Co-authored-by: Abhishek <77395788+abhishek-01k@users.noreply.github.com> Co-authored-by: Arch0125 Co-authored-by: Nilesh Gupta --- package.json | 4 +- public/svg/Arbitrum.svg | 22 +++ src/components/ChangeNetwork.tsx | 2 +- src/components/ChannelDetails.js | 30 ++-- src/components/Faucets.js | 12 ++ .../MetamaskSnap/SnapInformationModal.tsx | 4 +- src/components/SendNotifications.tsx | 143 ++++++++++++----- src/components/VerifyAlias.js | 4 + .../channel/AddSettingModalContent.tsx | 10 +- src/components/channel/ChannelButtons.tsx | 4 +- src/components/channel/ChannelInfoList.tsx | 10 +- .../channel/NotificationSettings.tsx | 37 ++++- src/components/channel/UserSettings.tsx | 4 + src/components/reusables/labels/Tag.tsx | 21 +++ src/config/Themization.js | 16 +- src/config/config-dev.js | 13 +- src/config/config-prod.js | 14 +- src/config/config-staging.js | 13 +- src/helpers/CaipHelper.ts | 2 +- src/helpers/UtilityHelper.ts | 33 +++- src/helpers/channel/types.ts | 5 +- src/hooks/useInactiveListener.ts | 4 +- .../receiveNotifs/MetamaskPushSnapModal.tsx | 2 +- src/modules/snap/AboutSnapModal.tsx | 12 +- src/modules/snap/SnapModule.tsx | 61 +++++-- src/structure/Header.tsx | 4 +- yarn.lock | 149 ++++++++++++++---- 27 files changed, 496 insertions(+), 139 deletions(-) create mode 100644 public/svg/Arbitrum.svg create mode 100644 src/components/reusables/labels/Tag.tsx diff --git a/package.json b/package.json index 5085ea406d..514c4fb351 100644 --- a/package.json +++ b/package.json @@ -34,9 +34,9 @@ "@mui/lab": "^5.0.0-alpha.72", "@mui/material": "^5.5.0", "@pushprotocol/ledgerlive": "latest", - "@pushprotocol/restapi": "1.4.18", + "@pushprotocol/restapi": "0.0.1-alpha.45", "@pushprotocol/socket": "latest", - "@pushprotocol/uiweb": "1.1.13", + "@pushprotocol/uiweb": "1.1.14", "@reduxjs/toolkit": "^1.7.1", "@testing-library/dom": "^9.0.1", "@testing-library/jest-dom": "^4.2.4", diff --git a/public/svg/Arbitrum.svg b/public/svg/Arbitrum.svg new file mode 100644 index 0000000000..a42cbaf4a0 --- /dev/null +++ b/public/svg/Arbitrum.svg @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/src/components/ChangeNetwork.tsx b/src/components/ChangeNetwork.tsx index 087d99e5f3..feb77e833b 100644 --- a/src/components/ChangeNetwork.tsx +++ b/src/components/ChangeNetwork.tsx @@ -52,7 +52,7 @@ const ChangeNetwork = () => { color="#fff" radius="15px" padding="20px 20px" - onClick={() => switchChain(appConfig.coreContractChain)} + onClick={() => switchChain(aliasChainId)} > state.contracts); @@ -121,6 +124,24 @@ export default function ChannelDetails({ isChannelExpired, setIsChannelExpired, } }, [delegatees, account]); + useEffect(() => { + if (account) { + (async () => { + try { + const channelAddressinCAIP = convertAddressToAddrCaip(account, chainId); + const channelDelegates = await getChannelDelegates({ channelCaipAddress: channelAddressinCAIP }); + if (channelDelegates) { + const delegateeList = channelDelegates.map((delegate) => delegate); + delegateeList.unshift(account); + setDelegateeList(delegateeList); + } + } catch (err) { + console.error(err); + } + })(); + } + }, [account]); + const channelSettings = useMemo(() => { if (delegatees) { const delegatee = delegatees.find(({ channel }) => channel === channelAddress); @@ -134,15 +155,6 @@ export default function ChannelDetails({ isChannelExpired, setIsChannelExpired, return []; }, [delegatees, channelAddress]); - const delegateeList = useMemo(() => { - if (delegatees) { - return delegatees.map(({ channel, alias_address }) => { - return onCoreNetwork ? channel : alias_address; - }); - } - return []; - }); - const removeDelegate = (walletAddress) => { return epnsCommWriteProvider.removeDelegate(walletAddress); }; diff --git a/src/components/Faucets.js b/src/components/Faucets.js index 8fceb32a14..dee6333407 100644 --- a/src/components/Faucets.js +++ b/src/components/Faucets.js @@ -26,31 +26,43 @@ const Faucets = () => { id: "5", value: "Goerli", title: "Goerli Faucet", + function: () => {}, link: "https://goerlifaucet.com/", }, { id: "80001", value: "Polygon", title: "Mumbai Faucet", + function: () => {}, link: "https://faucet.polygon.technology/", }, { id: "97", value: "BNB", title: "BNB Faucet", + function: () => {}, link: "https://testnet.bnbchain.org/faucet-smart", }, { id: "420", value: "Optimism", title: "Optimism Goerli Faucet", + function: () => {}, link: "https://faucet.quicknode.com/optimism/goerli", }, { id: "1442", value: "Polygon zkEVM", title: "Polygon zkEVM Bridge", + function: () => {}, link: "https://public.zkevm-test.net/" + }, + { + id: "421613", + value: "Arbitrum", + title: "Arbitrum Testnet Faucet", + function: () => {}, + link: "https://faucet.quicknode.com/arbitrum/goerli" } ]; diff --git a/src/components/MetamaskSnap/SnapInformationModal.tsx b/src/components/MetamaskSnap/SnapInformationModal.tsx index ad50e755e2..80cff0041b 100644 --- a/src/components/MetamaskSnap/SnapInformationModal.tsx +++ b/src/components/MetamaskSnap/SnapInformationModal.tsx @@ -58,7 +58,7 @@ const SnapInformationModal = () => { fontSize="22px" fontWeight="500" letterSpacing="-0.44px" - color={theme.modalMessageColor} + color={theme.snapPrimaryText} > Connect to Metamask Push Snap @@ -136,7 +136,7 @@ const PrimaryText = styled.p` font-size: 18px; font-weight: 500; align-self: baseline; - color:${(props)=>props.theme.modalMessageColor}; + color:${(props)=>props.theme.snapPrimaryText}; `; const SecondaryText = styled.p` diff --git a/src/components/SendNotifications.tsx b/src/components/SendNotifications.tsx index 03ac3bf29e..e834849437 100644 --- a/src/components/SendNotifications.tsx +++ b/src/components/SendNotifications.tsx @@ -12,12 +12,13 @@ import { MdCheckCircle, MdError } from 'react-icons/md'; import { useSelector } from 'react-redux'; import 'react-toastify/dist/ReactToastify.min.css'; import styled, { useTheme } from 'styled-components'; +import Slider from 'react-input-slider'; // Internal Compoonents import * as PushAPI from '@pushprotocol/restapi'; import { postReq } from 'api'; import LoaderSpinner, { LOADER_TYPE } from 'components/reusables/loaders/LoaderSpinner'; -import { AInlineV2, SectionV2 } from 'components/reusables/SharedStylingV2'; +import { AInlineV2, SectionV2, SpanV2 } from 'components/reusables/SharedStylingV2'; import { convertAddressToAddrCaip } from 'helpers/CaipHelper'; import CryptoHelper from 'helpers/CryptoHelper'; import { IPFSupload } from 'helpers/IpfsHelper'; @@ -41,6 +42,7 @@ import PreviewNotif from './PreviewNotif'; import { appConfig } from 'config'; import { useAccount, useDeviceWidthCheck } from 'hooks'; import APP_PATHS from 'config/AppPaths'; +import Tag from './reusables/labels/Tag'; // Constants const CORE_CHAIN_ID = appConfig.coreContractChain; @@ -144,6 +146,7 @@ function SendNotifications() { const [nfInfo, setNFInfo] = useState(''); const [nfSettingType, setNFSettingType] = useState(null); const [delegateeOptions, setDelegateeOptions] = useState([]); + const [nfSliderValue, setNfSliderValue] = useState(0); const channelDetailsFromBackend = useMemo(() => { if (delegatees) { @@ -166,12 +169,21 @@ function SendNotifications() { }, [channelDetailsFromBackend]); const channelSettingsOptions = useMemo(() => { - const defaultOption = { label: 'Default', value: null }; + const defaultOption = { label: 'Default', value: null, isRange: false }; if (channelSettings) { - const settingsOptions = channelSettings.map(setting => ({ - label: setting.description, + const settingsOptions = channelSettings.map((setting) => ({ + label: + setting.type === 2 ? ( + +
{setting.description}
+ Range +
+ ) : ( + setting.description + ), value: setting.index, + isRange: setting.type === 2, })); return [defaultOption, ...settingsOptions]; @@ -181,7 +193,7 @@ function SendNotifications() { }, [channelSettings]); const openManageSettings = () => { - const newPageUrl = APP_PATHS.Channels; // Replace with the URL of the manage settings later + const newPageUrl = APP_PATHS.ChannelSettings; // Use window.open() to open the URL in a new tab window.open(newPageUrl, '_blank'); @@ -285,6 +297,14 @@ function SendNotifications() { return validated; }; + const getIndex = () => { + if (nfSettingType === null) return undefined; + else if (channelSettings[nfSettingType - 1]?.type === 1) + return `${nfSettingType}-1`; + else if (channelSettings[nfSettingType - 1]?.type === 2) + return `${nfSettingType}-2-${nfSliderValue}`; + } + const handleSendMessage = async (e) => { // Check everything in order e.preventDefault(); @@ -399,6 +419,7 @@ function SendNotifications() { body: amsg, cta: acta, img: aimg, + index: getIndex(), }, recipients: notifRecipients, // recipient address channel: channelAddressInCaip, // your channel address @@ -610,7 +631,7 @@ function SendNotifications() { weight={isMobile ? "500" : "600"} textTransform="none" size={isMobile ? "15px" : "14px"} - color="#1E1E1E" + color={theme.default.color} padding="5px 15px" radius="30px"> Title @@ -623,7 +644,7 @@ function SendNotifications() { weight={isMobile ? "500" : "600"} textTransform="none" size={isMobile ? "15px" : "14px"} - color="#1E1E1E" + color={theme.default.color} padding="5px 15px" radius="30px"> Media URL @@ -636,7 +657,7 @@ function SendNotifications() { weight={isMobile ? "500" : "600"} textTransform="none" size={isMobile ? "15px" : "14px"} - color="#1E1E1E" + color={theme.default.color} padding="5px 15px" radius="30px"> CTA Link @@ -656,10 +677,11 @@ function SendNotifications() { padding="12px" weight="400" size="16px" - bg="white" + color={theme.default.color} + bg={theme.default.bg} height="25px" margin="7px 0px 0px 0px" - border="1px solid #BAC4D6" + border={`1px solid ${theme.snfBorder}`} focusBorder="1px solid #657795" radius="12px" value={nfRecipient} @@ -691,10 +713,11 @@ function SendNotifications() { padding="12px" weight="400" size="16px" - bg="white" + color={theme.default.color} + bg={theme.default.bg} height="25px" margin="7px 0px 0px 0px" - border="1px solid #BAC4D6" + border={`1px solid ${theme.snfBorder}`} focusBorder="1px solid #657795" radius="12px" value={tempRecipeint} @@ -744,10 +767,11 @@ function SendNotifications() { padding="12px" weight="400" size="16px" - bg="white" + color={theme.default.color} + bg={theme.default.bg} height="25px" margin="7px 0px 0px 0px" - border="1px solid #BAC4D6" + border={`1px solid ${theme.snfBorder}`} focusBorder="1px solid #657795" radius="12px" value={nfSub} @@ -788,10 +812,11 @@ function SendNotifications() { padding="12px" weight="400" margin="7px 0px 0px 0px" - border="1px solid #BAC4D6" + border={`1px solid ${theme.snfBorder}`} focusBorder="1px solid #657795" radius="12px" - bg="#fff" + color={theme.default.color} + bg={theme.default.bg} overflow="auto" value={nfMsg} onChange={(e) => { @@ -839,19 +864,57 @@ function SendNotifications() { options={channelSettingsOptions} onChange={(option) => { setNFSettingType(option.value); - console.log(option); + if(channelSettings[option.value - 1]?.type === 2) { + setNfSliderValue(channelSettings[option.value - 1]?.default); + } }} value={channelSettingsOptions[0]} />
- { - setNFSettingType(e.target.value); - }} - /> + {nfSettingType !== null && channelSettings[nfSettingType - 1]?.type === 2 && ( + + + + setNfSliderValue(x)} + xstep={1} + xmin={channelSettings[nfSettingType - 1]?.lowerLimit} + xmax={channelSettings[nfSettingType - 1]?.upperLimit} + /> + {nfSliderValue} + + + )} )} @@ -864,10 +927,11 @@ function SendNotifications() { padding="12px" weight="400" size="16px" - bg="white" + color={theme.default.color} + bg={theme.default.bg} height="25px" margin="7px 0px 0px 0px" - border="1px solid #BAC4D6" + border={`1px solid ${theme.snfBorder}`} focusBorder="1px solid #657795" radius="12px" value={nfMedia} @@ -888,10 +952,11 @@ function SendNotifications() { padding="12px" weight="400" size="16px" - bg="white" + color={theme.default.color} + bg={theme.default.bg} height="25px" margin="7px 0px 0px 0px" - border="1px solid #BAC4D6" + border={`1px solid ${theme.snfBorder}`} radius="12px" focusBorder="1px solid #657795" value={nfCTA} @@ -1069,9 +1134,9 @@ const Label = styled.div` const DropdownStyled = styled(Dropdown)` .Dropdown-control { - background-color: white; - color: #000; - border: 1px solid #bac4d6; + background-color: ${(props) => props.theme.default.bg}; + color: ${(props) => props.theme.default.color}; + border: 1px solid ${(props) => props.theme.snfBorder}; border-radius: 12px; flex: 1; outline: none; @@ -1102,14 +1167,14 @@ const DropdownStyled = styled(Dropdown)` } .Dropdown-option { - background-color: #fff; - color: #000; + background-color: ${(props) => props.theme.default.bg}; + color: ${(props) => props.theme.default.color}; font-size: 16px; padding: 20px 20px; } .Dropdown-option:hover { background-color: #d00775; - color: #000; + color: white; } `; @@ -1156,7 +1221,7 @@ const CustomDropdownItem = styled.div` margin-right: 10px; } div { - color: black; + color: ${(props) => props.theme.default.color}; font-size: 16px; letter-spacing: 2px; } @@ -1189,7 +1254,7 @@ const ToggleOption = styled(ItemH)` box-sizing: border-box; margin: 15px 0px; width: 10em; - background: #f4f5fa; + background: ${(props) => props.theme.snfToggleBg}; flex: none; padding: 15px; border-radius: 20px; @@ -1218,5 +1283,11 @@ const SubmitButton = styled(Button)` } `; +const DropdownLabel = styled.div` + display: flex; + flex-direction: row; + align-items: center; +`; + // Export Default export default SendNotifications; diff --git a/src/components/VerifyAlias.js b/src/components/VerifyAlias.js index 20ba6d81fa..a1d984beb5 100644 --- a/src/components/VerifyAlias.js +++ b/src/components/VerifyAlias.js @@ -47,6 +47,10 @@ const VerifyAlias = ({ aliasEthAccount, setAliasVerified }) => { 1442: { label: "Polygon zkEVM ETH", url: "https://public.zkevm-test.net/" + }, + 421613: { + label: "Goerli ArbETH", + url: "https://faucet.quicknode.com/arbitrum/goerli" } } diff --git a/src/components/channel/AddSettingModalContent.tsx b/src/components/channel/AddSettingModalContent.tsx index bebae1afd3..59c52059b0 100644 --- a/src/components/channel/AddSettingModalContent.tsx +++ b/src/components/channel/AddSettingModalContent.tsx @@ -66,7 +66,7 @@ const AddSettingModalContent = ({ const settingToEdit = InnerComponentProps?.settingToEdit || undefined; const [isLoading, setIsLoading] = useState(false); const [settingName, setSettingName] = useState(settingToEdit ? settingToEdit.description : ''); - const [isDefault, setIsDefault] = useState(settingToEdit && settingToEdit.default === true); + const [isDefault, setIsDefault] = useState(settingToEdit && settingToEdit.isDefaultEnabled === true); const [isRange, setIsRange] = useState(settingToEdit && settingToEdit.type === 2 ? true : false); const [lowerLimit, setLowerLimit] = useState( settingToEdit && settingToEdit.type === 2 ? settingToEdit.lowerLimit.toString() : '' @@ -75,7 +75,7 @@ const AddSettingModalContent = ({ settingToEdit && settingToEdit.type === 2 ? settingToEdit.upperLimit.toString() : '' ); const [defaultValue, setDefaultValue] = useState( - settingToEdit && settingToEdit.default ? settingToEdit.default.toString() : '' + settingToEdit && settingToEdit.type === 2 ? settingToEdit.defaultValue.toString() : '' ); const [errorInfo, setErrorInfo] = useState(); @@ -103,7 +103,8 @@ const AddSettingModalContent = ({ const settingData: ChannelSetting = isRange ? { type: 2, - default: Number(defaultValue), + defaultValue: Number(defaultValue), + isDefaultEnabled: isDefault, description: settingName, index: index, lowerLimit: Number(lowerLimit), @@ -111,7 +112,7 @@ const AddSettingModalContent = ({ } : { type: 1, - default: isDefault, + isDefaultEnabled: isDefault, description: settingName, index: index, }; @@ -297,6 +298,7 @@ const CloseButton = styled(MdClose)` align-self: flex-end; color: ${(props) => props.theme.default.secondaryColor}; font-size: 20px; + cursor: pointer; `; const ModalTitle = styled.div` diff --git a/src/components/channel/ChannelButtons.tsx b/src/components/channel/ChannelButtons.tsx index d54b41f9a6..3b717ca92b 100644 --- a/src/components/channel/ChannelButtons.tsx +++ b/src/components/channel/ChannelButtons.tsx @@ -50,7 +50,7 @@ export const AddSettingButton = ({ onClick }: ChannelButtonProps) => { }; const ChannelButton = styled(Button)` - height: 36px; + min-height: 36px; background: ${(props) => props.theme.default.primaryPushThemeTextColor}; color: #fff; z-index: 0; @@ -63,7 +63,7 @@ const ChannelButton = styled(Button)` `; const ChannelButtonWhite = styled.button` - height: 36px; + min-height: 36px; border: 1px solid ${(props) => props.theme.default.borderColor}; background: transparent; color: white; diff --git a/src/components/channel/ChannelInfoList.tsx b/src/components/channel/ChannelInfoList.tsx index 1659924bca..cfc1a44841 100644 --- a/src/components/channel/ChannelInfoList.tsx +++ b/src/components/channel/ChannelInfoList.tsx @@ -11,6 +11,7 @@ import DelegateInfo from 'components/DelegateInfo'; import LoaderSpinner, { LOADER_TYPE } from 'components/reusables/loaders/LoaderSpinner'; import DelegateSettingsDropdown, { ChannelDropdownOption } from './DelegateSettingsDropdown'; import EmptyNotificationSettings from './EmptyNotificationSettings'; +import Tag from '../reusables/labels/Tag'; // Internal Configs import { device } from 'config/Globals'; @@ -137,15 +138,6 @@ const DelegatesList = styled.div<{ isLoading: boolean }>` } `; -const Tag = styled.div` - padding: 4px 8px 4px 8px; - border-radius: 4px; - background-color: ${(props) => props.theme.default.secondaryBg}; - color: ${(props) => props.theme.tooltipContentDesc}; - font-size: 10px; - margin-left: 8px; -`; - const NotificationSettingName = styled.span` margin-left: 15px; color: ${(props) => diff --git a/src/components/channel/NotificationSettings.tsx b/src/components/channel/NotificationSettings.tsx index 0cb4cdf0a5..269e23b706 100644 --- a/src/components/channel/NotificationSettings.tsx +++ b/src/components/channel/NotificationSettings.tsx @@ -92,7 +92,27 @@ function NotificationSettings() { if (delegatee) { const { channel_settings } = delegatee; if (channel_settings !== null) { - const settings = JSON.parse(channel_settings); + const parsedData = JSON.parse(channel_settings); + const settings: ChannelSetting[] = parsedData.map((setting: any) => { + if(setting.type === 1) { + return { + type: 1, + isDefaultEnabled: setting.default, + description: setting.description, + index: setting.index, + } + } else { + return { + type: 2, + isDefaultEnabled: setting.enabled === 1 ? true : false, + defaultValue: setting.default, + description: setting.description, + index: setting.index, + lowerLimit: setting.lowerLimit, + upperLimit: setting.upperLimit, + } + } + }); setSettings(settings); setCurrentSettings(settings); setIsLoadingSettings(false); @@ -138,16 +158,17 @@ function NotificationSettings() { const parsedFees = ethers.utils.parseUnits(feesRequiredForEdit.toString(), 18); notificationToast.showLoaderToast({ loaderMessage: 'Waiting for Confirmation...' }); - const notifOptions = ethers.utils.parseUnits('2', 18); + const notifOptions = settings.length; let _notifSettings = ''; let _notifDescription = ''; settings.forEach((setting) => { if (_notifSettings !== '') _notifSettings += '+'; if (_notifDescription !== '') _notifDescription += '+'; + const isEnabled = setting.isDefaultEnabled ? '1' : '0'; if (setting.type === 1) { - _notifSettings += `${setting.type}-${setting.default ? '1' : '0'}`; + _notifSettings += `${setting.type}-${isEnabled}`; } else if (setting.type === 2) { - _notifSettings += `${setting.type}-${setting.default}-${setting.lowerLimit}-${setting.upperLimit}`; + _notifSettings += `${setting.type}-${isEnabled}-${setting.defaultValue}-${setting.lowerLimit}-${setting.upperLimit}`; } _notifDescription += setting.description; }); @@ -212,7 +233,6 @@ function NotificationSettings() { const settingsChanged = useMemo(() => { if (!settings || !currentSettings) return false; - console.log('Settings changed bro', settings, currentSettings); if (settings.length !== currentSettings.length) return true; let isUnchanged = true; for (let i = 0; i < settings.length; i++) { @@ -223,13 +243,14 @@ function NotificationSettings() { isUnchanged && setting1.type === setting2.type && setting1.description === setting2.description && - setting1.default === setting2.default; + setting1.isDefaultEnabled === setting2.isDefaultEnabled; } else if (setting1.type === 2) { isUnchanged = isUnchanged && setting1.type === setting2.type && setting1.description === setting2.description && - setting1.default === setting2.default && + setting1.defaultValue === setting2.defaultValue && + setting1.isDefaultEnabled === setting2.isDefaultEnabled && setting1.lowerLimit === setting2.lowerLimit && setting1.upperLimit === setting2.upperLimit; } @@ -252,7 +273,7 @@ function NotificationSettings() { isLoading={isLoadingSettings} items={settings} onClickEmptyListButton={showAddSettingModal} - emptyListButtonTitle='Add Setting' + emptyListButtonTitle="Add Setting" settingsDropdownOptions={[ { icon: , diff --git a/src/components/channel/UserSettings.tsx b/src/components/channel/UserSettings.tsx index 45209eaa74..d238a12624 100644 --- a/src/components/channel/UserSettings.tsx +++ b/src/components/channel/UserSettings.tsx @@ -163,6 +163,10 @@ export default UserSettings; const Container = styled.div` padding: 32px 24px; flex: 1; + + @media ${device.tablet} { + padding: 24px 12px; + } `; const PageTitle = styled.div` diff --git a/src/components/reusables/labels/Tag.tsx b/src/components/reusables/labels/Tag.tsx new file mode 100644 index 0000000000..ad44e47f72 --- /dev/null +++ b/src/components/reusables/labels/Tag.tsx @@ -0,0 +1,21 @@ +// React + Web3 Essentials +import React from 'react'; + +// External Packages +import styled from 'styled-components'; + +const Tag = ({ children }) => { + return {children}; +}; + +export default Tag; + +const TagLabel = styled.div` + padding: 4px 8px 4px 8px; + border-radius: 4px; + background-color: ${(props) => props.theme.default.secondaryBg}; + color: ${(props) => props.theme.tooltipContentDesc}; + font-size: 10px; + margin-left: 8px; + max-height: 20px; +`; diff --git a/src/config/Themization.js b/src/config/Themization.js index 4abc8d45cb..f49062faf2 100644 --- a/src/config/Themization.js +++ b/src/config/Themization.js @@ -272,14 +272,19 @@ const themeLight = { snapUIBackground:'#F9F9F9', snapButtonBackground:'#1E1E1E', - snapPrimaryText:'', + snapPrimaryText:'#000', snapSecondaryText:'#62626a', snapBackground:'#F2F2F2', + snapBorderColor:'#BAC4D6', // Notification Settings nfsError: '#ED5858', nfsDisabled: '#DFDEE9', nfsDisabledText: '#AFB3BF', + + // Send Notification + snfBorder: '#BAC4D6', + snfToggleBg: '#f4f5fa', }; const themeDark = { @@ -560,14 +565,19 @@ const themeDark = { snapUIBackground:'transparent', snapButtonBackground:'#404650', - snapPrimaryText:'', - snapSecondaryText:'#787E99', + snapPrimaryText:'#fff', + snapSecondaryText:'#B6BCD6', snapBackground:'#404650', + snapBorderColor:'#787E99', // Notification Settings nfsError: '#ED5858', nfsDisabled: '#AFB3BF', nfsDisabledText: '#787E99', + + // Send Notification + snfBorder: '#4A4F67', + snfToggleBg: '#404650', }; module.exports = { diff --git a/src/config/config-dev.js b/src/config/config-dev.js index bab02acd4d..778a9d8a3a 100644 --- a/src/config/config-dev.js +++ b/src/config/config-dev.js @@ -26,7 +26,8 @@ export const config = { 80001, //for mumbai polygon 97, // bnb testnet 420, // optimism goerli testnet - 1442 // polygon zkevm testnet + 1442, // polygon zkevm testnet + 421613 // arbitrum testnet ], /** @@ -40,7 +41,8 @@ export const config = { 80001: "https://polygon-mumbai.infura.io/v3/150f25623ae64d08ab7ec7dd0c6b6ee9", 97: "https://data-seed-prebsc-1-s1.binance.org:8545", 420: "https://opt-goerli.g.alchemy.com/v2/98vFj7OCg4vbGKvCDpj6pOAMSBVC7rzm", - 1442: "https://rpc.public.zkevm-test.net" + 1442: "https://rpc.public.zkevm-test.net", + 421613: "https://goerli-rollup.arbitrum.io/rpc" }, infuraApiUrl: 'https:/infura-ipfs.io/ipfs/', @@ -148,5 +150,12 @@ export const CHAIN_DETAILS = { chainId: 1442, rpcUrl: 'https://rpc.public.zkevm-test.net', commAddress: '0x630b152e4185c63D7177c656b56b26f878C61572' + }, + 421613: { + label: "Arbitrum Testnet", + name: "ARBITRUM_TESTNET", + chainId: 421613, + rpcUrl: '', + commAddress: '0x2f6aE0907116A2c50D712e78b48D874fadeB6850' } } \ No newline at end of file diff --git a/src/config/config-prod.js b/src/config/config-prod.js index a628131011..13d27f84fe 100644 --- a/src/config/config-prod.js +++ b/src/config/config-prod.js @@ -25,7 +25,8 @@ export const config = { 1, //for ethereum mainnet 137, //for polygon mainnet 56, // for bnb mainnet - // 10 // for optimism mainnet + // 10, // for optimism mainnet + // 42161, // arbitrum mainnet 1101 // polygon zkevm mainnet ], @@ -39,7 +40,9 @@ export const config = { aliasRPC: { 137: "https://polygon-mainnet.infura.io/v3/150f25623ae64d08ab7ec7dd0c6b6ee9", 56: "https://bsc-dataseed.binance.org/", - 10: "https://opt-mainnet.g.alchemy.com/v2/JYW0UaSC5Zd0hrI6vE2K9VN1wJupoY5B" + 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/', @@ -147,5 +150,12 @@ export const CHAIN_DETAILS = { 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' } } \ No newline at end of file diff --git a/src/config/config-staging.js b/src/config/config-staging.js index 55040010e9..2d257f4f66 100644 --- a/src/config/config-staging.js +++ b/src/config/config-staging.js @@ -27,7 +27,8 @@ export const config = { 80001, //for mumbai polygon 97, // bnb testnet 420, // optimism goerli testnet - 1442 // polygon zkevm testnet + 1442, // polygon zkevm testnet + 421613 // arbitrum testnet ], /** @@ -41,7 +42,8 @@ export const config = { 80001: "https://polygon-mumbai.infura.io/v3/150f25623ae64d08ab7ec7dd0c6b6ee9", 97: "https://data-seed-prebsc-1-s1.binance.org:8545", 420: "https://opt-goerli.g.alchemy.com/v2/98vFj7OCg4vbGKvCDpj6pOAMSBVC7rzm", - 1442: "https://rpc.public.zkevm-test.net" + 1442: "https://rpc.public.zkevm-test.net", + 421613: "https://goerli-rollup.arbitrum.io/rpc" }, infuraApiUrl: 'https:/infura-ipfs.io/ipfs/', @@ -149,5 +151,12 @@ export const CHAIN_DETAILS = { chainId: 1442, rpcUrl: 'https://rpc.public.zkevm-test.net', commAddress: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa' + }, + 421613: { + label: "Arbitrum Testnet", + name: "ARBITRUM_TESTNET", + chainId: 421613, + rpcUrl: '', + commAddress: '0xb3971BCef2D791bc4027BbfedFb47319A4AAaaAa' } } \ No newline at end of file diff --git a/src/helpers/CaipHelper.ts b/src/helpers/CaipHelper.ts index 0f63202d56..9233228479 100644 --- a/src/helpers/CaipHelper.ts +++ b/src/helpers/CaipHelper.ts @@ -1,7 +1,7 @@ // Internal Configs import { appConfig } from '../config'; -export const Eip155EnabledIds: Array = [1, 56, 137, 10, 5, 97, 80001, 420, 1442]; +export const Eip155EnabledIds: Array = [1, 56, 137, 10, 1101, 42161, 5, 97, 80001, 420, 1442, 421613]; // Types type CAIPProps = { diff --git a/src/helpers/UtilityHelper.ts b/src/helpers/UtilityHelper.ts index 745f5a9b85..74a71ebcd3 100644 --- a/src/helpers/UtilityHelper.ts +++ b/src/helpers/UtilityHelper.ts @@ -11,7 +11,7 @@ import { appConfig } from '../config'; // Utility Helper Functions const UtilityHelper = { isMainnet: (chainId: number):boolean => { - if (chainId === 1 || chainId === 137 || chainId === 56 || chainId === 1101) { + if (chainId === 1 || chainId === 137 || chainId === 56 || chainId === 1101 || chainId === 10 || chainId === 42161) { return true; } return false; @@ -48,8 +48,11 @@ export const MaskedAliasChannels:{ 10: {}, 420: {}, 1442: {}, - 1101: {} + 1101: {}, + 42161: {}, + 421613: {} } + export const findObject = (data: any,parentArray: any[],property: string ): boolean => { let isPresent = false; if(data) { @@ -106,7 +109,9 @@ export const networkName = { 420: "Optimism Goerli", 10: "Optimism Mainnet", 1442: "Polygon zkEVM Testnet", - 1101: "Polygon zkEVM Mainnet" + 1101: "Polygon zkEVM Mainnet", + 42161: "ArbitrumOne Mainnet", + 421613: "Arbitrum Testnet" }; export const chainNameBackendStandard = { @@ -123,6 +128,10 @@ export const aliasChainIdToChainName={ 137:'POLYGON', 56:'BSC', 10:'OPTIMISM', + 42161: 'ARBITRUMONE', + 421613: 'ARBITRUMONE', + 1101: "POLYGONZKEVM", + 1442: "POLYGONZKEVM" } export const aliasChainIdsMapping = { @@ -187,6 +196,20 @@ export const NETWORK_DETAILS = { nativeCurrency: {name: 'ETH', symbol: 'ETH', decimals: 18}, rpcUrls: ['https://rpc.polygon-zkevm.gateway.fm'], blockExplorerUrls: ['https://zkevm.polygonscan.com'] + }, + ARBITRUM_TESTNET: { + chainId: utils.hexValue(421613), + chainName: 'Arbitrum Testnet', + nativeCurrency: {name: 'ETH', symbol: 'ETH', decimals: 18}, + rpcUrls: ['https://goerli-rollup.arbitrum.io/rpc'], + blockExplorerUrls: ['https://testnet.arbiscan.io/'] + }, + ARBITRUMONE_MAINNET: { + chainId: utils.hexValue(42161), + chainName: 'ArbitrumOne Mainnet', + nativeCurrency: {name: 'ETH', symbol: 'ETH', decimals: 18}, + rpcUrls: ['https://arb1.arbitrum.io/rpc'], + blockExplorerUrls: ['https://arbiscan.io/'] } }; @@ -204,7 +227,9 @@ export const LOGO_FROM_CHAIN_ID: { 420: "Optimism.svg", 10: "Optimism.svg", 1442: "PolygonZkEVM.svg", - 1101: "PolygonZkEVM.svg" + 1101: "PolygonZkEVM.svg", + 42161: "Arbitrum.svg", + 421613: "Arbitrum.svg" } export type getAliasResponseType = { diff --git a/src/helpers/channel/types.ts b/src/helpers/channel/types.ts index 09616ad0bc..01eda9a9db 100644 --- a/src/helpers/channel/types.ts +++ b/src/helpers/channel/types.ts @@ -1,13 +1,14 @@ export type ChannelSetting = | { type: 1; // Boolean - default: boolean; + isDefaultEnabled: boolean; description: string; index: number; } | { type: 2; // Range - default: number; + defaultValue: number; + isDefaultEnabled: boolean; description: string; index: number; lowerLimit: number; diff --git a/src/hooks/useInactiveListener.ts b/src/hooks/useInactiveListener.ts index 1775e72f90..b50a66aec1 100644 --- a/src/hooks/useInactiveListener.ts +++ b/src/hooks/useInactiveListener.ts @@ -16,8 +16,8 @@ export function useInactiveListener() { if (appConfig.coreContractChain === 42) return 'Unsupported Network, please connect to the Ethereum Kovan network or Polygon Mumbai network'; else if (appConfig.coreContractChain === 5) - return 'Unsupported Network, please connect to the Ethereum Goerli, Polygon Mumbai, BNB testnet, Optimism Goerli or Polygon zkEVM testnet'; - else return 'Unsupported Network, please connect to the Ethereum, Polygon, BNB, or Polygon zkEVM Mainnet'; + return 'Unsupported Network, please connect to the Ethereum Goerli, Polygon Mumbai, BNB testnet, Optimism Goerli, Arbitrum testnet or Polygon zkEVM testnet'; + else return 'Unsupported Network, please connect to the Ethereum, Polygon, BNB, Optimism, Arbitrum or Polygon zkEVM Mainnet'; } useEffect(() => { diff --git a/src/modules/receiveNotifs/MetamaskPushSnapModal.tsx b/src/modules/receiveNotifs/MetamaskPushSnapModal.tsx index aadf61c932..2b4f044bb8 100644 --- a/src/modules/receiveNotifs/MetamaskPushSnapModal.tsx +++ b/src/modules/receiveNotifs/MetamaskPushSnapModal.tsx @@ -13,7 +13,7 @@ import InstallMetamaskSnapModal from 'components/MetamaskSnap/InstallMetamaskSna import SnapInformationModal from 'components/MetamaskSnap/SnapInformationModal'; import MetamaskSnapConfigureModal from 'components/MetamaskSnap/MetamaskSnapConfigureModal'; import { AppContext } from 'contexts/AppContext'; - +import GLOBALS, { device, globalsMargin } from "config/Globals"; const MetamaskPushSnapModal = ({ onClose diff --git a/src/modules/snap/AboutSnapModal.tsx b/src/modules/snap/AboutSnapModal.tsx index aa0ff4abf4..dd973ad8db 100644 --- a/src/modules/snap/AboutSnapModal.tsx +++ b/src/modules/snap/AboutSnapModal.tsx @@ -1,13 +1,15 @@ import React from 'react'; import { ReactComponent as Close } from 'assets/chat/group-chat/close.svg'; import { H2V2, ItemVV2, SpanV2 } from 'components/reusables/SharedStylingV2'; -import styled from 'styled-components'; +import styled, { useTheme } from 'styled-components'; const AboutSnapModal = ({ onClose }) => { + const theme = useTheme(); + const InfoDetails = [ { title: 'Step 1', @@ -40,13 +42,13 @@ const AboutSnapModal = ({ - Installing Push Snap + Installing Push Snap {InfoDetails.map((detail) => ( - {detail.title} - {detail.info} + {detail.title} + {detail.info} ))} @@ -64,6 +66,6 @@ const Container = styled(ItemVV2)` width: 375px; padding: 32px 24px; border-radius: 16px; - background: #FFF; align-items: end; + background: ${(props) => props.theme.default.bg}; ` \ No newline at end of file diff --git a/src/modules/snap/SnapModule.tsx b/src/modules/snap/SnapModule.tsx index 8c712b17bd..1318ab98c6 100644 --- a/src/modules/snap/SnapModule.tsx +++ b/src/modules/snap/SnapModule.tsx @@ -38,7 +38,7 @@ const SnapModule = () => { useEffect(() => { getInstalledSnaps(); getWalletAddresses(); - },[account,walletConnected]); + }, [account, walletConnected]); async function getInstalledSnaps() { const installedSnaps = await window.ethereum.request({ @@ -66,7 +66,7 @@ const SnapModule = () => { console.log(walletConnected); if (result.includes(account)) { setAddedAddress(true); - }else{ + } else { setAddedAddress(false); } } @@ -85,7 +85,6 @@ const SnapModule = () => { async function connectToMetaMask() { setLoading(true); - try { if (!snapInstalled) { await connectSnap(); @@ -141,6 +140,8 @@ const SnapModule = () => { showMetamaskPushSnap(); }; + const theme = useTheme(); + return ( { Push Snap @@ -179,7 +180,7 @@ const SnapModule = () => { powered by MetaMask @@ -191,14 +192,14 @@ const SnapModule = () => { Get started by opting in to channels on Push.{' '} Once you opt-in you will receive notifications on MetaMask. @@ -207,7 +208,7 @@ const SnapModule = () => { You’re about to install Push Snap which allows you to receive notifications from Push directly on MetaMask! @@ -247,7 +248,8 @@ const SnapModule = () => { )} {walletConnected || addedAddress ? ( - + + { Settings (window.location.href = '/channels')}>Get Started - + ) : ( { width={16} /> @@ -298,6 +300,17 @@ const Container = styled(Section)` padding: ${GLOBALS.ADJUSTMENTS.PADDING.BIG}; position: relative; margin: ${GLOBALS.ADJUSTMENTS.MARGIN.MINI_MODULES.DESKTOP}; + @media ${device.laptop} { + margin: ${GLOBALS.ADJUSTMENTS.MARGIN.MINI_MODULES.TABLET}; + padding: ${GLOBALS.ADJUSTMENTS.PADDING.DEFAULT}; + width: calc(100% - ${globalsMargin.MINI_MODULES.TABLET.RIGHT} - ${globalsMargin.MINI_MODULES.TABLET.LEFT} - ${GLOBALS.ADJUSTMENTS.PADDING.DEFAULT} - ${GLOBALS.ADJUSTMENTS.PADDING.DEFAULT}); + } + + @media ${device.mobileL} { + margin: ${GLOBALS.ADJUSTMENTS.MARGIN.MINI_MODULES.MOBILE}; + padding: ${GLOBALS.ADJUSTMENTS.PADDING.DEFAULT}; + width: calc(100% - ${globalsMargin.MINI_MODULES.MOBILE.RIGHT} - ${globalsMargin.MINI_MODULES.MOBILE.LEFT} - ${GLOBALS.ADJUSTMENTS.PADDING.DEFAULT} - ${GLOBALS.ADJUSTMENTS.PADDING.DEFAULT}); + } `; const SubContainer = styled(Section)` @@ -306,8 +319,12 @@ const SubContainer = styled(Section)` padding: 48px 24px; border-radius: 32px; background: #fff; + background: ${(props) => props.theme.default.bg}; box-shadow: 0px 4px 20px 0px rgba(0, 0, 0, 0.05); margin: 24px auto; + @media ${device.mobileL} { + width: 330px; + } `; const SnapButton = styled(ButtonV2)` @@ -328,6 +345,10 @@ const SnapButton = styled(ButtonV2)` @media (max-width: 600px) { font-size: 14px; } + + + + `; const ConnectButton = styled(SnapButton)` @@ -335,6 +356,8 @@ const ConnectButton = styled(SnapButton)` padding: 16px 24px; background: #d53a94; border: 1px solid #d53a94; + + `; const SettingsButton = styled(SnapButton)` @@ -343,19 +366,31 @@ const SettingsButton = styled(SnapButton)` text-align: center; width: 135px; padding: 16px 24px; - border: 1px solid #bac4d6; + border: 1px solid ${(props)=>props.theme.snapBorderColor}; background: ${(props) => props.theme.default.bg}; gap: 4px; + + @media ${device.mobileL} { + min-width: 246px; + } `; const FilledButton = styled(SnapButton)` width: 135px; padding: 16px 24px; background: #d53a94; + + @media ${device.mobileL} { + min-width: 246px; + } `; const InfoDiv = styled(ItemHV2)` cursor: pointer; `; -const ButtonContainer = styled(ItemHV2)``; +const ButtonContainer = styled(ItemHV2)` +@media ${device.mobileL} { + flex-direction:column; +} +`; diff --git a/src/structure/Header.tsx b/src/structure/Header.tsx index af72e0469c..5a829816c4 100644 --- a/src/structure/Header.tsx +++ b/src/structure/Header.tsx @@ -118,8 +118,8 @@ function Header({ isDarkMode, darkModeToggle }) { if (appConfig.coreContractChain === 42) return 'Unsupported Network, please connect to the Ethereum Kovan network or Polygon Mumbai network'; else if (appConfig.coreContractChain === 5) - return 'Unsupported Network, please connect to the Ethereum Goerli, Polygon Mumbai, BNB testnet, Optimism Goerli or Polygon zkEVM testnet'; - else return 'Unsupported Network, please connect to the Ethereum, Polygon, BNB, or Polygon zkEVM Mainnet'; + return 'Unsupported Network, please connect to the Ethereum Goerli, Polygon Mumbai, BNB testnet, Optimism Goerli, Arbitrum testnet or Polygon zkEVM testnet'; + else return 'Unsupported Network, please connect to the Ethereum, Polygon, BNB, Optimism, Arbitrum or Polygon zkEVM Mainnet'; } else { console.error(error); return 'An unknown error occurred. Check the console for more details'; diff --git a/yarn.lock b/yarn.lock index e66c89183d..9dc94d21d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -74,6 +74,13 @@ __metadata: languageName: node linkType: hard +"@adraffy/ens-normalize@npm:1.9.4": + version: 1.9.4 + resolution: "@adraffy/ens-normalize@npm:1.9.4" + checksum: 7d7fff58ebe2c4961f7e5e61dad123aa6a63fec0df5c84af1fa41079dc05d398599690be4427b3a94d2baa94084544bcfdf2d51cbed7504b9b0583b0960ad550 + languageName: node + linkType: hard + "@ambire/signature-validator@npm:^1.3.1": version: 1.3.1 resolution: "@ambire/signature-validator@npm:1.3.1" @@ -5104,7 +5111,7 @@ __metadata: languageName: node linkType: hard -"@noble/curves@npm:^1.0.0": +"@noble/curves@npm:1.2.0, @noble/curves@npm:^1.0.0, @noble/curves@npm:~1.2.0": version: 1.2.0 resolution: "@noble/curves@npm:1.2.0" dependencies: @@ -5120,7 +5127,7 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.3.2, @noble/hashes@npm:^1.3.1": +"@noble/hashes@npm:1.3.2, @noble/hashes@npm:^1.3.1, @noble/hashes@npm:~1.3.2": version: 1.3.2 resolution: "@noble/hashes@npm:1.3.2" checksum: fe23536b436539d13f90e4b9be843cc63b1b17666a07634a2b1259dded6f490be3d050249e6af98076ea8f2ea0d56f578773c2197f2aa0eeaa5fba5bc18ba474 @@ -5345,9 +5352,9 @@ __metadata: "@mui/lab": ^5.0.0-alpha.72 "@mui/material": ^5.5.0 "@pushprotocol/ledgerlive": latest - "@pushprotocol/restapi": 1.4.18 + "@pushprotocol/restapi": 0.0.1-alpha.45 "@pushprotocol/socket": latest - "@pushprotocol/uiweb": 1.1.13 + "@pushprotocol/uiweb": 1.1.14 "@reduxjs/toolkit": ^1.7.1 "@testing-library/dom": ^6.12.2 "@testing-library/jest-dom": ^4.2.4 @@ -5547,12 +5554,13 @@ __metadata: languageName: node linkType: hard -"@pushprotocol/restapi@npm:1.4.18": - version: 1.4.18 - resolution: "@pushprotocol/restapi@npm:1.4.18" +"@pushprotocol/restapi@npm:0.0.1-alpha.45": + version: 0.0.1-alpha.45 + resolution: "@pushprotocol/restapi@npm:0.0.1-alpha.45" dependencies: "@ambire/signature-validator": ^1.3.1 "@metamask/eth-sig-util": ^5.0.2 + "@pushprotocol/socket": ^0.5.2 axios: ^0.27.2 buffer: ^6.0.3 crypto-js: ^4.1.1 @@ -5561,13 +5569,15 @@ __metadata: livepeer: ^2.5.8 openpgp: ^5.5.0 simple-peer: ^9.11.1 + socket.io-client: ^4.5.2 tslib: ^2.3.0 unique-names-generator: ^4.7.1 uuid: ^9.0.0 video-stream-merger: ^4.0.1 + viem: ^1.3.0 peerDependencies: ethers: ^5.6.8 - checksum: a350b385db72c5ce106ed940d77acfac11cec1960409b18698d63d3359a84788da503b62fa3fd01f94b7a7d3634614e26346432d833eb0d69b717c00bb627415 + checksum: 1defe9cb71147e49e42ac8dcb1fa40ab6448c703bf8dd09ddcdbc49f737d58705a7eae7aef88bb88daf9e945244182fa6298cd74842273add7bbb00d8ff7b30f languageName: node linkType: hard @@ -5583,6 +5593,18 @@ __metadata: languageName: node linkType: hard +"@pushprotocol/socket@npm:^0.5.2": + version: 0.5.2 + resolution: "@pushprotocol/socket@npm:0.5.2" + dependencies: + socket.io-client: ^4.5.2 + tslib: ^2.3.0 + peerDependencies: + ethers: ^5.6.8 + checksum: 14a438269eae87979e10377e5b8a38953e190593648ce64f148def553b66467cf52ec29ce2b70956342c23f42b67ae179f54a1aab6f22d1495a3806ba0146bbc + languageName: node + linkType: hard + "@pushprotocol/socket@npm:latest": version: 0.4.2 resolution: "@pushprotocol/socket@npm:0.4.2" @@ -5595,9 +5617,9 @@ __metadata: languageName: node linkType: hard -"@pushprotocol/uiweb@npm:1.1.13": - version: 1.1.13 - resolution: "@pushprotocol/uiweb@npm:1.1.13" +"@pushprotocol/uiweb@npm:1.1.14": + version: 1.1.14 + resolution: "@pushprotocol/uiweb@npm:1.1.14" dependencies: "@livepeer/react": ^2.6.0 "@pushprotocol/socket": ^0.5.0 @@ -5618,7 +5640,7 @@ __metadata: ethers: ^5.7.1 react: ">=16.8.0" styled-components: ^5.3.5 - checksum: a03933a5e2c010435aad2bf36657198276b75025521146a64e66847f9f7f492ff85ec4612cf190758d6fac873634feb7250ccf2f3034f399859f478716a96c8e + checksum: c06f684851892d3c71e4c8f38210a4b04e8e1f039d82a2bda728f3cc1414d059989c6ef91a5f6bf07e945ad61fd84a630c6e2ac72cba9493f138387396eda421 languageName: node linkType: hard @@ -6386,6 +6408,13 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:~1.1.2": + version: 1.1.3 + resolution: "@scure/base@npm:1.1.3" + checksum: 1606ab8a4db898cb3a1ada16c15437c3bce4e25854fadc8eb03ae93cbbbac1ed90655af4b0be3da37e12056fef11c0374499f69b9e658c9e5b7b3e06353c630c + languageName: node + linkType: hard + "@scure/bip32@npm:1.3.1": version: 1.3.1 resolution: "@scure/bip32@npm:1.3.1" @@ -6397,6 +6426,17 @@ __metadata: languageName: node linkType: hard +"@scure/bip32@npm:1.3.2": + version: 1.3.2 + resolution: "@scure/bip32@npm:1.3.2" + dependencies: + "@noble/curves": ~1.2.0 + "@noble/hashes": ~1.3.2 + "@scure/base": ~1.1.2 + checksum: c5ae84fae43490853693b481531132b89e056d45c945fc8b92b9d032577f753dfd79c5a7bbcbf0a7f035951006ff0311b6cf7a389e26c9ec6335e42b20c53157 + languageName: node + linkType: hard + "@scure/bip39@npm:1.2.1": version: 1.2.1 resolution: "@scure/bip39@npm:1.2.1" @@ -7869,6 +7909,15 @@ __metadata: languageName: node linkType: hard +"@types/ws@npm:^8.5.5": + version: 8.5.6 + resolution: "@types/ws@npm:8.5.6" + dependencies: + "@types/node": "*" + checksum: 7addb0c5fa4e7713d5209afb8a90f1852b12c02cb537395adf7a05fbaf21205dc5f7c110fd5ad6f3dbf147112cbff33fb11d8633059cb344f0c14f595b1ea1fb + languageName: node + linkType: hard + "@types/yargs-parser@npm:*": version: 21.0.0 resolution: "@types/yargs-parser@npm:21.0.0" @@ -9521,6 +9570,21 @@ __metadata: languageName: node linkType: hard +"abitype@npm:0.9.8": + version: 0.9.8 + resolution: "abitype@npm:0.9.8" + peerDependencies: + typescript: ">=5.0.4" + zod: ^3 >=3.19.1 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + checksum: d7d887f29d6821e3f7a400de9620511b80ead3f85c5c87308aaec97965d3493e6687ed816e88722b4f512249bd66dee9e69231b49af0e1db8f69400a62c87cf6 + languageName: node + linkType: hard + "abort-controller@npm:^3.0.0": version: 3.0.0 resolution: "abort-controller@npm:3.0.0" @@ -18412,6 +18476,15 @@ __metadata: languageName: node linkType: hard +"isomorphic-ws@npm:5.0.0": + version: 5.0.0 + resolution: "isomorphic-ws@npm:5.0.0" + peerDependencies: + ws: "*" + checksum: e20eb2aee09ba96247465fda40c6d22c1153394c0144fa34fe6609f341af4c8c564f60ea3ba762335a7a9c306809349f9b863c8beedf2beea09b299834ad5398 + languageName: node + linkType: hard + "isomorphic-ws@npm:^4.0.1": version: 4.0.1 resolution: "isomorphic-ws@npm:4.0.1" @@ -29310,6 +29383,28 @@ __metadata: languageName: node linkType: hard +"viem@npm:^1.3.0": + version: 1.15.4 + resolution: "viem@npm:1.15.4" + dependencies: + "@adraffy/ens-normalize": 1.9.4 + "@noble/curves": 1.2.0 + "@noble/hashes": 1.3.2 + "@scure/bip32": 1.3.2 + "@scure/bip39": 1.2.1 + "@types/ws": ^8.5.5 + abitype: 0.9.8 + isomorphic-ws: 5.0.0 + ws: 8.13.0 + peerDependencies: + typescript: ">=5.0.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 66fe2d8c00504e308b18d730dfd5bb7139e7b8a8c7fe10d8170c3bc4b33b25a9862dd09c7d4fac095310053a898dea18f850b72fcc5b408a439866c0e85d3f94 + languageName: node + linkType: hard + "w3c-hr-time@npm:^1.0.2": version: 1.0.2 resolution: "w3c-hr-time@npm:1.0.2" @@ -30349,6 +30444,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:8.13.0, ws@npm:^8.13.0": + version: 8.13.0 + resolution: "ws@npm:8.13.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 53e991bbf928faf5dc6efac9b8eb9ab6497c69feeb94f963d648b7a3530a720b19ec2e0ec037344257e05a4f35bd9ad04d9de6f289615ffb133282031b18c61c + languageName: node + linkType: hard + "ws@npm:^3.0.0": version: 3.3.3 resolution: "ws@npm:3.3.3" @@ -30375,21 +30485,6 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.13.0": - version: 8.13.0 - resolution: "ws@npm:8.13.0" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 53e991bbf928faf5dc6efac9b8eb9ab6497c69feeb94f963d648b7a3530a720b19ec2e0ec037344257e05a4f35bd9ad04d9de6f289615ffb133282031b18c61c - languageName: node - linkType: hard - "ws@npm:^8.5.0": version: 8.14.2 resolution: "ws@npm:8.14.2" From 23c378e4bdfd3ce9c95e771233c4bac28fb62e62 Mon Sep 17 00:00:00 2001 From: Kalash Shah <81062983+kalashshah@users.noreply.github.com> Date: Tue, 10 Oct 2023 16:47:05 +0530 Subject: [PATCH 12/18] chore: state management for user and admin settings (#1271) --- src/components/ChannelDetails.js | 18 +---- src/components/InitState.tsx | 7 ++ src/components/ViewChannelItem.js | 14 ++-- .../channel/AddSettingModalContent.tsx | 13 ++-- .../channel/NotificationSettings.tsx | 71 ++++++------------- src/components/channel/UserSettings.tsx | 23 ++++-- .../dropdowns/ManageNotifSettingDropdown.tsx | 7 ++ .../dropdowns/OptinNotifSettingDropdown.tsx | 7 +- .../dropdowns/UpdateNotifSettingDropdown.tsx | 4 ++ src/helpers/channel/InputValidation.ts | 1 - src/redux/slices/channelSlice.js | 28 +++++++- src/segments/ViewChannels.tsx | 9 ++- 12 files changed, 116 insertions(+), 86 deletions(-) diff --git a/src/components/ChannelDetails.js b/src/components/ChannelDetails.js index e940fa2b91..ef19384ad4 100644 --- a/src/components/ChannelDetails.js +++ b/src/components/ChannelDetails.js @@ -44,6 +44,7 @@ export default function ChannelDetails({ isChannelExpired, setIsChannelExpired, canVerify, aliasDetails: { isAliasVerified, aliasAddrFromContract }, } = useSelector((state) => state.admin); + const { channelSettings } = useSelector((state) => state.channels); const { CHANNEL_ACTIVE_STATE, CHANNNEL_DEACTIVATED_STATE } = useSelector((state) => state.channels); const { processingState } = useSelector((state) => state.channelCreation); @@ -142,19 +143,6 @@ export default function ChannelDetails({ isChannelExpired, setIsChannelExpired, } }, [account]); - const channelSettings = useMemo(() => { - if (delegatees) { - const delegatee = delegatees.find(({ channel }) => channel === channelAddress); - if (delegatee) { - const { channel_settings } = delegatee; - if (channel_settings !== null) { - return JSON.parse(channel_settings); - } - } - } - return []; - }, [delegatees, channelAddress]); - const removeDelegate = (walletAddress) => { return epnsCommWriteProvider.removeDelegate(walletAddress); }; @@ -335,7 +323,7 @@ export default function ChannelDetails({ isChannelExpired, setIsChannelExpired, props.theme.default.borderColor}; -`; +`; \ No newline at end of file diff --git a/src/components/InitState.tsx b/src/components/InitState.tsx index f78fb8dd2b..9890124709 100644 --- a/src/components/InitState.tsx +++ b/src/components/InitState.tsx @@ -27,6 +27,7 @@ import { setUserChannelDetails, } from 'redux/slices/adminSlice'; import { setProcessingState } from 'redux/slices/channelCreationSlice'; +import { updateBulkChannelSettings } from 'redux/slices/channelSlice'; import { setPushAdmin } from 'redux/slices/contractSlice'; import { getChannelsSearch, getUserDelegations } from 'services'; import * as PushAPI from '@pushprotocol/restapi'; @@ -175,6 +176,12 @@ const InitState = () => { } const channelInformation = await Promise.all(channelInformationPromise); dispatch(setDelegatees(channelInformation)); + // get channel settings of all the channels + const channelSettings = {}; + for (const channel of channelInformation) { + channelSettings[channel.channel] = channel.channel_settings ? JSON.parse(channel.channel_settings) : []; + } + dispatch(updateBulkChannelSettings(channelSettings)); } else { dispatch(setDelegatees([])); } diff --git a/src/components/ViewChannelItem.js b/src/components/ViewChannelItem.js index 66240143ff..72f4561ba1 100644 --- a/src/components/ViewChannelItem.js +++ b/src/components/ViewChannelItem.js @@ -1,5 +1,5 @@ // React + Web3 Essentials -import React, { useEffect } from 'react'; +import React, { useEffect, useMemo } from 'react'; // External Packages import Skeleton from '@yisheng90/react-loading'; @@ -11,6 +11,7 @@ import { toast as toaster } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.min.css'; import styled, { css, useTheme } from 'styled-components'; import axios from 'axios'; +import { cloneDeep } from 'lodash'; // Internal Compoonents import * as PushAPI from '@pushprotocol/restapi'; @@ -19,7 +20,7 @@ import MetaInfoDisplayer from 'components/MetaInfoDisplayer'; import LoaderSpinner, { LOADER_TYPE } from 'components/reusables/loaders/LoaderSpinner'; import { convertAddressToAddrCaip } from 'helpers/CaipHelper'; import useToast from 'hooks/useToast'; -import { cacheChannelInfo, updateSubscriptionStatus } from 'redux/slices/channelSlice'; +import { cacheChannelInfo } from 'redux/slices/channelSlice'; import { addNewWelcomeNotif, incrementStepIndex } from 'redux/slices/userJourneySlice'; import ChannelTutorial, { isChannelTutorialized } from 'segments/ChannelTutorial'; import NotificationToast from '../primaries/NotificationToast'; @@ -51,7 +52,7 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { (state) => state.contracts ); const { canVerify } = useSelector((state) => state.admin); - const { channelsCache, CHANNEL_BLACKLIST, subscriptionStatus } = useSelector((state) => state.channels); + const { channelsCache, CHANNEL_BLACKLIST, subscriptionStatus, userSettings: currentUserSettings } = useSelector((state) => state.channels); const { account, provider, chainId } = useAccount(); const onCoreNetwork = chainId === appConfig.coreContractChain; @@ -186,6 +187,10 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { const generalToast = useToast(); + const userSettings = useMemo(() => { + return cloneDeep(currentUserSettings); + }, [currentUserSettings]); + const formatAddress = (addressText) => { return addressText.length > 40 ? `${shortenText(addressText, 4, 6)}` : addressText; }; @@ -853,7 +858,6 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { channelDetail={channelObject} setLoading={setTxInProgress} onSuccessOptin={() => { - dispatch(updateSubscriptionStatus({ channelAddress: channelObject.channel, status: true })); setSubscribed(true); setSubscriberCount((prevSubscriberCount) => prevSubscriberCount + 1) }} @@ -886,9 +890,9 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { centerOnMobile={true} channelDetail={channelObject} setSubscribed={setSubscribed} + userSetting={userSettings[channelObject.channel]} setSubscriberCount={setSubscriberCount} onSuccessOptout={() => { - dispatch(updateSubscriptionStatus({ channelAddress: channelObject.channel, status: false })); setSubscribed(false); setSubscriberCount((prevSubscriberCount) => prevSubscriberCount - 1) }} diff --git a/src/components/channel/AddSettingModalContent.tsx b/src/components/channel/AddSettingModalContent.tsx index 59c52059b0..3bf8225db1 100644 --- a/src/components/channel/AddSettingModalContent.tsx +++ b/src/components/channel/AddSettingModalContent.tsx @@ -66,7 +66,10 @@ const AddSettingModalContent = ({ const settingToEdit = InnerComponentProps?.settingToEdit || undefined; const [isLoading, setIsLoading] = useState(false); const [settingName, setSettingName] = useState(settingToEdit ? settingToEdit.description : ''); - const [isDefault, setIsDefault] = useState(settingToEdit && settingToEdit.isDefaultEnabled === true); + const [isDefault, setIsDefault] = useState( + settingToEdit && + ((settingToEdit.type === 1 && settingToEdit.default) || (settingToEdit.type === 2 && settingToEdit.enabled)) + ); const [isRange, setIsRange] = useState(settingToEdit && settingToEdit.type === 2 ? true : false); const [lowerLimit, setLowerLimit] = useState( settingToEdit && settingToEdit.type === 2 ? settingToEdit.lowerLimit.toString() : '' @@ -75,7 +78,7 @@ const AddSettingModalContent = ({ settingToEdit && settingToEdit.type === 2 ? settingToEdit.upperLimit.toString() : '' ); const [defaultValue, setDefaultValue] = useState( - settingToEdit && settingToEdit.type === 2 ? settingToEdit.defaultValue.toString() : '' + settingToEdit && settingToEdit.type === 2 ? settingToEdit.default.toString() : '' ); const [errorInfo, setErrorInfo] = useState(); @@ -103,8 +106,8 @@ const AddSettingModalContent = ({ const settingData: ChannelSetting = isRange ? { type: 2, - defaultValue: Number(defaultValue), - isDefaultEnabled: isDefault, + default: Number(defaultValue), + enabled: isDefault, description: settingName, index: index, lowerLimit: Number(lowerLimit), @@ -112,7 +115,7 @@ const AddSettingModalContent = ({ } : { type: 1, - isDefaultEnabled: isDefault, + default: isDefault, description: settingName, index: index, }; diff --git a/src/components/channel/NotificationSettings.tsx b/src/components/channel/NotificationSettings.tsx index 269e23b706..4e0cc885b7 100644 --- a/src/components/channel/NotificationSettings.tsx +++ b/src/components/channel/NotificationSettings.tsx @@ -4,7 +4,7 @@ import { ethers } from 'ethers'; // External Packages import 'react-dropdown/style.css'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import 'react-toastify/dist/ReactToastify.min.css'; import { useNavigate } from 'react-router-dom'; import { PiPencilSimpleBold } from 'react-icons/pi'; @@ -25,6 +25,7 @@ import { appConfig } from 'config'; import useModalBlur, { MODAL_POSITION } from 'hooks/useModalBlur'; import { ChannelSetting } from 'helpers/channel/types'; import { getChannel } from 'services'; +import { updateChannelSetting } from 'redux/slices/channelSlice'; // Constants const CORE_CHAIN_ID = appConfig.coreContractChain; @@ -33,6 +34,9 @@ function NotificationSettings() { const { account, chainId } = useAccount(); const { coreChannelAdmin, delegatees } = useSelector((state: any) => state.admin); const { epnsWriteProvider } = useSelector((state: any) => state.contracts); + const { channelSettings } = useSelector((state: any) => state.channels); + + const dispatch = useDispatch(); const onCoreNetwork = CORE_CHAIN_ID === chainId; const EDIT_SETTING_FEE = 50; @@ -41,7 +45,6 @@ function NotificationSettings() { const [settings, setSettings] = React.useState([]); const [settingToEdit, setSettingToEdit] = React.useState(undefined); const [isLoading, setIsLoading] = React.useState(false); - const [currentSettings, setCurrentSettings] = React.useState([]); const [isLoadingSettings, setIsLoadingSettings] = React.useState(true); const { @@ -75,7 +78,7 @@ function NotificationSettings() { if (isAddSettingModalOpen === false) setSettingToEdit(undefined); }, [isAddSettingModalOpen]); - React.useEffect(() => { + useEffect(() => { if (!account) return; if (!delegatees || !delegatees.length) { setChannelAddress(account); @@ -87,40 +90,11 @@ function NotificationSettings() { }, [delegatees, account]); useEffect(() => { - if (delegatees) { - const delegatee = delegatees.find(({ channel }) => channel === channelAddress); - if (delegatee) { - const { channel_settings } = delegatee; - if (channel_settings !== null) { - const parsedData = JSON.parse(channel_settings); - const settings: ChannelSetting[] = parsedData.map((setting: any) => { - if(setting.type === 1) { - return { - type: 1, - isDefaultEnabled: setting.default, - description: setting.description, - index: setting.index, - } - } else { - return { - type: 2, - isDefaultEnabled: setting.enabled === 1 ? true : false, - defaultValue: setting.default, - description: setting.description, - index: setting.index, - lowerLimit: setting.lowerLimit, - upperLimit: setting.upperLimit, - } - } - }); - setSettings(settings); - setCurrentSettings(settings); - setIsLoadingSettings(false); - } - } + if (channelAddress && channelSettings[channelAddress]) { + setSettings(channelSettings[channelAddress] || []); + setIsLoadingSettings(false); } - return null; - }, [delegatees, channelAddress]); + }, [channelAddress, channelSettings]); // Notification Toast const notificationToast = useToast(5000); @@ -164,11 +138,12 @@ function NotificationSettings() { settings.forEach((setting) => { if (_notifSettings !== '') _notifSettings += '+'; if (_notifDescription !== '') _notifDescription += '+'; - const isEnabled = setting.isDefaultEnabled ? '1' : '0'; if (setting.type === 1) { - _notifSettings += `${setting.type}-${isEnabled}`; + _notifSettings += `${setting.type}-${setting.default ? '1' : '0'}`; } else if (setting.type === 2) { - _notifSettings += `${setting.type}-${isEnabled}-${setting.defaultValue}-${setting.lowerLimit}-${setting.upperLimit}`; + _notifSettings += `${setting.type}-${setting.enabled ? '1' : '0'}-${setting.default}-${setting.lowerLimit}-${ + setting.upperLimit + }`; } _notifDescription += setting.description; }); @@ -183,7 +158,7 @@ function NotificationSettings() { console.log(tx); await tx.wait(); - setCurrentSettings(settings); + dispatch(updateChannelSetting({ channelAddress, settings })); setIsLoading(false); notificationToast.showMessageToast({ @@ -199,7 +174,6 @@ function NotificationSettings() { }); } catch (err) { setIsLoading(false); - console.log(err.message); if (err.code == 'ACTION_REJECTED') { // EIP-1193 userRejectedRequest error notificationToast.showMessageToast({ @@ -226,37 +200,36 @@ function NotificationSettings() { ), }); console.log('Error --> %o', err); - console.log({ err }); } } }; const settingsChanged = useMemo(() => { - if (!settings || !currentSettings) return false; - if (settings.length !== currentSettings.length) return true; + if (!settings || !channelSettings[account]) return false; + if (settings.length !== channelSettings[account].length) return true; let isUnchanged = true; for (let i = 0; i < settings.length; i++) { const setting1 = settings[i]; - const setting2 = currentSettings[i]; + const setting2 = channelSettings[account][i]; if (setting1.type === 1) { isUnchanged = isUnchanged && setting1.type === setting2.type && setting1.description === setting2.description && - setting1.isDefaultEnabled === setting2.isDefaultEnabled; + setting1.default === setting2.default; } else if (setting1.type === 2) { isUnchanged = isUnchanged && setting1.type === setting2.type && setting1.description === setting2.description && - setting1.defaultValue === setting2.defaultValue && - setting1.isDefaultEnabled === setting2.isDefaultEnabled && + setting1.default === setting2.default && + setting1.enabled === setting2.enabled && setting1.lowerLimit === setting2.lowerLimit && setting1.upperLimit === setting2.upperLimit; } } return isUnchanged === false; - }, [settings, currentSettings]); + }, [settings, channelSettings[account]]); return ( <> diff --git a/src/components/channel/UserSettings.tsx b/src/components/channel/UserSettings.tsx index b0866432ff..fbe2e0005b 100644 --- a/src/components/channel/UserSettings.tsx +++ b/src/components/channel/UserSettings.tsx @@ -1,11 +1,12 @@ // React + Web3 Essentials -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; // External Packages import styled from 'styled-components'; import { useDispatch, useSelector } from 'react-redux'; import { useNavigate } from 'react-router-dom'; import { AiOutlineMore } from 'react-icons/ai'; +import { cloneDeep } from 'lodash'; // Internal Components import { useAccount } from 'hooks'; @@ -14,7 +15,7 @@ import { ImageV2 } from 'components/reusables/SharedStylingV2'; import { getChannel, getUserSubscriptions } from 'services'; import LoaderSpinner from 'primaries/LoaderSpinner'; import EmptyNotificationSettings from './EmptyNotificationSettings'; -import { updateBulkSubscriptions } from 'redux/slices/channelSlice'; +import { updateBulkSubscriptions, updateBulkUserSettings } from 'redux/slices/channelSlice'; import { convertAddressToAddrCaip } from 'helpers/CaipHelper'; import ManageNotifSettingDropdown from 'components/dropdowns/ManageNotifSettingDropdown'; @@ -31,13 +32,13 @@ interface ChannelListItem { function UserSettings() { const { account, chainId } = useAccount(); + const { subscriptionStatus, userSettings: currentUserSettings } = useSelector((state: any) => state.channels); const [selectedOption, setSelectedOption] = useState(0); const [channelList, setChannelList] = useState([]); const [isLoading, setIsLoading] = useState(true); const navigate = useNavigate(); - const { subscriptionStatus } = useSelector((state: any) => state.channels); const dispatch = useDispatch(); const fetchChannelDetails = async (channel: string) => { @@ -48,7 +49,7 @@ function UserSettings() { id: details.id, icon: details.icon, name: details.name, - channel_settings: details.channel_settings + channel_settings: details.channel_settings, }; return updatedChannelItem; } else return undefined; @@ -72,8 +73,13 @@ function UserSettings() { const userCaipAddress = convertAddressToAddrCaip(account, chainId); const subscriptionsArr = await getUserSubscriptions({ userCaipAddress }); const subscriptionsMapping = {}; - subscriptionsArr.map(({ channel }) => (subscriptionsMapping[channel] = true)); + const userSettings = {}; + subscriptionsArr.map(({ channel, user_settings }) => { + subscriptionsMapping[channel] = true; + userSettings[channel] = user_settings ? JSON.parse(user_settings) : {}; + }); dispatch(updateBulkSubscriptions(subscriptionsMapping)); + dispatch(updateBulkUserSettings(userSettings)); await fillData(subscriptionsMapping); } else { await fillData(subscriptionStatus); @@ -93,6 +99,10 @@ function UserSettings() { }, ]; + const userSettings = useMemo(() => { + return cloneDeep(currentUserSettings); + }, [currentUserSettings]); + return ( Settings @@ -129,7 +139,8 @@ function UserSettings() { {channel.name} - { diff --git a/src/components/dropdowns/ManageNotifSettingDropdown.tsx b/src/components/dropdowns/ManageNotifSettingDropdown.tsx index b9dc820fd7..c4456ef52c 100644 --- a/src/components/dropdowns/ManageNotifSettingDropdown.tsx +++ b/src/components/dropdowns/ManageNotifSettingDropdown.tsx @@ -3,6 +3,7 @@ import React, { useContext, useMemo, useState } from "react"; // External Packages import styled, { css, useTheme } from "styled-components"; +import { useDispatch } from "react-redux"; // Internal Components import { DropdownBtnHandler } from "./DropdownBtnHandler"; @@ -18,6 +19,7 @@ import { MdCheckCircle, MdError } from "react-icons/md"; import LoaderSpinner, { LOADER_TYPE } from "components/reusables/loaders/LoaderSpinner"; import { convertAddressToAddrCaip } from "helpers/CaipHelper"; import { ChannelSetting, UserSetting } from "helpers/channel/types"; +import { removeUserSetting, updateSubscriptionStatus } from "redux/slices/channelSlice"; interface ManageNotifSettingDropdownProps { children: React.ReactNode; @@ -106,6 +108,9 @@ const ManageNotifSettingDropdown: React.FC = (o const [isOpen, setIsOpen] = useState(false); const { chainId } = useAccount(); const { userPushSDKInstance } = useContext(AppContext); + const dispatch = useDispatch(); + + console.log('user setting', userSetting); const channelSetting = useMemo(() => { if(channelDetail && channelDetail?.channel_settings) { @@ -140,6 +145,8 @@ const ManageNotifSettingDropdown: React.FC = (o await userPushSDKInstance.notification.unsubscribe(convertAddressToAddrCaip(channelAddress, chainId), { onSuccess: () => { onSuccessOptout(); + dispatch(updateSubscriptionStatus({ channelAddress: channelAddress, status: false })); + dispatch(removeUserSetting(channelAddress)); unsubscribeToast.showMessageToast({ toastTitle: 'Success', diff --git a/src/components/dropdowns/OptinNotifSettingDropdown.tsx b/src/components/dropdowns/OptinNotifSettingDropdown.tsx index a687981a91..d544a93d92 100644 --- a/src/components/dropdowns/OptinNotifSettingDropdown.tsx +++ b/src/components/dropdowns/OptinNotifSettingDropdown.tsx @@ -5,6 +5,7 @@ import React, { useContext, useMemo, useState } from "react"; import Switch from 'react-switch'; import Slider from 'react-input-slider'; import styled, { css, useTheme } from "styled-components"; +import { useDispatch } from "react-redux"; // Internal Components import { DropdownBtnHandler } from "./DropdownBtnHandler"; @@ -17,9 +18,10 @@ import { convertAddressToAddrCaip } from "helpers/CaipHelper"; import useToast from "hooks/useToast"; import { MdCheckCircle, MdError } from "react-icons/md"; import { ChannelSetting } from "helpers/channel/types"; -import { notifChannelSettingFormatString } from "helpers/channel/notifSetting"; +import { notifChannelSettingFormatString, userSettingsFromDefaultChannelSetting } from "helpers/channel/notifSetting"; import { AppContext } from "contexts/AppContext"; import LoaderSpinner, { LOADER_TYPE } from "components/reusables/loaders/LoaderSpinner"; +import { updateSubscriptionStatus, updateUserSetting } from "redux/slices/channelSlice"; interface OptinNotifSettingDropdownProps { children: React.ReactNode; @@ -136,6 +138,7 @@ const OptinNotifSettingDropdown: React.FC = (opt const { chainId } = useAccount(); const { userPushSDKInstance } = useContext(AppContext); const [isOpen, setIsOpen] = useState(false); + const dispatch = useDispatch(); const onCoreNetwork = chainId === appConfig.coreContractChain; @@ -172,6 +175,8 @@ const OptinNotifSettingDropdown: React.FC = (opt // settings: [], onSuccess: () => { onSuccessOptin(); + dispatch(updateSubscriptionStatus({ channelAddress, status: true })); + dispatch(updateUserSetting({ channelAddress, settings: userSettingsFromDefaultChannelSetting({ channelSetting: channelSettings })})); subscribeToast.showMessageToast({ toastTitle: 'Success', diff --git a/src/components/dropdowns/UpdateNotifSettingDropdown.tsx b/src/components/dropdowns/UpdateNotifSettingDropdown.tsx index 299a2bbd15..b3fc4de51d 100644 --- a/src/components/dropdowns/UpdateNotifSettingDropdown.tsx +++ b/src/components/dropdowns/UpdateNotifSettingDropdown.tsx @@ -5,6 +5,7 @@ import React, { useContext, useState } from "react"; import Switch from 'react-switch'; import Slider from 'react-input-slider'; import styled, { css, useTheme } from "styled-components"; +import { useDispatch } from "react-redux"; // Internal Components import { DropdownBtnHandler } from "./DropdownBtnHandler"; @@ -20,6 +21,7 @@ import { convertAddressToAddrCaip } from "helpers/CaipHelper"; import { notifUserSettingFormatString, userSettingsFromDefaultChannelSetting } from "helpers/channel/notifSetting"; import { MdCheckCircle, MdError } from "react-icons/md"; import LoaderSpinner, { LOADER_TYPE } from "components/reusables/loaders/LoaderSpinner"; +import { updateUserSetting } from "redux/slices/channelSlice"; interface UpdateNotifSettingDropdownProps { children: React.ReactNode; @@ -144,6 +146,7 @@ const UpdateNotifSettingDropdown: React.FC = ({ const { chainId } = useAccount(); const { userPushSDKInstance } = useContext(AppContext); + const dispatch = useDispatch(); const onCoreNetwork = chainId === appConfig.coreContractChain; @@ -175,6 +178,7 @@ const UpdateNotifSettingDropdown: React.FC = ({ onSuccess: () => { saveOnSuccessSettingFunc(); closeDropdown(); + dispatch(updateUserSetting({ channelAddress, settings: userSetting })); subscribeToast.showMessageToast({ toastTitle: 'Success', diff --git a/src/helpers/channel/InputValidation.ts b/src/helpers/channel/InputValidation.ts index eaa0d7843e..5da9309884 100644 --- a/src/helpers/channel/InputValidation.ts +++ b/src/helpers/channel/InputValidation.ts @@ -91,6 +91,5 @@ export const isAllFilledAndValid = ({ } } } - console.log('Has error', hasError); return !hasError; }; diff --git a/src/redux/slices/channelSlice.js b/src/redux/slices/channelSlice.js index 232a4f0496..7356f5797c 100644 --- a/src/redux/slices/channelSlice.js +++ b/src/redux/slices/channelSlice.js @@ -14,6 +14,8 @@ const initialState = { channels: [], // the channels meta-data subscriptionStatus: {}, // a mapping of channel address to user's subscription status channelsCache: {}, // a mapping of channel address to channel details + channelSettings: {}, // a mapping of channel address to channel settings + userSettings: {}, // a mapping of channel address to user settings }; export const channelSlice = createSlice({ @@ -48,7 +50,24 @@ export const channelSlice = createSlice({ updateSubscriptionStatus: (state, action) => { const { channelAddress, status } = action.payload; state.subscriptionStatus[channelAddress] = status; - } + }, + updateBulkUserSettings: (state, action) => { + state.userSettings = action.payload; + }, + updateUserSetting: (state, action) => { + const { channelAddress, settings } = action.payload; + state.userSettings[channelAddress] = settings; + }, + removeUserSetting: (state, action) => { + delete state.userSettings[action.payload]; + }, + updateBulkChannelSettings: (state, action) => { + state.channelSettings = action.payload; + }, + updateChannelSetting: (state, action) => { + const { channelAddress, settings } = action.payload; + state.channelSettings[channelAddress] = settings; + }, }, }); @@ -60,7 +79,12 @@ export const { cacheSubscribe, cacheUnsubscribe, updateBulkSubscriptions, - updateSubscriptionStatus + updateSubscriptionStatus, + updateBulkUserSettings, + updateUserSetting, + removeUserSetting, + updateBulkChannelSettings, + updateChannelSetting, } = channelSlice.actions; export default channelSlice.reducer; diff --git a/src/segments/ViewChannels.tsx b/src/segments/ViewChannels.tsx index 73ea1e2e08..3a06ffcf80 100644 --- a/src/segments/ViewChannels.tsx +++ b/src/segments/ViewChannels.tsx @@ -12,7 +12,7 @@ import Faucets from 'components/Faucets'; import LoaderSpinner, { LOADER_TYPE } from 'components/reusables/loaders/LoaderSpinner'; import ViewChannelItem from 'components/ViewChannelItem'; import UtilityHelper, { MaskedAliasChannels, MaskedChannels } from 'helpers/UtilityHelper'; -import { incrementPage, setChannelMeta, updateBulkSubscriptions } from 'redux/slices/channelSlice'; +import { incrementPage, setChannelMeta, updateBulkSubscriptions, updateBulkUserSettings } from 'redux/slices/channelSlice'; import { incrementStepIndex } from 'redux/slices/userJourneySlice'; import DisplayNotice from '../primaries/DisplayNotice'; import { Item, ItemH } from '../primaries/SharedStyling'; @@ -196,8 +196,13 @@ function ViewChannels({ loadTeaser, playTeaser }) { const userCaipAddress = convertAddressToAddrCaip(account, chainId); const subscriptionsArr = await getUserSubscriptions({ userCaipAddress }); const subscriptionsMapping = {}; - subscriptionsArr.map(({ channel }) => (subscriptionsMapping[channel] = true)); + const userSettings = {}; + subscriptionsArr.map(({ channel, user_settings }) => { + subscriptionsMapping[channel] = true; + userSettings[channel] = user_settings ? JSON.parse(user_settings) : {}; + }); dispatch(updateBulkSubscriptions(subscriptionsMapping)); + dispatch(updateBulkUserSettings(userSettings)); })(); }, [account]); From d5d791e6ca10c840a24c88786deef3a122ccc895 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Tue, 10 Oct 2023 16:48:49 +0530 Subject: [PATCH 13/18] few styling changes --- src/components/ViewChannelItem.js | 3 ++ .../dropdowns/ManageNotifSettingDropdown.tsx | 44 +++++++++---------- .../dropdowns/OptinNotifSettingDropdown.tsx | 2 +- .../dropdowns/UpdateNotifSettingDropdown.tsx | 4 +- 4 files changed, 28 insertions(+), 25 deletions(-) diff --git a/src/components/ViewChannelItem.js b/src/components/ViewChannelItem.js index 72f4561ba1..f33ba75faa 100644 --- a/src/components/ViewChannelItem.js +++ b/src/components/ViewChannelItem.js @@ -1362,6 +1362,9 @@ const UnsubscribeButton = styled(ChannelActionButton)` const OwnerButton = styled(ChannelActionButton)` background: #35c5f3; + border-radius: 8px; + min-height: 36px; + min-width: 108px; `; const Toaster = styled.div` diff --git a/src/components/dropdowns/ManageNotifSettingDropdown.tsx b/src/components/dropdowns/ManageNotifSettingDropdown.tsx index c4456ef52c..9bb7c6ea1f 100644 --- a/src/components/dropdowns/ManageNotifSettingDropdown.tsx +++ b/src/components/dropdowns/ManageNotifSettingDropdown.tsx @@ -52,28 +52,28 @@ const ManageNotifSettingDropdownContainer: React.FC - {channelSetting && - - - - - - Manage Settings - - - - + {(channelSetting && channelSetting.length != 0) && + + + + + + Manage Settings + + + + } optOutHandler({ setLoading: setTxInProgress })}> = (opt // render return ( - channelSetting ? + (channelSetting && channelSetting.length) ? - {setting.type === 2 && setting.enabled && ( + {setting.type === 2 && setting.enabled === true && ( Date: Tue, 10 Oct 2023 17:47:19 +0530 Subject: [PATCH 14/18] fix: fixes --- src/components/channel/UserSettings.tsx | 2 +- src/components/dropdowns/ManageNotifSettingDropdown.tsx | 2 -- src/components/dropdowns/OptinNotifSettingDropdown.tsx | 2 +- src/segments/ViewChannels.tsx | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/components/channel/UserSettings.tsx b/src/components/channel/UserSettings.tsx index fbe2e0005b..666486a91d 100644 --- a/src/components/channel/UserSettings.tsx +++ b/src/components/channel/UserSettings.tsx @@ -76,7 +76,7 @@ function UserSettings() { const userSettings = {}; subscriptionsArr.map(({ channel, user_settings }) => { subscriptionsMapping[channel] = true; - userSettings[channel] = user_settings ? JSON.parse(user_settings) : {}; + userSettings[channel] = user_settings ? JSON.parse(user_settings) : null; }); dispatch(updateBulkSubscriptions(subscriptionsMapping)); dispatch(updateBulkUserSettings(userSettings)); diff --git a/src/components/dropdowns/ManageNotifSettingDropdown.tsx b/src/components/dropdowns/ManageNotifSettingDropdown.tsx index 9bb7c6ea1f..00348ececb 100644 --- a/src/components/dropdowns/ManageNotifSettingDropdown.tsx +++ b/src/components/dropdowns/ManageNotifSettingDropdown.tsx @@ -110,8 +110,6 @@ const ManageNotifSettingDropdown: React.FC = (o const { userPushSDKInstance } = useContext(AppContext); const dispatch = useDispatch(); - console.log('user setting', userSetting); - const channelSetting = useMemo(() => { if(channelDetail && channelDetail?.channel_settings) { return JSON.parse(channelDetail?.channel_settings); diff --git a/src/components/dropdowns/OptinNotifSettingDropdown.tsx b/src/components/dropdowns/OptinNotifSettingDropdown.tsx index bdea07f8ba..e145c1d136 100644 --- a/src/components/dropdowns/OptinNotifSettingDropdown.tsx +++ b/src/components/dropdowns/OptinNotifSettingDropdown.tsx @@ -83,7 +83,7 @@ const OptinNotifSettingDropdownContainer: React.FC - {setting.type === 2 && setting.enabled && ( + {setting.type === 2 && setting.enabled === true && ( { subscriptionsMapping[channel] = true; - userSettings[channel] = user_settings ? JSON.parse(user_settings) : {}; + userSettings[channel] = user_settings ? JSON.parse(user_settings) : null; }); dispatch(updateBulkSubscriptions(subscriptionsMapping)); dispatch(updateBulkUserSettings(userSettings)); From c049e305b422495cf026a31126c686ffe1a806d0 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Tue, 10 Oct 2023 18:01:55 +0530 Subject: [PATCH 15/18] Enabled ArbitrumOne on prod --- src/config/config-prod.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/config-prod.js b/src/config/config-prod.js index 13d27f84fe..7403016e7f 100644 --- a/src/config/config-prod.js +++ b/src/config/config-prod.js @@ -26,7 +26,7 @@ export const config = { 137, //for polygon mainnet 56, // for bnb mainnet // 10, // for optimism mainnet - // 42161, // arbitrum mainnet + 42161, // arbitrum mainnet 1101 // polygon zkevm mainnet ], From c834d2ed68a78507a669e44c97dbf0a14de43345 Mon Sep 17 00:00:00 2001 From: kalashshah <202051096@iiitvadodara.ac.in> Date: Tue, 10 Oct 2023 18:45:16 +0530 Subject: [PATCH 16/18] chore: set default val to true in add setting --- src/components/channel/AddSettingModalContent.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/channel/AddSettingModalContent.tsx b/src/components/channel/AddSettingModalContent.tsx index 3bf8225db1..987e69db2a 100644 --- a/src/components/channel/AddSettingModalContent.tsx +++ b/src/components/channel/AddSettingModalContent.tsx @@ -67,8 +67,9 @@ const AddSettingModalContent = ({ const [isLoading, setIsLoading] = useState(false); const [settingName, setSettingName] = useState(settingToEdit ? settingToEdit.description : ''); const [isDefault, setIsDefault] = useState( - settingToEdit && - ((settingToEdit.type === 1 && settingToEdit.default) || (settingToEdit.type === 2 && settingToEdit.enabled)) + settingToEdit + ? (settingToEdit.type === 1 && settingToEdit.default) || (settingToEdit.type === 2 && settingToEdit.enabled) + : true ); const [isRange, setIsRange] = useState(settingToEdit && settingToEdit.type === 2 ? true : false); const [lowerLimit, setLowerLimit] = useState( From c4ceb7c28944aff05dd2164941141f83f66e65a9 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Fri, 13 Oct 2023 19:31:33 +0530 Subject: [PATCH 17/18] Updated sdk version --- package.json | 4 +- yarn.lock | 138 ++++++++++++++++++++++++--------------------------- 2 files changed, 66 insertions(+), 76 deletions(-) diff --git a/package.json b/package.json index cce645b5da..a8e7090b20 100644 --- a/package.json +++ b/package.json @@ -34,9 +34,9 @@ "@mui/lab": "^5.0.0-alpha.72", "@mui/material": "^5.5.0", "@pushprotocol/ledgerlive": "latest", - "@pushprotocol/restapi": "0.0.1-alpha.48", + "@pushprotocol/restapi": "0.0.1-alpha.49", "@pushprotocol/socket": "latest", - "@pushprotocol/uiweb": "0.0.1-alpha.17", + "@pushprotocol/uiweb": "0.0.1-alpha.20", "@reduxjs/toolkit": "^1.7.1", "@testing-library/dom": "^9.0.1", "@testing-library/jest-dom": "^4.2.4", diff --git a/yarn.lock b/yarn.lock index 0423a7f5cf..41039c32f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1695,9 +1695,9 @@ __metadata: linkType: hard "@bufbuild/protobuf@npm:^1.3.0": - version: 1.3.1 - resolution: "@bufbuild/protobuf@npm:1.3.1" - checksum: 5ee96282a6259a222ccd0daf56bcab587918253750dda34c1bf2caad0d89bc3796970f7b55b1c7e25bae5d033c8e9dd687638675fe15cac7d9d6558ebfd4dfa8 + version: 1.3.3 + resolution: "@bufbuild/protobuf@npm:1.3.3" + checksum: 20181f769b908c92598130a9afd6fc371417498377429e2088e8f4469e5aa4be9173a056a8e611ea0e8639cd24b2f6aeef24cf656a465bbbbdc6e6f66753a85f languageName: node linkType: hard @@ -5423,9 +5423,9 @@ __metadata: "@mui/lab": ^5.0.0-alpha.72 "@mui/material": ^5.5.0 "@pushprotocol/ledgerlive": latest - "@pushprotocol/restapi": 0.0.1-alpha.48 + "@pushprotocol/restapi": 0.0.1-alpha.49 "@pushprotocol/socket": latest - "@pushprotocol/uiweb": 0.0.1-alpha.17 + "@pushprotocol/uiweb": 0.0.1-alpha.20 "@reduxjs/toolkit": ^1.7.1 "@testing-library/dom": ^6.12.2 "@testing-library/jest-dom": ^4.2.4 @@ -5625,9 +5625,9 @@ __metadata: languageName: node linkType: hard -"@pushprotocol/restapi@npm:0.0.1-alpha.48": - version: 0.0.1-alpha.48 - resolution: "@pushprotocol/restapi@npm:0.0.1-alpha.48" +"@pushprotocol/restapi@npm:0.0.1-alpha.49": + version: 0.0.1-alpha.49 + resolution: "@pushprotocol/restapi@npm:0.0.1-alpha.49" dependencies: "@ambire/signature-validator": ^1.3.1 "@metamask/eth-sig-util": ^5.0.2 @@ -5648,7 +5648,7 @@ __metadata: viem: ^1.3.0 peerDependencies: ethers: ^5.6.8 - checksum: 49aa8e537574b4fbcefa154dbdde36c66bda285a306794dc259f6cbf7e056fdfe9e2d4a1bd3f4bfa0c0f25370b9687e083fabd0113c309e021a0b702ab42dd40 + checksum: 835229af4889c88573593ee19c97e9a73d3e67e70003ddcdd6ad16f65fe0bde1fd2f08adc6cb3fdb583cf8ef8746edd100fb20aada1578bb1a16f85ab6ab5ca5 languageName: node linkType: hard @@ -5688,9 +5688,9 @@ __metadata: languageName: node linkType: hard -"@pushprotocol/uiweb@npm:0.0.1-alpha.17": - version: 0.0.1-alpha.17 - resolution: "@pushprotocol/uiweb@npm:0.0.1-alpha.17" +"@pushprotocol/uiweb@npm:0.0.1-alpha.20": + version: 0.0.1-alpha.20 + resolution: "@pushprotocol/uiweb@npm:0.0.1-alpha.20" dependencies: "@livekit/components-react": ^1.2.2 "@livekit/components-styles": ^1.0.6 @@ -5721,7 +5721,7 @@ __metadata: axios: ^0.27.2 react: ">=16.8.0" styled-components: ^5.3.5 - checksum: 699b576cc1612b41e83790f3e9845fc7702ecf362fb542f28156dadec039f52e41962ab774b61a910c5c05a9afda4ce6d0b79e7d02d33240054f8df230bdad92 + checksum: 1845fc427cca2ac9cafe92de9c16af4aa6d94312f391032afee8ffc9d6efdfb5c9fa3cfe085eec1d6a20d4d423b461db5537c331ef03f0844d8abd6e90bd5e7a languageName: node linkType: hard @@ -7999,15 +7999,6 @@ __metadata: languageName: node linkType: hard -"@types/ws@npm:^8.5.5": - version: 8.5.6 - resolution: "@types/ws@npm:8.5.6" - dependencies: - "@types/node": "*" - checksum: 7addb0c5fa4e7713d5209afb8a90f1852b12c02cb537395adf7a05fbaf21205dc5f7c110fd5ad6f3dbf147112cbff33fb11d8633059cb344f0c14f595b1ea1fb - languageName: node - linkType: hard - "@types/yargs-parser@npm:*": version: 21.0.0 resolution: "@types/yargs-parser@npm:21.0.0" @@ -8696,9 +8687,9 @@ __metadata: languageName: node linkType: hard -"@walletconnect/core@npm:2.10.1": - version: 2.10.1 - resolution: "@walletconnect/core@npm:2.10.1" +"@walletconnect/core@npm:2.10.2": + version: 2.10.2 + resolution: "@walletconnect/core@npm:2.10.2" dependencies: "@walletconnect/heartbeat": 1.2.1 "@walletconnect/jsonrpc-provider": 1.0.13 @@ -8711,12 +8702,12 @@ __metadata: "@walletconnect/relay-auth": ^1.0.4 "@walletconnect/safe-json": ^1.0.2 "@walletconnect/time": ^1.0.2 - "@walletconnect/types": 2.10.1 - "@walletconnect/utils": 2.10.1 + "@walletconnect/types": 2.10.2 + "@walletconnect/utils": 2.10.2 events: ^3.3.0 lodash.isequal: 4.5.0 uint8arrays: ^3.1.0 - checksum: d58ae15c53efe1792da8c7aa1b7ba47efb49807cfe0c73f225d59c5cd847a0e50979ce6965b94915812412deba3e5aa2dca13a02bd41c087e85575e99afad223 + checksum: 78b6e56bdd44ee0d27157eacbc916d053d75013b1f9e00869efc63141c1d0b33a771eaf8ab6f2fcb1bef3c0f87173f31067674567962a6dd9d45a4376a306c91 languageName: node linkType: hard @@ -8804,24 +8795,24 @@ __metadata: linkType: hard "@walletconnect/ethereum-provider@npm:^2.10.1": - version: 2.10.1 - resolution: "@walletconnect/ethereum-provider@npm:2.10.1" + version: 2.10.2 + resolution: "@walletconnect/ethereum-provider@npm:2.10.2" dependencies: "@walletconnect/jsonrpc-http-connection": ^1.0.7 "@walletconnect/jsonrpc-provider": ^1.0.13 "@walletconnect/jsonrpc-types": ^1.0.3 "@walletconnect/jsonrpc-utils": ^1.0.8 - "@walletconnect/sign-client": 2.10.1 - "@walletconnect/types": 2.10.1 - "@walletconnect/universal-provider": 2.10.1 - "@walletconnect/utils": 2.10.1 + "@walletconnect/sign-client": 2.10.2 + "@walletconnect/types": 2.10.2 + "@walletconnect/universal-provider": 2.10.2 + "@walletconnect/utils": 2.10.2 events: ^3.3.0 peerDependencies: "@walletconnect/modal": ">=2" peerDependenciesMeta: "@walletconnect/modal": optional: true - checksum: ec3d88ba101a5d8f193262b5b1e770cccad6457ec56fa1f3d17fa531de4e07e8cf03a1341669122c61956f0d5c3a6eca57d3f12f524e046acddb401cdb76fe7c + checksum: 86aecdb2c6eaf5d5cccd6d577ae815ae0d5f743703562db8eebcc573485eaca7a85ae08a30a7bac1f17b35d5de8d1df6a2c8ccac6ca4c63ef7ef6e97f74ea064 languageName: node linkType: hard @@ -9138,20 +9129,20 @@ __metadata: languageName: node linkType: hard -"@walletconnect/sign-client@npm:2.10.1": - version: 2.10.1 - resolution: "@walletconnect/sign-client@npm:2.10.1" +"@walletconnect/sign-client@npm:2.10.2": + version: 2.10.2 + resolution: "@walletconnect/sign-client@npm:2.10.2" dependencies: - "@walletconnect/core": 2.10.1 + "@walletconnect/core": 2.10.2 "@walletconnect/events": ^1.0.1 "@walletconnect/heartbeat": 1.2.1 "@walletconnect/jsonrpc-utils": 1.0.8 "@walletconnect/logger": ^2.0.1 "@walletconnect/time": ^1.0.2 - "@walletconnect/types": 2.10.1 - "@walletconnect/utils": 2.10.1 + "@walletconnect/types": 2.10.2 + "@walletconnect/utils": 2.10.2 events: ^3.3.0 - checksum: dbdced8dece73b20ae73df9c0cf0d9e3eee753f6c81e264c87583ca60d1d13d4f7d61944e4b22d1f70c5f32424fd842a7de778838aa7d0ae27195976a86e102f + checksum: d74556906d46dd6c161548d26068ceb256a18e1d5dcb4967072d6dad891fa443a0f2aa92108b45301a71066246ad2de8dba1b32e40857a93c6f072b18cbb5cb2 languageName: node linkType: hard @@ -9203,9 +9194,9 @@ __metadata: languageName: node linkType: hard -"@walletconnect/types@npm:2.10.1": - version: 2.10.1 - resolution: "@walletconnect/types@npm:2.10.1" +"@walletconnect/types@npm:2.10.2": + version: 2.10.2 + resolution: "@walletconnect/types@npm:2.10.2" dependencies: "@walletconnect/events": ^1.0.1 "@walletconnect/heartbeat": 1.2.1 @@ -9213,7 +9204,7 @@ __metadata: "@walletconnect/keyvaluestorage": ^1.0.2 "@walletconnect/logger": ^2.0.1 events: ^3.3.0 - checksum: b663a236404bb423d3cc5cde656794ce42132f09193da5a51dac815d844f78eebb29c7275ebe10f6134492db21386ffd81b66ce42992332847b72c9128f74990 + checksum: dafcb840b2b93343db56ca6684edfe8a20d9b2f703f81b2d1fdbea558fe41de9fbddec12c24e9d51a50c75ee6298a1cfd347d7fa0202146033788670371cfd6a languageName: node linkType: hard @@ -9241,20 +9232,20 @@ __metadata: languageName: node linkType: hard -"@walletconnect/universal-provider@npm:2.10.1": - version: 2.10.1 - resolution: "@walletconnect/universal-provider@npm:2.10.1" +"@walletconnect/universal-provider@npm:2.10.2": + version: 2.10.2 + resolution: "@walletconnect/universal-provider@npm:2.10.2" dependencies: "@walletconnect/jsonrpc-http-connection": ^1.0.7 "@walletconnect/jsonrpc-provider": 1.0.13 "@walletconnect/jsonrpc-types": ^1.0.2 "@walletconnect/jsonrpc-utils": ^1.0.7 "@walletconnect/logger": ^2.0.1 - "@walletconnect/sign-client": 2.10.1 - "@walletconnect/types": 2.10.1 - "@walletconnect/utils": 2.10.1 + "@walletconnect/sign-client": 2.10.2 + "@walletconnect/types": 2.10.2 + "@walletconnect/utils": 2.10.2 events: ^3.3.0 - checksum: a33ad597a7601157cd96bceb7637c3463a5df981e5548c5343ab84f92c542bd7cae577fb2884d549164c9ad8262b097dc5fc0bc7fd9a515ee7c3f30b271cb034 + checksum: 4629c8031f8edbc1ecdd2f816a94963fd67ea4c8ba966fe12764585614d809fa5994b11d7ab30e7d8d2b51b8dac3a377c7433cb2cfca32070d4884d11d8dbbc7 languageName: node linkType: hard @@ -9280,9 +9271,9 @@ __metadata: languageName: node linkType: hard -"@walletconnect/utils@npm:2.10.1": - version: 2.10.1 - resolution: "@walletconnect/utils@npm:2.10.1" +"@walletconnect/utils@npm:2.10.2": + version: 2.10.2 + resolution: "@walletconnect/utils@npm:2.10.2" dependencies: "@stablelib/chacha20poly1305": 1.0.1 "@stablelib/hkdf": 1.0.1 @@ -9292,13 +9283,13 @@ __metadata: "@walletconnect/relay-api": ^1.0.9 "@walletconnect/safe-json": ^1.0.2 "@walletconnect/time": ^1.0.2 - "@walletconnect/types": 2.10.1 + "@walletconnect/types": 2.10.2 "@walletconnect/window-getters": ^1.0.1 "@walletconnect/window-metadata": ^1.0.1 detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: ^3.1.0 - checksum: 150d1a3c75ce0736ffc8ed8a844e3dc63476e556f7f308154ee6bc9d99e08907bc11a504b7ce3889951293b48d9eef4e32b84de1c7f27b7a84e6731a7bb65189 + checksum: 168e65d48ce6121f04f040662668fce63c8e42050c7c7d1da2948cf2e486657f8bf972f3386dc84251fcabf3626a26bb696e3363d55bc92826ec1602d7b493c7 languageName: node linkType: hard @@ -18782,21 +18773,21 @@ __metadata: languageName: node linkType: hard -"isomorphic-ws@npm:5.0.0": - version: 5.0.0 - resolution: "isomorphic-ws@npm:5.0.0" +"isomorphic-ws@npm:^4.0.1": + version: 4.0.1 + resolution: "isomorphic-ws@npm:4.0.1" peerDependencies: ws: "*" - checksum: e20eb2aee09ba96247465fda40c6d22c1153394c0144fa34fe6609f341af4c8c564f60ea3ba762335a7a9c306809349f9b863c8beedf2beea09b299834ad5398 + checksum: d7190eadefdc28bdb93d67b5f0c603385aaf87724fa2974abb382ac1ec9756ed2cfb27065cbe76122879c2d452e2982bc4314317f3d6c737ddda6c047328771a languageName: node linkType: hard -"isomorphic-ws@npm:^4.0.1": - version: 4.0.1 - resolution: "isomorphic-ws@npm:4.0.1" +"isows@npm:1.0.3": + version: 1.0.3 + resolution: "isows@npm:1.0.3" peerDependencies: ws: "*" - checksum: d7190eadefdc28bdb93d67b5f0c603385aaf87724fa2974abb382ac1ec9756ed2cfb27065cbe76122879c2d452e2982bc4314317f3d6c737ddda6c047328771a + checksum: 9cacd5cf59f67deb51e825580cd445ab1725ecb05a67c704050383fb772856f3cd5e7da8ad08f5a3bd2823680d77d099459d0c6a7037972a74d6429af61af440 languageName: node linkType: hard @@ -20593,8 +20584,8 @@ __metadata: linkType: hard "livekit-client@npm:^1.13.3": - version: 1.13.4 - resolution: "livekit-client@npm:1.13.4" + version: 1.14.0 + resolution: "livekit-client@npm:1.14.0" dependencies: "@bufbuild/protobuf": ^1.3.0 events: ^3.3.0 @@ -20603,7 +20594,7 @@ __metadata: ts-debounce: ^4.0.0 typed-emitter: ^2.1.0 webrtc-adapter: ^8.1.1 - checksum: 90b54ad3dee69bac2f91d09c3db03928448432f8c27ca456717114c891543fab1e2796547322f9a7e3da6047973a0ba36de4b74ed4af66dba0ada25daac228b8 + checksum: 8d7a462312d4ea4e8070a1a3b8cd7810a0853656c88f48d74cf4aa7bf17fad0d4e2ed277a2c8cdaefdeb0482d366d7fb5ffaa2554b768bb7423866b2d207b591 languageName: node linkType: hard @@ -29797,24 +29788,23 @@ __metadata: linkType: hard "viem@npm:^1.3.0": - version: 1.15.4 - resolution: "viem@npm:1.15.4" + version: 1.16.5 + resolution: "viem@npm:1.16.5" dependencies: "@adraffy/ens-normalize": 1.9.4 "@noble/curves": 1.2.0 "@noble/hashes": 1.3.2 "@scure/bip32": 1.3.2 "@scure/bip39": 1.2.1 - "@types/ws": ^8.5.5 abitype: 0.9.8 - isomorphic-ws: 5.0.0 + isows: 1.0.3 ws: 8.13.0 peerDependencies: typescript: ">=5.0.4" peerDependenciesMeta: typescript: optional: true - checksum: 66fe2d8c00504e308b18d730dfd5bb7139e7b8a8c7fe10d8170c3bc4b33b25a9862dd09c7d4fac095310053a898dea18f850b72fcc5b408a439866c0e85d3f94 + checksum: cc07bbc8ebf616c2cc6e14f08eea59d1b34df294d1dcba72090bf10e224e87c17014db593bcff223fe6c7c30999448d786d67d9e18691906f51900db940af821 languageName: node linkType: hard From e0ce6791c8a25e82427df8ace8d2ae3cce78c01e Mon Sep 17 00:00:00 2001 From: kalashshah <202051096@iiitvadodara.ac.in> Date: Mon, 16 Oct 2023 16:03:31 +0530 Subject: [PATCH 18/18] chore: fix uniswap modal ui overlap --- src/components/FaucetInfo.tsx | 104 +++++++++++++++++----------------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/src/components/FaucetInfo.tsx b/src/components/FaucetInfo.tsx index d511bbb399..9964974631 100644 --- a/src/components/FaucetInfo.tsx +++ b/src/components/FaucetInfo.tsx @@ -11,7 +11,7 @@ import { appConfig } from 'config'; import { device } from 'config/Globals'; import { useEffect, useState } from 'react'; import { getHasEnoughPushToken } from 'helpers'; -import useModalBlur, {MODAL_POSITION} from 'hooks/useModalBlur'; +import useModalBlur, { MODAL_POSITION } from 'hooks/useModalBlur'; import { UniswapWidgetModal } from './UniswapWidget'; type FaucetInfoType = { @@ -49,70 +49,70 @@ const FaucetInfo = ({ onMintPushToken, noOfPushTokensToCheck, containerProps }: useEffect(() => { (async () => { await checkSetFaucetVisibility(); - })() + })(); }, [noOfPushTokensToCheck]); return ( - - {isFaucetVisible ? ( - - - {isProd - ? 'You do not have sufficient PUSH Tokens. Swap to add more PUSH.' - : 'Follow these steps to ensure you have enough Testnet Push to proceed.'} - - {isProd ? ( - - - Swap Tokens for PUSH - - ) : ( - - - 1 - Goerli ETH Faucet - - { - await onMintPushToken(1000); - await checkSetFaucetVisibility(); - }} - > - 2 - Get Testnet PUSH - - - )} - - ) : ( - '' - )} - {isUniswapWidgetModalOpen && + <> + + {isFaucetVisible ? ( + + + {isProd + ? 'You do not have sufficient PUSH Tokens. Swap to add more PUSH.' + : 'Follow these steps to ensure you have enough Testnet Push to proceed.'} + + {isProd ? ( + + + Swap Tokens for PUSH + + ) : ( + + + 1 + Goerli ETH Faucet + + { + await onMintPushToken(1000); + await checkSetFaucetVisibility(); + }} + > + 2 + Get Testnet PUSH + + + )} + + ) : ( + '' + )} + + {isUniswapWidgetModalOpen && ( - } - - - + )} + ); }; const Container = styled.div` - width:100%; + width: 100%; transform: translateY(-40px); -` +`; const TextSpace = styled.div` box-sizing: border-box; @@ -125,7 +125,7 @@ const TextSpace = styled.div` background: #f4dcea; border-radius: 0px 0px 28px 28px; padding: 32px 32px 20px 32px; - margin-top:24px; + margin-top: 24px; margin-bottom: -40px; @media ${device.tablet} {