Componentize notification line setting, don't use useEffect
This commit is contained in:
		
							parent
							
								
									c9d323c83f
								
							
						
					
					
						commit
						df3d7b591d
					
				|  | @ -1,11 +1,10 @@ | ||||||
| import { usePrivateUser } from 'web/hooks/use-user' | import React, { memo, ReactNode, useEffect, useState } from 'react' | ||||||
| import React, { ReactNode, useEffect, useState } from 'react' |  | ||||||
| import { LoadingIndicator } from 'web/components/loading-indicator' |  | ||||||
| import { Row } from 'web/components/layout/row' | import { Row } from 'web/components/layout/row' | ||||||
| import clsx from 'clsx' | import clsx from 'clsx' | ||||||
| import { | import { | ||||||
|   notification_subscription_types, |   notification_subscription_types, | ||||||
|   notification_destination_types, |   notification_destination_types, | ||||||
|  |   PrivateUser, | ||||||
| } from 'common/user' | } from 'common/user' | ||||||
| import { updatePrivateUser } from 'web/lib/firebase/users' | import { updatePrivateUser } from 'web/lib/firebase/users' | ||||||
| import { Col } from 'web/components/layout/col' | import { Col } from 'web/components/layout/col' | ||||||
|  | @ -23,21 +22,22 @@ import { | ||||||
|   UsersIcon, |   UsersIcon, | ||||||
| } from '@heroicons/react/outline' | } from '@heroicons/react/outline' | ||||||
| import { WatchMarketModal } from 'web/components/contract/watch-market-modal' | import { WatchMarketModal } from 'web/components/contract/watch-market-modal' | ||||||
| import { filterDefined } from 'common/util/array' |  | ||||||
| import toast from 'react-hot-toast' | import toast from 'react-hot-toast' | ||||||
| import { SwitchSetting } from 'web/components/switch-setting' | import { SwitchSetting } from 'web/components/switch-setting' | ||||||
|  | import { uniq } from 'lodash' | ||||||
|  | import { | ||||||
|  |   storageStore, | ||||||
|  |   usePersistentState, | ||||||
|  | } from 'web/hooks/use-persistent-state' | ||||||
|  | import { safeLocalStorage } from 'web/lib/util/local' | ||||||
| 
 | 
 | ||||||
