diff --git a/common/user.ts b/common/user.ts index 90ee2070..8f8e6d0d 100644 --- a/common/user.ts +++ b/common/user.ts @@ -29,6 +29,7 @@ export type PrivateUser = { email?: string unsubscribedFromResolutionEmails?: boolean unsubscribedFromCommentEmails?: boolean + unsubscribedFromAnswerEmails?: boolean initialDeviceToken?: string initialIpAddress?: string } diff --git a/functions/src/create-answer.ts b/functions/src/create-answer.ts index d18a5a2a..5e711e03 100644 --- a/functions/src/create-answer.ts +++ b/functions/src/create-answer.ts @@ -5,7 +5,8 @@ import { Contract } from '../../common/contract' import { User } from '../../common/user' import { getNewMultiBetInfo } from '../../common/new-bet' import { Answer } from '../../common/answer' -import { getValues } from './utils' +import { getContract, getValues } from './utils' +import { sendNewAnswerEmail } from './emails' export const createAnswer = functions.runWith({ minInstances: 1 }).https.onCall( async ( @@ -28,7 +29,7 @@ export const createAnswer = functions.runWith({ minInstances: 1 }).https.onCall( return { status: 'error', message: 'Invalid text' } // Run as transaction to prevent race conditions. - return await firestore.runTransaction(async (transaction) => { + const result = await firestore.runTransaction(async (transaction) => { const userDoc = firestore.doc(`users/${userId}`) const userSnap = await transaction.get(userDoc) if (!userSnap.exists) @@ -103,8 +104,15 @@ export const createAnswer = functions.runWith({ minInstances: 1 }).https.onCall( }) transaction.update(userDoc, { balance: newBalance }) - return { status: 'success', answerId, betId: newBetDoc.id } + return { status: 'success', answerId, betId: newBetDoc.id, answer } }) + + const { answer } = result + const contract = await getContract(contractId) + + if (answer && contract) await sendNewAnswerEmail(answer, contract) + + return result } ) diff --git a/functions/src/email-templates/market-answer.html b/functions/src/email-templates/market-answer.html new file mode 100644 index 00000000..225436ad --- /dev/null +++ b/functions/src/email-templates/market-answer.html @@ -0,0 +1,499 @@ + + + + + + Market answer + + + + + + + + + + + +
+
+ + + + +
+ + + + + + + +
+ Manifold Markets +
+ + + + + + + + + + +
+
+ + {{name}} +
+
+
+ {{answer}} +
+
+ +
+
+
+ +
+
+ + diff --git a/functions/src/email-templates/market-comment.html b/functions/src/email-templates/market-comment.html index aa629646..8a472941 100644 --- a/functions/src/email-templates/market-comment.html +++ b/functions/src/email-templates/market-comment.html @@ -333,11 +333,12 @@ Arial, sans-serif; box-sizing: border-box; font-size: 16px; - white-space: pre-line; margin: 0; " > - {{comment}} + {{comment}} diff --git a/functions/src/emails.ts b/functions/src/emails.ts index da6b3c4b..456a92fe 100644 --- a/functions/src/emails.ts +++ b/functions/src/emails.ts @@ -1,4 +1,5 @@ import _ = require('lodash') +import { Answer } from '../../common/answer' import { getProbability } from '../../common/calculate' import { Comment } from '../../common/comment' import { Contract } from '../../common/contract' @@ -149,14 +150,11 @@ export const sendNewCommentEmail = async ( const privateUser = await getPrivateUser(userId) if ( !privateUser || - privateUser.unsubscribedFromCommentEmails || - !privateUser.email + !privateUser.email || + privateUser.unsubscribedFromCommentEmails ) return - const user = await getUser(userId) - if (!user) return - const { question, creatorUsername, slug } = contract const marketUrl = `https://manifold.markets/${creatorUsername}/${slug}` @@ -184,3 +182,43 @@ export const sendNewCommentEmail = async ( { from } ) } + +export const sendNewAnswerEmail = async ( + answer: Answer, + contract: Contract +) => { + // Send to just the creator for now. + const { creatorId: userId } = contract + const privateUser = await getPrivateUser(userId) + if ( + !privateUser || + !privateUser.email || + privateUser.unsubscribedFromAnswerEmails + ) + return + + const { question, creatorUsername, slug } = contract + const { name, avatarUrl, text } = answer + + const marketUrl = `https://manifold.markets/${creatorUsername}/${slug}` + const unsubscribeUrl = `https://us-central1-${ + isProd ? 'mantic-markets' : 'dev-mantic-markets' + }.cloudfunctions.net/unsubscribe?id=${userId}&type=market-answer` + + const subject = `New answer on ${question}` + const from = `${name} ` + + await sendTemplateEmail( + privateUser.email, + subject, + 'market-answer', + { + name, + avatarUrl: avatarUrl ?? '', + answer: text, + marketUrl, + unsubscribeUrl, + }, + { from } + ) +} diff --git a/functions/src/unsubscribe.ts b/functions/src/unsubscribe.ts index 97201ed8..25b11771 100644 --- a/functions/src/unsubscribe.ts +++ b/functions/src/unsubscribe.ts @@ -16,8 +16,15 @@ export const unsubscribe = functions const { name } = user const update: Partial = { - unsubscribedFromResolutionEmails: type === 'market-resolve', - unsubscribedFromCommentEmails: type === 'market-comment', + ...(type === 'market-resolve' && { + unsubscribedFromResolutionEmails: true, + }), + ...(type === 'market-comment' && { + unsubscribedFromCommentEmails: true, + }), + ...(type === 'market-answer' && { + unsubscribedFromAnswerEmails: true, + }), } await firestore.collection('private-users').doc(id).update(update) @@ -30,6 +37,10 @@ export const unsubscribe = functions 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 res.send(`${name}, you have been unsubscribed.`) } else { res.send('This user is not currently subscribed or does not exist.')