Skip to content

Commit

Permalink
Opt-In Dropdown for dashboard (#1802)
Browse files Browse the repository at this point in the history
* Opt-In Dropdown for dashboard

* Fixed the comments and issues

* Fixed the context issue
  • Loading branch information
abhishek-01k authored Aug 12, 2024
1 parent 970da38 commit a0a3ddd
Show file tree
Hide file tree
Showing 21 changed files with 799 additions and 91 deletions.
5 changes: 3 additions & 2 deletions src/blocks/dropdown/Dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import styled from 'styled-components';
import * as RadixDropdown from '@radix-ui/react-dropdown-menu';
import { DropdownProps } from './Dropdown.types';

const RadixDropdownContent = styled(RadixDropdown.Content)<DropdownProps>`
const RadixDropdownContent = styled(RadixDropdown.Content) <DropdownProps>`
/* Extra CSS props */
${(props) => props.css || ''}
`;
Expand Down Expand Up @@ -40,7 +40,8 @@ const Dropdown: FC<DropdownProps> = forwardRef<HTMLButtonElement, DropdownProps>
onPointerDownOutside={() => hideDropdown()}
{...props}
>
{overlay}
{typeof overlay === 'function' ? overlay(setIsOpen) : overlay}

</RadixDropdownContent>
</RadixDropdown.Portal>
</RadixDropdown.Root>
Expand Down
2 changes: 1 addition & 1 deletion src/blocks/dropdown/Dropdown.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export type DropdownComponentProps = {
// This is used for custom css instead of style prop, check Box/Text component
css?: FlattenSimpleInterpolation;
// This will be the contents of the dropdown overlay
overlay?: ReactNode;
overlay?: ReactNode | ((setIsOpen: (isOpen: boolean) => void) => ReactNode);
};

export type DropdownProps = DropdownComponentProps & DropdownMenuContentProps;
187 changes: 187 additions & 0 deletions src/common/components/ManageSettingsDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import { FC, useState } from 'react';

import { Box, Button, Separator, Text, ToggleSwitch } from 'blocks';

import InputSlider from 'components/reusables/sliders/InputSlider';
import RangeSlider from 'components/reusables/sliders/RangeSlider';

import { UserSetting } from 'helpers/channel/types';

type NotificationSettingsDropdownProps = {
userSetting: UserSetting[];
updateNotificationSettings: (setting: UserSetting[]) => Promise<void>;
updatingNotificationSettings: boolean;
unsubscribing: boolean;
unsubscribe: () => void;
};

const ManageSettingsDropdown: FC<NotificationSettingsDropdownProps> = ({
userSetting,
updateNotificationSettings,
updatingNotificationSettings,
unsubscribing,
unsubscribe
}) => {
const [modifiedSettings, setModifiedSettings] = useState([...userSetting]);

const handleSliderChange = (index: number, value: number | { lower: number; upper: number }) => {
const updatedSettings = [...modifiedSettings];
updatedSettings[index].user = value;
setModifiedSettings(updatedSettings);
};

const handleSwitchChange = (index: number) => {
const updatedSettings = [...modifiedSettings];
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 if (updatedSettings[index].type === 2) {
// 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;
} else {
// Type 3
// Use a type guard to narrow the type to UserSetting of type 2
const setting = updatedSettings[index] as UserSetting & { type: 3 };
setting.enabled = !setting.enabled;
}
setModifiedSettings(updatedSettings);
};

const handleUpdateNotificationSettings = () => {
updateNotificationSettings(modifiedSettings);
};

const handleOptOut = async () => {
unsubscribe();
};

return (
<Box
display="flex"
flexDirection="column"
alignItems="flex-start"
padding="spacing-xs"
border="border-sm solid stroke-secondary"
backgroundColor="surface-primary"
borderRadius="radius-sm"
>
<Box
display="flex"
flexDirection="column"
padding="spacing-none spacing-xs"
gap="spacing-xxs"
alignItems="center"
>
<Box display="flex" flexDirection="column" width="-webkit-fill-available">
{modifiedSettings.map((setting, index) => {
return (
<Box key={index}>
<Box
display="flex"
flexDirection="column"
padding="spacing-xs spacing-none"
gap="spacing-xxs"
alignSelf="stretch"
alignItems="flex-start"
>
<Box display="flex" flexDirection="row" justifyContent="space-between" alignSelf="stretch">
<Text variant="h6-bold" color="text-primary" textTransform="capitalize">
{setting.description}
</Text>
<ToggleSwitch
checked={setting.type === 1 ? setting.user : setting.enabled}
onCheckedChange={() => handleSwitchChange(index)}
/>
</Box>
{setting.type === 2 && setting.enabled === true && (
<Box
display="flex"
flexDirection="column"
gap="spacing-xxs"
alignItems="flex-start"
alignSelf="stretch"
>
<Text variant="h6-bold" color="text-primary" textTransform="capitalize">
{setting.user || setting.default}
</Text>
<InputSlider
val={setting.user}
max={setting.upperLimit}
min={setting.lowerLimit}
step={setting.ticker || 1}
defaultVal={setting.default}
onChange={({ x }) => handleSliderChange(index, x)}
/>
</Box>
)}

{setting.type === 3 && setting.enabled === true && (
<Box
display="flex"
flexDirection="column"
gap="spacing-xxs"
alignItems="flex-start"
alignSelf="stretch"
>
<Text variant="h6-bold" color="text-primary" textTransform="capitalize">
{setting.user.lower || setting.default.lower} - {setting.user.upper || setting.default.upper}
</Text>
<RangeSlider
startVal={setting.user.lower || setting.default.lower}
endVal={setting.user.upper || setting.default.upper}
max={setting.upperLimit}
min={setting.lowerLimit}
step={setting.ticker || 1}
defaultStartVal={setting.default.lower}
defaultEndVal={setting.default.upper}
onChange={({ startVal, endVal }) =>
handleSliderChange(index, { lower: startVal, upper: endVal })
}
/>
</Box>
)}
</Box>
<Separator />
</Box>
);
})}
</Box>

<Box
display="flex"
gap="spacing-xxs"
alignSelf="stretch"
alignItems="center"
justifyContent="flex-end"
flexDirection="column"
>
<Text color="text-tertiary" variant="bes-regular">
You will receive all important updates from this channel.
</Text>
<Box display="flex" flexDirection="column" gap="spacing-md" alignItems="center" width="100%">
<Button
size="small"
variant="primary"
onClick={handleUpdateNotificationSettings}
block
loading={updatingNotificationSettings}
>
{updatingNotificationSettings ? 'Updating' : 'Update Preferences'}
</Button>
<Box width="100%" cursor="pointer" onClick={handleOptOut}>
<Text textAlign="center" variant="bs-semibold">
{unsubscribing ? 'Unsubscribing' : 'Unsubscribe'}
</Text>
</Box>
</Box>
</Box>
</Box>
</Box>
);
};

export { ManageSettingsDropdown };
172 changes: 172 additions & 0 deletions src/common/components/NotificationSettingsDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import { FC, useState } from 'react';

import { Box, Button, Separator, Text, ToggleSwitch } from 'blocks';

import InputSlider from 'components/reusables/sliders/InputSlider';
import RangeSlider from 'components/reusables/sliders/RangeSlider';

import { ChannelSetting } from 'helpers/channel/types';

type NotificationSettingsDropdownProps = {
// TODO: Change this to the new channel Settings type
channelSettings: ChannelSetting[];
optInHandler: (channelSettings: ChannelSetting[]) => Promise<void>;
loading: boolean;
onClose: () => void;
};

const NotificationSettingsDropdown: FC<NotificationSettingsDropdownProps> = ({
optInHandler,
channelSettings,
loading,
onClose
}) => {
const [modifiedSettings, setModifiedSettings] = useState([...channelSettings]);

const handleSliderChange = (index: number, value: number | { lower: number; upper: number }) => {
const updatedSettings = [...modifiedSettings];
updatedSettings[index].default = value;
setModifiedSettings(updatedSettings);
};

const handleSwitchChange = (index: number) => {
const updatedSettings = [...modifiedSettings];
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);
};

const handleOptIn = async () => {
await optInHandler(modifiedSettings);
};

return (
<Box
display="flex"
flexDirection="column"
alignItems="flex-start"
padding="spacing-xs"
border="border-sm solid stroke-secondary"
backgroundColor="surface-primary"
borderRadius="radius-sm"
>
<Box
display="flex"
flexDirection="column"
padding="spacing-none spacing-xs"
gap="spacing-xxs"
alignItems="center"
alignSelf="stretch"
>
<Box display="flex" flexDirection="column" width="-webkit-fill-available">
{modifiedSettings.map((setting, index) => {
return (
<Box key={index}>
<Box
display="flex"
flexDirection="column"
padding="spacing-xs spacing-none"
gap="spacing-xxs"
alignSelf="stretch"
alignItems="flex-start"
>
<Box display="flex" flexDirection="row" justifyContent="space-between" alignSelf="stretch">
<Text variant="h6-bold" color="text-primary" textTransform="capitalize">
{setting.description}
</Text>
<ToggleSwitch
checked={setting.type === 1 ? setting.default : setting.enabled}
onCheckedChange={() => handleSwitchChange(index)}
/>
</Box>
{setting.type === 2 && setting.enabled === true && (
<Box
display="flex"
flexDirection="column"
gap="spacing-xxs"
alignItems="flex-start"
alignSelf="stretch"
>
<Text variant="h6-bold" color="text-primary" textTransform="capitalize">
{setting.default}
</Text>
<InputSlider
val={setting.default}
max={setting.upperLimit}
min={setting.lowerLimit}
step={setting.ticker || 1}
defaultVal={setting.default}
onChange={({ x }) => handleSliderChange(index, x)}
/>
</Box>
)}

{setting.type === 3 && setting.enabled === true && (
<Box
display="flex"
flexDirection="column"
gap="spacing-xxs"
alignItems="flex-start"
alignSelf="stretch"
>
<Text variant="h6-bold" color="text-primary" textTransform="capitalize">
{setting.default.lower} - {setting.default.upper}
</Text>
<RangeSlider
startVal={setting.default.lower}
endVal={setting.default.upper}
max={setting.upperLimit}
min={setting.lowerLimit}
step={setting.ticker || 1}
defaultStartVal={setting.default.lower}
defaultEndVal={setting.default.upper}
onChange={({ startVal, endVal }) =>
handleSliderChange(index, { lower: startVal, upper: endVal })
}
/>
</Box>
)}
</Box>
<Separator />
</Box>
);
})}
</Box>

<Box
display="flex"
gap="spacing-xxs"
alignSelf="stretch"
alignItems="center"
justifyContent="flex-end"
flexDirection="column"
>
<Text color="text-tertiary" variant="bes-regular">
You will receive all important updates from this channel.
</Text>
<Box display="flex" flexDirection="column" gap="spacing-md" alignItems="center" width="100%">
<Button size="small" variant="primary" onClick={handleOptIn} block loading={loading}>
{loading ? 'Subscribing' : 'Subscribe'}
</Button>
<Box width="100%" cursor="pointer" onClick={onClose}>
<Text textAlign="center" variant="bs-semibold">
Cancel
</Text>
</Box>
</Box>
</Box>
</Box>
</Box>
);
};

export { NotificationSettingsDropdown };
Loading

0 comments on commit a0a3ddd

Please sign in to comment.