Direct & highlight sections for notif mgmt from emails
This commit is contained in:
parent
4c4fe665ae
commit
c1945b8ae9
|
@ -116,6 +116,8 @@ export type notification_subscription_types = {
|
|||
contract_from_followed_user: notification_destination_types[]
|
||||
trending_markets: notification_destination_types[]
|
||||
profit_loss_updates: notification_destination_types[]
|
||||
onboarding_flow: notification_destination_types[]
|
||||
thank_you_for_purchases: notification_destination_types[]
|
||||
}
|
||||
export type notification_subscribe_types = 'all' | 'less' | 'none'
|
||||
|
||||
|
@ -143,6 +145,7 @@ export const getDefaultNotificationSettings = (
|
|||
unsubscribedFromAnswerEmails,
|
||||
unsubscribedFromResolutionEmails,
|
||||
unsubscribedFromWeeklyTrendingEmails,
|
||||
unsubscribedFromGenericEmails,
|
||||
} = privateUser || {}
|
||||
|
||||
const constructPref = (browserIf: boolean, emailIf: boolean) => {
|
||||
|
@ -258,5 +261,10 @@ export const getDefaultNotificationSettings = (
|
|||
wantsAll || wantsLess,
|
||||
false
|
||||
),
|
||||
thank_you_for_purchases: constructPref(
|
||||
false,
|
||||
!unsubscribedFromGenericEmails
|
||||
),
|
||||
onboarding_flow: constructPref(false, !unsubscribedFromGenericEmails),
|
||||
} as notification_subscription_types
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
sendNewAnswerEmail,
|
||||
sendNewCommentEmail,
|
||||
} from './emails'
|
||||
import { DOMAIN } from 'common/lib/envs/constants'
|
||||
const firestore = admin.firestore()
|
||||
|
||||
type recipients_to_reason_texts = {
|
||||
|
@ -92,7 +93,7 @@ export const createNotification = async (
|
|||
if (!sendToEmail) continue
|
||||
|
||||
if (reason === 'your_contract_closed' && privateUser && sourceContract) {
|
||||
await sendMarketCloseEmail(sourceUser, privateUser, sourceContract)
|
||||
await sendMarketCloseEmail(reason, sourceUser, sourceContract)
|
||||
} else if (reason === 'tagged_user') {
|
||||
// TODO: send email to tagged user in new contract
|
||||
} else if (reason === 'subsidized_your_market') {
|
||||
|
@ -196,7 +197,7 @@ export const createNotification = async (
|
|||
await sendNotificationsIfSettingsPermit(userToReasonTexts)
|
||||
}
|
||||
|
||||
const getDestinationsForUser = async (
|
||||
export const getDestinationsForUser = async (
|
||||
userId: string,
|
||||
reason: notification_reason_types | keyof notification_subscription_types
|
||||
) => {
|
||||
|
@ -206,12 +207,13 @@ const getDestinationsForUser = async (
|
|||
|
||||
const notificationSettings = privateUser.notificationSubscriptionTypes
|
||||
let destinations
|
||||
let subscriptionType: keyof notification_subscription_types | undefined
|
||||
if (Object.keys(notificationSettings).includes(reason)) {
|
||||
const key = reason as keyof notification_subscription_types
|
||||
destinations = notificationSettings[key]
|
||||
subscriptionType = reason as keyof notification_subscription_types
|
||||
destinations = notificationSettings[subscriptionType]
|
||||
} else {
|
||||
const key = reason as notification_reason_types
|
||||
const subscriptionType = notificationReasonToSubscriptionType[key]
|
||||
subscriptionType = notificationReasonToSubscriptionType[key]
|
||||
destinations = subscriptionType
|
||||
? notificationSettings[subscriptionType]
|
||||
: []
|
||||
|
@ -220,6 +222,7 @@ const getDestinationsForUser = async (
|
|||
sendToEmail: destinations.includes('email'),
|
||||
sendToBrowser: destinations.includes('browser'),
|
||||
privateUser,
|
||||
urlToManageThisNotification: `${DOMAIN}/notifications?section=${subscriptionType}`,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,6 +329,7 @@ export const createCommentOrAnswerOrUpdatedContractNotification = async (
|
|||
if (sourceType === 'comment') {
|
||||
// if the source contract is a free response contract, send the email
|
||||
await sendNewCommentEmail(
|
||||
reason,
|
||||
userId,
|
||||
sourceUser,
|
||||
sourceContract,
|
||||
|
@ -338,6 +342,7 @@ export const createCommentOrAnswerOrUpdatedContractNotification = async (
|
|||
)
|
||||
} else if (sourceType === 'answer')
|
||||
await sendNewAnswerEmail(
|
||||
reason,
|
||||
userId,
|
||||
sourceUser.name,
|
||||
sourceText,
|
||||
|
@ -350,6 +355,7 @@ export const createCommentOrAnswerOrUpdatedContractNotification = async (
|
|||
resolutionData
|
||||
)
|
||||
await sendMarketResolutionEmail(
|
||||
reason,
|
||||
userId,
|
||||
resolutionData.userInvestments[userId],
|
||||
resolutionData.userPayouts[userId],
|
||||
|
|
|
@ -284,9 +284,12 @@
|
|||
style="font-size:0px;padding:10px 25px;padding-top:0px;padding-bottom:0px;word-break:break-word;">
|
||||
<div
|
||||
style="font-family:Arial, sans-serif;font-size:11px;letter-spacing:normal;line-height:22px;text-align:center;color:#000000;">
|
||||
<p style="margin: 10px 0;">This e-mail has been sent to {{name}}, <a
|
||||
href="{{unsubscribeLink}}” style=" color:inherit;text-decoration:none;"
|
||||
target="_blank">click here to unsubscribe</a>.</p>
|
||||
<p style="margin: 10px 0;">This e-mail has been sent to {{name}},
|
||||
<a href="{{unsubscribeLink}}" style="
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
" target="_blank">click here to manage your notifications</a>.
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -493,7 +493,7 @@
|
|||
<a href="{{unsubscribeLink}}" style="
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
" target="_blank">click here to unsubscribe</a>.
|
||||
" target="_blank">click here to manage your notifications</a>.
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -440,11 +440,10 @@
|
|||
<p style="margin: 10px 0">
|
||||
This e-mail has been sent to
|
||||
{{name}},
|
||||
<a href="{{unsubscribeLink}}"
|
||||
style="
|
||||
<a href="{{unsubscribeLink}}" style="
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
" target="_blank">click here to unsubscribe</a> from future recommended markets.
|
||||
" target="_blank">click here to manage your notifications</a>.
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -526,19 +526,10 @@
|
|||
"
|
||||
>our Discord</a
|
||||
>! Or,
|
||||
<a
|
||||
href="{{unsubscribeUrl}}"
|
||||
style="
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
box-sizing: border-box;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
text-decoration: underline;
|
||||
margin: 0;
|
||||
"
|
||||
>unsubscribe</a
|
||||
>.
|
||||
<a href="{{unsubscribeLink}}" style="
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
" target="_blank">click here to manage your notifications</a>.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -366,15 +366,10 @@
|
|||
text-decoration: underline;
|
||||
margin: 0;
|
||||
">our Discord</a>! Or,
|
||||
<a href="{{unsubscribeUrl}}" style="
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
box-sizing: border-box;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
text-decoration: underline;
|
||||
margin: 0;
|
||||
">unsubscribe</a>.
|
||||
<a href="{{unsubscribeLink}}" style="
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
" target="_blank">click here to manage your notifications</a>.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -484,15 +484,10 @@
|
|||
text-decoration: underline;
|
||||
margin: 0;
|
||||
">our Discord</a>! Or,
|
||||
<a href="{{unsubscribeUrl}}" style="
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
box-sizing: border-box;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
text-decoration: underline;
|
||||
margin: 0;
|
||||
">unsubscribe</a>.
|
||||
<a href="{{unsubscribeLink}}" style="
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
" target="_blank">click here to manage your notifications</a>.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -366,15 +366,10 @@
|
|||
text-decoration: underline;
|
||||
margin: 0;
|
||||
">our Discord</a>! Or,
|
||||
<a href="{{unsubscribeUrl}}" style="
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
box-sizing: border-box;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
text-decoration: underline;
|
||||
margin: 0;
|
||||
">unsubscribe</a>.
|
||||
<a href="{{unsubscribeLink}}" style="
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
" target="_blank">click here to manage your notifications</a>.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -499,15 +499,10 @@
|
|||
text-decoration: underline;
|
||||
margin: 0;
|
||||
">our Discord</a>! Or,
|
||||
<a href="{{unsubscribeUrl}}" style="
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial,
|
||||
sans-serif;
|
||||
box-sizing: border-box;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
text-decoration: underline;
|
||||
margin: 0;
|
||||
">unsubscribe</a>.
|
||||
<a href="{{unsubscribeLink}}" style="
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
" target="_blank">click here to manage your notifications</a>.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -487,15 +487,10 @@
|
|||
>
|
||||
<p style="margin: 10px 0">
|
||||
This e-mail has been sent to {{name}},
|
||||
<a
|
||||
href="{{unsubscribeLink}}"
|
||||
style="
|
||||
<a href="{{unsubscribeLink}}" style="
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
"
|
||||
target="_blank"
|
||||
>click here to unsubscribe</a
|
||||
>.
|
||||
" target="_blank">click here to manage your notifications</a>.
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -214,10 +214,12 @@
|
|||
<div
|
||||
style="font-family:Arial, sans-serif;font-size:11px;letter-spacing:normal;line-height:22px;text-align:center;color:#000000;">
|
||||
<p style="margin: 10px 0;">This e-mail has been sent
|
||||
to {{name}}, <a href="{{unsubscribeLink}}"
|
||||
style="color:inherit;text-decoration:none;"
|
||||
target="_blank">click here to
|
||||
unsubscribe</a>.</p>
|
||||
to {{name}},
|
||||
<a href="{{unsubscribeLink}}" style="
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
" target="_blank">click here to manage your notifications</a>.
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -286,9 +286,12 @@
|
|||
style="font-size:0px;padding:10px 25px;padding-top:0px;padding-bottom:0px;word-break:break-word;">
|
||||
<div
|
||||
style="font-family:Arial, sans-serif;font-size:11px;letter-spacing:normal;line-height:22px;text-align:center;color:#000000;">
|
||||
<p style="margin: 10px 0;">This e-mail has been sent to {{name}}, <a
|
||||
href="{{unsubscribeLink}}” style=" color:inherit;text-decoration:none;"
|
||||
target="_blank">click here to unsubscribe</a>.</p>
|
||||
<p style="margin: 10px 0;">This e-mail has been sent to {{name}},
|
||||
<a href="{{unsubscribeLink}}" style="
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
" target="_blank">click here to manage your notifications</a>.
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -2,7 +2,11 @@ import { DOMAIN } from '../../common/envs/constants'
|
|||
import { Bet } from '../../common/bet'
|
||||
import { getProbability } from '../../common/calculate'
|
||||
import { Contract } from '../../common/contract'
|
||||
import { PrivateUser, User } from '../../common/user'
|
||||
import {
|
||||
notification_subscription_types,
|
||||
PrivateUser,
|
||||
User,
|
||||
} from '../../common/user'
|
||||
import {
|
||||
formatLargeNumber,
|
||||
formatMoney,
|
||||
|
@ -12,13 +16,13 @@ import { getValueFromBucket } from '../../common/calculate-dpm'
|
|||
import { formatNumericProbability } from '../../common/pseudo-numeric'
|
||||
|
||||
import { sendTemplateEmail, sendTextEmail } from './send-email'
|
||||
import { getPrivateUser, getUser } from './utils'
|
||||
import { getFunctionUrl } from '../../common/api'
|
||||
import { getUser } from './utils'
|
||||
import { buildCardUrl, getOpenGraphProps } from '../../common/contract-details'
|
||||
|
||||
const UNSUBSCRIBE_ENDPOINT = getFunctionUrl('unsubscribe')
|
||||
import { notification_reason_types } from '../../common/notification'
|
||||
import { getDestinationsForUser } from './create-notification'
|
||||
|
||||
export const sendMarketResolutionEmail = async (
|
||||
reason: notification_reason_types,
|
||||
userId: string,
|
||||
investment: number,
|
||||
payout: number,
|
||||
|
@ -29,13 +33,12 @@ export const sendMarketResolutionEmail = async (
|
|||
resolutionProbability?: number,
|
||||
resolutions?: { [outcome: string]: number }
|
||||
) => {
|
||||
const privateUser = await getPrivateUser(userId)
|
||||
if (
|
||||
!privateUser ||
|
||||
privateUser.unsubscribedFromResolutionEmails ||
|
||||
!privateUser.email
|
||||
)
|
||||
return
|
||||
const {
|
||||
privateUser,
|
||||
sendToEmail,
|
||||
urlToManageThisNotification: unsubscribeUrl,
|
||||
} = await getDestinationsForUser(userId, reason)
|
||||
if (!privateUser || !privateUser.email || !sendToEmail) return
|
||||
|
||||
const user = await getUser(userId)
|
||||
if (!user) return
|
||||
|
@ -54,9 +57,6 @@ export const sendMarketResolutionEmail = async (
|
|||
? ` (plus ${formatMoney(creatorPayout)} in commissions)`
|
||||
: ''
|
||||
|
||||
const emailType = 'market-resolved'
|
||||
const unsubscribeUrl = `${UNSUBSCRIBE_ENDPOINT}?id=${userId}&type=${emailType}`
|
||||
|
||||
const displayedInvestment =
|
||||
Number.isNaN(investment) || investment < 0
|
||||
? formatMoney(0)
|
||||
|
@ -151,11 +151,12 @@ export const sendWelcomeEmail = async (
|
|||
) => {
|
||||
if (!privateUser || !privateUser.email) return
|
||||
|
||||
const { name, id: userId } = user
|
||||
const { name } = user
|
||||
const firstName = name.split(' ')[0]
|
||||
|
||||
const emailType = 'generic'
|
||||
const unsubscribeLink = `${UNSUBSCRIBE_ENDPOINT}?id=${userId}&type=${emailType}`
|
||||
const unsubscribeLink = `${DOMAIN}/notifications?section=${
|
||||
'onboarding_flow' as keyof notification_subscription_types
|
||||
}`
|
||||
|
||||
return await sendTemplateEmail(
|
||||
privateUser.email,
|
||||
|
@ -214,16 +215,16 @@ export const sendOneWeekBonusEmail = async (
|
|||
if (
|
||||
!privateUser ||
|
||||
!privateUser.email ||
|
||||
privateUser.unsubscribedFromGenericEmails
|
||||
!privateUser.notificationSubscriptionTypes.onboarding_flow.includes('email')
|
||||
)
|
||||
return
|
||||
|
||||
const { name, id: userId } = user
|
||||
const firstName = name.split(' ')[0]
|
||||
|
||||
const emailType = 'generic'
|
||||
const unsubscribeLink = `${UNSUBSCRIBE_ENDPOINT}?id=${userId}&type=${emailType}`
|
||||
|
||||
const unsubscribeLink = `${DOMAIN}/notifications?section=${
|
||||
'onboarding_flow' as keyof notification_subscription_types
|
||||
}`
|
||||
return await sendTemplateEmail(
|
||||
privateUser.email,
|
||||
'Manifold Markets one week anniversary gift',
|
||||
|
@ -247,16 +248,16 @@ export const sendCreatorGuideEmail = async (
|
|||
if (
|
||||
!privateUser ||
|
||||
!privateUser.email ||
|
||||
privateUser.unsubscribedFromGenericEmails
|
||||
!privateUser.notificationSubscriptionTypes.onboarding_flow.includes('email')
|
||||
)
|
||||
return
|
||||
|
||||
const { name, id: userId } = user
|
||||
const firstName = name.split(' ')[0]
|
||||
|
||||
const emailType = 'generic'
|
||||
const unsubscribeLink = `${UNSUBSCRIBE_ENDPOINT}?id=${userId}&type=${emailType}`
|
||||
|
||||
const unsubscribeLink = `${DOMAIN}/notifications?section=${
|
||||
'onboarding_flow' as keyof notification_subscription_types
|
||||
}`
|
||||
return await sendTemplateEmail(
|
||||
privateUser.email,
|
||||
'Create your own prediction market',
|
||||
|
@ -279,15 +280,18 @@ export const sendThankYouEmail = async (
|
|||
if (
|
||||
!privateUser ||
|
||||
!privateUser.email ||
|
||||
privateUser.unsubscribedFromGenericEmails
|
||||
!privateUser.notificationSubscriptionTypes.thank_you_for_purchases.includes(
|
||||
'email'
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
const { name, id: userId } = user
|
||||
const { name } = user
|
||||
const firstName = name.split(' ')[0]
|
||||
|
||||
const emailType = 'generic'
|
||||
const unsubscribeLink = `${UNSUBSCRIBE_ENDPOINT}?id=${userId}&type=${emailType}`
|
||||
const unsubscribeLink = `${DOMAIN}/notifications?section=${
|
||||
'thank_you_for_purchases' as keyof notification_subscription_types
|
||||
}`
|
||||
|
||||
return await sendTemplateEmail(
|
||||
privateUser.email,
|
||||
|
@ -304,16 +308,17 @@ export const sendThankYouEmail = async (
|
|||
}
|
||||
|
||||
export const sendMarketCloseEmail = async (
|
||||
reason: notification_reason_types,
|
||||
user: User,
|
||||
privateUser: PrivateUser,
|
||||
contract: Contract
|
||||
) => {
|
||||
if (
|
||||
!privateUser ||
|
||||
privateUser.unsubscribedFromResolutionEmails ||
|
||||
!privateUser.email
|
||||
)
|
||||
return
|
||||
const {
|
||||
privateUser,
|
||||
sendToEmail,
|
||||
urlToManageThisNotification: unsubscribeUrl,
|
||||
} = await getDestinationsForUser(user.id, reason)
|
||||
|
||||
if (!privateUser || !privateUser.email || !sendToEmail) return
|
||||
|
||||
const { username, name, id: userId } = user
|
||||
const firstName = name.split(' ')[0]
|
||||
|
@ -321,8 +326,6 @@ export const sendMarketCloseEmail = async (
|
|||
const { question, slug, volume } = contract
|
||||
|
||||
const url = `https://${DOMAIN}/${username}/${slug}`
|
||||
const emailType = 'market-resolve'
|
||||
const unsubscribeUrl = `${UNSUBSCRIBE_ENDPOINT}?id=${userId}&type=${emailType}`
|
||||
|
||||
return await sendTemplateEmail(
|
||||
privateUser.email,
|
||||
|
@ -340,6 +343,7 @@ export const sendMarketCloseEmail = async (
|
|||
}
|
||||
|
||||
export const sendNewCommentEmail = async (
|
||||
reason: notification_reason_types,
|
||||
userId: string,
|
||||
commentCreator: User,
|
||||
contract: Contract,
|
||||
|
@ -349,18 +353,15 @@ export const sendNewCommentEmail = async (
|
|||
answerText?: string,
|
||||
answerId?: string
|
||||
) => {
|
||||
const privateUser = await getPrivateUser(userId)
|
||||
if (
|
||||
!privateUser ||
|
||||
!privateUser.email ||
|
||||
privateUser.unsubscribedFromCommentEmails
|
||||
)
|
||||
return
|
||||
const {
|
||||
privateUser,
|
||||
sendToEmail,
|
||||
urlToManageThisNotification: unsubscribeUrl,
|
||||
} = await getDestinationsForUser(userId, reason)
|
||||
if (!privateUser || !privateUser.email || !sendToEmail) return
|
||||
|
||||
const { question } = contract
|
||||
const marketUrl = `https://${DOMAIN}/${contract.creatorUsername}/${contract.slug}#${commentId}`
|
||||
const emailType = 'market-comment'
|
||||
const unsubscribeUrl = `${UNSUBSCRIBE_ENDPOINT}?id=${userId}&type=${emailType}`
|
||||
|
||||
const { name: commentorName, avatarUrl: commentorAvatarUrl } = commentCreator
|
||||
|
||||
|
@ -419,6 +420,7 @@ export const sendNewCommentEmail = async (
|
|||
}
|
||||
|
||||
export const sendNewAnswerEmail = async (
|
||||
reason: notification_reason_types,
|
||||
userId: string,
|
||||
name: string,
|
||||
text: string,
|
||||
|
@ -429,19 +431,16 @@ export const sendNewAnswerEmail = async (
|
|||
// Don't send the creator's own answers.
|
||||
if (userId === creatorId) return
|
||||
|
||||
const privateUser = await getPrivateUser(userId)
|
||||
if (
|
||||
!privateUser ||
|
||||
!privateUser.email ||
|
||||
privateUser.unsubscribedFromAnswerEmails
|
||||
)
|
||||
return
|
||||
const {
|
||||
privateUser,
|
||||
sendToEmail,
|
||||
urlToManageThisNotification: unsubscribeUrl,
|
||||
} = await getDestinationsForUser(userId, reason)
|
||||
if (!privateUser || !privateUser.email || !sendToEmail) return
|
||||
|
||||
const { question, creatorUsername, slug } = contract
|
||||
|
||||
const marketUrl = `https://${DOMAIN}/${creatorUsername}/${slug}`
|
||||
const emailType = 'market-answer'
|
||||
const unsubscribeUrl = `${UNSUBSCRIBE_ENDPOINT}?id=${userId}&type=${emailType}`
|
||||
|
||||
const subject = `New answer on ${question}`
|
||||
const from = `${name} <info@manifold.markets>`
|
||||
|
@ -470,12 +469,15 @@ export const sendInterestingMarketsEmail = async (
|
|||
if (
|
||||
!privateUser ||
|
||||
!privateUser.email ||
|
||||
privateUser?.unsubscribedFromWeeklyTrendingEmails
|
||||
!privateUser.notificationSubscriptionTypes.trending_markets.includes(
|
||||
'email'
|
||||
)
|
||||
)
|
||||
return
|
||||
|
||||
const emailType = 'weekly-trending'
|
||||
const unsubscribeUrl = `${UNSUBSCRIBE_ENDPOINT}?id=${privateUser.id}&type=${emailType}`
|
||||
const unsubscribeUrl = `${DOMAIN}/notifications?section=${
|
||||
'trending_markets' as keyof notification_subscription_types
|
||||
}`
|
||||
|
||||
const { name } = user
|
||||
const firstName = name.split(' ')[0]
|
||||
|
|
|
@ -27,7 +27,10 @@ import { WatchMarketModal } from 'web/components/contract/watch-market-modal'
|
|||
import { filterDefined } from 'common/util/array'
|
||||
import toast from 'react-hot-toast'
|
||||
|
||||
export function NotificationSettings() {
|
||||
export function NotificationSettings(props: {
|
||||
navigateToSection: string | undefined
|
||||
}) {
|
||||
const { navigateToSection } = props
|
||||
const privateUser = usePrivateUser()
|
||||
const [showWatchModal, setShowWatchModal] = useState(false)
|
||||
|
||||
|
@ -53,6 +56,8 @@ export function NotificationSettings() {
|
|||
|
||||
'tagged_user',
|
||||
'trending_markets',
|
||||
'onboarding_flow',
|
||||
'thank_you_for_purchases',
|
||||
|
||||
// TODO: add these
|
||||
// 'contract_from_followed_user',
|
||||
|
@ -67,77 +72,94 @@ export function NotificationSettings() {
|
|||
// 'probability_updates_on_watched_markets',
|
||||
// 'limit_order_fills',
|
||||
]
|
||||
const browserDisabled = ['trending_markets', 'profit_loss_updates']
|
||||
const browserDisabled: Array<keyof notification_subscription_types> = [
|
||||
'trending_markets',
|
||||
'profit_loss_updates',
|
||||
'onboarding_flow',
|
||||
'thank_you_for_purchases',
|
||||
]
|
||||
|
||||
const watched_markets_explanations_comments: {
|
||||
type sectionData = {
|
||||
label: string
|
||||
subscriptionTypeToDescription: {
|
||||
[key in keyof Partial<notification_subscription_types>]: string
|
||||
} = {
|
||||
all_comments_on_watched_markets: 'All',
|
||||
all_replies_to_my_comments_on_watched_markets: 'Replies to your comments',
|
||||
all_comments_on_contracts_with_shares_in_on_watched_markets:
|
||||
'On markets you have shares in',
|
||||
// comments_by_followed_users_on_watched_markets: 'By followed users',
|
||||
}
|
||||
const watched_markets_explanations_answers: {
|
||||
[key in keyof Partial<notification_subscription_types>]: string
|
||||
} = {
|
||||
all_answers_on_watched_markets: 'All',
|
||||
all_replies_to_my_answers_on_watched_markets: 'Replies to your answers',
|
||||
all_answers_on_contracts_with_shares_in_on_watched_markets:
|
||||
'On markets you have shares in',
|
||||
}
|
||||
|
||||
const comments: sectionData = {
|
||||
label: 'New Comments',
|
||||
subscriptionTypeToDescription: {
|
||||
all_comments_on_watched_markets: 'All new comments',
|
||||
all_comments_on_contracts_with_shares_in_on_watched_markets: `Only on markets you're invested in`,
|
||||
all_replies_to_my_comments_on_watched_markets:
|
||||
'Only replies to your comments',
|
||||
// comments_by_followed_users_on_watched_markets: 'By followed users',
|
||||
},
|
||||
}
|
||||
|
||||
const answers: sectionData = {
|
||||
label: 'New Answers',
|
||||
subscriptionTypeToDescription: {
|
||||
all_answers_on_watched_markets: 'All new answers',
|
||||
all_answers_on_contracts_with_shares_in_on_watched_markets: `Only on markets you're invested in`,
|
||||
all_replies_to_my_answers_on_watched_markets:
|
||||
'Only replies to your answers',
|
||||
// answers_by_followed_users_on_watched_markets: 'By followed users',
|
||||
// answers_by_market_creator_on_watched_markets: 'By market creator',
|
||||
},
|
||||
}
|
||||
const watched_markets_explanations_your_markets: {
|
||||
[key in keyof Partial<notification_subscription_types>]: string
|
||||
} = {
|
||||
const updates: sectionData = {
|
||||
label: 'Updates & Resolutions',
|
||||
subscriptionTypeToDescription: {
|
||||
market_updates_on_watched_markets: 'All creator updates',
|
||||
market_updates_on_watched_markets_with_shares_in: `Only creator updates on markets you're invested in`,
|
||||
resolutions_on_watched_markets: 'All market resolutions',
|
||||
resolutions_on_watched_markets_with_shares_in: `Only market resolutions you're invested in`,
|
||||
// probability_updates_on_watched_markets: 'Probability updates',
|
||||
},
|
||||
}
|
||||
const yourMarkets: sectionData = {
|
||||
label: 'Markets You Created',
|
||||
subscriptionTypeToDescription: {
|
||||
your_contract_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',
|
||||
subsidized_your_market: 'Your market was subsidized',
|
||||
tips_on_your_markets: 'Likes on your markets',
|
||||
},
|
||||
}
|
||||
const watched_markets_explanations_market_updates: {
|
||||
[key in keyof Partial<notification_subscription_types>]: string
|
||||
} = {
|
||||
market_updates_on_watched_markets: 'Updates made by the creator',
|
||||
market_updates_on_watched_markets_with_shares_in:
|
||||
'Updates made by the creator on markets you have shares in',
|
||||
resolutions_on_watched_markets: 'Market resolutions',
|
||||
resolutions_on_watched_markets_with_shares_in:
|
||||
'Market resolutions you have shares in',
|
||||
// probability_updates_on_watched_markets: 'Probability updates',
|
||||
}
|
||||
|
||||
const bonuses_explanations: {
|
||||
[key in keyof Partial<notification_subscription_types>]: string
|
||||
} = {
|
||||
const bonuses: sectionData = {
|
||||
label: 'Bonuses',
|
||||
subscriptionTypeToDescription: {
|
||||
betting_streaks: 'Betting streak bonuses',
|
||||
referral_bonuses: 'Referral bonuses from referring users',
|
||||
unique_bettors_on_your_contract: 'Unique bettor bonuses on your markets',
|
||||
},
|
||||
}
|
||||
|
||||
const other_balance_change_explanations: {
|
||||
[key in keyof Partial<notification_subscription_types>]: string
|
||||
} = {
|
||||
const otherBalances: sectionData = {
|
||||
label: 'Other',
|
||||
subscriptionTypeToDescription: {
|
||||
loan_income: 'Automatic loans from your profitable bets',
|
||||
limit_order_fills: 'Limit order fills',
|
||||
tips_on_your_comments: 'Tips on your comments',
|
||||
},
|
||||
}
|
||||
|
||||
const general_explanations: {
|
||||
[key in keyof Partial<notification_subscription_types>]: string
|
||||
} = {
|
||||
const userInteractions: sectionData = {
|
||||
label: 'Users',
|
||||
subscriptionTypeToDescription: {
|
||||
tagged_user: 'A user tagged you',
|
||||
trending_markets: 'Weekly trending markets',
|
||||
// profit_loss_updates: 'Weekly profit/loss updates',
|
||||
}
|
||||
|
||||
const follows_and_followers_explanations: {
|
||||
[key in keyof Partial<notification_subscription_types>]: string
|
||||
} = {
|
||||
on_new_follow: 'New followers',
|
||||
on_new_follow: 'Someone followed you',
|
||||
contract_from_followed_user: 'New markets created by users you follow',
|
||||
},
|
||||
}
|
||||
const generalOther: sectionData = {
|
||||
label: 'Other',
|
||||
subscriptionTypeToDescription: {
|
||||
trending_markets: 'Weekly interesting markets',
|
||||
thank_you_for_purchases: 'Thank you notes for your purchases',
|
||||
onboarding_flow: 'Explanatory emails to help you get started',
|
||||
// profit_loss_updates: 'Weekly profit/loss updates',
|
||||
},
|
||||
}
|
||||
|
||||
const NotificationSettingLine = (
|
||||
|
@ -151,6 +173,7 @@ export function NotificationSettings() {
|
|||
const [emailEnabled, setEmailEnabled] = useState(previousEmailValue)
|
||||
const loading = 'Changing Notifications Settings'
|
||||
const success = 'Changed Notification Settings!'
|
||||
const highlight = navigateToSection === key
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
|
@ -183,7 +206,12 @@ export function NotificationSettings() {
|
|||
])
|
||||
|
||||
return (
|
||||
<Row className={clsx('my-1 gap-1 text-gray-300')}>
|
||||
<Row
|
||||
className={clsx(
|
||||
'my-1 gap-1 text-gray-300',
|
||||
highlight ? 'rounded-md bg-indigo-100 p-1' : ''
|
||||
)}
|
||||
>
|
||||
<Col className="ml-3 gap-2 text-sm">
|
||||
<Row className="gap-2 font-medium text-gray-700">
|
||||
<span>{description}</span>
|
||||
|
@ -251,16 +279,20 @@ export function NotificationSettings() {
|
|||
return privateUser.notificationSubscriptionTypes[key] ?? []
|
||||
}
|
||||
|
||||
const Section = (
|
||||
icon: ReactNode,
|
||||
label: string,
|
||||
subscriptionTypeToDescription: {
|
||||
[key in keyof Partial<notification_subscription_types>]: string
|
||||
}
|
||||
) => {
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
const Section = (icon: ReactNode, data: sectionData) => {
|
||||
const { label, subscriptionTypeToDescription } = data
|
||||
const expand =
|
||||
navigateToSection &&
|
||||
Object.keys(subscriptionTypeToDescription).includes(navigateToSection)
|
||||
const [expanded, setExpanded] = useState(expand)
|
||||
|
||||
// Not working as the default value for expanded, so using a useEffect
|
||||
useEffect(() => {
|
||||
if (expand) setExpanded(true)
|
||||
}, [expand])
|
||||
|
||||
return (
|
||||
<Col className={'ml-2 gap-2'}>
|
||||
<Col className={clsx('ml-2 gap-2')}>
|
||||
<Row
|
||||
className={'mt-1 cursor-pointer items-center gap-2 text-gray-600'}
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
|
@ -303,52 +335,20 @@ export function NotificationSettings() {
|
|||
onClick={() => setShowWatchModal(true)}
|
||||
/>
|
||||
</Row>
|
||||
{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(
|
||||
<TrendingUpIcon className={'h-6 w-6'} />,
|
||||
'Updates & Resolutions',
|
||||
watched_markets_explanations_market_updates
|
||||
)}
|
||||
{Section(
|
||||
<UserIcon className={'h-6 w-6'} />,
|
||||
'Markets You Created',
|
||||
watched_markets_explanations_your_markets
|
||||
)}
|
||||
{Section(<ChatIcon className={'h-6 w-6'} />, comments)}
|
||||
{Section(<LightBulbIcon className={'h-6 w-6'} />, answers)}
|
||||
{Section(<TrendingUpIcon className={'h-6 w-6'} />, updates)}
|
||||
{Section(<UserIcon className={'h-6 w-6'} />, yourMarkets)}
|
||||
<Row className={'gap-2 text-xl text-gray-700'}>
|
||||
<span>Balance Changes</span>
|
||||
</Row>
|
||||
{Section(
|
||||
<CurrencyDollarIcon className={'h-6 w-6'} />,
|
||||
'Bonuses',
|
||||
bonuses_explanations
|
||||
)}
|
||||
{Section(
|
||||
<CashIcon className={'h-6 w-6'} />,
|
||||
'Other',
|
||||
other_balance_change_explanations
|
||||
)}
|
||||
{Section(<CurrencyDollarIcon className={'h-6 w-6'} />, bonuses)}
|
||||
{Section(<CashIcon className={'h-6 w-6'} />, otherBalances)}
|
||||
<Row className={'gap-2 text-xl text-gray-700'}>
|
||||
<span>General</span>
|
||||
</Row>
|
||||
{Section(
|
||||
<UsersIcon className={'h-6 w-6'} />,
|
||||
'Follows & Followers',
|
||||
follows_and_followers_explanations
|
||||
)}
|
||||
{Section(
|
||||
<InboxInIcon className={'h-6 w-6'} />,
|
||||
'Other',
|
||||
general_explanations
|
||||
)}
|
||||
{Section(<UsersIcon className={'h-6 w-6'} />, userInteractions)}
|
||||
{Section(<InboxInIcon className={'h-6 w-6'} />, generalOther)}
|
||||
<WatchMarketModal open={showWatchModal} setOpen={setShowWatchModal} />
|
||||
</Col>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Tabs } from 'web/components/layout/tabs'
|
||||
import { ControlledTabs } from 'web/components/layout/tabs'
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
import Router from 'next/router'
|
||||
import Router, { useRouter } from 'next/router'
|
||||
import { Notification, notification_source_types } from 'common/notification'
|
||||
import { Avatar, EmptyAvatar } from 'web/components/avatar'
|
||||
import { Row } from 'web/components/layout/row'
|
||||
|
@ -56,24 +56,38 @@ const HIGHLIGHT_CLASS = 'bg-indigo-50'
|
|||
|
||||
export default function Notifications() {
|
||||
const privateUser = usePrivateUser()
|
||||
const router = useRouter()
|
||||
const [navigateToSection, setNavigateToSection] = useState<string>()
|
||||
const [activeIndex, setActiveIndex] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
if (privateUser === null) Router.push('/')
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const query = { ...router.query }
|
||||
if (query.section) {
|
||||
setNavigateToSection(query.section as string)
|
||||
setActiveIndex(1)
|
||||
}
|
||||
}, [router.query])
|
||||
|
||||
return (
|
||||
<Page>
|
||||
<div className={'px-2 pt-4 sm:px-4 lg:pt-0'}>
|
||||
<Title text={'Notifications'} className={'hidden md:block'} />
|
||||
<SEO title="Notifications" description="Manifold user notifications" />
|
||||
|
||||
{privateUser && (
|
||||
{privateUser && router.isReady && (
|
||||
<div>
|
||||
<Tabs
|
||||
<ControlledTabs
|
||||
currentPageForAnalytics={'notifications'}
|
||||
labelClassName={'pb-2 pt-1 '}
|
||||
className={'mb-0 sm:mb-2'}
|
||||
defaultIndex={0}
|
||||
activeIndex={activeIndex}
|
||||
onClick={(title, index) => {
|
||||
setActiveIndex(index)
|
||||
}}
|
||||
tabs={[
|
||||
{
|
||||
title: 'Notifications',
|
||||
|
@ -82,9 +96,9 @@ export default function Notifications() {
|
|||
{
|
||||
title: 'Settings',
|
||||
content: (
|
||||
<div className={''}>
|
||||
<NotificationSettings />
|
||||
</div>
|
||||
<NotificationSettings
|
||||
navigateToSection={navigateToSection}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
|
|
Loading…
Reference in New Issue
Block a user