[WIP] Fully customizable notifications (#860)
* Notifications Settings page working * Update import * Linked notification settings to notification rules * Add more subscribe types * It's alive... It's alive, it's moving, it's alive, it's alive, it's alive, it's alive, IT'S ALIVE' * UI Tweaks * Clean up comments * Direct & highlight sections for notif mgmt from emails * Comment cleanup * Comment cleanup, lint * More comment cleanup * Update email templates to predict * Move private user out of getDestinationsForUser * Fix resolution messages * Remove magic * Extract switch to switch-setting * Change tab in url * Show 0 as invested or payout * All emails use unsubscribeUrlpull/873/head
parent
28f0c6b1f8
commit
5c6328ffc2
@ -1,519 +1,316 @@
|
||||
<!DOCTYPE html>
|
||||
<html
|
||||
xmlns="http://www.w3.org/1999/xhtml"
|
||||
xmlns:v="urn:schemas-microsoft-com:vml"
|
||||
xmlns:o="urn:schemas-microsoft-com:office:office"
|
||||
>
|
||||
<head>
|
||||
<title>7th Day Anniversary Gift!</title>
|
||||
<!--[if !mso]><!-->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<!--<![endif]-->
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||||
<style type="text/css">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
|
||||
xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<title>Manifold Markets 7th Day Anniversary Gift!</title>
|
||||
<!--[if !mso]><!-->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<!--<![endif]-->
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<style type="text/css">
|
||||
#outlook a {
|
||||
padding: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
table,
|
||||
td {
|
||||
border-collapse: collapse;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
border-collapse: collapse;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
height: auto;
|
||||
line-height: 100%;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
border: 0;
|
||||
height: auto;
|
||||
line-height: 100%;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
}
|
||||
|
||||
p {
|
||||
display: block;
|
||||
margin: 13px 0;
|
||||
display: block;
|
||||
margin: 13px 0;
|
||||
}
|
||||
</style>
|
||||
<!--[if mso]>
|
||||
<noscript>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG />
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml> </noscript
|
||||
>z
|
||||
<![endif]-->
|
||||
<!--[if lte mso 11]>
|
||||
<style type="text/css">
|
||||
.mj-outlook-group-fix {
|
||||
width: 100% !important;
|
||||
}
|
||||
</style>
|
||||
<![endif]-->
|
||||
<style type="text/css">
|
||||
@media only screen and (min-width: 480px) {
|
||||
.mj-column-per-100 {
|
||||
width: 100% !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
<!--[if mso]>
|
||||
<noscript>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG/>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
</noscript>
|
||||
<![endif]-->
|
||||
<!--[if lte mso 11]>
|
||||
<style type="text/css">
|
||||
.mj-outlook-group-fix { width:100% !important; }
|
||||
</style>
|
||||
<![endif]-->
|
||||
<style type="text/css">
|
||||
@media only screen and (min-width:480px) {
|
||||
.mj-column-per-100 {
|
||||
width: 100% !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style media="screen and (min-width:480px)">
|
||||
</style>
|
||||
<style media="screen and (min-width:480px)">
|
||||
.moz-text-html .mj-column-per-100 {
|
||||
width: 100% !important;
|
||||
max-width: 100%;
|
||||
width: 100% !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
</style>
|
||||
<style type="text/css">
|
||||
[owa] .mj-column-per-100 {
|
||||
width: 100% !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
@media only screen and (max-width: 480px) {
|
||||
table.mj-full-width-mobile {
|
||||
width: 100% !important;
|
||||
}
|
||||
max-width: 100%;
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
@media only screen and (max-width:480px) {
|
||||
table.mj-full-width-mobile {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
td.mj-full-width-mobile {
|
||||
width: auto !important;
|
||||
}
|
||||
td.mj-full-width-mobile {
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body style="word-spacing: normal; background-color: #f4f4f4">
|
||||
<div style="background-color: #f4f4f4">
|
||||
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div
|
||||
style="
|
||||
background: #ffffff;
|
||||
background-color: #ffffff;
|
||||
margin: 0px auto;
|
||||
max-width: 600px;
|
||||
"
|
||||
>
|
||||
<table
|
||||
align="center"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
role="presentation"
|
||||
style="background: #ffffff; background-color: #ffffff; width: 100%"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
direction: ltr;
|
||||
font-size: 0px;
|
||||
padding: 0px 0px 0px 0px;
|
||||
padding-bottom: 0px;
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
padding-top: 0px;
|
||||
text-align: center;
|
||||
"
|
||||
>
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
|
||||
<div
|
||||
class="mj-column-per-100 mj-outlook-group-fix"
|
||||
style="
|
||||
font-size: 0px;
|
||||
text-align: left;
|
||||
direction: ltr;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 100%;
|
||||
"
|
||||
>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
role="presentation"
|
||||
style="vertical-align: top"
|
||||
width="100%"
|
||||
>
|
||||
<body style="word-spacing:normal;background-color:#F4F4F4;">
|
||||
<div style="background-color:#F4F4F4;">
|
||||
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#ffffff" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation"
|
||||
style="background:#ffffff;background-color:#ffffff;width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style="direction:ltr;font-size:0px;padding:0px 0px 0px 0px;padding-bottom:0px;padding-left:0px;padding-right:0px;padding-top:0px;text-align:center;">
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix"
|
||||
style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;"
|
||||
width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center"
|
||||
style="font-size:0px;padding:0px 25px 0px 25px;padding-top:0px;padding-right:25px;padding-bottom:0px;padding-left:25px;word-break:break-word;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation"
|
||||
style="border-collapse:collapse;border-spacing:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
align="center"
|
||||
style="
|
||||
font-size: 0px;
|
||||
padding: 0px 25px 0px 25px;
|
||||
padding-top: 0px;
|
||||
padding-right: 25px;
|
||||
padding-bottom: 0px;
|
||||
padding-left: 25px;
|
||||
word-break: break-word;
|
||||
"
|
||||
>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
role="presentation"
|
||||
style="
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0px;
|
||||
"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 550px">
|
||||
<a
|
||||
href="https://manifold.markets/home"
|
||||
target="_blank"
|
||||
><img
|
||||
alt=""
|
||||
height="auto"
|
||||
src="https://03jlj.mjt.lu/img/03jlj/b/u71/sjvu.gif"
|
||||
style="
|
||||
border: none;
|
||||
display: block;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
height: auto;
|
||||
width: 100%;
|
||||
font-size: 13px;
|
||||
"
|
||||
width="550"
|
||||
/></a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
align="left"
|
||||
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: 18px;
|
||||
letter-spacing: normal;
|
||||
line-height: 1;
|
||||
text-align: left;
|
||||
color: #000000;
|
||||
"
|
||||
>
|
||||
<p
|
||||
class="text-build-content"
|
||||
style="
|
||||
text-align: center;
|
||||
margin: 10px 0;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
"
|
||||
data-testid="4XoHRGw1Y"
|
||||
>
|
||||
<span
|
||||
style="
|
||||
color: #000000;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 18px;
|
||||
"
|
||||
>Hopefully you haven't gambled all your M$
|
||||
away already... but if you have I bring good
|
||||
news! Click the link below to recieve a one time
|
||||
gift of M$ 500 to your account!</span
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
align="center"
|
||||
style="
|
||||
font-size: 0px;
|
||||
padding: 10px 25px 25px 25px;
|
||||
padding-top: 10px;
|
||||
padding-right: 25px;
|
||||
padding-bottom: 25px;
|
||||
padding-left: 25px;
|
||||
word-break: break-word;
|
||||
"
|
||||
>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
role="presentation"
|
||||
style="
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0px;
|
||||
"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width: 550px">
|
||||
<a href="{{manalink}}" target="_blank">
|
||||
<img
|
||||
alt="Get M$500"
|
||||
height="auto"
|
||||
src="https://03jlj.mjt.lu/img/03jlj/b/u71/sjgt.png"
|
||||
style="
|
||||
border: none;
|
||||
display: block;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
height: auto;
|
||||
width: 100%;
|
||||
font-size: 13px;
|
||||
"
|
||||
width="550"
|
||||
/></a>
|
||||
<< /td>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
align="left"
|
||||
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: 18px;
|
||||
letter-spacing: normal;
|
||||
line-height: 1;
|
||||
text-align: left;
|
||||
color: #000000;
|
||||
"
|
||||
>
|
||||
<p
|
||||
class="text-build-content"
|
||||
style="
|
||||
line-height: 23px;
|
||||
text-align: center;
|
||||
margin: 10px 0;
|
||||
margin-top: 10px;
|
||||
"
|
||||
data-testid="3Q8BP69fq"
|
||||
>
|
||||
<span
|
||||
style="
|
||||
color: #000000;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-size: 18px;
|
||||
"
|
||||
>If you are still engaging with our markets then
|
||||
at this point you might as well join our </span
|
||||
><a
|
||||
class="link-build-content"
|
||||
style="color: inherit; text-decoration: none"
|
||||
target="_blank"
|
||||
href="https://discord.gg/VARzUpyCSa"
|
||||
><span
|
||||
style="
|
||||
color: #0c21bf;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
"
|
||||
><u>Discord server</u></span
|
||||
><span
|
||||
style="
|
||||
color: #000000;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
"
|
||||
><u>.</u>
|
||||
</span></a
|
||||
><span
|
||||
style="
|
||||
color: #000000;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
"
|
||||
>You can always leave if you dont like it but
|
||||
I'd be willing to make a market betting
|
||||
you'll stay.</span
|
||||
>
|
||||
</p>
|
||||
<p
|
||||
class="text-build-content"
|
||||
data-testid="3Q8BP69fq"
|
||||
style="margin: 10px 0"
|
||||
></p>
|
||||
<br />
|
||||
<p
|
||||
class="text-build-content"
|
||||
data-testid="3Q8BP69fq"
|
||||
style="margin: 10px 0"
|
||||
>
|
||||
<span
|
||||
style="
|
||||
color: #000000;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
"
|
||||
>Cheers,</span
|
||||
>
|
||||
</p>
|
||||
<p
|
||||
class="text-build-content"
|
||||
data-testid="3Q8BP69fq"
|
||||
style="margin: 10px 0"
|
||||
>
|
||||
<span
|
||||
style="
|
||||
color: #000000;
|
||||
font-family: Arial;
|
||||
font-size: 18px;
|
||||
"
|
||||
>David from Manifold</span
|
||||
>
|
||||
</p>
|
||||
<p
|
||||
class="text-build-content"
|
||||
data-testid="3Q8BP69fq"
|
||||
style="margin: 10px 0; margin-bottom: 10px"
|
||||
></p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="width:550px;"><a href="https://manifold.markets/home" target="_blank"><img
|
||||
alt="" height="auto" src="https://i.imgur.com/8EP8Y8q.gif"
|
||||
style="border:none;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;"
|
||||
width="550"></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="margin: 0px auto; max-width: 600px">
|
||||
<table
|
||||
align="center"
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
role="presentation"
|
||||
style="width: 100%"
|
||||
>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
style="
|
||||
direction: ltr;
|
||||
font-size: 0px;
|
||||
padding: 20px 0px 20px 0px;
|
||||
text-align: center;
|
||||
"
|
||||
>
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
|
||||
<div
|
||||
class="mj-column-per-100 mj-outlook-group-fix"
|
||||
style="
|
||||
font-size: 0px;
|
||||
text-align: left;
|
||||
direction: ltr;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 100%;
|
||||
"
|
||||
>
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
role="presentation"
|
||||
width="100%"
|
||||
>
|
||||
<tbody>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"
|
||||
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:18px;letter-spacing:normal;line-height:1;text-align:left;color:#000000;">
|
||||
<p class="text-build-content"
|
||||
style="line-height: 24px; margin: 10px 0; margin-top: 10px; margin-bottom: 10px;"
|
||||
data-testid="4XoHRGw1Y"><span
|
||||
style="color:#000000;font-family:Arial, Helvetica, sans-serif;font-size:18px;">
|
||||
Hi {{name}},</span></p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"
|
||||
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:18px;letter-spacing:normal;line-height:1;text-align:left;color:#000000;">
|
||||
<p class="text-build-content"
|
||||
style="line-height: 24px; margin: 10px 0; margin-top: 10px; margin-bottom: 10px;"
|
||||
data-testid="4XoHRGw1Y"><span
|
||||
style="color:#000000;font-family:Arial, Helvetica, sans-serif;font-size:18px;">Thanks for
|
||||
using Manifold Markets. Running low
|
||||
on mana (M$)? Click the link below to receive a one time gift of M$500!</span></p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr>
|
||||
<td style="border-radius: 2px;" bgcolor="#4337c9">
|
||||
<a href="{{manalink}}" target="_blank"
|
||||
style="padding: 12px 16px; border: 1px solid #4337c9;border-radius: 16px;font-family: Helvetica, Arial, sans-serif;font-size: 24px; color: #ffffff;text-decoration: none;font-weight:bold;display: inline-block;">
|
||||
Claim M$500
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left"
|
||||
style="font-size:0px;padding:15px 25px 0px 25px;padding-top:15px;padding-right:25px;padding-bottom:0px;padding-left:25px;word-break:break-word;">
|
||||
<div
|
||||
style="font-family:Arial, sans-serif;font-size:18px;letter-spacing:normal;line-height:1;text-align:left;color:#000000;">
|
||||
<p class="text-build-content" style="line-height: 23px; margin: 10px 0; margin-top: 10px;"
|
||||
data-testid="3Q8BP69fq"><span style="font-family:Arial, sans-serif;font-size:18px;">Did
|
||||
you know, besides making correct predictions, there are
|
||||
plenty of other ways to earn mana?</span></p>
|
||||
<ul>
|
||||
<li style="line-height:23px;"><span
|
||||
style="font-family:Arial, sans-serif;font-size:18px;">Predicting
|
||||
consecutive days to earn streak rewards</span></li>
|
||||
<li style="line-height:23px;"><span
|
||||
style="font-family:Arial, sans-serif;font-size:18px;">Receiving
|
||||
tips on comments and markets</span></li>
|
||||
<li style="line-height:23px;"><span
|
||||
style="font-family:Arial, sans-serif;font-size:18px;">Unique
|
||||
predictor bonus for each user who predicts on your
|
||||
markets</span></li>
|
||||
<li style="line-height:23px;"><span style="font-family:Arial, sans-serif;font-size:18px;"><a
|
||||
class="link-build-content" style="color:inherit;; text-decoration: none;"
|
||||
target="_blank" href="https://manifold.markets/referrals"><span
|
||||
style="color:#55575d;font-family:Arial;font-size:18px;"><u>Referring
|
||||
friends</u></span></a></span></li>
|
||||
<li style="line-height:23px;"><a class="link-build-content"
|
||||
style="color:inherit;; text-decoration: none;" target="_blank"
|
||||
href="https://manifold.markets/group/bugs?s=most-traded"><span
|
||||
style="color:#55575d;font-family:Arial;font-size:18px;"><u>Reporting
|
||||
bugs</u></span></a><span style="font-family:Arial, sans-serif;font-size:18px;">
|
||||
and </span><a class="link-build-content" style="color:inherit;; text-decoration: none;"
|
||||
target="_blank"
|
||||
href="https://manifold.markets/group/manifold-features-25bad7c7792e/chat?s=most-traded"><span
|
||||
style="color:#55575d;font-family:Arial;font-size:18px;"><u>giving
|
||||
feedback</u></span></a></li>
|
||||
</ul>
|
||||
<p class="text-build-content" data-testid="3Q8BP69fq" style="margin: 10px 0;"> </p>
|
||||
<p class="text-build-content" data-testid="3Q8BP69fq" style="margin: 10px 0;"><span
|
||||
style="color:#000000;font-family:Arial;font-size:18px;">Cheers,</span>
|
||||
</p>
|
||||
<p class="text-build-content" data-testid="3Q8BP69fq" style="margin: 10px 0;"><span
|
||||
style="color:#000000;font-family:Arial;font-size:18px;">David
|
||||
from Manifold</span></p>
|
||||
<p class="text-build-content" data-testid="3Q8BP69fq"
|
||||
style="margin: 10px 0; margin-bottom: 10px;"> </p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:0 0 20px 0;text-align:center;">
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix"
|
||||
style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;"
|
||||
width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center" style="font-size:0px;padding:0px;word-break:break-word;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation"
|
||||
style="border-collapse:collapse;border-spacing:0px;">
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
|
||||
<div style="margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation"
|
||||
style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:20px 0px 20px 0px;text-align:center;">
|
||||
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix"
|
||||
style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="vertical-align: top; padding: 0">
|
||||
<table
|
||||
border="0"
|
||||
cellpadding="0"
|
||||
cellspacing="0"
|
||||
role="presentation"
|
||||
width="100%"
|
||||
>
|
||||
<td style="vertical-align:top;padding:0;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
align="center"
|
||||
style="
|
||||
<tr>
|
||||
<td align="center" style="
|
||||
font-size: 0px;
|
||||
padding: 10px 25px;
|
||||
padding-top: 0px;
|
||||
padding-bottom: 0px;
|
||||
word-break: break-word;
|
||||
"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
font-family: Arial, sans-serif;
|
||||
">
|
||||
<div style="
|
||||
font-family: Ubuntu, Helvetica, 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="
|
||||
">
|
||||
<p style="margin: 10px 0">
|
||||
This e-mail has been sent to {{name}},
|
||||
<a href="{{unsubscribeUrl}}" style="
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
"
|
||||
target="_blank"
|
||||
>click here to unsubscribe</a
|
||||
>.
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
" target="_blank">click here to manage your notifications</a>.
|
||||
</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"
|
||||
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;">
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]></td></tr></table><![endif]-->
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -0,0 +1,30 @@
|
||||
import * as admin from 'firebase-admin'
|
||||
|
||||
import { initAdmin } from './script-init'
|
||||
import { getDefaultNotificationSettings } from 'common/user'
|
||||
import { getAllPrivateUsers, isProd } from 'functions/src/utils'
|
||||
initAdmin()
|
||||
|
||||
const firestore = admin.firestore()
|
||||
|
||||
async function main() {
|
||||
const privateUsers = await getAllPrivateUsers()
|
||||
const disableEmails = !isProd()
|
||||
await Promise.all(
|
||||
privateUsers.map((privateUser) => {
|
||||
if (!privateUser.id) return Promise.resolve()
|
||||
return firestore
|
||||
.collection('private-users')
|
||||
.doc(privateUser.id)
|
||||
.update({
|
||||
notificationSubscriptionTypes: getDefaultNotificationSettings(
|
||||
privateUser.id,
|
||||
privateUser,
|
||||
disableEmails
|
||||
),
|
||||
})
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (require.main === module) main().then(() => process.exit())
|
@ -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>
|
||||
)
|
||||
}
|
@ -0,0 +1,320 @@
|
||||
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 {
|
||||
notification_subscription_types,
|
||||
notification_destination_types,
|
||||
} from 'common/user'
|
||||
import { updatePrivateUser } from 'web/lib/firebase/users'
|
||||
import { Col } from 'web/components/layout/col'
|
||||
import {
|
||||
CashIcon,
|
||||
ChatIcon,
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
CurrencyDollarIcon,
|
||||
InboxInIcon,
|
||||
InformationCircleIcon,
|
||||
LightBulbIcon,
|
||||
TrendingUpIcon,
|
||||
UserIcon,
|
||||
UsersIcon,
|
||||
} 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'
|
||||
import { SwitchSetting } from 'web/components/switch-setting'
|
||||
|
||||
export function NotificationSettings(props: {
|
||||
navigateToSection: string | undefined
|
||||
}) {
|
||||
const { navigateToSection } = props
|
||||
const privateUser = usePrivateUser()
|
||||
const [showWatchModal, setShowWatchModal] = useState(false)
|
||||
|
||||
if (!privateUser || !privateUser.notificationSubscriptionTypes) {
|
||||
return <LoadingIndicator spinnerClassName={'border-gray-500 h-4 w-4'} />
|
||||
}
|
||||
|
||||
const emailsEnabled: Array<keyof notification_subscription_types> = [
|
||||
'all_comments_on_watched_markets',
|
||||
'all_replies_to_my_comments_on_watched_markets',
|
||||
'all_comments_on_contracts_with_shares_in_on_watched_markets',
|
||||
|
||||
'all_answers_on_watched_markets',
|
||||
'all_replies_to_my_answers_on_watched_markets',
|
||||
'all_answers_on_contracts_with_shares_in_on_watched_markets',
|
||||
|
||||
'your_contract_closed',
|
||||
'all_comments_on_my_markets',
|
||||
'all_answers_on_my_markets',
|
||||
|
||||
'resolutions_on_watched_markets_with_shares_in',
|
||||
'resolutions_on_watched_markets',
|
||||
|
||||
'tagged_user',
|
||||
'trending_markets',
|
||||
'onboarding_flow',
|
||||
'thank_you_for_purchases',
|
||||
|
||||
// TODO: add these
|
||||
// 'contract_from_followed_user',
|
||||
// 'referral_bonuses',
|
||||
// 'unique_bettors_on_your_contract',
|
||||
// 'tips_on_your_markets',
|
||||
// 'tips_on_your_comments',
|
||||
// 'subsidized_your_market',
|
||||
// 'on_new_follow',
|
||||
// maybe the following?
|
||||
// 'profit_loss_updates',
|
||||
// 'probability_updates_on_watched_markets',
|
||||
// 'limit_order_fills',
|
||||
]
|
||||
const browserDisabled: Array<keyof notification_subscription_types> = [
|
||||
'trending_markets',
|
||||
'profit_loss_updates',
|
||||
'onboarding_flow',
|
||||
'thank_you_for_purchases',
|
||||
]
|
||||
|
||||
type sectionData = {
|
||||
label: string
|
||||
subscriptionTypeToDescription: {
|
||||
[key in keyof Partial<notification_subscription_types>]: string
|
||||
}
|
||||
}
|
||||
|
||||
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 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 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 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 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',
|
||||
},
|
||||
}
|
||||
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 = (
|
||||
description: string,
|
||||
key: keyof notification_subscription_types,
|
||||
value: notification_destination_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!'
|
||||
const highlight = navigateToSection === key
|
||||
|
||||
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,
|
||||
])
|
||||
|
||||
return (
|
||||
<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>
|
||||
</Row>
|
||||
<Row className={'gap-4'}>
|
||||
{!browserDisabled.includes(key) && (
|
||||
<SwitchSetting
|
||||
checked={inAppEnabled}
|
||||
onChange={setInAppEnabled}
|
||||
label={'In App'}
|
||||
/>
|
||||
)}
|
||||
{emailsEnabled.includes(key) && (
|
||||
<SwitchSetting
|
||||
checked={emailEnabled}
|
||||
onChange={setEmailEnabled}
|
||||
label={'Emails'}
|
||||
/>
|
||||
)}
|
||||
</Row>
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
const getUsersSavedPreference = (
|
||||
key: keyof notification_subscription_types
|
||||
) => {
|
||||
return privateUser.notificationSubscriptionTypes[key] ?? []
|
||||
}
|
||||
|
||||
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={clsx('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(subscriptionTypeToDescription).map(([key, value]) =>
|
||||
NotificationSettingLine(
|
||||
value,
|
||||
key as keyof notification_subscription_types,
|
||||
getUsersSavedPreference(
|
||||
key as keyof notification_subscription_types
|
||||
)
|
||||
)
|
||||
)}
|
||||
</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>
|
||||
{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)}
|
||||
{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'} />, userInteractions)}
|
||||
{Section(<InboxInIcon className={'h-6 w-6'} />, generalOther)}
|
||||
<WatchMarketModal open={showWatchModal} setOpen={setShowWatchModal} />
|
||||
</Col>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
import { Switch } from '@headlessui/react'
|
||||
import clsx from 'clsx'
|
||||
import React from 'react'
|
||||
|
||||
export const SwitchSetting = (props: {
|
||||
checked: boolean
|
||||
onChange: (checked: boolean) => void
|
||||
label: string
|
||||
}) => {
|
||||
const { checked, onChange, label } = props
|
||||
return (
|
||||
<Switch.Group as="div" className="flex items-center">
|
||||
<Switch
|
||||
checked={checked}
|
||||
onChange={onChange}
|
||||
className={clsx(
|
||||
checked ? '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(
|
||||
checked ? '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">{label}</span>
|
||||
</Switch.Label>
|
||||
</Switch.Group>
|
||||
)
|
||||
}
|
Loading…
Reference in new issue