diff --git a/package.json b/package.json index 514c4fb351..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": "0.0.1-alpha.45", + "@pushprotocol/restapi": "0.0.1-alpha.48", "@pushprotocol/socket": "latest", - "@pushprotocol/uiweb": "1.1.14", + "@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/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 a2e58cbc68..f33ba75faa 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,11 +187,9 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { const generalToast = useToast(); - // to subscribe - const subscribe = async () => { - console.log('click executed'); - subscribeAction(false); - }; + const userSettings = useMemo(() => { + return cloneDeep(currentUserSettings); + }, [currentUserSettings]); const formatAddress = (addressText) => { return addressText.length > 40 ? `${shortenText(addressText, 4, 6)}` : addressText; @@ -325,125 +324,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 +344,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 +854,14 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { <> {isOwner && Owner} {!isOwner && ( - + { + setSubscribed(true); + setSubscriberCount((prevSubscriberCount) => prevSubscriberCount + 1) + }} + > {}} disabled={txInProgress} @@ -1066,7 +886,17 @@ function ViewChannelItem({ channelObjectProp, loadTeaser, playTeaser }) { <> {isOwner && Owner} {!isOwner && ( - + { + setSubscribed(false); + setSubscriberCount((prevSubscriberCount) => prevSubscriberCount - 1) + }} + > {}} disabled={txInProgress} @@ -1532,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/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 d238a12624..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'; @@ -26,17 +27,18 @@ interface ChannelListItem { icon: string; name: string; id: number; + channel_settings: string; } 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) => { @@ -47,6 +49,7 @@ function UserSettings() { id: details.id, icon: details.icon, name: details.name, + channel_settings: details.channel_settings, }; return updatedChannelItem; } else return undefined; @@ -70,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); @@ -91,6 +99,10 @@ function UserSettings() { }, ]; + const userSettings = useMemo(() => { + return cloneDeep(currentUserSettings); + }, [currentUserSettings]); + return ( Settings @@ -127,7 +139,16 @@ 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..9bb7c6ea1f 100644 --- a/src/components/dropdowns/ManageNotifSettingDropdown.tsx +++ b/src/components/dropdowns/ManageNotifSettingDropdown.tsx @@ -1,8 +1,9 @@ // 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"; +import { useDispatch } from "react-redux"; // Internal Components import { DropdownBtnHandler } from "./DropdownBtnHandler"; @@ -10,46 +11,113 @@ 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"; +import { removeUserSetting, updateSubscriptionStatus } from "redux/slices/channelSlice"; 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 ( - - - - - Manage Settings - - - - + {(channelSetting && channelSetting.length != 0) && + + + + + + 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 dispatch = useDispatch(); + + console.log('user setting', userSetting); + + const channelSetting = useMemo(() => { + if(channelDetail && channelDetail?.channel_settings) { + return JSON.parse(channelDetail?.channel_settings); + } + return null; + }, [channelDetail]); const toggleDropdown = () => { setIsOpen(!isOpen); @@ -59,13 +127,90 @@ 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(); + dispatch(updateSubscriptionStatus({ channelAddress: channelAddress, status: false })); + dispatch(removeUserSetting(channelAddress)); + + 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 +243,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..bdea07f8ba 100644 --- a/src/components/dropdowns/OptinNotifSettingDropdown.tsx +++ b/src/components/dropdowns/OptinNotifSettingDropdown.tsx @@ -1,37 +1,65 @@ // React + Web3 Essentials -import React, { useState } from "react"; +import React, { useContext, useMemo, useState } from "react"; // External Packages 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"; // 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, 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; + 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 +73,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 +83,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 dispatch = useDispatch(); + + 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 +157,91 @@ 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(); + dispatch(updateSubscriptionStatus({ channelAddress, status: true })); + dispatch(updateUserSetting({ channelAddress, settings: userSettingsFromDefaultChannelSetting({ channelSetting: channelSettings })})); + + 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 && channelSetting.length) ? } + renderDropdownContainer={} containerPadding="0px 16px 16px 16px" > {children} + : + + {children} + + ); } @@ -190,7 +283,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 +325,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..18272d4f72 100644 --- a/src/components/dropdowns/UpdateNotifSettingDropdown.tsx +++ b/src/components/dropdowns/UpdateNotifSettingDropdown.tsx @@ -1,38 +1,67 @@ // React + Web3 Essentials -import React, { useState } from "react"; +import React, { useContext, useState } from "react"; // External Packages 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"; // 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"; +import { updateUserSetting } from "redux/slices/channelSlice"; 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 +75,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 +85,7 @@ const UpdateNotifSettingDropdownContainer: React.FC<{ settings: any[] }> = ({ se handleDiameter={12} /> - {setting.type === 2 && setting.default && ( + {setting.type === 2 && setting.enabled === true && ( = ({ 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 dispatch = useDispatch(); + + const onCoreNetwork = chainId === appConfig.coreContractChain; + const toggleDropdown = () => { setIsOpen(!isOpen); }; @@ -105,38 +158,73 @@ 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(); + dispatch(updateUserSetting({ channelAddress, settings: userSetting })); + + 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 +233,11 @@ const UpdateNotifSettingDropdown: React.FC = ({ showDropdown={isOpen} toggleDropdown={toggleDropdown} closeDropdown={closeDropdown} - renderDropdownContainer={} + renderDropdownContainer={ + } containerPadding="0px 16px 16px 16px" > {children} @@ -191,7 +283,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; @@ -233,3 +325,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 01eda9a9db..e0f24954b1 100644 --- a/src/helpers/channel/types.ts +++ b/src/helpers/channel/types.ts @@ -1,16 +1,36 @@ export type ChannelSetting = - | { - type: 1; // Boolean - isDefaultEnabled: boolean; - description: string; - index: number; - } - | { - type: 2; // Range - defaultValue: number; - isDefaultEnabled: boolean; - 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; + }; + 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]); 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 9dc94d21d4..0423a7f5cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1694,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" @@ -3670,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" @@ -3679,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" @@ -3708,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" @@ -4503,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" @@ -5352,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.45 + "@pushprotocol/restapi": 0.0.1-alpha.48 "@pushprotocol/socket": latest - "@pushprotocol/uiweb": 1.1.14 + "@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 @@ -5554,9 +5625,9 @@ __metadata: languageName: node linkType: hard -"@pushprotocol/restapi@npm:0.0.1-alpha.45": - version: 0.0.1-alpha.45 - resolution: "@pushprotocol/restapi@npm:0.0.1-alpha.45" +"@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 @@ -5577,7 +5648,7 @@ __metadata: viem: ^1.3.0 peerDependencies: ethers: ^5.6.8 - checksum: 1defe9cb71147e49e42ac8dcb1fa40ab6448c703bf8dd09ddcdbc49f737d58705a7eae7aef88bb88daf9e945244182fa6298cd74842273add7bbb00d8ff7b30f + checksum: 49aa8e537574b4fbcefa154dbdde36c66bda285a306794dc259f6cbf7e056fdfe9e2d4a1bd3f4bfa0c0f25370b9687e083fabd0113c309e021a0b702ab42dd40 languageName: node linkType: hard @@ -5617,30 +5688,40 @@ __metadata: languageName: node linkType: hard -"@pushprotocol/uiweb@npm:1.1.14": - version: 1.1.14 - resolution: "@pushprotocol/uiweb@npm:1.1.14" +"@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: c06f684851892d3c71e4c8f38210a4b04e8e1f039d82a2bda728f3cc1414d059989c6ef91a5f6bf07e945ad61fd84a630c6e2ac72cba9493f138387396eda421 + checksum: 699b576cc1612b41e83790f3e9845fc7702ecf362fb542f28156dadec039f52e41962ab774b61a910c5c05a9afda4ce6d0b79e7d02d33240054f8df230bdad92 languageName: node linkType: hard @@ -6178,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" @@ -8606,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" @@ -8689,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" @@ -8866,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" @@ -8878,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" @@ -8888,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" @@ -8971,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" @@ -9019,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" @@ -9043,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" @@ -9065,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" @@ -9157,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" @@ -9197,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" @@ -11987,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" @@ -13943,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" @@ -15374,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: @@ -16498,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" @@ -20255,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" @@ -20266,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" @@ -20548,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 @@ -26333,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" @@ -26351,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" @@ -26595,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" @@ -26602,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" @@ -28569,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" @@ -28812,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" @@ -29205,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" @@ -29311,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" @@ -29344,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" @@ -29952,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"