FEATURE: Add email dark mode (#16104)

implement dark mode emails when `SiteSetting.dark_mode_emails_active` is active.
This commit is contained in:
Isaac Janzen 2022-04-11 12:27:50 -05:00 committed by GitHub
parent a0ef25f4f0
commit 6c0abe15e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 104 additions and 8 deletions

View File

@ -29,11 +29,69 @@ module EmailHelper
EmailStyle.new.html EmailStyle.new.html
.sub('%{email_content}') { capture { yield } } .sub('%{email_content}') { capture { yield } }
.gsub('%{html_lang}', html_lang) .gsub('%{html_lang}', html_lang)
.gsub('%{dark_mode_meta_tags}', SiteSetting.dark_mode_emails_active ? dark_mode_meta_tags : "")
.gsub('%{dark_mode_styles}', SiteSetting.dark_mode_emails_active ? dark_mode_styles : "")
.html_safe .html_safe
end end
protected protected
def dark_mode_meta_tags
"
<meta name='color-scheme' content='light dark' />
<meta name='supported-color-schemes' content='light dark' />
"
end
def dark_mode_styles
"
<style>
@media (prefers-color-scheme: dark) {
html {
background: #151515 !important;
}
h1,
h2,
h3,
h4,
h5,
h6,
p,
span {
color: #dddddd !important;
}
[dm='light-img'] {
display: none !important;
}
[dm='dark-img'] {
display: block !important;
}
[dm='text-color'] {
color: #dddddd;
}
[dm='header'] {
background: #151515 !important;
}
[dm='body'] {
background: #222222 !important;
color: #dddddd !important;
}
[dm='body_primary'] {
background: #062e3d !important;
color: #dddddd !important;
}
}
</style>
"
end
def extract_details(topic) def extract_details(topic)
if SiteSetting.private_email? if SiteSetting.private_email?
[topic.slugless_url, private_topic_title(topic)] [topic.slugless_url, private_topic_title(topic)]

View File

@ -10,6 +10,7 @@
name="viewport" name="viewport"
content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no, width=device-width" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no, width=device-width"
/> />
%{dark_mode_meta_tags}
<!-- prevent ios zooming + autoscaling --> <!-- prevent ios zooming + autoscaling -->
<meta name="x-apple-disable-message-reformatting" /> <meta name="x-apple-disable-message-reformatting" />
<title></title> <title></title>
@ -45,5 +46,6 @@
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</div> </div>
%{dark_mode_styles}
</body> </body>
</html> </html>

View File

@ -104,7 +104,7 @@
</tbody> </tbody>
</table> </table>
<table class="with-dir" style="vertical-align:top;width:100%"> <table class="digest-topic-title-wrapper with-dir" style="vertical-align:top;width:100%">
<tbody> <tbody>
<tr> <tr>
<td class="with-dir" style="padding:<%= rtl? ? '0 16px 8px 8px' : '0 8px 8px 16px' %>;width:100%;"> <td class="with-dir" style="padding:<%= rtl? ? '0 16px 8px 8px' : '0 8px 8px 16px' %>;width:100%;">
@ -121,7 +121,7 @@
</tbody> </tbody>
</table> </table>
<table class="with-dir" style="padding:0;position:relative;vertical-align:top;width:100%"> <table class="digest-topic-title-wrapper with-dir" style="padding:0;position:relative;vertical-align:top;width:100%">
<tbody> <tbody>
<tr> <tr>
<td class="digest-topic-op" style="color:#0a0a0a;line-height:1.3;margin:0 auto;padding:<%= rtl? ? '0 16px 0 0' : '0 0 0 16px' %>;width:50px;vertical-align:top;"> <td class="digest-topic-op" style="color:#0a0a0a;line-height:1.3;margin:0 auto;padding:<%= rtl? ? '0 16px 0 0' : '0 0 0 16px' %>;width:50px;vertical-align:top;">
@ -156,15 +156,17 @@
</table> </table>
<%- end %> <%- end %>
<table class="digest-topic-stats with-dir" style="padding:0;vertical-align:top;width:100%; margin-top:20px;"> <table class="digest-topic-stats with-dir" style="padding:0;vertical-align:top;width:100%; padding-top:20px;">
<tbody> <tbody>
<tr> <tr>
<td class="digest-topic-stat" style="padding:<%= rtl? ? '0 16px 16px 8px' : '0 8px 16px 16px' %>;white-space:nowrap;vertical-align:top;width:75px"> <td class="digest-topic-stat" style="padding:<%= rtl? ? '0 16px 16px 8px' : '0 8px 16px 16px' %>;white-space:nowrap;vertical-align:top;width:75px">
<img class="digest-icon" src="<%= email_image_url 'heart.png' -%>" style="clear:both;display:inline-block;float:<%= rtl? ? 'right' : 'left' %>;height:20px;margin:0;max-width:100%;opacity:.4;outline:0;text-decoration:none;width:auto" alt="likes"> <img class="digest-icon" src="<%= email_image_url 'heart.png' -%>" style="clear:both;display:inline-block;float:<%= rtl? ? 'right' : 'left' %>;height:20px;margin:0;max-width:100%;opacity:.4;outline:0;text-decoration:none;width:auto" alt="likes" dm="light-img">
<img class="digest-icon" src="<%= email_image_url 'heart_dark.png' -%>" style="clear:both;display:inline-block;float:<%= rtl? ? 'right' : 'left' %>;height:20px;margin:0;max-width:100%;opacity:.4;outline:0;text-decoration:none;width:auto;display:none;" alt="likes" dm="dark-img">
<span style="color:#8f8f8f;float:<%= rtl? ? 'right' : 'left' %>;line-height:1.3;margin:0 5px 10px 5px;padding:0;font-weight:400;">&nbsp;<%= t.like_count -%></span> <span style="color:#8f8f8f;float:<%= rtl? ? 'right' : 'left' %>;line-height:1.3;margin:0 5px 10px 5px;padding:0;font-weight:400;">&nbsp;<%= t.like_count -%></span>
</td> </td>
<td class="digest-topic-stat" style="padding:0 8px 16px 8px;white-space:nowrap;vertical-align:top;width:75px"> <td class="digest-topic-stat" style="padding:0 8px 16px 8px;white-space:nowrap;vertical-align:top;width:75px">
<img class="digest-icon" src="<%= email_image_url 'comment.png' -%>" style="clear:none;display:inline-block;float:<%= rtl? ? 'right' : 'left' %>;height:20px;margin:0;max-width:100%;opacity:.4;outline:0;text-decoration:none;width:auto" alt="replies"> <img class="digest-icon" src="<%= email_image_url 'comment.png' -%>" style="clear:none;display:inline-block;float:<%= rtl? ? 'right' : 'left' %>;height:20px;margin:0;max-width:100%;opacity:.4;outline:0;text-decoration:none;width:auto" alt="replies" dm="light-img">
<img class="digest-icon" src="<%= email_image_url 'comment_dark.png' -%>" style="clear:both;display:inline-block;float:<%= rtl? ? 'right' : 'left' %>;height:20px;margin:0;max-width:100%;opacity:.4;outline:0;text-decoration:none;width:auto;display:none;" alt="likes" dm="dark-img">
<span style="color:#8f8f8f;float:<%= rtl? ? 'right' : 'left' %>;line-height:1.3;margin:0 5px 10px 5px;padding:0;font-weight:400;">&nbsp;<%= t.posts_count - 1 -%></span> <span style="color:#8f8f8f;float:<%= rtl? ? 'right' : 'left' %>;line-height:1.3;margin:0 5px 10px 5px;padding:0;font-weight:400;">&nbsp;<%= t.posts_count - 1 -%></span>
</td> </td>
<td class="digest-topic-posters" style="padding:0 8px 16px 8px;white-space:nowrap;vertical-align:top;"> <td class="digest-topic-posters" style="padding:0 8px 16px 8px;white-space:nowrap;vertical-align:top;">
@ -245,7 +247,8 @@
<tbody> <tbody>
<tr> <tr>
<td style="border-spacing:0;padding:0;color:#0a0a0a;line-height:1.3;padding:<%= rtl? ? '0 65px 0 0' : '0 0 0 65px' %>;"> <td style="border-spacing:0;padding:0;color:#0a0a0a;line-height:1.3;padding:<%= rtl? ? '0 65px 0 0' : '0 0 0 65px' %>;">
<img src="<%= email_image_url 'right_triangle.png' -%>" style="clear:both;display:block;height:20px;width:20px;outline:0;text-decoration:none;" alt=""> <img src="<%= email_image_url 'right_triangle.png' -%>" style="clear:both;display:block;height:20px;width:20px;outline:0;text-decoration:none;" alt="" dm="light-img">
<img src="<%= email_image_url 'right_triangle_dark.png' -%>" style="clear:both;display:block;height:20px;width:20px;outline:0;text-decoration:none;display:none;" alt="" dm="dark-img">
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -330,11 +333,13 @@
<% end %> <% end %>
</td> </td>
<td class="digest-new-topic-stat with-dir" style="padding:8px;"> <td class="digest-new-topic-stat with-dir" style="padding:8px;">
<img class="digest-icon" src="<%= email_image_url 'heart.png' -%>" style="clear:both;display:inline-block;float:<%= rtl? ? 'right' : 'left' %>;height:20px;margin:0;max-width:100%;opacity:.4;outline:0;text-decoration:none;width:auto" alt="likes"> <img class="digest-icon" src="<%= email_image_url 'heart.png' -%>" style="clear:both;display:inline-block;float:<%= rtl? ? 'right' : 'left' %>;height:20px;margin:0;max-width:100%;opacity:.4;outline:0;text-decoration:none;width:auto" alt="likes" dm="light-img">
<img class="digest-icon" src="<%= email_image_url 'heart_dark.png' -%>" style="clear:both;display:inline-block;float:<%= rtl? ? 'right' : 'left' %>;height:20px;margin:0;max-width:100%;opacity:.4;outline:0;text-decoration:none;width:auto;display:none;" alt="likes" dm="dark-img">
<p style="color:#8f8f8f;float:<%= rtl? ? 'right' : 'left' %>;line-height:1.3;margin:0 5px 10px 5px;padding:0;font-weight:400;"><%= t.like_count -%></p> <p style="color:#8f8f8f;float:<%= rtl? ? 'right' : 'left' %>;line-height:1.3;margin:0 5px 10px 5px;padding:0;font-weight:400;"><%= t.like_count -%></p>
</td> </td>
<td class="digest-new-topic-stat digest-replies with-dir" style="padding:8px;"> <td class="digest-new-topic-stat digest-replies with-dir" style="padding:8px;">
<img class="digest-icon" src="<%= email_image_url 'comment.png' -%>" style="clear:none;display:inline-block;float:<%= rtl? ? 'right' : 'left' %>;height:20px;margin:0;max-width:100%;opacity:.4;outline:0;text-decoration:none;width:auto" alt="replies"> <img class="digest-icon" src="<%= email_image_url 'comment.png' -%>" style="clear:none;display:inline-block;float:<%= rtl? ? 'right' : 'left' %>;height:20px;margin:0;max-width:100%;opacity:.4;outline:0;text-decoration:none;width:auto" alt="replies" dm="light-img">
<img class="digest-icon" src="<%= email_image_url 'comment_dark.png' -%>" style="clear:none;display:inline-block;float:<%= rtl? ? 'right' : 'left' %>;height:20px;margin:0;max-width:100%;opacity:.4;outline:0;text-decoration:none;width:auto;display:none;" alt="replies" dm="dark-img">
<p style="color:#8f8f8f;float:<%= rtl? ? 'right' : 'left' %>;line-height:1.3;margin:0 5px 10px 5px;padding:0;font-weight:400;"><%= t.posts_count - 1 -%></p> <p style="color:#8f8f8f;float:<%= rtl? ? 'right' : 'left' %>;line-height:1.3;margin:0 5px 10px 5px;padding:0;font-weight:400;"><%= t.posts_count - 1 -%></p>
</td> </td>
</tr> </tr>

View File

@ -283,6 +283,9 @@ basic:
type: enum type: enum
enum: "ColorSchemeSetting" enum: "ColorSchemeSetting"
client: true client: true
dark_mode_emails_active:
default: false
hidden: true
relative_date_duration: relative_date_duration:
client: true client: true
default: 30 default: 30

View File

@ -238,6 +238,7 @@ module Email
onebox_styles onebox_styles
plugin_styles plugin_styles
dark_mode_styles if SiteSetting.dark_mode_emails_active
style('.post-excerpt img', "max-width: 50%; max-height: #{MAX_IMAGE_DIMENSION}px;") style('.post-excerpt img', "max-width: 50%; max-height: #{MAX_IMAGE_DIMENSION}px;")
@ -333,6 +334,16 @@ module Email
private private
def dark_mode_styles
# When we ship the email template and its styles we strip all css classes so to give our
# dark mode styles we are including in the template a selector we add a data-attr of 'dm=value' to
# the appropriate place
style(".digest-header, .digest-topic, .digest-topic-body, .digest-topic-title-wrapper, .digest-topic-stats, .popular-post-excerpt", nil, dm: "header")
style(".digest-content, .header-popular-posts, .spacer, .popular-post-spacer, .popular-post-meta, .digest-new-header, .digest-new-topic, .body", nil, dm: "body")
style(".with-accent-colors, .digest-content-header", nil, dm: "body_primary")
style(".summary-footer", nil, dm: "text-color")
end
def replace_relative_urls def replace_relative_urls
forum_uri = URI(Discourse.base_url) forum_uri = URI(Discourse.base_url)
host = forum_uri.host host = forum_uri.host

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

View File

@ -156,6 +156,23 @@ describe Email::Styles do
end end
context "dark mode emails" do
before do
SiteSetting.dark_mode_emails_active = true
end
it "adds dark_mode_styles when site setting active" do
frag = html_fragment('<div class="body">test</div>')
styler = Email::Styles.new(frag)
styler.format_basic
styler.format_html
@frag = Nokogiri::HTML5.fragment(styler.to_s)
# dark mode attribute
expect(@frag.css('[dm="body"]')).to be_present
end
end
context "strip_avatars_and_emojis" do context "strip_avatars_and_emojis" do
it "works for lonesome emoji with no title" do it "works for lonesome emoji with no title" do
emoji = "<img src='/images/emoji/twitter/crying_cat_face.png'>" emoji = "<img src='/images/emoji/twitter/crying_cat_face.png'>"