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') {
|
||||||
|
setInAppEnabled(newValue)
|
||||||
|
} else {
|
||||||
|
setEmailEnabled(newValue)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}, [
|
|
||||||
inAppEnabled,
|
|
||||||
emailEnabled,
|
|
||||||
previousInAppValue,
|
|
||||||
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