Notifications Settings page working
This commit is contained in:
parent
87060488f5
commit
e18ac28675
|
@ -64,8 +64,48 @@ export type PrivateUser = {
|
||||||
initialIpAddress?: string
|
initialIpAddress?: string
|
||||||
apiKey?: string
|
apiKey?: string
|
||||||
notificationPreferences?: notification_subscribe_types
|
notificationPreferences?: notification_subscribe_types
|
||||||
|
notificationSubscriptionTypes: exhaustive_notification_subscribe_types
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type notification_receive_types = 'email' | 'browser'
|
||||||
|
|
||||||
|
export type exhaustive_notification_subscribe_types = {
|
||||||
|
// Watched Markets
|
||||||
|
all_comments: notification_receive_types[] // Email currently - seems bad
|
||||||
|
all_answers: notification_receive_types[] // Email currently - seems bad
|
||||||
|
|
||||||
|
// Comments
|
||||||
|
tipped_comments: notification_receive_types[] // Email
|
||||||
|
comments_by_followed_users: notification_receive_types[]
|
||||||
|
all_replies_to_my_comments: notification_receive_types[] // Email
|
||||||
|
all_replies_to_my_answers: notification_receive_types[] // Email
|
||||||
|
|
||||||
|
// Answers
|
||||||
|
answers_by_followed_users: notification_receive_types[]
|
||||||
|
answers_by_market_creator: notification_receive_types[]
|
||||||
|
|
||||||
|
// On users' markets
|
||||||
|
my_markets_closed: notification_receive_types[] // Email, Recommended
|
||||||
|
all_comments_on_my_markets: notification_receive_types[] // Email
|
||||||
|
all_answers_on_my_markets: notification_receive_types[] // Email
|
||||||
|
|
||||||
|
// Market updates
|
||||||
|
resolutions: notification_receive_types[] // Email
|
||||||
|
market_updates: notification_receive_types[]
|
||||||
|
probability_updates: notification_receive_types[] // Email - would want persistent changes only though
|
||||||
|
|
||||||
|
// Balance Changes
|
||||||
|
loans: notification_receive_types[]
|
||||||
|
betting_streaks: notification_receive_types[]
|
||||||
|
referral_bonuses: notification_receive_types[]
|
||||||
|
unique_bettor_bonuses: notification_receive_types[]
|
||||||
|
|
||||||
|
// General
|
||||||
|
user_tagged_you: notification_receive_types[] // Email
|
||||||
|
new_markets_by_followed_users: notification_receive_types[] // Email
|
||||||
|
trending_markets: notification_receive_types[] // Email
|
||||||
|
profit_loss_updates: notification_receive_types[] // Email
|
||||||
|
}
|
||||||
export type notification_subscribe_types = 'all' | 'less' | 'none'
|
export type notification_subscribe_types = 'all' | 'less' | 'none'
|
||||||
|
|
||||||
export type PortfolioMetrics = {
|
export type PortfolioMetrics = {
|
||||||
|
|
|
@ -75,7 +75,7 @@ service cloud.firestore {
|
||||||
allow read: if userId == request.auth.uid || isAdmin();
|
allow read: if userId == request.auth.uid || isAdmin();
|
||||||
allow update: if (userId == request.auth.uid || isAdmin())
|
allow update: if (userId == request.auth.uid || isAdmin())
|
||||||
&& request.resource.data.diff(resource.data).affectedKeys()
|
&& request.resource.data.diff(resource.data).affectedKeys()
|
||||||
.hasOnly(['apiKey', 'unsubscribedFromResolutionEmails', 'unsubscribedFromCommentEmails', 'unsubscribedFromAnswerEmails', 'notificationPreferences', 'unsubscribedFromWeeklyTrendingEmails' ]);
|
.hasOnly(['apiKey', 'unsubscribedFromResolutionEmails', 'unsubscribedFromCommentEmails', 'unsubscribedFromAnswerEmails', 'notificationPreferences', 'unsubscribedFromWeeklyTrendingEmails','notificationSubscriptionTypes' ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
match /private-users/{userId}/views/{viewId} {
|
match /private-users/{userId}/views/{viewId} {
|
||||||
|
|
|
@ -1,236 +0,0 @@
|
||||||
import { useUser } from 'web/hooks/use-user'
|
|
||||||
import React, { useEffect, useState } from 'react'
|
|
||||||
import { notification_subscribe_types, PrivateUser } from 'common/user'
|
|
||||||
import { listenForPrivateUser, updatePrivateUser } from 'web/lib/firebase/users'
|
|
||||||
import toast from 'react-hot-toast'
|
|
||||||
import { track } from '@amplitude/analytics-browser'
|
|
||||||
import { LoadingIndicator } from 'web/components/loading-indicator'
|
|
||||||
import { Row } from 'web/components/layout/row'
|
|
||||||
import clsx from 'clsx'
|
|
||||||
import { CheckIcon, XIcon } from '@heroicons/react/outline'
|
|
||||||
import { ChoicesToggleGroup } from 'web/components/choices-toggle-group'
|
|
||||||
import { Col } from 'web/components/layout/col'
|
|
||||||
import { FollowMarketModal } from 'web/components/contract/follow-market-modal'
|
|
||||||
|
|
||||||
export function NotificationSettings() {
|
|
||||||
const user = useUser()
|
|
||||||
const [notificationSettings, setNotificationSettings] =
|
|
||||||
useState<notification_subscribe_types>('all')
|
|
||||||
const [emailNotificationSettings, setEmailNotificationSettings] =
|
|
||||||
useState<notification_subscribe_types>('all')
|
|
||||||
const [privateUser, setPrivateUser] = useState<PrivateUser | null>(null)
|
|
||||||
const [showModal, setShowModal] = useState(false)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (user) listenForPrivateUser(user.id, setPrivateUser)
|
|
||||||
}, [user])
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!privateUser) return
|
|
||||||
if (privateUser.notificationPreferences) {
|
|
||||||
setNotificationSettings(privateUser.notificationPreferences)
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
privateUser.unsubscribedFromResolutionEmails &&
|
|
||||||
privateUser.unsubscribedFromCommentEmails &&
|
|
||||||
privateUser.unsubscribedFromAnswerEmails
|
|
||||||
) {
|
|
||||||
setEmailNotificationSettings('none')
|
|
||||||
} else if (
|
|
||||||
!privateUser.unsubscribedFromResolutionEmails &&
|
|
||||||
!privateUser.unsubscribedFromCommentEmails &&
|
|
||||||
!privateUser.unsubscribedFromAnswerEmails
|
|
||||||
) {
|
|
||||||
setEmailNotificationSettings('all')
|
|
||||||
} else {
|
|
||||||
setEmailNotificationSettings('less')
|
|
||||||
}
|
|
||||||
}, [privateUser])
|
|
||||||
|
|
||||||
const loading = 'Changing Notifications Settings'
|
|
||||||
const success = 'Notification Settings Changed!'
|
|
||||||
function changeEmailNotifications(newValue: notification_subscribe_types) {
|
|
||||||
if (!privateUser) return
|
|
||||||
if (newValue === 'all') {
|
|
||||||
toast.promise(
|
|
||||||
updatePrivateUser(privateUser.id, {
|
|
||||||
unsubscribedFromResolutionEmails: false,
|
|
||||||
unsubscribedFromCommentEmails: false,
|
|
||||||
unsubscribedFromAnswerEmails: false,
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
loading,
|
|
||||||
success,
|
|
||||||
error: (err) => `${err.message}`,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else if (newValue === 'less') {
|
|
||||||
toast.promise(
|
|
||||||
updatePrivateUser(privateUser.id, {
|
|
||||||
unsubscribedFromResolutionEmails: false,
|
|
||||||
unsubscribedFromCommentEmails: true,
|
|
||||||
unsubscribedFromAnswerEmails: true,
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
loading,
|
|
||||||
success,
|
|
||||||
error: (err) => `${err.message}`,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
} else if (newValue === 'none') {
|
|
||||||
toast.promise(
|
|
||||||
updatePrivateUser(privateUser.id, {
|
|
||||||
unsubscribedFromResolutionEmails: true,
|
|
||||||
unsubscribedFromCommentEmails: true,
|
|
||||||
unsubscribedFromAnswerEmails: true,
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
loading,
|
|
||||||
success,
|
|
||||||
error: (err) => `${err.message}`,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function changeInAppNotificationSettings(
|
|
||||||
newValue: notification_subscribe_types
|
|
||||||
) {
|
|
||||||
if (!privateUser) return
|
|
||||||
track('In-App Notification Preferences Changed', {
|
|
||||||
newPreference: newValue,
|
|
||||||
oldPreference: privateUser.notificationPreferences,
|
|
||||||
})
|
|
||||||
toast.promise(
|
|
||||||
updatePrivateUser(privateUser.id, {
|
|
||||||
notificationPreferences: newValue,
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
loading,
|
|
||||||
success,
|
|
||||||
error: (err) => `${err.message}`,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (privateUser && privateUser.notificationPreferences)
|
|
||||||
setNotificationSettings(privateUser.notificationPreferences)
|
|
||||||
else setNotificationSettings('all')
|
|
||||||
}, [privateUser])
|
|
||||||
|
|
||||||
if (!privateUser) {
|
|
||||||
return <LoadingIndicator spinnerClassName={'border-gray-500 h-4 w-4'} />
|
|
||||||
}
|
|
||||||
|
|
||||||
function NotificationSettingLine(props: {
|
|
||||||
label: string | React.ReactNode
|
|
||||||
highlight: boolean
|
|
||||||
onClick?: () => void
|
|
||||||
}) {
|
|
||||||
const { label, highlight, onClick } = props
|
|
||||||
return (
|
|
||||||
<Row
|
|
||||||
className={clsx(
|
|
||||||
'my-1 gap-1 text-gray-300',
|
|
||||||
highlight && '!text-black',
|
|
||||||
onClick ? 'cursor-pointer' : ''
|
|
||||||
)}
|
|
||||||
onClick={onClick}
|
|
||||||
>
|
|
||||||
{highlight ? <CheckIcon height={20} /> : <XIcon height={20} />}
|
|
||||||
{label}
|
|
||||||
</Row>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={'p-2'}>
|
|
||||||
<div>In App Notifications</div>
|
|
||||||
<ChoicesToggleGroup
|
|
||||||
currentChoice={notificationSettings}
|
|
||||||
choicesMap={{ All: 'all', Less: 'less', None: 'none' }}
|
|
||||||
setChoice={(choice) =>
|
|
||||||
changeInAppNotificationSettings(
|
|
||||||
choice as notification_subscribe_types
|
|
||||||
)
|
|
||||||
}
|
|
||||||
className={'col-span-4 p-2'}
|
|
||||||
toggleClassName={'w-24'}
|
|
||||||
/>
|
|
||||||
<div className={'mt-4 text-sm'}>
|
|
||||||
<Col className={''}>
|
|
||||||
<Row className={'my-1'}>
|
|
||||||
You will receive notifications for these general events:
|
|
||||||
</Row>
|
|
||||||
<NotificationSettingLine
|
|
||||||
highlight={notificationSettings !== 'none'}
|
|
||||||
label={"Income & referral bonuses you've received"}
|
|
||||||
/>
|
|
||||||
<Row className={'my-1'}>
|
|
||||||
You will receive new comment, answer, & resolution notifications on
|
|
||||||
questions:
|
|
||||||
</Row>
|
|
||||||
<NotificationSettingLine
|
|
||||||
highlight={notificationSettings !== 'none'}
|
|
||||||
label={
|
|
||||||
<span>
|
|
||||||
That <span className={'font-bold'}>you watch </span>- you
|
|
||||||
auto-watch questions if:
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
onClick={() => setShowModal(true)}
|
|
||||||
/>
|
|
||||||
<Col
|
|
||||||
className={clsx(
|
|
||||||
'mb-2 ml-8',
|
|
||||||
'gap-1 text-gray-300',
|
|
||||||
notificationSettings !== 'none' && '!text-black'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Row>• You create it</Row>
|
|
||||||
<Row>• You bet, comment on, or answer it</Row>
|
|
||||||
<Row>• You add liquidity to it</Row>
|
|
||||||
<Row>
|
|
||||||
• If you select 'Less' and you've commented on or answered a
|
|
||||||
question, you'll only receive notification on direct replies to
|
|
||||||
your comments or answers
|
|
||||||
</Row>
|
|
||||||
</Col>
|
|
||||||
</Col>
|
|
||||||
</div>
|
|
||||||
<div className={'mt-4'}>Email Notifications</div>
|
|
||||||
<ChoicesToggleGroup
|
|
||||||
currentChoice={emailNotificationSettings}
|
|
||||||
choicesMap={{ All: 'all', Less: 'less', None: 'none' }}
|
|
||||||
setChoice={(choice) =>
|
|
||||||
changeEmailNotifications(choice as notification_subscribe_types)
|
|
||||||
}
|
|
||||||
className={'col-span-4 p-2'}
|
|
||||||
toggleClassName={'w-24'}
|
|
||||||
/>
|
|
||||||
<div className={'mt-4 text-sm'}>
|
|
||||||
<div>
|
|
||||||
You will receive emails for:
|
|
||||||
<NotificationSettingLine
|
|
||||||
label={"Resolution of questions you're betting on"}
|
|
||||||
highlight={emailNotificationSettings !== 'none'}
|
|
||||||
/>
|
|
||||||
<NotificationSettingLine
|
|
||||||
label={'Closure of your questions'}
|
|
||||||
highlight={emailNotificationSettings !== 'none'}
|
|
||||||
/>
|
|
||||||
<NotificationSettingLine
|
|
||||||
label={'Activity on your questions'}
|
|
||||||
highlight={emailNotificationSettings === 'all'}
|
|
||||||
/>
|
|
||||||
<NotificationSettingLine
|
|
||||||
label={"Activity on questions you've answered or commented on"}
|
|
||||||
highlight={emailNotificationSettings === 'all'}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<FollowMarketModal setOpen={setShowModal} open={showModal} />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -4,7 +4,7 @@ import { EyeIcon } from '@heroicons/react/outline'
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
|
||||||
export const FollowMarketModal = (props: {
|
export const WatchMarketModal = (props: {
|
||||||
open: boolean
|
open: boolean
|
||||||
setOpen: (b: boolean) => void
|
setOpen: (b: boolean) => void
|
||||||
title?: string
|
title?: string
|
||||||
|
@ -18,20 +18,21 @@ export const FollowMarketModal = (props: {
|
||||||
<Col className={'gap-2'}>
|
<Col className={'gap-2'}>
|
||||||
<span className={'text-indigo-700'}>• What is watching?</span>
|
<span className={'text-indigo-700'}>• What is watching?</span>
|
||||||
<span className={'ml-2'}>
|
<span className={'ml-2'}>
|
||||||
You can receive notifications on questions you're interested in by
|
You'll receive notifications on markets by betting, commenting, or
|
||||||
clicking the
|
clicking the
|
||||||
<EyeIcon
|
<EyeIcon
|
||||||
className={clsx('ml-1 inline h-6 w-6 align-top')}
|
className={clsx('ml-1 inline h-6 w-6 align-top')}
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
/>
|
/>
|
||||||
️ button on a question.
|
️ button on them.
|
||||||
</span>
|
</span>
|
||||||
<span className={'text-indigo-700'}>
|
<span className={'text-indigo-700'}>
|
||||||
• What types of notifications will I receive?
|
• What types of notifications will I receive?
|
||||||
</span>
|
</span>
|
||||||
<span className={'ml-2'}>
|
<span className={'ml-2'}>
|
||||||
You'll receive in-app notifications for new comments, answers, and
|
You'll receive notifications for new comments, answers, and updates
|
||||||
updates to the question.
|
to the question. See the notifications settings pages to customize
|
||||||
|
which types of notifications you receive on watched markets.
|
||||||
</span>
|
</span>
|
||||||
</Col>
|
</Col>
|
||||||
</Col>
|
</Col>
|
|
@ -11,7 +11,7 @@ import { User } from 'common/user'
|
||||||
import { useContractFollows } from 'web/hooks/use-follows'
|
import { useContractFollows } from 'web/hooks/use-follows'
|
||||||
import { firebaseLogin, updateUser } from 'web/lib/firebase/users'
|
import { firebaseLogin, updateUser } from 'web/lib/firebase/users'
|
||||||
import { track } from 'web/lib/service/analytics'
|
import { track } from 'web/lib/service/analytics'
|
||||||
import { FollowMarketModal } from 'web/components/contract/follow-market-modal'
|
import { WatchMarketModal } from 'web/components/contract/follow-market-modal'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { Col } from 'web/components/layout/col'
|
import { Col } from 'web/components/layout/col'
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ export const FollowMarketButton = (props: {
|
||||||
Watch
|
Watch
|
||||||
</Col>
|
</Col>
|
||||||
)}
|
)}
|
||||||
<FollowMarketModal
|
<WatchMarketModal
|
||||||
open={open}
|
open={open}
|
||||||
setOpen={setOpen}
|
setOpen={setOpen}
|
||||||
title={`You ${
|
title={`You ${
|
||||||
|
|
379
web/components/notification-settings.tsx
Normal file
379
web/components/notification-settings.tsx
Normal file
|
@ -0,0 +1,379 @@
|
||||||
|
import { usePrivateUser } from 'web/hooks/use-user'
|
||||||
|
import React, { ReactNode, useEffect, useState } from 'react'
|
||||||
|
import { LoadingIndicator } from 'web/components/loading-indicator'
|
||||||
|
import { Row } from 'web/components/layout/row'
|
||||||
|
import clsx from 'clsx'
|
||||||
|
import {
|
||||||
|
exhaustive_notification_subscribe_types,
|
||||||
|
notification_receive_types,
|
||||||
|
} from 'common/user'
|
||||||
|
import { updatePrivateUser } from 'web/lib/firebase/users'
|
||||||
|
import { Switch } from '@headlessui/react'
|
||||||
|
import { Col } from 'web/components/layout/col'
|
||||||
|
import {
|
||||||
|
AdjustmentsIcon,
|
||||||
|
CashIcon,
|
||||||
|
ChatIcon,
|
||||||
|
ChevronDownIcon,
|
||||||
|
ChevronUpIcon,
|
||||||
|
InformationCircleIcon,
|
||||||
|
LightBulbIcon,
|
||||||
|
TrendingUpIcon,
|
||||||
|
UserIcon,
|
||||||
|
} from '@heroicons/react/outline'
|
||||||
|
import { WatchMarketModal } from 'web/components/contract/watch-market-modal'
|
||||||
|
import { filterDefined } from 'common/util/array'
|
||||||
|
import toast from 'react-hot-toast'
|
||||||
|
|
||||||
|
export function NotificationSettings() {
|
||||||
|
const privateUser = usePrivateUser()
|
||||||
|
const [showWatchModal, setShowWatchModal] = useState(false)
|
||||||
|
const prevPref = privateUser?.notificationPreferences
|
||||||
|
const browserOnly = ['browser']
|
||||||
|
const emailOnly = ['email']
|
||||||
|
const both = ['email', 'browser']
|
||||||
|
const wantsLess = prevPref === 'less'
|
||||||
|
const wantsAll = prevPref === 'all'
|
||||||
|
|
||||||
|
const constructPref = (browserIf: boolean, emailIf: boolean | undefined) => {
|
||||||
|
const browser = browserIf ? 'browser' : undefined
|
||||||
|
const email = emailIf ? 'email' : undefined
|
||||||
|
return filterDefined([browser, email]) as notification_receive_types[]
|
||||||
|
}
|
||||||
|
if (privateUser && !privateUser.notificationSubscriptionTypes) {
|
||||||
|
updatePrivateUser(privateUser.id, {
|
||||||
|
notificationSubscriptionTypes: {
|
||||||
|
// Watched Markets
|
||||||
|
all_comments: constructPref(
|
||||||
|
wantsAll,
|
||||||
|
!privateUser.unsubscribedFromCommentEmails
|
||||||
|
),
|
||||||
|
all_answers: constructPref(
|
||||||
|
wantsAll,
|
||||||
|
!privateUser.unsubscribedFromAnswerEmails
|
||||||
|
),
|
||||||
|
|
||||||
|
// Comments
|
||||||
|
tipped_comments: constructPref(wantsAll || wantsLess, true),
|
||||||
|
comments_by_followed_users: constructPref(wantsAll, false), //wantsAll ? browserOnly : none,
|
||||||
|
all_replies_to_my_comments: constructPref(wantsAll || wantsLess, true), //wantsAll || wantsLess ? both : none,
|
||||||
|
all_replies_to_my_answers: constructPref(wantsAll || wantsLess, true), //wantsAll || wantsLess ? both : none,
|
||||||
|
|
||||||
|
// Answers
|
||||||
|
answers_by_followed_users: constructPref(
|
||||||
|
wantsAll || wantsLess,
|
||||||
|
!privateUser.unsubscribedFromAnswerEmails
|
||||||
|
), //wantsAll || wantsLess ? both : none,
|
||||||
|
answers_by_market_creator: constructPref(
|
||||||
|
wantsAll || wantsLess,
|
||||||
|
!privateUser.unsubscribedFromAnswerEmails
|
||||||
|
), //wantsAll || wantsLess ? both : none,
|
||||||
|
|
||||||
|
// On users' markets
|
||||||
|
my_markets_closed: constructPref(
|
||||||
|
wantsAll || wantsLess,
|
||||||
|
!privateUser.unsubscribedFromResolutionEmails
|
||||||
|
), //wantsAll || wantsLess ? both : none, // High priority
|
||||||
|
all_comments_on_my_markets: constructPref(wantsAll || wantsLess, true), //wantsAll || wantsLess ? both : none,
|
||||||
|
all_answers_on_my_markets: constructPref(wantsAll || wantsLess, true), //wantsAll || wantsLess ? both : none,
|
||||||
|
|
||||||
|
// Market updates
|
||||||
|
resolutions: constructPref(wantsAll || wantsLess, true),
|
||||||
|
market_updates: constructPref(wantsAll || wantsLess, false),
|
||||||
|
|
||||||
|
//Balance Changes
|
||||||
|
loans: browserOnly,
|
||||||
|
betting_streaks: browserOnly,
|
||||||
|
referral_bonuses: both,
|
||||||
|
unique_bettor_bonuses: browserOnly,
|
||||||
|
|
||||||
|
// General
|
||||||
|
user_tagged_you: constructPref(wantsAll || wantsLess, true), //wantsAll || wantsLess ? both : none,
|
||||||
|
new_markets_by_followed_users: constructPref(
|
||||||
|
wantsAll || wantsLess,
|
||||||
|
true
|
||||||
|
), //wantsAll || wantsLess ? both : none,
|
||||||
|
trending_markets: constructPref(
|
||||||
|
false,
|
||||||
|
!privateUser.unsubscribedFromWeeklyTrendingEmails
|
||||||
|
),
|
||||||
|
profit_loss_updates: emailOnly,
|
||||||
|
} as exhaustive_notification_subscribe_types,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!privateUser || !privateUser.notificationSubscriptionTypes) {
|
||||||
|
return <LoadingIndicator spinnerClassName={'border-gray-500 h-4 w-4'} />
|
||||||
|
}
|
||||||
|
|
||||||
|
const emailsEnabled = [
|
||||||
|
'all_comments',
|
||||||
|
'all_answers',
|
||||||
|
'resolutions',
|
||||||
|
'all_replies_to_my_comments',
|
||||||
|
'all_replies_to_my_answers',
|
||||||
|
'all_comments_on_my_markets',
|
||||||
|
'all_answers_on_my_markets',
|
||||||
|
'my_markets_closed',
|
||||||
|
'probability_updates',
|
||||||
|
'user_tagged_you',
|
||||||
|
'new_markets_by_followed_users',
|
||||||
|
'trending_markets',
|
||||||
|
'profit_loss_updates',
|
||||||
|
]
|
||||||
|
const browserDisabled = ['trending_markets', 'profit_loss_updates']
|
||||||
|
|
||||||
|
const watched_markets_explanations_comments: {
|
||||||
|
[key in keyof Partial<exhaustive_notification_subscribe_types>]: string
|
||||||
|
} = {
|
||||||
|
all_comments: 'All',
|
||||||
|
// tipped_comments: 'Tipped',
|
||||||
|
// comments_by_followed_users: 'By followed users',
|
||||||
|
all_replies_to_my_comments: 'Replies to your comments',
|
||||||
|
}
|
||||||
|
const watched_markets_explanations_answers: {
|
||||||
|
[key in keyof Partial<exhaustive_notification_subscribe_types>]: string
|
||||||
|
} = {
|
||||||
|
all_answers: 'All',
|
||||||
|
all_replies_to_my_answers: 'Replies to your answers',
|
||||||
|
// answers_by_followed_users: 'By followed users',
|
||||||
|
// answers_by_market_creator: 'Submitted by the market creator',
|
||||||
|
}
|
||||||
|
const watched_markets_explanations_your_markets: {
|
||||||
|
[key in keyof Partial<exhaustive_notification_subscribe_types>]: string
|
||||||
|
} = {
|
||||||
|
my_markets_closed: 'Your market has closed (and needs resolution)',
|
||||||
|
all_comments_on_my_markets: 'Comments on your markets',
|
||||||
|
all_answers_on_my_markets: 'Answers on your markets',
|
||||||
|
}
|
||||||
|
const watched_markets_explanations_market_updates: {
|
||||||
|
[key in keyof Partial<exhaustive_notification_subscribe_types>]: string
|
||||||
|
} = {
|
||||||
|
resolutions: 'Market resolutions',
|
||||||
|
market_updates: 'Updates made by the creator',
|
||||||
|
// probability_updates: 'Changes in probability',
|
||||||
|
}
|
||||||
|
|
||||||
|
const balance_change_explanations: {
|
||||||
|
[key in keyof Partial<exhaustive_notification_subscribe_types>]: string
|
||||||
|
} = {
|
||||||
|
loans: 'Automatic loans from your profitable bets',
|
||||||
|
betting_streaks: 'Betting streak bonuses',
|
||||||
|
referral_bonuses: 'Referral bonuses from referring users',
|
||||||
|
unique_bettor_bonuses: 'Unique bettor bonuses on your markets',
|
||||||
|
}
|
||||||
|
|
||||||
|
const general_explanations: {
|
||||||
|
[key in keyof Partial<exhaustive_notification_subscribe_types>]: string
|
||||||
|
} = {
|
||||||
|
user_tagged_you: 'A user tagged you',
|
||||||
|
new_markets_by_followed_users: 'New markets created by users you follow',
|
||||||
|
trending_markets: 'Weekly trending markets',
|
||||||
|
// profit_loss_updates: 'Weekly profit and loss updates',
|
||||||
|
}
|
||||||
|
|
||||||
|
const NotificationSettingLine = (
|
||||||
|
description: string,
|
||||||
|
key: string,
|
||||||
|
value: notification_receive_types[]
|
||||||
|
) => {
|
||||||
|
const previousInAppValue = value.includes('browser')
|
||||||
|
const previousEmailValue = value.includes('email')
|
||||||
|
const [inAppEnabled, setInAppEnabled] = useState(previousInAppValue)
|
||||||
|
const [emailEnabled, setEmailEnabled] = useState(previousEmailValue)
|
||||||
|
const loading = 'Changing Notifications Settings'
|
||||||
|
const success = 'Changed Notification Settings!'
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
inAppEnabled !== previousInAppValue ||
|
||||||
|
emailEnabled !== previousEmailValue
|
||||||
|
) {
|
||||||
|
toast.promise(
|
||||||
|
updatePrivateUser(privateUser.id, {
|
||||||
|
notificationSubscriptionTypes: {
|
||||||
|
...privateUser.notificationSubscriptionTypes,
|
||||||
|
[key]: filterDefined([
|
||||||
|
inAppEnabled ? 'browser' : undefined,
|
||||||
|
emailEnabled ? 'email' : undefined,
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
success,
|
||||||
|
loading,
|
||||||
|
error: 'Error changing notification settings. Try again?',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
inAppEnabled,
|
||||||
|
emailEnabled,
|
||||||
|
previousInAppValue,
|
||||||
|
previousEmailValue,
|
||||||
|
key,
|
||||||
|
])
|
||||||
|
|
||||||
|
// for each entry in the exhaustive_notification_subscribe_types we'll want to load whether the user
|
||||||
|
// wants email, browser, both, or none
|
||||||
|
return (
|
||||||
|
<Row className={clsx('my-1 gap-1 text-gray-300')}>
|
||||||
|
<Col className="ml-3 gap-2 text-sm">
|
||||||
|
<Row className="gap-2 font-medium text-gray-700">
|
||||||
|
<span>{description}</span>
|
||||||
|
</Row>
|
||||||
|
<Row className={'gap-4'}>
|
||||||
|
{!browserDisabled.includes(key) && (
|
||||||
|
<Switch.Group as="div" className="flex items-center">
|
||||||
|
<Switch
|
||||||
|
checked={inAppEnabled}
|
||||||
|
onChange={setInAppEnabled}
|
||||||
|
className={clsx(
|
||||||
|
inAppEnabled ? 'bg-indigo-600' : 'bg-gray-200',
|
||||||
|
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
className={clsx(
|
||||||
|
inAppEnabled ? 'translate-x-5' : 'translate-x-0',
|
||||||
|
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Switch>
|
||||||
|
<Switch.Label as="span" className="ml-3">
|
||||||
|
<span className="text-sm font-medium text-gray-900">
|
||||||
|
In-app
|
||||||
|
</span>
|
||||||
|
</Switch.Label>
|
||||||
|
</Switch.Group>
|
||||||
|
)}
|
||||||
|
{emailsEnabled.includes(key) && (
|
||||||
|
<Switch.Group as="div" className="flex items-center">
|
||||||
|
<Switch
|
||||||
|
checked={emailEnabled}
|
||||||
|
onChange={setEmailEnabled}
|
||||||
|
className={clsx(
|
||||||
|
emailEnabled ? 'bg-indigo-600' : 'bg-gray-200',
|
||||||
|
'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
aria-hidden="true"
|
||||||
|
className={clsx(
|
||||||
|
emailEnabled ? 'translate-x-5' : 'translate-x-0',
|
||||||
|
'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</Switch>
|
||||||
|
<Switch.Label as="span" className="ml-3">
|
||||||
|
<span className="text-sm font-medium text-gray-900">
|
||||||
|
Emails
|
||||||
|
</span>
|
||||||
|
</Switch.Label>
|
||||||
|
</Switch.Group>
|
||||||
|
)}
|
||||||
|
</Row>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getUsersSavedPreference = (key: string) => {
|
||||||
|
return Object.keys(privateUser.notificationSubscriptionTypes).includes(key)
|
||||||
|
? // eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
//@ts-ignore
|
||||||
|
privateUser.notificationSubscriptionTypes[
|
||||||
|
Object.keys(privateUser.notificationSubscriptionTypes).filter(
|
||||||
|
(x) => x === key
|
||||||
|
)[0]
|
||||||
|
]
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const Section = (
|
||||||
|
icon: ReactNode,
|
||||||
|
label: string,
|
||||||
|
map: { [key: string]: string }
|
||||||
|
) => {
|
||||||
|
const [expanded, setExpanded] = useState(false)
|
||||||
|
return (
|
||||||
|
<Col className={'ml-2 gap-2'}>
|
||||||
|
<Row
|
||||||
|
className={'mt-1 cursor-pointer items-center gap-2 text-gray-600'}
|
||||||
|
onClick={() => setExpanded(!expanded)}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
<span>{label}</span>
|
||||||
|
|
||||||
|
{expanded ? (
|
||||||
|
<ChevronUpIcon className="h-5 w-5 text-xs text-gray-500">
|
||||||
|
Hide
|
||||||
|
</ChevronUpIcon>
|
||||||
|
) : (
|
||||||
|
<ChevronDownIcon className="h-5 w-5 text-xs text-gray-500">
|
||||||
|
Show
|
||||||
|
</ChevronDownIcon>
|
||||||
|
)}
|
||||||
|
</Row>
|
||||||
|
<Col className={clsx(expanded ? 'block' : 'hidden', 'gap-2 p-2')}>
|
||||||
|
{Object.entries(map).map(([key, value]) =>
|
||||||
|
NotificationSettingLine(value, key, getUsersSavedPreference(key))
|
||||||
|
)}
|
||||||
|
</Col>
|
||||||
|
</Col>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={'p-2'}>
|
||||||
|
<Col className={'gap-6'}>
|
||||||
|
<Row className={'gap-2 text-xl text-gray-700'}>
|
||||||
|
<span>Notifications for Watched Markets</span>
|
||||||
|
<InformationCircleIcon
|
||||||
|
className="-mb-1 h-5 w-5 cursor-pointer text-gray-500"
|
||||||
|
onClick={() => setShowWatchModal(true)}
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
{/*// TODO: add none option to each section*/}
|
||||||
|
{Section(
|
||||||
|
<ChatIcon className={'h-6 w-6'} />,
|
||||||
|
'New Comments',
|
||||||
|
watched_markets_explanations_comments
|
||||||
|
)}
|
||||||
|
{Section(
|
||||||
|
<LightBulbIcon className={'h-6 w-6'} />,
|
||||||
|
'New Answers',
|
||||||
|
watched_markets_explanations_answers
|
||||||
|
)}
|
||||||
|
{Section(
|
||||||
|
<UserIcon className={'h-6 w-6'} />,
|
||||||
|
'On Your Markets',
|
||||||
|
watched_markets_explanations_your_markets
|
||||||
|
)}
|
||||||
|
{Section(
|
||||||
|
<TrendingUpIcon className={'h-6 w-6'} />,
|
||||||
|
'Market Updates',
|
||||||
|
watched_markets_explanations_market_updates
|
||||||
|
)}
|
||||||
|
<Row className={'gap-2 text-xl text-gray-700'}>
|
||||||
|
<span>Balance Changes</span>
|
||||||
|
</Row>
|
||||||
|
{Section(
|
||||||
|
<CashIcon className={'h-6 w-6'} />,
|
||||||
|
'Loans and Bonuses',
|
||||||
|
balance_change_explanations
|
||||||
|
)}
|
||||||
|
<Row className={'gap-2 text-xl text-gray-700'}>
|
||||||
|
<span>Other</span>
|
||||||
|
</Row>
|
||||||
|
{Section(
|
||||||
|
<AdjustmentsIcon className={'h-6 w-6'} />,
|
||||||
|
'General',
|
||||||
|
general_explanations
|
||||||
|
)}
|
||||||
|
<WatchMarketModal open={showWatchModal} setOpen={setShowWatchModal} />
|
||||||
|
</Col>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
|
@ -40,7 +40,7 @@ import { Pagination } from 'web/components/pagination'
|
||||||
import { useWindowSize } from 'web/hooks/use-window-size'
|
import { useWindowSize } from 'web/hooks/use-window-size'
|
||||||
import { safeLocalStorage } from 'web/lib/util/local'
|
import { safeLocalStorage } from 'web/lib/util/local'
|
||||||
import { SiteLink } from 'web/components/site-link'
|
import { SiteLink } from 'web/components/site-link'
|
||||||
import { NotificationSettings } from 'web/components/NotificationSettings'
|
import { NotificationSettings } from 'web/components/notification-settings'
|
||||||
import { SEO } from 'web/components/SEO'
|
import { SEO } from 'web/components/SEO'
|
||||||
import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
||||||
import { UserLink } from 'web/components/user-link'
|
import { UserLink } from 'web/components/user-link'
|
||||||
|
|
Loading…
Reference in New Issue
Block a user