| export function NotificationSettings(props: { | export function NotificationSettings(props: { | ||||||
|   navigateToSection: string | undefined |   navigateToSection: string | undefined | ||||||
|  |   privateUser: PrivateUser | ||||||
| }) { | }) { | ||||||
|   const { navigateToSection } = props |   const { navigateToSection, privateUser } = props | ||||||
|   const privateUser = usePrivateUser() |  | ||||||
|   const [showWatchModal, setShowWatchModal] = useState(false) |   const [showWatchModal, setShowWatchModal] = useState(false) | ||||||
| 
 | 
 | ||||||
|   if (!privateUser || !privateUser.notificationSubscriptionTypes) { |  | ||||||
|     return <LoadingIndicator spinnerClassName={'border-gray-500 h-4 w-4'} /> |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   const emailsEnabled: Array<keyof notification_subscription_types> = [ |   const emailsEnabled: Array<keyof notification_subscription_types> = [ | ||||||
|     'all_comments_on_watched_markets', |     'all_comments_on_watched_markets', | ||||||
|     'all_replies_to_my_comments_on_watched_markets', |     'all_replies_to_my_comments_on_watched_markets', | ||||||
|  | @ -165,32 +165,29 @@ export function NotificationSettings(props: { | ||||||
|     }, |     }, | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const NotificationSettingLine = ( |   function NotificationSettingLine(props: { | ||||||
|     description: string, |     description: string | ||||||
|     key: keyof notification_subscription_types, |     subscriptionTypeKey: keyof notification_subscription_types | ||||||
|     value: notification_destination_types[] |     destinations: notification_destination_types[] | ||||||
|   ) => { |   }) { | ||||||
|     const previousInAppValue = value.includes('browser') |     const { description, subscriptionTypeKey, destinations } = props | ||||||
|     const previousEmailValue = value.includes('email') |     const previousInAppValue = destinations.includes('browser') | ||||||
|  |     const previousEmailValue = destinations.includes('email') | ||||||
|     const [inAppEnabled, setInAppEnabled] = useState(previousInAppValue) |     const [inAppEnabled, setInAppEnabled] = useState(previousInAppValue) | ||||||
|     const [emailEnabled, setEmailEnabled] = useState(previousEmailValue) |     const [emailEnabled, setEmailEnabled] = useState(previousEmailValue) | ||||||
|     const loading = 'Changing Notifications Settings' |     const loading = 'Changing Notifications Settings' | ||||||
|     const success = 'Changed Notification Settings!' |     const success = 'Changed Notification Settings!' | ||||||
|     const highlight = navigateToSection === key |     const highlight = navigateToSection === subscriptionTypeKey | ||||||
| 
 | 
 | ||||||
|     useEffect(() => { |     const changeSetting = (setting: 'browser' | 'email', newValue: boolean) => { | ||||||
|       if ( |       toast | ||||||
|         inAppEnabled !== previousInAppValue || |         .promise( | ||||||
|         emailEnabled !== previousEmailValue |  | ||||||
|       ) { |  | ||||||
|         toast.promise( |  | ||||||
|           updatePrivateUser(privateUser.id, { |           updatePrivateUser(privateUser.id, { | ||||||
|             notificationSubscriptionTypes: { |             notificationSubscriptionTypes: { | ||||||
|               ...privateUser.notificationSubscriptionTypes, |               ...privateUser.notificationSubscriptionTypes, | ||||||
|               [key]: filterDefined([ |               [subscriptionTypeKey]: destinations.includes(setting) | ||||||
|                 inAppEnabled ? 'browser' : undefined, |                 ? destinations.filter((d) => d !== setting) | ||||||
|                 emailEnabled ? 'email' : undefined, |                 : uniq([...destinations, setting]), | ||||||
|               ]), |  | ||||||
|             }, |             }, | ||||||
|           }), |           }), | ||||||
|           { |           { | ||||||
|  | @ -199,14 +196,14 @@ export function NotificationSettings(props: { | ||||||
|             error: 'Error changing notification settings. Try again?', |             error: 'Error changing notification settings. Try again?', | ||||||
|           } |           } | ||||||
|         ) |         ) | ||||||
|       } |         .then(() => { | ||||||
|     }, [ |           if (setting === 'browser') { | ||||||
|       inAppEnabled, |             setInAppEnabled(newValue) | ||||||
|       emailEnabled, |           } else { | ||||||
|       previousInAppValue, |             setEmailEnabled(newValue) | ||||||
|       previousEmailValue, |           } | ||||||
|       key, |         }) | ||||||
|     ]) |     } | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Row |       <Row | ||||||
|  | @ -220,17 +217,17 @@ export function NotificationSettings(props: { | ||||||
|             <span>{description}</span> |             <span>{description}</span> | ||||||
|           </Row> |           </Row> | ||||||
|           <Row className={'gap-4'}> |           <Row className={'gap-4'}> | ||||||
|             {!browserDisabled.includes(key) && ( |             {!browserDisabled.includes(subscriptionTypeKey) && ( | ||||||
|               <SwitchSetting |               <SwitchSetting | ||||||
|                 checked={inAppEnabled} |                 checked={inAppEnabled} | ||||||
|                 onChange={setInAppEnabled} |                 onChange={(newVal) => changeSetting('browser', newVal)} | ||||||
|                 label={'Web'} |                 label={'Web'} | ||||||
|               /> |               /> | ||||||
|             )} |             )} | ||||||
|             {emailsEnabled.includes(key) && ( |             {emailsEnabled.includes(subscriptionTypeKey) && ( | ||||||
|               <SwitchSetting |               <SwitchSetting | ||||||
|                 checked={emailEnabled} |                 checked={emailEnabled} | ||||||
|                 onChange={setEmailEnabled} |                 onChange={(newVal) => changeSetting('email', newVal)} | ||||||
|                 label={'Email'} |                 label={'Email'} | ||||||
|               /> |               /> | ||||||
|             )} |             )} | ||||||
|  | @ -246,12 +243,22 @@ export function NotificationSettings(props: { | ||||||
|     return privateUser.notificationSubscriptionTypes[key] ?? [] |     return privateUser.notificationSubscriptionTypes[key] ?? [] | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const Section = (icon: ReactNode, data: sectionData) => { |   const Section = memo(function Section(props: { | ||||||
|  |     icon: ReactNode | ||||||
|  |     data: sectionData | ||||||
|  |   }) { | ||||||
|  |     const { icon, data } = props | ||||||
|     const { label, subscriptionTypeToDescription } = data |     const { label, subscriptionTypeToDescription } = data | ||||||
|     const expand = |     const expand = | ||||||
|       navigateToSection && |       navigateToSection && | ||||||
|       Object.keys(subscriptionTypeToDescription).includes(navigateToSection) |       Object.keys(subscriptionTypeToDescription).includes(navigateToSection) | ||||||
|     const [expanded, setExpanded] = useState(expand) | 
 | ||||||
|  |     // Not sure how to prevent re-render (and collapse of an open section)
 | ||||||
|  |     // due to a private user settings change. Just going to persist expanded state here
 | ||||||
|  |     const [expanded, setExpanded] = usePersistentState(expand ?? false, { | ||||||
|  |       key: 'NotificationsSettingsSection-' + label, | ||||||
|  |       store: storageStore(safeLocalStorage()), | ||||||
|  |     }) | ||||||
| 
 | 
 | ||||||
|     // Not working as the default value for expanded, so using a useEffect
 |     // Not working as the default value for expanded, so using a useEffect
 | ||||||
|     useEffect(() => { |     useEffect(() => { | ||||||
|  | @ -278,19 +285,19 @@ export function NotificationSettings(props: { | ||||||
|           )} |           )} | ||||||
|         </Row> |         </Row> | ||||||
|         <Col className={clsx(expanded ? 'block' : 'hidden', 'gap-2 p-2')}> |         <Col className={clsx(expanded ? 'block' : 'hidden', 'gap-2 p-2')}> | ||||||
|           {Object.entries(subscriptionTypeToDescription).map(([key, value]) => |           {Object.entries(subscriptionTypeToDescription).map(([key, value]) => ( | ||||||
|             NotificationSettingLine( |             <NotificationSettingLine | ||||||
|               value, |               subscriptionTypeKey={key as keyof notification_subscription_types} | ||||||
|               key as keyof notification_subscription_types, |               destinations={getUsersSavedPreference( | ||||||
|               getUsersSavedPreference( |  | ||||||
|                 key as keyof notification_subscription_types |                 key as keyof notification_subscription_types | ||||||
|               ) |               )} | ||||||
|             ) |               description={value} | ||||||
|           )} |             /> | ||||||
|  |           ))} | ||||||
|         </Col> |         </Col> | ||||||
|       </Col> |       </Col> | ||||||
|     ) |     ) | ||||||
|   } |   }) | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <div className={'p-2'}> |     <div className={'p-2'}> | ||||||
|  | @ -302,20 +309,38 @@ export function NotificationSettings(props: { | ||||||
|             onClick={() => setShowWatchModal(true)} |             onClick={() => setShowWatchModal(true)} | ||||||
|           /> |           /> | ||||||
|         </Row> |         </Row> | ||||||
|         {Section(<ChatIcon className={'h-6 w-6'} />, comments)} |         <Section icon={<ChatIcon className={'h-6 w-6'} />} data={comments} /> | ||||||
|         {Section(<LightBulbIcon className={'h-6 w-6'} />, answers)} |         <Section | ||||||
|         {Section(<TrendingUpIcon className={'h-6 w-6'} />, updates)} |           icon={<TrendingUpIcon className={'h-6 w-6'} />} | ||||||
|         {Section(<UserIcon className={'h-6 w-6'} />, yourMarkets)} |           data={updates} | ||||||
|  |         /> | ||||||
|  |         <Section | ||||||
|  |           icon={<LightBulbIcon className={'h-6 w-6'} />} | ||||||
|  |           data={answers} | ||||||
|  |         /> | ||||||
|  |         <Section icon={<UserIcon className={'h-6 w-6'} />} data={yourMarkets} /> | ||||||
|         <Row className={'gap-2 text-xl text-gray-700'}> |         <Row className={'gap-2 text-xl text-gray-700'}> | ||||||
|           <span>Balance Changes</span> |           <span>Balance Changes</span> | ||||||
|         </Row> |         </Row> | ||||||
|         {Section(<CurrencyDollarIcon className={'h-6 w-6'} />, bonuses)} |         <Section | ||||||
|         {Section(<CashIcon className={'h-6 w-6'} />, otherBalances)} |           icon={<CurrencyDollarIcon className={'h-6 w-6'} />} | ||||||
|  |           data={bonuses} | ||||||
|  |         /> | ||||||
|  |         <Section | ||||||
|  |           icon={<CashIcon className={'h-6 w-6'} />} | ||||||
|  |           data={otherBalances} | ||||||
|  |         /> | ||||||
|         <Row className={'gap-2 text-xl text-gray-700'}> |         <Row className={'gap-2 text-xl text-gray-700'}> | ||||||
|           <span>General</span> |           <span>General</span> | ||||||
|         </Row> |         </Row> | ||||||
|         {Section(<UsersIcon className={'h-6 w-6'} />, userInteractions)} |         <Section | ||||||
|         {Section(<InboxInIcon className={'h-6 w-6'} />, generalOther)} |           icon={<UsersIcon className={'h-6 w-6'} />} | ||||||
|  |           data={userInteractions} | ||||||
|  |         /> | ||||||
|  |         <Section | ||||||
|  |           icon={<InboxInIcon className={'h-6 w-6'} />} | ||||||
|  |           data={generalOther} | ||||||
|  |         /> | ||||||
|         <WatchMarketModal open={showWatchModal} setOpen={setShowWatchModal} /> |         <WatchMarketModal open={showWatchModal} setOpen={setShowWatchModal} /> | ||||||
|       </Col> |       </Col> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|  | @ -112,6 +112,7 @@ export default function Notifications() { | ||||||
|                   content: ( |                   content: ( | ||||||
|                     <NotificationSettings |                     <NotificationSettings | ||||||
|                       navigateToSection={navigateToSection} |                       navigateToSection={navigateToSection} | ||||||
|  |                       privateUser={privateUser} | ||||||
|                     /> |                     /> | ||||||
|                   ), |                   ), | ||||||
|                 }, |                 }, | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user