Implement one-click-unsubscribe (#192)
* Implement one-click-unsubscribe feature WIP * Adjust email subscription page's css * Add mobile scss * Apply subscription logic to other email type * Add back default email footer * Create EmailSubscriptions request spec
This commit is contained in:
parent
d67c2437bc
commit
0ea3f38dbf
12 changed files with 112 additions and 9 deletions
20
app/assets/stylesheets/email_subscriptions.scss
Normal file
20
app/assets/stylesheets/email_subscriptions.scss
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
.unsubscribed {
|
||||
background: #66e2d5;
|
||||
color: white;
|
||||
margin: 100px auto;
|
||||
padding: 40px 10px;
|
||||
|
||||
@media screen and (min-width: 550px) {
|
||||
margin: 100px auto;
|
||||
width: 450px;
|
||||
padding: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.unsubscribed__title {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.unsubscribed__message {
|
||||
font-weight: 300;
|
||||
}
|
||||
|
|
@ -29,3 +29,5 @@
|
|||
|
||||
@import 'chat';
|
||||
@import 'livechat/chat';
|
||||
|
||||
@import 'email_subscriptions';
|
||||
|
|
|
|||
14
app/controllers/email_subscriptions_controller.rb
Normal file
14
app/controllers/email_subscriptions_controller.rb
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
class EmailSubscriptionsController < ApplicationController
|
||||
def unsubscribe
|
||||
verified_params = Rails.application.message_verifier(:unsubscribe).verify(params[:ut])
|
||||
|
||||
if verified_params[:expires_at] > Time.now
|
||||
user = User.find(verified_params[:user_id])
|
||||
user.update(verified_params[:email_type] => false)
|
||||
else
|
||||
not_found
|
||||
end
|
||||
rescue ActiveSupport::MessageVerifier::InvalidSignature
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
|
@ -17,9 +17,11 @@ class EmailDigest
|
|||
user_email_heuristic = EmailLogic.new(user).analyze
|
||||
next unless user_email_heuristic.should_receive_email?
|
||||
articles = user_email_heuristic.articles_to_send
|
||||
DigestMailer.digest_email(user, articles).deliver
|
||||
rescue
|
||||
puts "Email issue"
|
||||
begin
|
||||
DigestMailer.digest_email(user, articles).deliver
|
||||
rescue StandardError
|
||||
puts "Email issue"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
class ApplicationMailer < ActionMailer::Base
|
||||
default from: "The DEV Community <yo@dev.to>"
|
||||
layout 'mailer'
|
||||
layout "mailer"
|
||||
|
||||
def generate_unsubscribe_token(id, email_type)
|
||||
Rails.application.message_verifier(:unsubscribe).generate(
|
||||
user_id: id,
|
||||
email_type: email_type.to_sym,
|
||||
expires_at: Time.now + 2.days,
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ class DigestMailer < ApplicationMailer
|
|||
user
|
||||
end
|
||||
@articles = articles.first(6)
|
||||
@unsubscribe = generate_unsubscribe_token(@user.id, :email_digest_periodic)
|
||||
@digest_email = true
|
||||
mail(from: "DEV Digest <yo@dev.to>", to: @user.email, subject: "#{adjusted_title(@articles.first)} + #{@articles.size - 1} #{email_end_phrase} #{random_emoji}") do |format|
|
||||
format.html { render "layouts/mailer" }
|
||||
|
|
@ -14,12 +15,11 @@ class DigestMailer < ApplicationMailer
|
|||
|
||||
def adjusted_title(article)
|
||||
title = article.title.strip
|
||||
|
||||
title = "\"#{title}\"" unless title.start_with? '"'
|
||||
"\"#{title}\"" unless title.start_with? '"'
|
||||
end
|
||||
|
||||
def random_emoji
|
||||
["🤓","🎉","🙈","🔥","💬","👋","👏","🐶","🦁","🐙","🦄","❤️","😇"].shuffle.take(3).join
|
||||
["🤓", "🎉", "🙈", "🔥", "💬", "👋", "👏", "🐶", "🦁", "🐙", "🦄", "❤️", "😇"].shuffle.take(3).join
|
||||
end
|
||||
|
||||
def email_end_phrase
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ class NotifyMailer < ApplicationMailer
|
|||
comment.parent_user
|
||||
end
|
||||
return if RateLimitChecker.new.limit_by_email_recipient_address(@user.email)
|
||||
@unsubscribe = generate_unsubscribe_token(@user.id, :email_comment_notifications)
|
||||
@comment = comment
|
||||
mail(to: @user.email, subject: "#{@comment.user.name} replied to your #{@comment.parent_type}") do |format|
|
||||
format.html { render "layouts/mailer" }
|
||||
|
|
@ -22,6 +23,7 @@ class NotifyMailer < ApplicationMailer
|
|||
end
|
||||
return if RateLimitChecker.new.limit_by_email_recipient_address(@user.email)
|
||||
@follower = follow.follower
|
||||
@unsubscribe = generate_unsubscribe_token(@user.id, :email_follower_notifications)
|
||||
|
||||
mail(to: @user.email, subject: "#{@follower.name} just followed you on dev.to") do |format|
|
||||
format.html { render 'layouts/mailer' }
|
||||
|
|
@ -35,6 +37,7 @@ class NotifyMailer < ApplicationMailer
|
|||
@mentioner = User.find(mention.mentionable.user_id)
|
||||
@mentionable = mention.mentionable
|
||||
@mention = mention
|
||||
@unsubscribe = generate_unsubscribe_token(@user.id, :email_mention_notifications)
|
||||
|
||||
mail(to: @user.email, subject: "#{@mentioner.name} just mentioned you!") do |format|
|
||||
format.html { render 'layouts/mailer' }
|
||||
|
|
@ -50,6 +53,7 @@ class NotifyMailer < ApplicationMailer
|
|||
end
|
||||
return if RateLimitChecker.new.limit_by_email_recipient_address(@user.email)
|
||||
@unread_notifications_count = NotificationCounter.new(@user).unread_notification_count
|
||||
@unsubscribe = generate_unsubscribe_token(@user.id, :email_unread_notifications)
|
||||
mail(to: @user.email, subject: "🔥 You have #{@unread_notifications_count} unread notifications on dev.to") do |format|
|
||||
format.html { render 'layouts/mailer' }
|
||||
format.text { render plain: "Visit https://dev.to/notifications to read all of your notifications" }
|
||||
|
|
|
|||
10
app/views/email_subscriptions/unsubscribe.html.erb
Normal file
10
app/views/email_subscriptions/unsubscribe.html.erb
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<div class="unsubscribed">
|
||||
<div class="unsubscribed__title">
|
||||
You have been successfully unsubscribed. 😔
|
||||
</div>
|
||||
<br/>
|
||||
<div class="unsubscribed__message">
|
||||
If that was a mistake or you would like to update other email preferences,
|
||||
<a href="https://dev.to/settings/notifications">click here</a>.
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -219,5 +219,4 @@
|
|||
<% end %>
|
||||
</em>
|
||||
</center>
|
||||
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,11 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td style="padding:3% 12% 2% 4%;font-size:16px;line-height:22px;color:#7d7d7d">
|
||||
Don't want to get emails like this? Adjust your email settings at <a href="https://dev.to/settings/notifications">dev.to/settings/notifications</a>
|
||||
<%if @unsubscribe %>
|
||||
<%= link_to "Unsubscribe", email_subscriptions_unsubscribe_url(ut: @unsubscribe) %> | <a href="https://dev.to/settings/notifications">Adjust your email settings</a>
|
||||
<% else %>
|
||||
Don't want to get emails like this? Adjust your email settings at <a href="https://dev.to/settings/notifications">dev.to/settings/notifications</a>
|
||||
<% end %>
|
||||
<br/><br/>
|
||||
<% if @user.twitter_username.blank? %>
|
||||
Reminder: You used <b>GitHub</b> to authenticate your account, so use that to sign in if prompted.
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ Rails.application.routes.draw do
|
|||
resources :additional_content_boxes, only: [:index]
|
||||
get "/notifications/:username" => "notifications#index"
|
||||
patch "/onboarding_update" => "users#onboarding_update"
|
||||
get "email_subscriptions/unsubscribe"
|
||||
# resources :users
|
||||
|
||||
### Subscription vanity url
|
||||
|
|
|
|||
39
spec/requests/email_subscriptions_spec.rb
Normal file
39
spec/requests/email_subscriptions_spec.rb
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe "EmailSubscriptions", type: :request do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
def generate_token(user_id)
|
||||
Rails.application.message_verifier(:unsubscribe).generate(
|
||||
user_id: user_id,
|
||||
email_type: :email_mention_notifications,
|
||||
expires_at: Time.now + 2.days,
|
||||
)
|
||||
end
|
||||
|
||||
describe "GET /email_subscriptions/unsubscribe" do
|
||||
it "returns 200 if valid" do
|
||||
get email_subscriptions_unsubscribe_url(ut: generate_token(user.id))
|
||||
expect(response.status).to be(200)
|
||||
end
|
||||
|
||||
it "does unsubscribe the user" do
|
||||
get email_subscriptions_unsubscribe_url(ut: generate_token(user.id))
|
||||
user.reload
|
||||
expect(user.email_mention_notifications).to be(false)
|
||||
end
|
||||
|
||||
it "handles error properly" do
|
||||
expect { get email_subscriptions_unsubscribe_url }.
|
||||
to raise_error(ActionController::RoutingError)
|
||||
end
|
||||
|
||||
it "won't work if it's past expireation date" do
|
||||
token = generate_token(user.id)
|
||||
Timecop.freeze(Date.today + 3) do
|
||||
expect { get email_subscriptions_unsubscribe_url(ut: token) }.
|
||||
to raise_error(ActionController::RoutingError)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Reference in a new issue