From b0aa2392dd98b7f6304d2bfafee65959917b84f9 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 9 Nov 2023 18:30:15 +0530 Subject: [PATCH 1/7] Added Checkbox in reusables --- .../reusables/checkbox/Checkbox.tsx | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 src/components/reusables/checkbox/Checkbox.tsx diff --git a/src/components/reusables/checkbox/Checkbox.tsx b/src/components/reusables/checkbox/Checkbox.tsx new file mode 100644 index 0000000000..d896c536d7 --- /dev/null +++ b/src/components/reusables/checkbox/Checkbox.tsx @@ -0,0 +1,32 @@ +import styled from 'styled-components'; + +type CheckboxPropsType = { + checked: boolean; + onChange: () => void; +}; + +const Checkbox = ({ checked, onChange }: CheckboxPropsType) => { + return ( + + ); +}; + +const CheckboxInput = styled.input` + cursor: pointer; + z-index: 1; + border-radius: 15px; + width: 16px; + height: 20px; + + /* Change the color of the checkbox */ + &:checked { + accent-color: #D53A94; + } +`; + +export default Checkbox; From 25a926d51152bdcf05cf2aa2d7687e9f784a99da Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 9 Nov 2023 18:30:30 +0530 Subject: [PATCH 2/7] Created Range Slider Component --- .../reusables/sliders/RangeSlider.tsx | 254 ++++++++++++++++++ 1 file changed, 254 insertions(+) create mode 100644 src/components/reusables/sliders/RangeSlider.tsx diff --git a/src/components/reusables/sliders/RangeSlider.tsx b/src/components/reusables/sliders/RangeSlider.tsx new file mode 100644 index 0000000000..2a60edf0a4 --- /dev/null +++ b/src/components/reusables/sliders/RangeSlider.tsx @@ -0,0 +1,254 @@ +import React, { ComponentPropsWithoutRef, useEffect, useRef } from 'react'; +import styled from 'styled-components'; + +interface RangeSliderProps extends Omit, 'children' | 'onChange'> { + disabled?: boolean; + startVal: number; + endVal: number; + min: number; + max: number; + step: number; + defaultStartVal: number; + defaultEndVal: number; + preview?: boolean; + onChange: (value: { startVal: number, endVal: number }) => void; + onDragStart?: (e: React.MouseEvent | React.TouchEvent) => void; + onDragEnd?: (e: React.MouseEvent | React.TouchEvent) => void; +} + +const RangeSlider = ({ + disabled, + startVal, + endVal, + min, + max, + step, + defaultStartVal, + defaultEndVal, + onChange, + onDragStart, + onDragEnd, + preview = false, + ...props +}: RangeSliderProps) => { + const thumbStartRef = useRef(null); + const thumbEndRef = useRef(null); + const containerRef = useRef(null); + const previewSliderStartRef = useRef(null); + const previewSliderEndRef = useRef(null); + const activeRef = useRef(null); + const inactiveLeftRef = useRef(null); + const inactiveRightRef = useRef(null); + + const handleMouseDownLeftThumb = (e: React.MouseEvent | React.TouchEvent) => { + if (disabled) return; + if (onDragStart) onDragStart(e); + // Add event listeners for mousemove and touchmove to track thumb movement + document.addEventListener('mousemove', handleMouseMoveLeftThumb); + document.addEventListener('mouseup', handleMouseUpLeftThumb); + document.addEventListener('touchmove', handleMouseMoveLeftThumb); + document.addEventListener('touchend', handleMouseUpLeftThumb); + }; + + const handleMouseMoveLeftThumb = (e) => { + if (disabled) return; + if (!containerRef.current) return; + + const { left, width } = containerRef.current.getBoundingClientRect(); + const { clientX } = e instanceof MouseEvent ? e : e.touches[0]; + let x = (clientX - left) / width; + const lowerBound = defaultStartVal - Math.floor((defaultStartVal - min) / step) * step; + const upperBound = defaultStartVal + Math.floor((max - defaultStartVal) / step) * step; + + if (x <= 0) x = lowerBound; + else if (x >= 1) x = upperBound; + else { + const stepCount = Math.floor((x * (max - min) + min - defaultStartVal) / step); + x = defaultStartVal + stepCount * step; + if (x < lowerBound) x = lowerBound; + if (x > upperBound) x = upperBound; + } + const decimalPlaces = (step.toString().split('.')[1] || '').length; + + if (Number(x.toFixed(decimalPlaces)) >= endVal) return; + onChange({ startVal: Number(x.toFixed(decimalPlaces)), endVal: endVal }); + }; + + const handleMouseUpLeftThumb = (e) => { + if (disabled) return; + if (onDragEnd) onDragEnd(e); + // Remove event listeners when the dragging ends + document.removeEventListener('mousemove', handleMouseMoveLeftThumb); + document.removeEventListener('mouseup', handleMouseUpLeftThumb); + document.removeEventListener('touchmove', handleMouseMoveLeftThumb); + document.removeEventListener('touchend', handleMouseUpLeftThumb); + }; + + const handleMouseDownRightThumb = (e: React.MouseEvent | React.TouchEvent) => { + if (disabled) return; + if (onDragStart) onDragStart(e); + // Add event listeners for mousemove and touchmove to track thumb movement + document.addEventListener('mousemove', handleMouseMoveRightThumb); + document.addEventListener('mouseup', handleMouseUpRightThumb); + document.addEventListener('touchmove', handleMouseMoveRightThumb); + document.addEventListener('touchend', handleMouseUpRightThumb); + }; + + const handleMouseMoveRightThumb = (e) => { + if (disabled) return; + if (!containerRef.current) return; + + const { left, width } = containerRef.current.getBoundingClientRect(); + const { clientX } = e instanceof MouseEvent ? e : e.touches[0]; + let x = (clientX - left) / width; + const lowerBound = defaultEndVal - Math.floor((defaultEndVal - min) / step) * step; + const upperBound = defaultEndVal + Math.floor((max - defaultEndVal) / step) * step; + + if (x <= 0) x = lowerBound; + else if (x >= 1) x = upperBound; + else { + const stepCount = Math.floor((x * (max - min) + min - defaultEndVal) / step); + x = defaultEndVal + stepCount * step; + if (x < lowerBound) x = lowerBound; + if (x > upperBound) x = upperBound; + } + const decimalPlaces = (step.toString().split('.')[1] || '').length; + + if (Number(x.toFixed(decimalPlaces)) <= startVal) return; + + onChange({ startVal: startVal, endVal: Number(x.toFixed(decimalPlaces)) }); + }; + + const handleMouseUpRightThumb = (e) => { + if (disabled) return; + if (onDragEnd) onDragEnd(e); + // Remove event listeners when the dragging ends + document.removeEventListener('mousemove', handleMouseMoveRightThumb); + document.removeEventListener('mouseup', handleMouseUpRightThumb); + document.removeEventListener('touchmove', handleMouseMoveRightThumb); + document.removeEventListener('touchend', handleMouseUpRightThumb); + }; + + const showPreview = () => { + previewSliderStartRef.current?.style.setProperty('display', 'flex'); + previewSliderEndRef.current?.style.setProperty('display', 'flex'); + } + + const hidePreview = () => { + previewSliderStartRef.current?.style.setProperty('display', 'none'); + previewSliderEndRef.current?.style.setProperty('display', 'none'); + } + + useEffect(() => { + if (thumbStartRef.current && inactiveLeftRef.current) { + thumbStartRef.current.style.left = `${((startVal - min) / (max - min)) * 98}%`; + inactiveLeftRef.current.style.width = `${((startVal - min) / (max - min)) * 100}%`; + activeRef.current.style.width = `${((endVal - startVal) / (max - min)) * 100}%`; + thumbEndRef.current.style.left = `${((endVal) / (max - min)) * 98}%`; + inactiveRightRef.current.style.width = `${((max - endVal) / (max - min)) * 100}%`; + + previewSliderStartRef.current?.style.setProperty( + 'left', + `${((Number(startVal) - Number(min)) / (Number(max) - Number(min))) * 90}%` + ); + previewSliderEndRef.current?.style.setProperty( + 'left', + `${((Number(endVal) - Number(min)) / (Number(max) - Number(min))) * 90}%` + ); + } + }, [thumbStartRef, thumbEndRef, activeRef, inactiveLeftRef, inactiveRightRef, startVal, endVal, min, max]); + + return ( + + + + + + + {preview && !Number.isNaN(Number(startVal)) && {startVal}} + {preview && !Number.isNaN(Number(endVal)) && {endVal}} + + ); +}; + +const Thumb = styled.div` + width: 16px; + height: 16px; + background-color: ${(props) => props.theme.default.bg}; + border: 1px solid ${(props) => props.theme.default.border}; + border-radius: 50%; + user-select: none; + cursor: pointer; + z-index: 1; + position: absolute; +`; + +const Active = styled.div` + width: 100%; + height: 4px; + background-color: ${(props) => props.theme.default.primaryPushThemeTextColor}; + border-top-left-radius: 8px; + border-bottom-left-radius: 8px; +`; + +const Inactive = styled.div` + width: 100%; + height: 4px; + background-color: ${(props) => props.theme.snfBorder}; + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; +`; + +const Container = styled.div` + height: 24px; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + position: relative; + flex: 1; + width: 100%; +`; + +const PreviewContainer = styled.div` + display: none; + position: absolute; + bottom: -48px; + border-radius: 4px; + border: 1px solid ${(props) => props.theme.default.border}; + background: ${(props) => props.theme.default.bg}; + color: ${(props) => props.theme.default.color}; + width: max-content; + padding: 8px; + justify-content: center; + align-items: center; + gap: 10px; +`; + +const SliderRange = styled.div` + position: absolute; + height: 4px; + background-color: #999; +`; + +export default RangeSlider; From 6b00d4acf4444c470409258903ffbc407b572053 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 9 Nov 2023 18:31:12 +0530 Subject: [PATCH 3/7] Added Range slider in add setting modal --- .../channel/AddSettingModalContent.tsx | 192 ++++++++++++++---- 1 file changed, 155 insertions(+), 37 deletions(-) diff --git a/src/components/channel/AddSettingModalContent.tsx b/src/components/channel/AddSettingModalContent.tsx index b47b4bc5c9..3932d59e9e 100644 --- a/src/components/channel/AddSettingModalContent.tsx +++ b/src/components/channel/AddSettingModalContent.tsx @@ -18,6 +18,8 @@ import { Item } from 'components/SharedStyling'; import { FormSubmision, Input, Span } from 'primaries/SharedStyling'; import { IOSSwitch } from 'components/SendNotifications'; import { isAllFilledAndValid } from 'helpers/channel/InputValidation'; +import RangeSlider from 'components/reusables/sliders/RangeSlider'; +import Checkbox from 'components/reusables/checkbox/Checkbox'; const ToggleItem = ({ checked, onChange, label, description }) => { return ( @@ -79,13 +81,27 @@ const AddSettingModalContent = ({ const [upperLimit, setUpperLimit] = useState( settingToEdit && settingToEdit.type === 2 ? settingToEdit.upperLimit.toString() : '' ); - const [defaultValue, setDefaultValue] = useState( - settingToEdit && settingToEdit.type === 2 ? settingToEdit.default.toString() : '' - ); const [sliderStep, setSliderStep] = useState( settingToEdit && settingToEdit.type === 2 && settingToEdit.ticker ? settingToEdit.ticker.toString() : '1' ); + + const [enableMultiRange, setEnableMultiRange] = useState(false); + + // for single value slider + const [defaultValue, setDefaultValue] = useState( + settingToEdit && settingToEdit.type === 2 ? settingToEdit.default.toString() : '' + ); const [sliderPreviewVal, setSliderPreviewVal] = useState(); + + // for range slider + const [defaultStartValue, setDefaultStartValue] = useState( + settingToEdit && settingToEdit.type === 3 ? settingToEdit.defaultStartVal.toString() : '' + ); + const [defaultEndValue, setDefaultEndValue] = useState( + settingToEdit && settingToEdit.type === 3 ? settingToEdit.defaultEndVal.toString() : '' + ); + const [sliderPreviewStartVal, setSliderPreviewStartVal] = useState(); + const [sliderPreviewEndVal, setSliderPreviewEndVal] = useState(); const [errorInfo, setErrorInfo] = useState(); const theme = useTheme(); @@ -142,15 +158,21 @@ const AddSettingModalContent = ({ return ( lowerLimit !== '' && upperLimit !== '' && - defaultValue !== '' && + (enableMultiRange ? (defaultStartValue !== '' && defaultEndValue !== '') : defaultValue !== '') && sliderStep !== '' && Number(lowerLimit) <= Number(upperLimit) && Number(sliderStep) > 0 && Number(sliderStep) <= Number(upperLimit) - Number(lowerLimit) && - Number(defaultValue) >= Number(lowerLimit) && - Number(defaultValue) <= Number(upperLimit) + (enableMultiRange ? + (Number(defaultStartValue) >= Number(lowerLimit) && + Number(defaultEndValue) <= Number(upperLimit) && + Number(defaultEndValue) > Number(defaultStartValue)) + : + (Number(defaultValue) >= Number(lowerLimit) && + Number(defaultValue) <= Number(upperLimit)) + ) ); - }, [lowerLimit, upperLimit, defaultValue, sliderStep]); + }, [lowerLimit, upperLimit, defaultValue, sliderStep, defaultStartValue, defaultEndValue]); return ( @@ -281,40 +303,113 @@ const AddSettingModalContent = ({ {errorInfo?.upperLimit} - - { - setErrorInfo((prev) => ({ ...prev, default: undefined })); - if (isInvalidNumber(e.target.value)) return; - setDefaultValue(e.target.value); - setSliderPreviewVal(Number(e.target.value)); - }} - autocomplete="off" - hasError={errorInfo?.default ? true : false} - /> - {errorInfo?.default} + setEnableMultiRange(!enableMultiRange)}/> + + + User can select a range of values in the slider + + {!enableMultiRange && + + + { + setErrorInfo((prev) => ({ ...prev, default: undefined })); + if (isInvalidNumber(e.target.value)) return; + setDefaultValue(e.target.value); + setSliderPreviewVal(Number(e.target.value)); + }} + autocomplete="off" + hasError={errorInfo?.default ? true : false} + /> + {errorInfo?.default} + + } + {enableMultiRange && + + + + { + setDefaultStartValue(e.target.value); + setSliderPreviewStartVal(Number(e.target.value)); + }} + autocomplete="off" + /> + + { + setDefaultEndValue(e.target.value); + setSliderPreviewEndVal(Number(e.target.value)); + }} + autocomplete="off" + /> + + + } Preview - + {!enableMultiRange && + + + } + + {enableMultiRange && + + + { + setSliderPreviewStartVal(startVal) + setSliderPreviewEndVal(endVal) + }} + preview={true} + /> + + } )} From c328a5de7e08e908a95e1530d558ee889252d79f Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Fri, 10 Nov 2023 16:27:44 +0530 Subject: [PATCH 4/7] Changes --- .../dropdowns/OptinNotifSettingDropdown.tsx | 18 ++++++++++++++++++ src/helpers/channel/types.ts | 13 ++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/components/dropdowns/OptinNotifSettingDropdown.tsx b/src/components/dropdowns/OptinNotifSettingDropdown.tsx index 3a42bbd6c4..68e417bb5f 100644 --- a/src/components/dropdowns/OptinNotifSettingDropdown.tsx +++ b/src/components/dropdowns/OptinNotifSettingDropdown.tsx @@ -22,6 +22,7 @@ import { notifChannelSettingFormatString, userSettingsFromDefaultChannelSetting import { AppContext } from "contexts/AppContext"; import LoaderSpinner, { LOADER_TYPE } from "components/reusables/loaders/LoaderSpinner"; import { updateSubscriptionStatus, updateUserSetting } from "redux/slices/channelSlice"; +import RangeSlider from "components/reusables/sliders/RangeSlider"; interface OptinNotifSettingDropdownProps { children: React.ReactNode; @@ -98,6 +99,23 @@ const OptinNotifSettingDropdownContainer: React.FC )} + {setting.type === 3 && setting.enabled === true && ( + + handleSliderChange(index, x)} + /> + + {setting.defaultStartVal} - {setting.defaultEndVal} + + + )} ))} diff --git a/src/helpers/channel/types.ts b/src/helpers/channel/types.ts index f520078a29..bfd77d74e6 100644 --- a/src/helpers/channel/types.ts +++ b/src/helpers/channel/types.ts @@ -14,7 +14,18 @@ export type ChannelSetting = lowerLimit: number; upperLimit: number; ticker: number; - }; + } +| { + type: 3; // Range + defaultStartVal: number; + defaultEndVal: number; + enabled: boolean; + description: string; + index: number; + lowerLimit: number; + upperLimit: number; + ticker: number; +};; export type UserSetting = | { From 600edd9c9d3d25b1a7514e2cd8980bc6360e2539 Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Wed, 29 Nov 2023 16:07:10 +0530 Subject: [PATCH 5/7] Added SDK and type changes for multi-range notif --- package.json | 2 +- src/components/SendNotifications.tsx | 7 ++- .../channel/AddSettingModalContent.tsx | 33 ++++++++-- src/components/channel/ChannelInfoList.tsx | 1 + .../channel/NotificationSettings.tsx | 28 ++++++++- .../dropdowns/OptinNotifSettingDropdown.tsx | 53 ++++++++-------- .../dropdowns/UpdateNotifSettingDropdown.tsx | 58 ++++++++++++------ .../reusables/sliders/RangeSlider.tsx | 2 +- src/helpers/channel/InputValidation.ts | 60 ++++++++++++++----- src/helpers/channel/notifSetting.ts | 4 +- src/helpers/channel/types.ts | 25 +++++++- yarn.lock | 10 ++-- 12 files changed, 206 insertions(+), 77 deletions(-) diff --git a/package.json b/package.json index 2cef235029..3da1e12a06 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "@mui/lab": "^5.0.0-alpha.72", "@mui/material": "^5.5.0", "@pushprotocol/ledgerlive": "latest", - "@pushprotocol/restapi": "1.4.38", + "@pushprotocol/restapi": "1.4.41", "@pushprotocol/socket": "0.5.3", "@pushprotocol/uiweb": "1.1.19", "@reduxjs/toolkit": "^1.7.1", diff --git a/src/components/SendNotifications.tsx b/src/components/SendNotifications.tsx index 517d8e09e3..6332968b75 100644 --- a/src/components/SendNotifications.tsx +++ b/src/components/SendNotifications.tsx @@ -170,7 +170,12 @@ function SendNotifications() { if (channelSettings) { const settingsOptions = channelSettings.map((setting) => ({ label: - setting.type === 2 ? ( + setting.type === 3 ? ( + +
{setting.description}
+ Multi-Range +
+ ) : setting.type === 2 ? (
{setting.description}
Range diff --git a/src/components/channel/AddSettingModalContent.tsx b/src/components/channel/AddSettingModalContent.tsx index 3932d59e9e..3814572ba7 100644 --- a/src/components/channel/AddSettingModalContent.tsx +++ b/src/components/channel/AddSettingModalContent.tsx @@ -95,10 +95,10 @@ const AddSettingModalContent = ({ // for range slider const [defaultStartValue, setDefaultStartValue] = useState( - settingToEdit && settingToEdit.type === 3 ? settingToEdit.defaultStartVal.toString() : '' + settingToEdit && settingToEdit.type === 3 ? settingToEdit.default.lower.toString() : '' ); const [defaultEndValue, setDefaultEndValue] = useState( - settingToEdit && settingToEdit.type === 3 ? settingToEdit.defaultEndVal.toString() : '' + settingToEdit && settingToEdit.type === 3 ? settingToEdit.default.upper.toString() : '' ); const [sliderPreviewStartVal, setSliderPreviewStartVal] = useState(); const [sliderPreviewEndVal, setSliderPreviewEndVal] = useState(); @@ -117,17 +117,33 @@ const AddSettingModalContent = ({ if ( isAllFilledAndValid({ setErrorInfo, - defaultValue, + defaultValue: enableMultiRange ? { lower: defaultStartValue, upper: defaultEndValue } : defaultValue, settingName, lowerLimit, - type: isRange ? 2 : 1, + type: isRange ? (enableMultiRange ? 3 : 2) : 1, upperLimit, sliderStep, }) ) { const index = settingToEdit ? settingToEdit.index : Math.floor(Math.random() * 1000000); const settingData: ChannelSetting = isRange - ? { + ? enableMultiRange + ? + { + type: 3, + default: { + lower: Number(defaultStartValue), + upper: Number(defaultEndValue) + }, + enabled: isDefault, + description: settingName, + index: index, + lowerLimit: Number(lowerLimit), + upperLimit: Number(upperLimit), + ticker: Number(sliderStep), + } + : + { type: 2, default: Number(defaultValue), enabled: isDefault, @@ -379,10 +395,13 @@ const AddSettingModalContent = ({ color={theme.editChannelPrimaryText} value={defaultStartValue} onChange={(e) => { + setErrorInfo((prev) => ({ ...prev, defaultStart: undefined })); + if (isInvalidNumber(e.target.value)) return; setDefaultStartValue(e.target.value); setSliderPreviewStartVal(Number(e.target.value)); }} autocomplete="off" + hasError={errorInfo?.defaultStart ? true : false} /> { + setErrorInfo((prev) => ({ ...prev, defaultEnd: undefined })); + if (isInvalidNumber(e.target.value)) return; setDefaultEndValue(e.target.value); setSliderPreviewEndVal(Number(e.target.value)); }} autocomplete="off" + hasError={errorInfo?.defaultEnd ? true : false} /> + {errorInfo?.defaultStart || errorInfo?.defaultEnd} } { <> {item.description} {item.type === 2 && Range} + {item.type === 3 && Multi-Range} )} {props.isAddress && isOwner(props.account, item) && Creator} diff --git a/src/components/channel/NotificationSettings.tsx b/src/components/channel/NotificationSettings.tsx index bd70d99d87..74718d0ba3 100644 --- a/src/components/channel/NotificationSettings.tsx +++ b/src/components/channel/NotificationSettings.tsx @@ -142,7 +142,32 @@ function NotificationSettings() { default: setting.default ? 1 : 0, }; } - if (setting.type === 2) { + else if (setting.type === 2) { + console.log( + { + type: setting.type, + description: setting.description, + default: setting.default, + data: { + lower: setting.lowerLimit, + upper: setting.upperLimit, + ticker: setting.ticker, + enabled: setting.enabled, + }, + } + ) + return { + type: setting.type, + description: setting.description, + default: setting.default, + data: { + lower: setting.lowerLimit, + upper: setting.upperLimit, + ticker: setting.ticker, + enabled: setting.enabled, + }, + }; + } else if (setting.type === 3) { console.log( { type: setting.type, @@ -169,6 +194,7 @@ function NotificationSettings() { }; } }); + console.log(settingData); await userPushSDKInstance.channel.setting(settingData); dispatch(updateChannelSetting({ channelAddress, settings })); diff --git a/src/components/dropdowns/OptinNotifSettingDropdown.tsx b/src/components/dropdowns/OptinNotifSettingDropdown.tsx index 68e417bb5f..d2580ce9f4 100644 --- a/src/components/dropdowns/OptinNotifSettingDropdown.tsx +++ b/src/components/dropdowns/OptinNotifSettingDropdown.tsx @@ -42,7 +42,7 @@ const OptinNotifSettingDropdownContainer: React.FC { + const handleSliderChange = (index: number, value: number | { lower: number, upper: number }) => { const updatedSettings = [...modifiedSettings]; updatedSettings[index].default = value; setModifiedSettings(updatedSettings); @@ -84,36 +84,36 @@ const OptinNotifSettingDropdownContainer: React.FC - {setting.type === 2 && setting.enabled === true && ( - + {setting.type === 2 && setting.enabled === true && ( + + + {setting.default} + handleSliderChange(index, x)} /> - - {setting.default} - - - )} + + )} {setting.type === 3 && setting.enabled === true && ( - handleSliderChange(index, x)} - /> - - {setting.defaultStartVal} - {setting.defaultEndVal} - + + {setting.default.lower} - {setting.default.upper} + + handleSliderChange(index, {lower: startVal, upper: endVal})} + /> )} @@ -276,14 +276,14 @@ const DropdownSwitchItem = styled.div` display: flex; justify-content: space-between; align-items: center; - padding: 10px 0px; + padding: 12px 0px; `; const DropdownSubmitItem = styled.div` display: flex; justify-content: space-between; align-items: center; - padding: 10px 0px; + padding: 12px 0px; `; const DropdownSubmitButton = styled.button` @@ -329,9 +329,10 @@ const DropdownSubmitButton = styled.button` const DropdownSliderItem = styled.div` display: flex; - justify-content: space-between; + flex-direction: column; + gap: 13px; align-items: center; - padding-bottom: 10px; + padding-bottom: 12px; `; const ActionTitle = styled.span<{ hideIt: boolean }>` diff --git a/src/components/dropdowns/UpdateNotifSettingDropdown.tsx b/src/components/dropdowns/UpdateNotifSettingDropdown.tsx index 17c7b324d8..14f3b3b621 100644 --- a/src/components/dropdowns/UpdateNotifSettingDropdown.tsx +++ b/src/components/dropdowns/UpdateNotifSettingDropdown.tsx @@ -9,6 +9,7 @@ import { useDispatch, useSelector } from "react-redux"; // Internal Components import { DropdownBtnHandler } from "./DropdownBtnHandler"; import InputSlider from "components/reusables/sliders/InputSlider"; +import RangeSlider from "components/reusables/sliders/RangeSlider"; // Internal Configs import { SpanV2 } from "components/reusables/SharedStylingV2"; @@ -43,7 +44,7 @@ const UpdateNotifSettingDropdownContainer: React.FC { + const handleSliderChange = (index: number, value: number | {lower: number, upper: number}) => { const updatedSettings = [...modifiedSettings]; updatedSettings[index].user = value; setModifiedSettings(updatedSettings); @@ -56,11 +57,16 @@ const UpdateNotifSettingDropdownContainer: React.FC {setting.type === 2 && setting.enabled === true && ( - handleSliderChange(index, x)} - /> - - {setting.user} - + + {setting.user || setting.default} + + handleSliderChange(index, x)} + /> + + )} + {setting.type === 3 && setting.enabled === true && ( + + + {setting.user.lower || setting.default.lower} - {setting.user.upper || setting.default.upper} + + handleSliderChange(index, {lower: startVal, upper: endVal})} + /> )} @@ -258,14 +281,14 @@ const DropdownSwitchItem = styled.div` display: flex; justify-content: space-between; align-items: center; - padding: 10px 0px; + padding: 12px 0px; `; const DropdownSubmitItem = styled.div` display: flex; justify-content: space-between; align-items: center; - padding: 10px 0px; + padding: 12px 0px; `; const DropdownSubmitButton = styled.button` @@ -311,9 +334,10 @@ const DropdownSubmitButton = styled.button` const DropdownSliderItem = styled.div` display: flex; - justify-content: space-between; + flex-direction: column; + gap: 13px; align-items: center; - padding-bottom: 10px; + padding-bottom: 12px; `; const ActionTitle = styled.span<{ hideIt: boolean }>` diff --git a/src/components/reusables/sliders/RangeSlider.tsx b/src/components/reusables/sliders/RangeSlider.tsx index 2a60edf0a4..9d1551846f 100644 --- a/src/components/reusables/sliders/RangeSlider.tsx +++ b/src/components/reusables/sliders/RangeSlider.tsx @@ -144,7 +144,7 @@ const RangeSlider = ({ thumbStartRef.current.style.left = `${((startVal - min) / (max - min)) * 98}%`; inactiveLeftRef.current.style.width = `${((startVal - min) / (max - min)) * 100}%`; activeRef.current.style.width = `${((endVal - startVal) / (max - min)) * 100}%`; - thumbEndRef.current.style.left = `${((endVal) / (max - min)) * 98}%`; + thumbEndRef.current.style.left = `${((endVal - min) / (max - min)) * 95}%`; inactiveRightRef.current.style.width = `${((max - endVal) / (max - min)) * 100}%`; previewSliderStartRef.current?.style.setProperty( diff --git a/src/helpers/channel/InputValidation.ts b/src/helpers/channel/InputValidation.ts index 824072b301..637756ae91 100644 --- a/src/helpers/channel/InputValidation.ts +++ b/src/helpers/channel/InputValidation.ts @@ -26,12 +26,11 @@ export const isAllFilledAndValid = ({ sliderStep: string; type: ChannelSetting['type']; settingName: string; - defaultValue: string; + defaultValue: string | { lower: string, upper: string }; }): boolean => { setErrorInfo(undefined); let hasError = false; - if (isEmpty(settingName)) { setErrorInfo((x) => ({ ...x, @@ -40,7 +39,7 @@ export const isAllFilledAndValid = ({ hasError = true; } - if (type === 2) { + if (type === 2 || type === 3) { if (isEmpty(lowerLimit)) { setErrorInfo((x) => ({ ...x, @@ -55,11 +54,25 @@ export const isAllFilledAndValid = ({ })); hasError = true; } - if (isEmpty(defaultValue)) { - setErrorInfo((x) => ({ - ...x, - default: 'Default value is required', - })); + if ((typeof defaultValue === "string" ? isEmpty(defaultValue) : (isEmpty(defaultValue.lower) || isEmpty(defaultValue.upper)))) { + if (typeof defaultValue === "string") { + setErrorInfo((x) => ({ + ...x, + default: 'Default value is required', + })); + } + else { + if (isEmpty(defaultValue.lower)) + setErrorInfo((x) => ({ + ...x, + defaultStart: 'Default start value is required', + })); + if (isEmpty(defaultValue.upper)) + setErrorInfo((x) => ({ + ...x, + defaultEnd: 'Default end value is required', + })); + } hasError = true; } if (isEmpty(sliderStep)) { @@ -69,7 +82,7 @@ export const isAllFilledAndValid = ({ })); hasError = true; } - if (!isEmpty(lowerLimit) && !isEmpty(upperLimit) && !isEmpty(defaultValue) && !isEmpty(sliderStep)) { + if (!isEmpty(lowerLimit) && !isEmpty(upperLimit) && !isEmpty(sliderStep)) { if (Number(lowerLimit) < 0) { setErrorInfo((x) => ({ ...x, @@ -91,12 +104,29 @@ export const isAllFilledAndValid = ({ })); hasError = true; } - if (Number(defaultValue) < Number(lowerLimit) || Number(defaultValue) > Number(upperLimit)) { - setErrorInfo((x) => ({ - ...x, - default: 'Default value not in range', - })); - hasError = true; + if (typeof defaultValue === "string") { + if (Number(defaultValue) < Number(lowerLimit) || Number(defaultValue) > Number(upperLimit)) { + setErrorInfo((x) => ({ + ...x, + default: 'Default value not in range', + })); + hasError = true; + } + } else { + if (Number(defaultValue.lower) < Number(lowerLimit) || Number(defaultValue.lower) > Number(upperLimit)) { + setErrorInfo((x) => ({ + ...x, + defaultStart: 'Default value not in range', + })); + hasError = true; + } + if (Number(defaultValue.upper) < Number(lowerLimit) || Number(defaultValue.upper) > Number(upperLimit) || Number(defaultValue.lower) > Number(defaultValue.upper)) { + setErrorInfo((x) => ({ + ...x, + defaultEnd: 'Default value not in range', + })); + hasError = true; + } } if (Number(sliderStep) <= 0) { setErrorInfo((x) => ({ diff --git a/src/helpers/channel/notifSetting.ts b/src/helpers/channel/notifSetting.ts index 8daba1d1c9..2683800193 100644 --- a/src/helpers/channel/notifSetting.ts +++ b/src/helpers/channel/notifSetting.ts @@ -6,8 +6,8 @@ export const notifChannelSettingFormatString = ({ settings }: { settings: Channe 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 })); + ? _notifSettings.push({ enabled: (setting as ChannelSetting & { type: 1 }).default }) + : _notifSettings.push({ value: (setting as ChannelSetting & { type: 2 }).default , enabled: (setting as ChannelSetting & { type: 2 }).enabled })); return _notifSettings; } diff --git a/src/helpers/channel/types.ts b/src/helpers/channel/types.ts index bfd77d74e6..93a5936a48 100644 --- a/src/helpers/channel/types.ts +++ b/src/helpers/channel/types.ts @@ -17,15 +17,17 @@ export type ChannelSetting = } | { type: 3; // Range - defaultStartVal: number; - defaultEndVal: number; + default: { + lower: number; + upper: number; + }; enabled: boolean; description: string; index: number; lowerLimit: number; upperLimit: number; ticker: number; -};; +}; export type UserSetting = | { @@ -45,4 +47,21 @@ export type UserSetting = upperLimit: number; user: number; ticker: number; + } +| { + type: 3; // Range + default: { + lower: number; + upper: number; }; + enabled: boolean; + description: string; + index: number; + lowerLimit: number; + upperLimit: number; + user: { + lower: number; + upper: number; + }; + ticker: number; +}; diff --git a/yarn.lock b/yarn.lock index 93bcf61d73..d7b375f072 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5442,7 +5442,7 @@ __metadata: "@mui/lab": ^5.0.0-alpha.72 "@mui/material": ^5.5.0 "@pushprotocol/ledgerlive": latest - "@pushprotocol/restapi": 1.4.38 + "@pushprotocol/restapi": 1.4.41 "@pushprotocol/socket": 0.5.3 "@pushprotocol/uiweb": 1.1.19 "@reduxjs/toolkit": ^1.7.1 @@ -5643,9 +5643,9 @@ __metadata: languageName: node linkType: hard -"@pushprotocol/restapi@npm:1.4.38": - version: 1.4.38 - resolution: "@pushprotocol/restapi@npm:1.4.38" +"@pushprotocol/restapi@npm:1.4.41": + version: 1.4.41 + resolution: "@pushprotocol/restapi@npm:1.4.41" dependencies: "@ambire/signature-validator": ^1.3.1 "@metamask/eth-sig-util": ^5.0.2 @@ -5666,7 +5666,7 @@ __metadata: viem: ^1.3.0 peerDependencies: ethers: ^5.6.8 - checksum: 25a2159b4bc27a71b3cd9021e2fdeef9b4a61c9302e1357b3ff45e3f3e6615390bd22aa942a2a0cefc6835925a728c1bb7f3eaafd012cf664041b83766cd5ee5 + checksum: bdf64d7d7d89d820f6bfb292c5ec3c8bf999d03dd560e4497977e910deed72ee3ea29c72c9193b37034b990fb2d901b75246829313368aa0c3634faec317802b languageName: node linkType: hard From 1b5c1118e44fdcd992e8fefd4ce89eeaa87f038d Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 30 Nov 2023 14:02:46 +0530 Subject: [PATCH 6/7] Resolve issue of not populating modal in multi-range notif --- .../channel/AddSettingModalContent.tsx | 26 ++++++++++++------- .../reusables/sliders/RangeSlider.tsx | 2 +- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/components/channel/AddSettingModalContent.tsx b/src/components/channel/AddSettingModalContent.tsx index 3814572ba7..da2088f746 100644 --- a/src/components/channel/AddSettingModalContent.tsx +++ b/src/components/channel/AddSettingModalContent.tsx @@ -71,27 +71,31 @@ const AddSettingModalContent = ({ const [settingName, setSettingName] = useState(settingToEdit ? settingToEdit.description : ''); const [isDefault, setIsDefault] = useState( settingToEdit - ? (settingToEdit.type === 1 && settingToEdit.default) || (settingToEdit.type === 2 && settingToEdit.enabled) + ? (settingToEdit.type === 1 && settingToEdit.default) || (settingToEdit.type === 2 && settingToEdit.enabled) || (settingToEdit.type === 3 && settingToEdit.enabled) : true ); - const [isRange, setIsRange] = useState(settingToEdit && settingToEdit.type === 2 ? true : false); + const [isRange, setIsRange] = useState(settingToEdit && (settingToEdit.type === 2 || settingToEdit.type === 3) ? true : false); const [lowerLimit, setLowerLimit] = useState( - settingToEdit && settingToEdit.type === 2 ? settingToEdit.lowerLimit.toString() : '' + settingToEdit && (settingToEdit.type === 2 || settingToEdit.type === 3) ? settingToEdit.lowerLimit.toString() : '' ); const [upperLimit, setUpperLimit] = useState( - settingToEdit && settingToEdit.type === 2 ? settingToEdit.upperLimit.toString() : '' + settingToEdit && (settingToEdit.type === 2 || settingToEdit.type === 3) ? settingToEdit.upperLimit.toString() : '' ); const [sliderStep, setSliderStep] = useState( - settingToEdit && settingToEdit.type === 2 && settingToEdit.ticker ? settingToEdit.ticker.toString() : '1' + settingToEdit && (settingToEdit.type === 2 || settingToEdit.type === 3) && settingToEdit.ticker ? settingToEdit.ticker.toString() : '1' ); - const [enableMultiRange, setEnableMultiRange] = useState(false); + const [enableMultiRange, setEnableMultiRange] = useState( + settingToEdit && settingToEdit.type === 3 ? true : false + ); // for single value slider const [defaultValue, setDefaultValue] = useState( settingToEdit && settingToEdit.type === 2 ? settingToEdit.default.toString() : '' ); - const [sliderPreviewVal, setSliderPreviewVal] = useState(); + const [sliderPreviewVal, setSliderPreviewVal] = useState( + settingToEdit && settingToEdit.type === 2 ? settingToEdit.default : 0 + ); // for range slider const [defaultStartValue, setDefaultStartValue] = useState( @@ -100,8 +104,12 @@ const AddSettingModalContent = ({ const [defaultEndValue, setDefaultEndValue] = useState( settingToEdit && settingToEdit.type === 3 ? settingToEdit.default.upper.toString() : '' ); - const [sliderPreviewStartVal, setSliderPreviewStartVal] = useState(); - const [sliderPreviewEndVal, setSliderPreviewEndVal] = useState(); + const [sliderPreviewStartVal, setSliderPreviewStartVal] = useState( + settingToEdit && settingToEdit.type === 3 ? settingToEdit.default.lower : 0 + ); + const [sliderPreviewEndVal, setSliderPreviewEndVal] = useState( + settingToEdit && settingToEdit.type === 3 ? settingToEdit.default.upper : 0 + ); const [errorInfo, setErrorInfo] = useState(); const theme = useTheme(); diff --git a/src/components/reusables/sliders/RangeSlider.tsx b/src/components/reusables/sliders/RangeSlider.tsx index 9d1551846f..5008f7a4c2 100644 --- a/src/components/reusables/sliders/RangeSlider.tsx +++ b/src/components/reusables/sliders/RangeSlider.tsx @@ -140,7 +140,7 @@ const RangeSlider = ({ } useEffect(() => { - if (thumbStartRef.current && inactiveLeftRef.current) { + if (thumbStartRef.current && inactiveLeftRef.current && thumbEndRef.current && activeRef.current && inactiveRightRef.current) { thumbStartRef.current.style.left = `${((startVal - min) / (max - min)) * 98}%`; inactiveLeftRef.current.style.width = `${((startVal - min) / (max - min)) * 100}%`; activeRef.current.style.width = `${((endVal - startVal) / (max - min)) * 100}%`; From 8369fd3d386238b2472550fbb6005cd22e7a961a Mon Sep 17 00:00:00 2001 From: Nilesh Gupta Date: Thu, 30 Nov 2023 14:33:21 +0530 Subject: [PATCH 7/7] Changes for updating slider --- src/components/channel/AddSettingModalContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/channel/AddSettingModalContent.tsx b/src/components/channel/AddSettingModalContent.tsx index da2088f746..e6f11f10e3 100644 --- a/src/components/channel/AddSettingModalContent.tsx +++ b/src/components/channel/AddSettingModalContent.tsx @@ -196,7 +196,7 @@ const AddSettingModalContent = ({ Number(defaultValue) <= Number(upperLimit)) ) ); - }, [lowerLimit, upperLimit, defaultValue, sliderStep, defaultStartValue, defaultEndValue]); + }, [lowerLimit, upperLimit, defaultValue, sliderStep, defaultStartValue, defaultEndValue, enableMultiRange]); return (