-
-
-
-
-
\ No newline at end of file
diff --git a/functions/src/email-templates/creating-market.html b/functions/src/email-templates/creating-market.html
index c73f7458..bf163f69 100644
--- a/functions/src/email-templates/creating-market.html
+++ b/functions/src/email-templates/creating-market.html
@@ -494,7 +494,7 @@
click here to manage your notifications.
+ " target="_blank">click here to unsubscribe from this type of notification.
diff --git a/functions/src/email-templates/interesting-markets.html b/functions/src/email-templates/interesting-markets.html
index 7c3e653d..0cee6269 100644
--- a/functions/src/email-templates/interesting-markets.html
+++ b/functions/src/email-templates/interesting-markets.html
@@ -443,7 +443,7 @@
click here to manage your notifications.
+ " target="_blank">click here to unsubscribe from this type of notification.
diff --git a/functions/src/email-templates/market-answer-comment.html b/functions/src/email-templates/market-answer-comment.html
index a19aa7c3..4b98730f 100644
--- a/functions/src/email-templates/market-answer-comment.html
+++ b/functions/src/email-templates/market-answer-comment.html
@@ -529,7 +529,7 @@
click here to manage your notifications.
+ " target="_blank">click here to unsubscribe from this type of notification.
diff --git a/functions/src/email-templates/market-answer.html b/functions/src/email-templates/market-answer.html
index b2d7f727..e3d42b9d 100644
--- a/functions/src/email-templates/market-answer.html
+++ b/functions/src/email-templates/market-answer.html
@@ -369,7 +369,7 @@
click here to manage your notifications.
+ " target="_blank">click here to unsubscribe from this type of notification.
diff --git a/functions/src/email-templates/market-close.html b/functions/src/email-templates/market-close.html
index ee7976b0..4abd225e 100644
--- a/functions/src/email-templates/market-close.html
+++ b/functions/src/email-templates/market-close.html
@@ -487,7 +487,7 @@
click here to manage your notifications.
+ " target="_blank">click here to unsubscribe from this type of notification.
diff --git a/functions/src/email-templates/market-comment.html b/functions/src/email-templates/market-comment.html
index 23e20dac..ce0669f1 100644
--- a/functions/src/email-templates/market-comment.html
+++ b/functions/src/email-templates/market-comment.html
@@ -369,7 +369,7 @@
click here to manage your notifications.
+ " target="_blank">click here to unsubscribe from this type of notification.
diff --git a/functions/src/email-templates/market-resolved-no-bets.html b/functions/src/email-templates/market-resolved-no-bets.html
index ff5f541f..5d886adf 100644
--- a/functions/src/email-templates/market-resolved-no-bets.html
+++ b/functions/src/email-templates/market-resolved-no-bets.html
@@ -470,7 +470,7 @@
click here to manage your notifications.
+ " target="_blank">click here to unsubscribe from this type of notification.
diff --git a/functions/src/email-templates/market-resolved.html b/functions/src/email-templates/market-resolved.html
index de29a0f1..767202b6 100644
--- a/functions/src/email-templates/market-resolved.html
+++ b/functions/src/email-templates/market-resolved.html
@@ -502,7 +502,7 @@
click here to manage your notifications.
+ " target="_blank">click here to unsubscribe from this type of notification.
diff --git a/functions/src/email-templates/new-market-from-followed-user.html b/functions/src/email-templates/new-market-from-followed-user.html
index 877d554f..49633fb2 100644
--- a/functions/src/email-templates/new-market-from-followed-user.html
+++ b/functions/src/email-templates/new-market-from-followed-user.html
@@ -318,7 +318,7 @@
click here to manage your notifications.
+ " target="_blank">click here to unsubscribe from this type of notification.
diff --git a/functions/src/email-templates/new-unique-bettor.html b/functions/src/email-templates/new-unique-bettor.html
index 30da8b99..51026121 100644
--- a/functions/src/email-templates/new-unique-bettor.html
+++ b/functions/src/email-templates/new-unique-bettor.html
@@ -376,7 +376,7 @@
click here to manage your notifications.
+ " target="_blank">click here to unsubscribe from this type of notification.
diff --git a/functions/src/email-templates/new-unique-bettors.html b/functions/src/email-templates/new-unique-bettors.html
index eb4c04e2..09c44d03 100644
--- a/functions/src/email-templates/new-unique-bettors.html
+++ b/functions/src/email-templates/new-unique-bettors.html
@@ -480,7 +480,7 @@
click here to manage your notifications.
+ " target="_blank">click here to unsubscribe from this type of notification.
diff --git a/functions/src/email-templates/one-week.html b/functions/src/email-templates/one-week.html
index b8e233d5..e7d14a7e 100644
--- a/functions/src/email-templates/one-week.html
+++ b/functions/src/email-templates/one-week.html
@@ -283,7 +283,7 @@
click here to manage your notifications.
+ " target="_blank">click here to unsubscribe from this type of notification.
diff --git a/functions/src/email-templates/thank-you.html b/functions/src/email-templates/thank-you.html
index 7ac72d0a..beef11ee 100644
--- a/functions/src/email-templates/thank-you.html
+++ b/functions/src/email-templates/thank-you.html
@@ -218,7 +218,7 @@
click here to manage your notifications.
+ " target="_blank">click here to unsubscribe from this type of notification.
diff --git a/functions/src/email-templates/welcome.html b/functions/src/email-templates/welcome.html
index dccec695..d6caaa0c 100644
--- a/functions/src/email-templates/welcome.html
+++ b/functions/src/email-templates/welcome.html
@@ -290,7 +290,7 @@
click here to manage your notifications.
+ " target="_blank">click here to unsubscribe from this type of notification.
diff --git a/functions/src/emails.ts b/functions/src/emails.ts
index bb9f7195..98309ebe 100644
--- a/functions/src/emails.ts
+++ b/functions/src/emails.ts
@@ -2,11 +2,7 @@ import { DOMAIN } from '../../common/envs/constants'
import { Bet } from '../../common/bet'
import { getProbability } from '../../common/calculate'
import { Contract } from '../../common/contract'
-import {
- notification_subscription_types,
- PrivateUser,
- User,
-} from '../../common/user'
+import { PrivateUser, User } from '../../common/user'
import {
formatLargeNumber,
formatMoney,
@@ -18,11 +14,12 @@ import { formatNumericProbability } from '../../common/pseudo-numeric'
import { sendTemplateEmail, sendTextEmail } from './send-email'
import { getUser } from './utils'
import { buildCardUrl, getOpenGraphProps } from '../../common/contract-details'
-import {
- notification_reason_types,
- getDestinationsForUser,
-} from '../../common/notification'
+import { notification_reason_types } from '../../common/notification'
import { Dictionary } from 'lodash'
+import {
+ getNotificationDestinationsForUser,
+ notification_preference,
+} from '../../common/user-notification-preferences'
export const sendMarketResolutionEmail = async (
reason: notification_reason_types,
@@ -36,8 +33,10 @@ export const sendMarketResolutionEmail = async (
resolutionProbability?: number,
resolutions?: { [outcome: string]: number }
) => {
- const { sendToEmail, urlToManageThisNotification: unsubscribeUrl } =
- await getDestinationsForUser(privateUser, reason)
+ const { sendToEmail, unsubscribeUrl } = getNotificationDestinationsForUser(
+ privateUser,
+ reason
+ )
if (!privateUser || !privateUser.email || !sendToEmail) return
const user = await getUser(privateUser.id)
@@ -154,7 +153,7 @@ export const sendWelcomeEmail = async (
const firstName = name.split(' ')[0]
const unsubscribeUrl = `${DOMAIN}/notifications?tab=settings§ion=${
- 'onboarding_flow' as keyof notification_subscription_types
+ 'onboarding_flow' as notification_preference
}`
return await sendTemplateEmail(
@@ -222,7 +221,7 @@ export const sendOneWeekBonusEmail = async (
const firstName = name.split(' ')[0]
const unsubscribeUrl = `${DOMAIN}/notifications?tab=settings§ion=${
- 'onboarding_flow' as keyof notification_subscription_types
+ 'onboarding_flow' as notification_preference
}`
return await sendTemplateEmail(
privateUser.email,
@@ -255,7 +254,7 @@ export const sendCreatorGuideEmail = async (
const firstName = name.split(' ')[0]
const unsubscribeUrl = `${DOMAIN}/notifications?tab=settings§ion=${
- 'onboarding_flow' as keyof notification_subscription_types
+ 'onboarding_flow' as notification_preference
}`
return await sendTemplateEmail(
privateUser.email,
@@ -289,7 +288,7 @@ export const sendThankYouEmail = async (
const firstName = name.split(' ')[0]
const unsubscribeUrl = `${DOMAIN}/notifications?tab=settings§ion=${
- 'thank_you_for_purchases' as keyof notification_subscription_types
+ 'thank_you_for_purchases' as notification_preference
}`
return await sendTemplateEmail(
@@ -312,8 +311,10 @@ export const sendMarketCloseEmail = async (
privateUser: PrivateUser,
contract: Contract
) => {
- const { sendToEmail, urlToManageThisNotification: unsubscribeUrl } =
- await getDestinationsForUser(privateUser, reason)
+ const { sendToEmail, unsubscribeUrl } = getNotificationDestinationsForUser(
+ privateUser,
+ reason
+ )
if (!privateUser.email || !sendToEmail) return
@@ -350,8 +351,10 @@ export const sendNewCommentEmail = async (
answerText?: string,
answerId?: string
) => {
- const { sendToEmail, urlToManageThisNotification: unsubscribeUrl } =
- await getDestinationsForUser(privateUser, reason)
+ const { sendToEmail, unsubscribeUrl } = getNotificationDestinationsForUser(
+ privateUser,
+ reason
+ )
if (!privateUser || !privateUser.email || !sendToEmail) return
const { question } = contract
@@ -425,8 +428,10 @@ export const sendNewAnswerEmail = async (
// Don't send the creator's own answers.
if (privateUser.id === creatorId) return
- const { sendToEmail, urlToManageThisNotification: unsubscribeUrl } =
- await getDestinationsForUser(privateUser, reason)
+ const { sendToEmail, unsubscribeUrl } = getNotificationDestinationsForUser(
+ privateUser,
+ reason
+ )
if (!privateUser.email || !sendToEmail) return
const { question, creatorUsername, slug } = contract
@@ -465,7 +470,7 @@ export const sendInterestingMarketsEmail = async (
return
const unsubscribeUrl = `${DOMAIN}/notifications?tab=settings§ion=${
- 'trending_markets' as keyof notification_subscription_types
+ 'trending_markets' as notification_preference
}`
const { name } = user
@@ -516,8 +521,10 @@ export const sendNewFollowedMarketEmail = async (
privateUser: PrivateUser,
contract: Contract
) => {
- const { sendToEmail, urlToManageThisNotification: unsubscribeUrl } =
- await getDestinationsForUser(privateUser, reason)
+ const { sendToEmail, unsubscribeUrl } = getNotificationDestinationsForUser(
+ privateUser,
+ reason
+ )
if (!privateUser.email || !sendToEmail) return
const user = await getUser(privateUser.id)
if (!user) return
@@ -553,8 +560,10 @@ export const sendNewUniqueBettorsEmail = async (
userBets: Dictionary<[Bet, ...Bet[]]>,
bonusAmount: number
) => {
- const { sendToEmail, urlToManageThisNotification: unsubscribeUrl } =
- await getDestinationsForUser(privateUser, reason)
+ const { sendToEmail, unsubscribeUrl } = getNotificationDestinationsForUser(
+ privateUser,
+ reason
+ )
if (!privateUser.email || !sendToEmail) return
const user = await getUser(privateUser.id)
if (!user) return
diff --git a/functions/src/scripts/create-new-notification-preferences.ts b/functions/src/scripts/create-new-notification-preferences.ts
index 2796f2f7..4ba2e25e 100644
--- a/functions/src/scripts/create-new-notification-preferences.ts
+++ b/functions/src/scripts/create-new-notification-preferences.ts
@@ -1,8 +1,8 @@
import * as admin from 'firebase-admin'
import { initAdmin } from './script-init'
-import { getDefaultNotificationSettings } from 'common/user'
import { getAllPrivateUsers, isProd } from 'functions/src/utils'
+import { getDefaultNotificationPreferences } from 'common/user-notification-preferences'
initAdmin()
const firestore = admin.firestore()
@@ -17,7 +17,7 @@ async function main() {
.collection('private-users')
.doc(privateUser.id)
.update({
- notificationPreferences: getDefaultNotificationSettings(
+ notificationPreferences: getDefaultNotificationPreferences(
privateUser.id,
privateUser,
disableEmails
diff --git a/functions/src/scripts/create-private-users.ts b/functions/src/scripts/create-private-users.ts
index 21e117cf..762e801a 100644
--- a/functions/src/scripts/create-private-users.ts
+++ b/functions/src/scripts/create-private-users.ts
@@ -3,8 +3,9 @@ import * as admin from 'firebase-admin'
import { initAdmin } from './script-init'
initAdmin()
-import { getDefaultNotificationSettings, PrivateUser, User } from 'common/user'
+import { PrivateUser, User } from 'common/user'
import { STARTING_BALANCE } from 'common/economy'
+import { getDefaultNotificationPreferences } from 'common/user-notification-preferences'
const firestore = admin.firestore()
@@ -21,7 +22,7 @@ async function main() {
id: user.id,
email,
username,
- notificationPreferences: getDefaultNotificationSettings(user.id),
+ notificationPreferences: getDefaultNotificationPreferences(user.id),
}
if (user.totalDeposits === undefined) {
diff --git a/functions/src/unsubscribe.ts b/functions/src/unsubscribe.ts
index da7b507f..418282c7 100644
--- a/functions/src/unsubscribe.ts
+++ b/functions/src/unsubscribe.ts
@@ -1,79 +1,227 @@
import * as admin from 'firebase-admin'
import { EndpointDefinition } from './api'
-import { getUser } from './utils'
+import { getPrivateUser } from './utils'
import { PrivateUser } from '../../common/user'
+import { NOTIFICATION_DESCRIPTIONS } from '../../common/notification'
+import { notification_preference } from '../../common/user-notification-preferences'
export const unsubscribe: EndpointDefinition = {
opts: { method: 'GET', minInstances: 1 },
handler: async (req, res) => {
const id = req.query.id as string
- let type = req.query.type as string
+ const type = req.query.type as string
if (!id || !type) {
- res.status(400).send('Empty id or type parameter.')
+ res.status(400).send('Empty id or subscription type parameter.')
+ return
+ }
+ console.log(`Unsubscribing ${id} from ${type}`)
+ const notificationSubscriptionType = type as notification_preference
+ if (notificationSubscriptionType === undefined) {
+ res.status(400).send('Invalid subscription type parameter.')
return
}
- if (type === 'market-resolved') type = 'market-resolve'
-
- if (
- ![
- 'market-resolve',
- 'market-comment',
- 'market-answer',
- 'generic',
- 'weekly-trending',
- ].includes(type)
- ) {
- res.status(400).send('Invalid type parameter.')
- return
- }
-
- const user = await getUser(id)
+ const user = await getPrivateUser(id)
if (!user) {
res.send('This user is not currently subscribed or does not exist.')
return
}
- const { name } = user
+ const previousDestinations =
+ user.notificationPreferences[notificationSubscriptionType]
+
+ console.log(previousDestinations)
+ const { email } = user
const update: Partial = {
- ...(type === 'market-resolve' && {
- unsubscribedFromResolutionEmails: true,
- }),
- ...(type === 'market-comment' && {
- unsubscribedFromCommentEmails: true,
- }),
- ...(type === 'market-answer' && {
- unsubscribedFromAnswerEmails: true,
- }),
- ...(type === 'generic' && {
- unsubscribedFromGenericEmails: true,
- }),
- ...(type === 'weekly-trending' && {
- unsubscribedFromWeeklyTrendingEmails: true,
- }),
+ notificationPreferences: {
+ ...user.notificationPreferences,
+ [notificationSubscriptionType]: previousDestinations.filter(
+ (destination) => destination !== 'email'
+ ),
+ },
}
await firestore.collection('private-users').doc(id).update(update)
- if (type === 'market-resolve')
- res.send(
- `${name}, you have been unsubscribed from market resolution emails on Manifold Markets.`
- )
- else if (type === 'market-comment')
- res.send(
- `${name}, you have been unsubscribed from market comment emails on Manifold Markets.`
- )
- else if (type === 'market-answer')
- res.send(
- `${name}, you have been unsubscribed from market answer emails on Manifold Markets.`
- )
- else if (type === 'weekly-trending')
- res.send(
- `${name}, you have been unsubscribed from weekly trending emails on Manifold Markets.`
- )
- else res.send(`${name}, you have been unsubscribed.`)
+ res.send(
+ `
+
+
+
+
+ Manifold Markets 7th Day Anniversary Gift!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ${email} has been unsubscribed from email notifications related to:
+
+
+
+
+ ${NOTIFICATION_DESCRIPTIONS[notificationSubscriptionType].detailed}.
+
+
+
+
+ Click
+ here
+ to manage the rest of your notification settings.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`
+ )
},
}
diff --git a/web/components/notification-settings.tsx b/web/components/notification-settings.tsx
index d18896bd..8730ce7f 100644
--- a/web/components/notification-settings.tsx
+++ b/web/components/notification-settings.tsx
@@ -1,11 +1,7 @@
import React, { memo, ReactNode, useEffect, useState } from 'react'
import { Row } from 'web/components/layout/row'
import clsx from 'clsx'
-import {
- notification_subscription_types,
- notification_destination_types,
- PrivateUser,
-} from 'common/user'
+import { PrivateUser } from 'common/user'
import { updatePrivateUser } from 'web/lib/firebase/users'
import { Col } from 'web/components/layout/col'
import {
@@ -30,6 +26,11 @@ import {
usePersistentState,
} from 'web/hooks/use-persistent-state'
import { safeLocalStorage } from 'web/lib/util/local'
+import { NOTIFICATION_DESCRIPTIONS } from 'common/notification'
+import {
+ notification_destination_types,
+ notification_preference,
+} from 'common/user-notification-preferences'
export function NotificationSettings(props: {
navigateToSection: string | undefined
@@ -38,7 +39,7 @@ export function NotificationSettings(props: {
const { navigateToSection, privateUser } = props
const [showWatchModal, setShowWatchModal] = useState(false)
- const emailsEnabled: Array = [
+ const emailsEnabled: Array = [
'all_comments_on_watched_markets',
'all_replies_to_my_comments_on_watched_markets',
'all_comments_on_contracts_with_shares_in_on_watched_markets',
@@ -74,7 +75,7 @@ export function NotificationSettings(props: {
// 'probability_updates_on_watched_markets',
// 'limit_order_fills',
]
- const browserDisabled: Array = [
+ const browserDisabled: Array = [
'trending_markets',
'profit_loss_updates',
'onboarding_flow',
@@ -83,91 +84,82 @@ export function NotificationSettings(props: {
type SectionData = {
label: string
- subscriptionTypeToDescription: {
- [key in keyof Partial]: string
- }
+ subscriptionTypes: Partial[]
}
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`,
+ subscriptionTypes: [
+ 'all_comments_on_watched_markets',
+ 'all_comments_on_contracts_with_shares_in_on_watched_markets',
// TODO: combine these two
- all_replies_to_my_comments_on_watched_markets:
- 'Only replies to your comments',
- all_replies_to_my_answers_on_watched_markets:
- 'Only replies to your answers',
- // comments_by_followed_users_on_watched_markets: 'By followed users',
- },
+ 'all_replies_to_my_comments_on_watched_markets',
+ 'all_replies_to_my_answers_on_watched_markets',
+ ],
}
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`,
- // answers_by_followed_users_on_watched_markets: 'By followed users',
- // answers_by_market_creator_on_watched_markets: 'By market creator',
- },
+ subscriptionTypes: [
+ 'all_answers_on_watched_markets',
+ 'all_answers_on_contracts_with_shares_in_on_watched_markets',
+ ],
}
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',
- },
+ subscriptionTypes: [
+ 'market_updates_on_watched_markets',
+ 'market_updates_on_watched_markets_with_shares_in',
+ 'resolutions_on_watched_markets',
+ 'resolutions_on_watched_markets_with_shares_in',
+ ],
}
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',
- },
+ subscriptionTypes: [
+ 'your_contract_closed',
+ 'all_comments_on_my_markets',
+ 'all_answers_on_my_markets',
+ 'subsidized_your_market',
+ 'tips_on_your_markets',
+ ],
}
const bonuses: SectionData = {
label: 'Bonuses',
- subscriptionTypeToDescription: {
- betting_streaks: 'Prediction streak bonuses',
- referral_bonuses: 'Referral bonuses from referring users',
- unique_bettors_on_your_contract: 'Unique bettor bonuses on your markets',
- },
+ subscriptionTypes: [
+ 'betting_streaks',
+ 'referral_bonuses',
+ 'unique_bettors_on_your_contract',
+ ],
}
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',
- },
+ subscriptionTypes: [
+ 'loan_income',
+ 'limit_order_fills',
+ 'tips_on_your_comments',
+ ],
}
const userInteractions: SectionData = {
label: 'Users',
- subscriptionTypeToDescription: {
- tagged_user: 'A user tagged you',
- on_new_follow: 'Someone followed you',
- contract_from_followed_user: 'New markets created by users you follow',
- },
+ subscriptionTypes: [
+ 'tagged_user',
+ 'on_new_follow',
+ 'contract_from_followed_user',
+ ],
}
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',
- },
+ subscriptionTypes: [
+ 'trending_markets',
+ 'thank_you_for_purchases',
+ 'onboarding_flow',
+ ],
}
function NotificationSettingLine(props: {
description: string
- subscriptionTypeKey: keyof notification_subscription_types
+ subscriptionTypeKey: notification_preference
destinations: notification_destination_types[]
}) {
const { description, subscriptionTypeKey, destinations } = props
@@ -237,9 +229,7 @@ export function NotificationSettings(props: {
)
}
- const getUsersSavedPreference = (
- key: keyof notification_subscription_types
- ) => {
+ const getUsersSavedPreference = (key: notification_preference) => {
return privateUser.notificationPreferences[key] ?? []
}
@@ -248,17 +238,17 @@ export function NotificationSettings(props: {
data: SectionData
}) {
const { icon, data } = props
- const { label, subscriptionTypeToDescription } = data
+ const { label, subscriptionTypes } = data
const expand =
navigateToSection &&
- Object.keys(subscriptionTypeToDescription).includes(navigateToSection)
+ Object.keys(subscriptionTypes).includes(navigateToSection)
// 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-' +
- Object.keys(subscriptionTypeToDescription).join('-'),
+ Object.keys(subscriptionTypes).join('-'),
store: storageStore(safeLocalStorage()),
})
@@ -287,13 +277,13 @@ export function NotificationSettings(props: {
)}
- {Object.entries(subscriptionTypeToDescription).map(([key, value]) => (
+ {subscriptionTypes.map((subType) => (
))}
From 9aa56dd19300e084361d14cc606ac690aa434f7d Mon Sep 17 00:00:00 2001
From: Ian Philips
Date: Wed, 14 Sep 2022 17:25:17 -0600
Subject: [PATCH 15/37] Only show prev opened notif setting section
---
web/components/notification-settings.tsx | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/web/components/notification-settings.tsx b/web/components/notification-settings.tsx
index 8730ce7f..b806dfb2 100644
--- a/web/components/notification-settings.tsx
+++ b/web/components/notification-settings.tsx
@@ -241,14 +241,12 @@ export function NotificationSettings(props: {
const { label, subscriptionTypes } = data
const expand =
navigateToSection &&
- Object.keys(subscriptionTypes).includes(navigateToSection)
+ subscriptionTypes.includes(navigateToSection as notification_preference)
// 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-' +
- Object.keys(subscriptionTypes).join('-'),
+ key: 'NotificationsSettingsSection-' + subscriptionTypes.join('-'),
store: storageStore(safeLocalStorage()),
})
From ccf02bdba8f565e94fbb01fb9ac5cb7c9de93fc2 Mon Sep 17 00:00:00 2001
From: ingawei <46611122+ingawei@users.noreply.github.com>
Date: Wed, 14 Sep 2022 22:28:40 -0500
Subject: [PATCH 16/37] Inga/admin rules resolve (#880)
* Giving admin permission to resolve all markets that have closed after 7 days.
---
common/envs/constants.ts | 5 ++-
common/envs/prod.ts | 1 +
firestore.rules | 3 +-
functions/src/resolve-market.ts | 9 ++++--
.../answers/answer-resolve-panel.tsx | 20 ++++++++++--
web/components/answers/answers-panel.tsx | 28 ++++++++++-------
web/components/numeric-resolution-panel.tsx | 20 +++++++++---
web/components/resolution-panel.tsx | 11 +++++--
web/pages/[username]/[contractSlug].tsx | 31 +++++++++++++++----
9 files changed, 99 insertions(+), 29 deletions(-)
diff --git a/common/envs/constants.ts b/common/envs/constants.ts
index ba460d58..0502322a 100644
--- a/common/envs/constants.ts
+++ b/common/envs/constants.ts
@@ -21,7 +21,10 @@ export function isWhitelisted(email?: string) {
}
// TODO: Before open sourcing, we should turn these into env vars
-export function isAdmin(email: string) {
+export function isAdmin(email?: string) {
+ if (!email) {
+ return false
+ }
return ENV_CONFIG.adminEmails.includes(email)
}
diff --git a/common/envs/prod.ts b/common/envs/prod.ts
index b3b552eb..6bf781b7 100644
--- a/common/envs/prod.ts
+++ b/common/envs/prod.ts
@@ -74,6 +74,7 @@ export const PROD_CONFIG: EnvConfig = {
'iansphilips@gmail.com', // Ian
'd4vidchee@gmail.com', // D4vid
'federicoruizcassarino@gmail.com', // Fede
+ 'ingawei@gmail.com', //Inga
],
visibility: 'PUBLIC',
diff --git a/firestore.rules b/firestore.rules
index 6f2ea90a..08214b10 100644
--- a/firestore.rules
+++ b/firestore.rules
@@ -14,7 +14,8 @@ service cloud.firestore {
'manticmarkets@gmail.com',
'iansphilips@gmail.com',
'd4vidchee@gmail.com',
- 'federicoruizcassarino@gmail.com'
+ 'federicoruizcassarino@gmail.com',
+ 'ingawei@gmail.com'
]
}
diff --git a/functions/src/resolve-market.ts b/functions/src/resolve-market.ts
index b867b609..44293898 100644
--- a/functions/src/resolve-market.ts
+++ b/functions/src/resolve-market.ts
@@ -16,7 +16,7 @@ import {
groupPayoutsByUser,
Payout,
} from '../../common/payouts'
-import { isManifoldId } from '../../common/envs/constants'
+import { isAdmin, isManifoldId } from '../../common/envs/constants'
import { removeUndefinedProps } from '../../common/util/object'
import { LiquidityProvision } from '../../common/liquidity-provision'
import { APIError, newEndpoint, validate } from './api'
@@ -76,13 +76,18 @@ export const resolvemarket = newEndpoint(opts, async (req, auth) => {
throw new APIError(404, 'No contract exists with the provided ID')
const contract = contractSnap.data() as Contract
const { creatorId, closeTime } = contract
+ const firebaseUser = await admin.auth().getUser(auth.uid)
const { value, resolutions, probabilityInt, outcome } = getResolutionParams(
contract,
req.body
)
- if (creatorId !== auth.uid && !isManifoldId(auth.uid))
+ if (
+ creatorId !== auth.uid &&
+ !isManifoldId(auth.uid) &&
+ !isAdmin(firebaseUser.email)
+ )
throw new APIError(403, 'User is not creator of contract')
if (contract.resolution) throw new APIError(400, 'Contract already resolved')
diff --git a/web/components/answers/answer-resolve-panel.tsx b/web/components/answers/answer-resolve-panel.tsx
index 0a4ac1e1..4594ea35 100644
--- a/web/components/answers/answer-resolve-panel.tsx
+++ b/web/components/answers/answer-resolve-panel.tsx
@@ -11,6 +11,8 @@ import { ResolveConfirmationButton } from '../confirmation-button'
import { removeUndefinedProps } from 'common/util/object'
export function AnswerResolvePanel(props: {
+ isAdmin: boolean
+ isCreator: boolean
contract: FreeResponseContract | MultipleChoiceContract
resolveOption: 'CHOOSE' | 'CHOOSE_MULTIPLE' | 'CANCEL' | undefined
setResolveOption: (
@@ -18,7 +20,14 @@ export function AnswerResolvePanel(props: {
) => void
chosenAnswers: { [answerId: string]: number }
}) {
- const { contract, resolveOption, setResolveOption, chosenAnswers } = props
+ const {
+ contract,
+ resolveOption,
+ setResolveOption,
+ chosenAnswers,
+ isAdmin,
+ isCreator,
+ } = props
const answers = Object.keys(chosenAnswers)
const [isSubmitting, setIsSubmitting] = useState(false)
@@ -76,7 +85,14 @@ export function AnswerResolvePanel(props: {
return (