Use new UserSetting and UserNotificationSettings and Ignore Related User Table Fields (#14121)
* schema file undelete description * feat: v1 of the script * Flesh out remaining enums under their categories * complete UsersSettings data update script * complete DUS for relevant attributes in users and profiles tables * complete DUS for users_notification_settings * alphabetize user_settings sql file * safeguard against null values for "null: false" settings * Set up actual UsersSettings DUS and specs files * fix broken DUS script * complete specs for UsersSetting DUS * Address QA of specs * complete specs for users_notification_settings DUS * fix the typos (thanks Julianna!) * begin implementation * still building * add missing attribute "email_membership_newsletter" * complete sync code (except race condition for user profile) * complete implementation, remains tests * Address PR review and fix Travis fails * remove superfluous Profile.new * fix travis fails * feat: update the users_notification_setting attributes from the user model * feat: use the config fonts enums to display the fonts * feat: loop through the keys * fix profile = nil blowing up; add specs for notification_setting model * remove unneeded spec * remove feed validation until after sync code removed; fixes feed_import spec failures * remove spec associated with feed_url validation in user_setting model * fix failing spec 😅 * add TODO * feat: set the user settings in the user controller and use it in the customization form * feat: move some update logic to the users settings controller thats being used from customization * feat: show the updated values form the users_settingd and not the user instance * Generalize redirect back to current tab * still trying to reflect changed theme upon refresh * customizations take effect on refresh * remove 'with_feed' scope from user model Co-authored-by: Jamie Gaskins <jamie@forem.com> * start with takeover for fields previously in profiles table * Takeover code for `publishing_from_rss` section in Settings (#13914) * implement takeover code part 1 * implement takeover code * fix feed fetch * need rhymes help * complete implementation; specs pending * fix STUPID omission that caused so many headaches 😫 * implement profile fields pointing to users_settings 🎉 * run migrations * implement inbox type & guidelines takeover code; specs pending (#13911) * Point changes in notification settings to `users_notification_settings` table (#13910) * implement takeover code; remains specs * address PR feedback; remove related sync code * address PR review feedback * need help with routing and specs * address pr review * addressing pr review * Treat implementation edge cases and omissions 😅 * fix uncommented comment * fixing implementation cases * address more PR review feedback * fixing notifications use-cases * refactor settings controller * more pr review changes * solving bugs * fix broken onboarding * handle eperience_level calls * more fixes * remove unneeded mappings * add To-dos for quety updates * remove done TODO * purge done TODOs * update notification_settings-related queries * start fixing specs * fixing specs * fix notification and lrg_forem specs * fixing broken specs * still fixing * fix line dif and remove reloads from user.rb * run specs * silence bullet and other fixes * remove setting migration scripts and specs, fix more settings for specs * handle missing user for article builder and fix notification specs * fix some final controller specs and re-add incorrectly removed specs * remove deprecated data update scripts and related workers, put travis back * refactor admin tags mods controller, write/move specs for users notifications settings controller * schema cleanup and other small refactors for consistency * set field we can invalidate in spec via active record instead of at the db level * remove I think an uneccessary hook call from subscribe_to_mailchimp_newsletter * use bnefore_create to setup settings, please dont blow up the test suite * mailchimp bot fix * remove decorator in favor of single model method Co-authored-by: Arit Amana <msarit@gmail.com> Co-authored-by: Ridhwana <ridhwana.khan16@gmail.com> Co-authored-by: Arit Amana <32520970+msarit@users.noreply.github.com> Co-authored-by: Jamie Gaskins <jamie@forem.com>
This commit is contained in:
parent
e99e803867
commit
e87dead7ad
103 changed files with 795 additions and 870 deletions
|
|
@ -11,25 +11,37 @@ module Admin
|
|||
|
||||
def create
|
||||
user = User.find_by(id: tag_params[:user_id])
|
||||
if user&.update(email_tag_mod_newsletter: true)
|
||||
unless user
|
||||
flash[:error] = "Error: User ID ##{tag_params[:user_id]} was not found"
|
||||
return redirect_to edit_admin_tag_path(params[:tag_id])
|
||||
end
|
||||
|
||||
notification_setting = user.notification_setting
|
||||
if notification_setting.update(email_tag_mod_newsletter: true)
|
||||
TagModerators::Add.call([user.id], [params[:tag_id]])
|
||||
flash[:success] = "#{user.username} was added as a tag moderator!"
|
||||
else
|
||||
flash[:error] = "Error: User ID ##{tag_params[:user_id]} was not found,
|
||||
or their account has errors: #{user&.errors_as_sentence}"
|
||||
or their account has errors: #{notification_setting.errors_as_sentence}"
|
||||
end
|
||||
redirect_to edit_admin_tag_path(params[:tag_id])
|
||||
end
|
||||
|
||||
def destroy
|
||||
user = User.find_by(id: tag_params[:user_id])
|
||||
unless user
|
||||
flash[:error] = "Error: User ID ##{tag_params[:user_id]} was not found"
|
||||
return redirect_to edit_admin_tag_path(params[:tag_id])
|
||||
end
|
||||
|
||||
notification_setting = user.notification_setting
|
||||
tag = Tag.find_by(id: params[:tag_id])
|
||||
if user&.update(email_tag_mod_newsletter: false)
|
||||
if notification_setting.update(email_tag_mod_newsletter: false)
|
||||
TagModerators::Remove.call(user, tag)
|
||||
flash[:success] = "@#{user.username} - ID ##{user.id} was removed as a tag moderator."
|
||||
else
|
||||
flash[:error] = "Error: User ID ##{tag_params[:user_id]} was not found,
|
||||
or their account has errors: #{user&.errors_as_sentence}"
|
||||
or their account has errors: #{notification_setting.errors_as_sentence}"
|
||||
end
|
||||
redirect_to edit_admin_tag_path(tag.id)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ class ArticlesController < ApplicationController
|
|||
|
||||
def base_editor_assigments
|
||||
@user = current_user
|
||||
@version = @user.editor_version if @user
|
||||
@version = @user.setting.editor_version if @user
|
||||
@organizations = @user&.organizations
|
||||
@tag = Tag.find_by(name: params[:template])
|
||||
@prefill = params[:prefill].to_s.gsub("\\n ", "\n").gsub("\\n", "\n")
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ class ChatChannelsController < ApplicationController
|
|||
valid_listing = Listing.where(user_id: params[:user_id], contact_via_connect: true).limit(1)
|
||||
authorize ChatChannel
|
||||
|
||||
if chat_recipient.inbox_type == "open" || valid_listing.length == 1
|
||||
if chat_recipient.setting.inbox_type == "open" || valid_listing.length == 1
|
||||
chat = ChatChannels::CreateWithUsers.call(users: [current_user, chat_recipient], channel_type: "direct")
|
||||
message_markdown = params[:message]
|
||||
message = Message.new(
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ class EmailSubscriptionsController < ApplicationController
|
|||
|
||||
if verified_params[:expires_at] > Time.current
|
||||
user = User.find(verified_params[:user_id])
|
||||
user.update(verified_params[:email_type] => false)
|
||||
user.notification_setting.update(verified_params[:email_type] => false)
|
||||
@email_type = preferred_email_name.fetch(verified_params[:email_type], "this list")
|
||||
else
|
||||
render "invalid_token"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ module IncomingWebhooks
|
|||
def create
|
||||
not_authorized unless valid_secret?
|
||||
user = User.find_by!(email: params.dig(:data, :email))
|
||||
user.update(email_type => false)
|
||||
user.notification_setting.update(email_type => false)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
class ProfilesController < ApplicationController
|
||||
before_action :authenticate_user!
|
||||
|
||||
ALLOWED_USER_PARAMS = %i[name email username profile_image].freeze
|
||||
ALLOWED_USERS_SETTING_PARAMS = %i[display_email_on_profile brand_color1 brand_color2].freeze
|
||||
|
||||
def update
|
||||
update_result = Profiles::Update.call(current_user, update_params)
|
||||
|
|
@ -13,7 +15,8 @@ class ProfilesController < ApplicationController
|
|||
flash[:error] = "Error: #{update_result.errors_as_sentence}"
|
||||
render template: "users/edit", locals: {
|
||||
user: update_params[:user],
|
||||
profile: update_params[:profile]
|
||||
profile: update_params[:profile],
|
||||
users_setting: update_params[:users_setting]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
|
@ -21,6 +24,8 @@ class ProfilesController < ApplicationController
|
|||
private
|
||||
|
||||
def update_params
|
||||
params.permit(profile: Profile.attributes + Profile.static_fields, user: ALLOWED_USER_PARAMS)
|
||||
params.permit(profile: Profile.attributes + Profile.static_fields,
|
||||
user: ALLOWED_USER_PARAMS,
|
||||
users_setting: ALLOWED_USERS_SETTING_PARAMS)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class ReactionsController < ApplicationController
|
|||
|
||||
result = "create"
|
||||
|
||||
if category == "readinglist" && current_user.experience_level
|
||||
if category == "readinglist" && current_user.setting.experience_level
|
||||
rate_article(reaction)
|
||||
end
|
||||
|
||||
|
|
@ -146,7 +146,7 @@ class ReactionsController < ApplicationController
|
|||
group: "experience_level",
|
||||
user_id: current_user.id,
|
||||
context: "readinglist_reaction",
|
||||
rating: current_user.experience_level)
|
||||
rating: current_user.setting.experience_level)
|
||||
end
|
||||
|
||||
def clear_moderator_reactions(id, type, mod, category)
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ class RegistrationsController < Devise::RegistrationsController
|
|||
def build_devise_resource
|
||||
build_resource(sign_up_params)
|
||||
resource.registered_at = Time.current
|
||||
resource.build_setting(editor_version: "v2")
|
||||
resource.remote_profile_image_url = Users::ProfileImageGenerator.call if resource.remote_profile_image_url.blank?
|
||||
check_allowed_email(resource) if resource.email.present?
|
||||
resource.save if resource.email.present?
|
||||
|
|
|
|||
|
|
@ -344,7 +344,7 @@ class StoriesController < ApplicationController
|
|||
sameAs: user_same_as,
|
||||
image: Images::Profile.call(@user.profile_image_url, length: 320),
|
||||
name: @user.name,
|
||||
email: @user.email_public ? @user.email : nil,
|
||||
email: @user.setting.display_email_on_profile ? @user.email : nil,
|
||||
jobTitle: @user.employment_title.presence,
|
||||
description: @user.summary.presence || "404 bio not found",
|
||||
worksFor: [user_works_for].compact,
|
||||
|
|
|
|||
63
app/controllers/users/notification_settings_controller.rb
Normal file
63
app/controllers/users/notification_settings_controller.rb
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
module Users
|
||||
class NotificationSettingsController < ApplicationController
|
||||
before_action :raise_suspended
|
||||
before_action :authenticate_user!
|
||||
after_action :verify_authorized
|
||||
|
||||
ALLOWED_PARAMS = %i[email_badge_notifications
|
||||
email_comment_notifications
|
||||
email_community_mod_newsletter
|
||||
email_connect_messages
|
||||
email_digest_periodic
|
||||
email_follower_notifications
|
||||
email_membership_newsletter
|
||||
email_mention_notifications
|
||||
email_newsletter
|
||||
email_tag_mod_newsletter
|
||||
email_unread_notifications
|
||||
mobile_comment_notifications
|
||||
mod_roundrobin_notifications
|
||||
reaction_notifications
|
||||
welcome_notifications].freeze
|
||||
|
||||
def update
|
||||
authorize current_user, policy_class: UserPolicy
|
||||
|
||||
if current_user.notification_setting.update(users_notification_setting_params)
|
||||
flash[:settings_notice] = "Your notification settings have been updated."
|
||||
else
|
||||
Honeycomb.add_field("error", current_user.notification_setting.errors.messages.compact_blank)
|
||||
Honeycomb.add_field("errored", true)
|
||||
flash[:error] = current_user.notification_setting.errors_as_sentence
|
||||
end
|
||||
redirect_to user_settings_path(:notifications)
|
||||
end
|
||||
|
||||
def onboarding_notifications_checkbox_update
|
||||
authorize User
|
||||
|
||||
if params[:notifications]
|
||||
permitted_params = %i[email_newsletter email_digest_periodic]
|
||||
current_user.notification_setting.assign_attributes(params[:notifications].permit(permitted_params))
|
||||
end
|
||||
|
||||
current_user.saw_onboarding = true
|
||||
success = current_user.notification_setting.save
|
||||
render_update_response(success, current_user.notification_setting.errors_as_sentence)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def render_update_response(success, errors = nil)
|
||||
status = success ? 200 : 422
|
||||
|
||||
respond_to do |format|
|
||||
format.json { render json: { errors: errors }, status: status }
|
||||
end
|
||||
end
|
||||
|
||||
def users_notification_setting_params
|
||||
params.require(:users_notification_setting).permit(ALLOWED_PARAMS)
|
||||
end
|
||||
end
|
||||
end
|
||||
55
app/controllers/users/settings_controller.rb
Normal file
55
app/controllers/users/settings_controller.rb
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
module Users
|
||||
class SettingsController < ApplicationController
|
||||
before_action :raise_suspended
|
||||
before_action :authenticate_user!
|
||||
after_action :verify_authorized
|
||||
|
||||
ALLOWED_PARAMS = %i[config_theme
|
||||
config_font
|
||||
config_navbar
|
||||
display_announcements
|
||||
display_sponsors
|
||||
editor_version
|
||||
experience_level
|
||||
feed_fetched_at
|
||||
feed_mark_canonical
|
||||
feed_referential_link
|
||||
feed_url
|
||||
inbox_guidelines
|
||||
inbox_type
|
||||
permit_adjacent_sponsors].freeze
|
||||
|
||||
def update
|
||||
authorize current_user, policy_class: UserPolicy
|
||||
users_setting = current_user.setting
|
||||
tab = params["users_setting"]["tab"] || "profile"
|
||||
|
||||
if users_setting.update(users_setting_params)
|
||||
import_articles_from_feed(users_setting)
|
||||
|
||||
if users_setting.experience_level.present?
|
||||
cookies.permanent[:user_experience_level] = users_setting.experience_level.to_s
|
||||
end
|
||||
current_user.touch(:profile_updated_at)
|
||||
flash[:settings_notice] = "Your config has been updated. Refresh to see all changes."
|
||||
else
|
||||
Honeycomb.add_field("error", users_setting.errors.messages.compact_blank)
|
||||
Honeycomb.add_field("errored", true)
|
||||
flash[:error] = users_setting.errors_as_sentence
|
||||
end
|
||||
redirect_to user_settings_path(tab)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def import_articles_from_feed(users_setting)
|
||||
return if users_setting.feed_url.blank?
|
||||
|
||||
Feeds::ImportArticlesWorker.perform_async(users_setting.user_id)
|
||||
end
|
||||
|
||||
def users_setting_params
|
||||
params.require(:users_setting).permit(ALLOWED_PARAMS)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -11,6 +11,7 @@ class UsersController < ApplicationController
|
|||
before_action :initialize_stripe, only: %i[edit]
|
||||
|
||||
ALLOWED_USER_PARAMS = %i[last_onboarding_page username].freeze
|
||||
ALLOWED_ONBOARDING_PARAMS = %i[checked_code_of_conduct checked_terms_and_conditions].freeze
|
||||
INDEX_ATTRIBUTES_FOR_SERIALIZATION = %i[id name username summary profile_image].freeze
|
||||
private_constant :INDEX_ATTRIBUTES_FOR_SERIALIZATION
|
||||
REMOVE_IDENTITY_ERROR = "An error occurred. Please try again or send an email to: %<email>s".freeze
|
||||
|
|
@ -35,6 +36,7 @@ class UsersController < ApplicationController
|
|||
return redirect_to sign_up_path
|
||||
end
|
||||
set_user
|
||||
set_users_setting_and_notification_setting
|
||||
set_current_tab(params["tab"] || "profile")
|
||||
handle_settings_tab
|
||||
end
|
||||
|
|
@ -42,6 +44,7 @@ class UsersController < ApplicationController
|
|||
# PATCH/PUT /users/:id.:format
|
||||
def update
|
||||
set_current_tab(params["user"]["tab"])
|
||||
set_users_setting_and_notification_setting
|
||||
|
||||
@user.assign_attributes(permitted_attributes(@user))
|
||||
|
||||
|
|
@ -51,14 +54,13 @@ class UsersController < ApplicationController
|
|||
import_articles_from_feed(@user)
|
||||
|
||||
notice = "Your profile was successfully updated."
|
||||
if config_changed?
|
||||
notice = "Your config has been updated. Refresh to see all changes."
|
||||
end
|
||||
if @user.export_requested?
|
||||
notice += " The export will be emailed to you shortly."
|
||||
ExportContentWorker.perform_async(@user.id, @user.email)
|
||||
end
|
||||
cookies.permanent[:user_experience_level] = @user.experience_level.to_s if @user.experience_level.present?
|
||||
if @user.setting.experience_level.present?
|
||||
cookies.permanent[:user_experience_level] = @user.setting.experience_level.to_s
|
||||
end
|
||||
flash[:settings_notice] = notice
|
||||
@user.touch(:profile_updated_at)
|
||||
redirect_to "/settings/#{@tab}"
|
||||
|
|
@ -185,10 +187,7 @@ class UsersController < ApplicationController
|
|||
|
||||
def onboarding_checkbox_update
|
||||
if params[:user]
|
||||
permitted_params = %i[
|
||||
checked_code_of_conduct checked_terms_and_conditions email_newsletter email_digest_periodic
|
||||
]
|
||||
current_user.assign_attributes(params[:user].permit(permitted_params))
|
||||
current_user.assign_attributes(params[:user].permit(ALLOWED_ONBOARDING_PARAMS))
|
||||
end
|
||||
|
||||
current_user.saw_onboarding = true
|
||||
|
|
@ -360,12 +359,15 @@ class UsersController < ApplicationController
|
|||
authorize @user
|
||||
end
|
||||
|
||||
def set_current_tab(current_tab = "profile")
|
||||
@tab = current_tab
|
||||
def set_users_setting_and_notification_setting
|
||||
return unless @user
|
||||
|
||||
@users_setting = @user.setting
|
||||
@users_notification_setting = @user.notification_setting
|
||||
end
|
||||
|
||||
def config_changed?
|
||||
params[:user].include?(:config_theme)
|
||||
def set_current_tab(current_tab = "profile")
|
||||
@tab = current_tab
|
||||
end
|
||||
|
||||
def destroy_request_in_progress?
|
||||
|
|
@ -373,7 +375,7 @@ class UsersController < ApplicationController
|
|||
end
|
||||
|
||||
def import_articles_from_feed(user)
|
||||
return if user.feed_url.blank?
|
||||
return if user.setting.feed_url.blank?
|
||||
|
||||
Feeds::ImportArticlesWorker.perform_async(user.id)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -36,35 +36,31 @@ class UserDecorator < ApplicationDecorator
|
|||
end
|
||||
|
||||
def enriched_colors
|
||||
if bg_color_hex.blank? || text_color_hex.blank?
|
||||
if setting.brand_color1.blank? || setting.brand_color2.blank?
|
||||
{
|
||||
bg: assigned_color[:bg],
|
||||
text: assigned_color[:text]
|
||||
}
|
||||
else
|
||||
{
|
||||
bg: bg_color_hex,
|
||||
text: text_color_hex
|
||||
bg: setting.brand_color1,
|
||||
text: setting.brand_color2
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def config_font_name
|
||||
config_font.gsub("default", Settings::UserExperience.default_font)
|
||||
end
|
||||
|
||||
def config_body_class
|
||||
body_class = [
|
||||
config_theme.tr("_", "-"),
|
||||
"#{config_font_name.tr('_', '-')}-article-body",
|
||||
setting.config_theme.tr("_", "-"),
|
||||
"#{setting.resolved_font_name.tr('_', '-')}-article-body",
|
||||
"trusted-status-#{trusted}",
|
||||
"#{config_navbar.tr('_', '-')}-header",
|
||||
"#{setting.config_navbar.tr('_', '-')}-header",
|
||||
]
|
||||
body_class.join(" ")
|
||||
end
|
||||
|
||||
def dark_theme?
|
||||
config_theme == "night_theme" || config_theme == "ten_x_hacker_theme"
|
||||
setting.config_theme == "night_theme" || setting.config_theme == "ten_x_hacker_theme"
|
||||
end
|
||||
|
||||
def assigned_color
|
||||
|
|
@ -113,4 +109,8 @@ class UserDecorator < ApplicationDecorator
|
|||
|
||||
created_at.after?(min_days.days.ago)
|
||||
end
|
||||
|
||||
delegate :display_sponsors, to: :setting
|
||||
|
||||
delegate :display_announcements, to: :setting
|
||||
end
|
||||
|
|
|
|||
|
|
@ -24,13 +24,13 @@ export class EmailPreferencesForm extends Component {
|
|||
onSubmit() {
|
||||
const csrfToken = getContentOfToken('csrf-token');
|
||||
|
||||
fetch('/onboarding_checkbox_update', {
|
||||
fetch('/onboarding_notifications_checkbox_update', {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'X-CSRF-Token': csrfToken,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ user: this.state }),
|
||||
body: JSON.stringify({ notifications: this.state }),
|
||||
credentials: 'same-origin',
|
||||
}).then((response) => {
|
||||
if (response.ok) {
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class BadgeAchievement < ApplicationRecord
|
|||
|
||||
def send_email_notification
|
||||
return unless user.is_a?(User)
|
||||
return unless user.email && user.email_badge_notifications
|
||||
return unless user.email && user.notification_setting.email_badge_notifications
|
||||
|
||||
BadgeAchievements::SendEmailNotificationWorker.perform_async(id)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ class Comment < ApplicationRecord
|
|||
parent_exists? &&
|
||||
parent_user.class.name != "Podcast" &&
|
||||
parent_user != user &&
|
||||
parent_user.email_comment_notifications &&
|
||||
parent_user.notification_setting.email_comment_notifications &&
|
||||
parent_user.email &&
|
||||
parent_or_root_article.receive_notifications
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class Mention < ApplicationRecord
|
|||
|
||||
def send_email_notification
|
||||
user = User.find(user_id)
|
||||
return unless user.email.present? && user.email_mention_notifications
|
||||
return unless user.email.present? && user.notification_setting.email_mention_notifications
|
||||
|
||||
Mentions::SendEmailNotificationWorker.perform_async(id)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class Message < ApplicationRecord
|
|||
after_create :update_all_has_unopened_messages_statuses
|
||||
|
||||
def preferred_user_color
|
||||
color_options = [user.bg_color_hex || "#000000", user.text_color_hex || "#000000"]
|
||||
color_options = [user.setting.brand_color1 || "#000000", user.setting.brand_color2 || "#000000"]
|
||||
Color::CompareHex.new(color_options).brightness(0.9)
|
||||
end
|
||||
|
||||
|
|
@ -217,7 +217,7 @@ class Message < ApplicationRecord
|
|||
recipient.chat_channel_memberships.order(last_opened_at: :desc)
|
||||
.first.last_opened_at > 15.hours.ago ||
|
||||
chat_channel.last_message_at > 30.minutes.ago ||
|
||||
recipient.email_connect_messages == false
|
||||
recipient.notification_setting.email_connect_messages == false
|
||||
|
||||
NotifyMailer.with(message: self).new_message_email.deliver_now
|
||||
end
|
||||
|
|
|
|||
|
|
@ -26,9 +26,6 @@ class Profile < ApplicationRecord
|
|||
# NOTE: @citizen428 This is a temporary mapping so we don't break DEV during
|
||||
# profile migration/generalization work.
|
||||
MAPPED_ATTRIBUTES = {
|
||||
brand_color1: :bg_color_hex,
|
||||
brand_color2: :text_color_hex,
|
||||
display_email_on_profile: :email_public,
|
||||
education: :education,
|
||||
skills_languages: :mostly_work_with
|
||||
}.with_indifferent_access.freeze
|
||||
|
|
|
|||
|
|
@ -36,7 +36,47 @@ class User < ApplicationRecord
|
|||
youtube_url
|
||||
].freeze
|
||||
|
||||
self.ignored_columns = PROFILE_COLUMNS
|
||||
COLUMNS_NOW_IN_USERS_SETTINGS = %w[
|
||||
config_theme
|
||||
config_font
|
||||
config_navbar
|
||||
display_announcements
|
||||
display_sponsors
|
||||
editor_version
|
||||
experience_level
|
||||
feed_mark_canonical
|
||||
feed_referential_link
|
||||
feed_url
|
||||
inbox_guidelines
|
||||
inbox_type
|
||||
permit_adjacent_sponsors
|
||||
].freeze
|
||||
|
||||
COLUMNS_NOW_IN_USERS_NOTIFICATION_SETTINGS = %w[
|
||||
email_badge_notifications
|
||||
email_comment_notifications
|
||||
email_community_mod_newsletter
|
||||
email_connect_messages
|
||||
email_digest_periodic
|
||||
email_follower_notifications
|
||||
email_membership_newsletter
|
||||
email_mention_notifications
|
||||
email_newsletter
|
||||
email_tag_mod_newsletter
|
||||
email_unread_notifications
|
||||
mobile_comment_notifications
|
||||
mod_roundrobin_notifications
|
||||
reaction_notifications
|
||||
welcome_notifications
|
||||
].freeze
|
||||
|
||||
INACTIVE_PROFILE_COLUMNS = %w[
|
||||
bg_color_hex
|
||||
text_color_hex
|
||||
email_public
|
||||
].freeze
|
||||
|
||||
self.ignored_columns = PROFILE_COLUMNS + COLUMNS_NOW_IN_USERS_SETTINGS + COLUMNS_NOW_IN_USERS_NOTIFICATION_SETTINGS
|
||||
|
||||
# NOTE: @citizen428 This is temporary code during profile migration and will
|
||||
# be removed.
|
||||
|
|
@ -53,6 +93,8 @@ class User < ApplicationRecord
|
|||
|
||||
# Getters and setters for unmapped profile attributes
|
||||
(PROFILE_COLUMNS - Profile::MAPPED_ATTRIBUTES.values).each do |column|
|
||||
next if INACTIVE_PROFILE_COLUMNS.include?(column)
|
||||
|
||||
delegate column, "#{column}=", to: :profile, allow_nil: true
|
||||
end
|
||||
|
||||
|
|
@ -67,18 +109,9 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
ANY_ADMIN_ROLES = %i[admin super_admin].freeze
|
||||
EDITORS = %w[v1 v2].freeze
|
||||
FONTS = %w[serif sans_serif monospace comic_sans open_dyslexic].freeze
|
||||
INBOXES = %w[open private].freeze
|
||||
NAVBARS = %w[default static].freeze
|
||||
THEMES = %w[default night_theme pink_theme minimal_light_theme ten_x_hacker_theme].freeze
|
||||
USERNAME_MAX_LENGTH = 30
|
||||
USERNAME_REGEXP = /\A[a-zA-Z0-9_]+\z/.freeze
|
||||
MESSAGES = {
|
||||
invalid_config_font: "%<value>s is not a valid font selection",
|
||||
invalid_config_navbar: "%<value>s is not a valid navbar value",
|
||||
invalid_config_theme: "%<value>s is not a valid theme",
|
||||
invalid_editor_version: "%<value>s must be either v1 or v2",
|
||||
reserved_username: "username is reserved"
|
||||
}.freeze
|
||||
# follow the syntax in https://interledger.org/rfcs/0026-payment-pointers/#payment-pointer-syntax
|
||||
|
|
@ -90,57 +123,6 @@ class User < ApplicationRecord
|
|||
\z
|
||||
}x.freeze
|
||||
|
||||
# Relevant Fields for migration from Users table to Users_Settings table
|
||||
USER_FIELDS_TO_MIGRATE_TO_USERS_SETTINGS_TABLE = %w[
|
||||
config_font
|
||||
config_navbar
|
||||
config_theme
|
||||
display_announcements
|
||||
display_sponsors
|
||||
editor_version
|
||||
experience_level
|
||||
feed_mark_canonical
|
||||
feed_referential_link
|
||||
feed_url
|
||||
inbox_guidelines
|
||||
inbox_type
|
||||
permit_adjacent_sponsors
|
||||
].to_set.freeze
|
||||
|
||||
# Relevant Fields for migration from Profiles table to Users_Settings table
|
||||
PROFILE_FIELDS_TO_MIGRATE_TO_USERS_SETTINGS_TABLE = %w[
|
||||
brand_color1
|
||||
brand_color2
|
||||
display_email_on_profile
|
||||
].to_set.freeze
|
||||
|
||||
# Relevant Fields for migration from Users table to Users_Notification_Settings table
|
||||
USER_FIELDS_TO_MIGRATE_TO_USERS_NOTIFICATION_SETTINGS_TABLE = %w[
|
||||
email_badge_notifications
|
||||
email_comment_notifications
|
||||
email_community_mod_newsletter
|
||||
email_connect_messages
|
||||
email_digest_periodic
|
||||
email_follower_notifications
|
||||
email_membership_newsletter
|
||||
email_mention_notifications
|
||||
email_newsletter
|
||||
email_tag_mod_newsletter
|
||||
email_unread_notifications
|
||||
mobile_comment_notifications
|
||||
mod_roundrobin_notifications
|
||||
reaction_notifications
|
||||
welcome_notifications
|
||||
].to_set.freeze
|
||||
|
||||
USER_SETTINGS_ENUM_FIELDS = %w[
|
||||
config_font
|
||||
config_navbar
|
||||
config_theme
|
||||
editor_version
|
||||
inbox_type
|
||||
].to_set.freeze
|
||||
|
||||
attr_accessor :scholar_email, :new_note, :note_for_current_role, :user_status, :merge_user_id,
|
||||
:add_credits, :remove_credits, :add_org_credits, :remove_org_credits, :ip_address,
|
||||
:current_password
|
||||
|
|
@ -236,25 +218,12 @@ class User < ApplicationRecord
|
|||
validates :blocked_by_count, presence: true
|
||||
validates :blocking_others_count, presence: true
|
||||
validates :comments_count, presence: true
|
||||
validates :config_font, inclusion: { in: FONTS + ["default".freeze], message: MESSAGES[:invalid_config_font] }
|
||||
validates :config_font, presence: true
|
||||
validates :config_navbar, inclusion: { in: NAVBARS, message: MESSAGES[:invalid_config_navbar] }
|
||||
validates :config_navbar, presence: true
|
||||
validates :config_theme, inclusion: { in: THEMES, message: MESSAGES[:invalid_config_theme] }
|
||||
validates :config_theme, presence: true
|
||||
validates :credits_count, presence: true
|
||||
validates :editor_version, inclusion: { in: EDITORS, message: MESSAGES[:invalid_editor_version] }
|
||||
validates :email, length: { maximum: 50 }, email: true, allow_nil: true
|
||||
validates :email, uniqueness: { allow_nil: true, case_sensitive: false }, if: :email_changed?
|
||||
validates :email_digest_periodic, inclusion: { in: [true, false] }
|
||||
validates :experience_level, numericality: { less_than_or_equal_to: 10 }, allow_blank: true
|
||||
validates :feed_referential_link, inclusion: { in: [true, false] }
|
||||
validates :feed_url, length: { maximum: 500 }, allow_nil: true
|
||||
validates :following_orgs_count, presence: true
|
||||
validates :following_tags_count, presence: true
|
||||
validates :following_users_count, presence: true
|
||||
validates :inbox_guidelines, length: { maximum: 250 }, allow_nil: true
|
||||
validates :inbox_type, inclusion: { in: INBOXES }
|
||||
validates :name, length: { in: 1..100 }
|
||||
validates :password, length: { in: 8..100 }, allow_nil: true
|
||||
validates :payment_pointer, format: PAYMENT_POINTER_REGEXP, allow_blank: true
|
||||
|
|
@ -269,7 +238,6 @@ class User < ApplicationRecord
|
|||
validates :username, uniqueness: { case_sensitive: false, message: lambda do |_obj, data|
|
||||
"#{data[:value]} is taken."
|
||||
end }, if: :username_changed?
|
||||
validates :welcome_notifications, inclusion: { in: [true, false] }
|
||||
|
||||
# add validators for provider related usernames
|
||||
Authentication::Providers.username_fields.each do |username_field|
|
||||
|
|
@ -285,7 +253,6 @@ class User < ApplicationRecord
|
|||
|
||||
validate :non_banished_username, :username_changed?
|
||||
validate :unique_including_orgs_and_podcasts, if: :username_changed?
|
||||
validate :validate_feed_url, if: :feed_url_changed?
|
||||
validate :can_send_confirmation_email
|
||||
validate :update_rate_limit
|
||||
# NOTE: when updating the password on a Devise enabled model, the :encrypted_password
|
||||
|
|
@ -293,9 +260,6 @@ class User < ApplicationRecord
|
|||
validate :password_matches_confirmation, if: :encrypted_password_changed?
|
||||
|
||||
alias_attribute :public_reactions_count, :reactions_count
|
||||
alias_attribute :subscribed_to_welcome_notifications?, :welcome_notifications
|
||||
alias_attribute :subscribed_to_mod_roundrobin_notifications?, :mod_roundrobin_notifications
|
||||
alias_attribute :subscribed_to_email_follower_notifications?, :email_follower_notifications
|
||||
|
||||
scope :eager_load_serialized_data, -> { includes(:roles) }
|
||||
scope :registered, -> { where(registered: true) }
|
||||
|
|
@ -332,15 +296,14 @@ class User < ApplicationRecord
|
|||
),
|
||||
)
|
||||
}
|
||||
scope :with_feed, -> { where.not(feed_url: [nil, ""]) }
|
||||
|
||||
before_validation :check_for_username_change
|
||||
before_validation :downcase_email
|
||||
before_validation :set_config_input
|
||||
# make sure usernames are not empty, to be able to use the database unique index
|
||||
before_validation :verify_email
|
||||
before_validation :set_username
|
||||
before_validation :strip_payment_pointer
|
||||
before_create :create_users_settings_and_notification_settings_records
|
||||
before_destroy :unsubscribe_from_newsletters, prepend: true
|
||||
before_destroy :destroy_follows, prepend: true
|
||||
|
||||
|
|
@ -350,7 +313,6 @@ class User < ApplicationRecord
|
|||
after_create_commit :send_welcome_notification
|
||||
|
||||
after_commit :subscribe_to_mailchimp_newsletter
|
||||
after_commit :sync_users_settings_table, :sync_users_notification_settings_table, on: %i[create update]
|
||||
after_commit :bust_cache
|
||||
|
||||
def self.dev_account
|
||||
|
|
@ -548,7 +510,7 @@ class User < ApplicationRecord
|
|||
return unless registered && email.present?
|
||||
return if Settings::General.mailchimp_api_key.blank?
|
||||
return if saved_changes.key?(:unconfirmed_email) && saved_changes.key?(:confirmation_sent_at)
|
||||
return unless saved_changes.key?(:email) || saved_changes.key?(:email_newsletter)
|
||||
return unless saved_changes.key?(:email)
|
||||
|
||||
Users::SubscribeToMailchimpNewsletterWorker.perform_async(id)
|
||||
end
|
||||
|
|
@ -621,6 +583,18 @@ class User < ApplicationRecord
|
|||
"User:#{id}"
|
||||
end
|
||||
|
||||
def subscribed_to_welcome_notifications?
|
||||
notification_setting.welcome_notifications
|
||||
end
|
||||
|
||||
def subscribed_to_mod_roundrobin_notifications?
|
||||
notification_setting.mod_roundrobin_notifications
|
||||
end
|
||||
|
||||
def subscribed_to_email_follower_notifications?
|
||||
notification_setting.email_follower_notifications
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Send emails asynchronously
|
||||
|
|
@ -632,43 +606,9 @@ class User < ApplicationRecord
|
|||
|
||||
private
|
||||
|
||||
def sync_relevant_profile_fields_to_user_settings_table(users_setting_record)
|
||||
PROFILE_FIELDS_TO_MIGRATE_TO_USERS_SETTINGS_TABLE.each do |field|
|
||||
# rubocop:disable Layout/LineLength
|
||||
users_setting_record.assign_attributes(field => profile.public_send(field)) if profile&.public_send(field).present?
|
||||
# rubocop:enable Layout/LineLength
|
||||
end
|
||||
end
|
||||
|
||||
def migrate_users_and_profile_fields_to_users_settings(users_setting_record)
|
||||
USER_FIELDS_TO_MIGRATE_TO_USERS_SETTINGS_TABLE.each do |field|
|
||||
if USER_SETTINGS_ENUM_FIELDS.include?(field)
|
||||
field_enums = Users::Setting.defined_enums[field]
|
||||
users_setting_record.assign_attributes(field => field_enums[public_send(field).to_sym])
|
||||
else
|
||||
users_setting_record.assign_attributes(field => public_send(field))
|
||||
end
|
||||
end
|
||||
|
||||
sync_relevant_profile_fields_to_user_settings_table(users_setting_record)
|
||||
|
||||
users_setting_record.save
|
||||
end
|
||||
|
||||
def sync_users_settings_table
|
||||
users_setting_record = Users::Setting.create_or_find_by(user_id: id)
|
||||
|
||||
migrate_users_and_profile_fields_to_users_settings(users_setting_record)
|
||||
end
|
||||
|
||||
def sync_users_notification_settings_table
|
||||
users_notification_setting_record = Users::NotificationSetting.create_or_find_by(user_id: id)
|
||||
|
||||
USER_FIELDS_TO_MIGRATE_TO_USERS_NOTIFICATION_SETTINGS_TABLE.each do |field|
|
||||
users_notification_setting_record.assign_attributes(field => public_send(field))
|
||||
end
|
||||
|
||||
users_notification_setting_record.save
|
||||
def create_users_settings_and_notification_settings_records
|
||||
self.setting = Users::Setting.create
|
||||
self.notification_setting = Users::NotificationSetting.create
|
||||
end
|
||||
|
||||
def send_welcome_notification
|
||||
|
|
@ -711,12 +651,6 @@ class User < ApplicationRecord
|
|||
self.email = email.downcase if email
|
||||
end
|
||||
|
||||
def set_config_input
|
||||
self.config_theme = config_theme&.tr(" ", "_")
|
||||
self.config_font = config_font&.tr(" ", "_")
|
||||
self.config_navbar = config_navbar&.tr(" ", "_")
|
||||
end
|
||||
|
||||
def check_for_username_change
|
||||
return unless username_changed?
|
||||
|
||||
|
|
@ -736,16 +670,6 @@ class User < ApplicationRecord
|
|||
Users::BustCacheWorker.perform_async(id)
|
||||
end
|
||||
|
||||
def validate_feed_url
|
||||
return if feed_url.blank?
|
||||
|
||||
valid = Feeds::ValidateUrl.call(feed_url)
|
||||
|
||||
errors.add(:feed_url, "is not a valid RSS/Atom feed") unless valid
|
||||
rescue StandardError => e
|
||||
errors.add(:feed_url, e.message)
|
||||
end
|
||||
|
||||
def tag_keywords_for_search
|
||||
"#{employer_name}#{mostly_work_with}#{available_for}"
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,10 +1,21 @@
|
|||
module Users
|
||||
class NotificationSetting < ApplicationRecord
|
||||
self.table_name_prefix = "users_"
|
||||
|
||||
belongs_to :user, touch: true
|
||||
|
||||
validates :email_digest_periodic, inclusion: { in: [true, false] }
|
||||
|
||||
alias_attribute :subscribed_to_welcome_notifications?, :welcome_notifications
|
||||
alias_attribute :subscribed_to_mod_roundrobin_notifications?, :mod_roundrobin_notifications
|
||||
alias_attribute :subscribed_to_email_follower_notifications?, :email_follower_notifications
|
||||
|
||||
after_commit :subscribe_to_mailchimp_newsletter
|
||||
|
||||
def subscribe_to_mailchimp_newsletter
|
||||
return unless email_newsletter
|
||||
|
||||
Users::SubscribeToMailchimpNewsletterWorker.perform_async(user.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ module Users
|
|||
class Setting < ApplicationRecord
|
||||
self.table_name_prefix = "users_"
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :user, touch: true
|
||||
scope :with_feed, -> { where.not(feed_url: [nil, ""]) }
|
||||
|
||||
# TODO: @msarit Double-check how these suffixes have impacted the rest of the codebase
|
||||
enum editor_version: { v2: 0, v1: 1 }, _suffix: :editor
|
||||
enum config_font: { default: 0, comic_sans: 1, monospace: 2, open_dyslexic: 3, sans_serif: 4, serif: 5 },
|
||||
_suffix: :font
|
||||
|
|
@ -18,7 +18,23 @@ module Users
|
|||
validates :feed_referential_link, inclusion: { in: [true, false] }
|
||||
validates :feed_url, length: { maximum: 500 }, allow_nil: true
|
||||
validates :inbox_guidelines, length: { maximum: 250 }, allow_nil: true
|
||||
end
|
||||
|
||||
# TODO: @msarit Re-add feed_url validation after updates are pointed directly to users_settings table
|
||||
validate :validate_feed_url, if: :feed_url_changed?
|
||||
|
||||
def resolved_font_name
|
||||
config_font.gsub("default", Settings::UserExperience.default_font)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_feed_url
|
||||
return if feed_url.blank?
|
||||
|
||||
valid = Feeds::ValidateUrl.call(feed_url)
|
||||
|
||||
errors.add(:feed_url, "is not a valid RSS/Atom feed") unless valid
|
||||
rescue StandardError => e
|
||||
errors.add(:feed_url, e.message)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -62,6 +62,10 @@ class UserPolicy < ApplicationPolicy
|
|||
true
|
||||
end
|
||||
|
||||
def onboarding_notifications_checkbox_update?
|
||||
true
|
||||
end
|
||||
|
||||
def update?
|
||||
current_user?
|
||||
end
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class ArticleWithVideoCreationService
|
|||
end
|
||||
|
||||
def initial_article_with_params(article)
|
||||
if @current_user.editor_version == "v1"
|
||||
if @current_user.setting.editor_version == "v1"
|
||||
title = "Unpublished Video ~ #{rand(100_000).to_s(26)}"
|
||||
article.body_markdown = "---\ntitle: #{title}\npublished: false\ndescription: \ntags: \n---\n\n"
|
||||
else
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ module Articles
|
|||
@tag = tag
|
||||
@prefill = prefill
|
||||
|
||||
@editor_version2 = @user&.editor_version == "v2"
|
||||
@editor_version2 = @user&.setting&.editor_version == "v2"
|
||||
end
|
||||
|
||||
def self.call(...)
|
||||
|
|
|
|||
|
|
@ -105,7 +105,8 @@ module Articles
|
|||
end
|
||||
|
||||
def score_experience_level(article)
|
||||
- (((article.experience_level_rating - (@user&.experience_level || 5)).abs / 2) * @experience_level_weight)
|
||||
user_experience_level = @user&.setting&.experience_level || 5
|
||||
- (((article.experience_level_rating - user_experience_level).abs / 2) * @experience_level_weight)
|
||||
end
|
||||
|
||||
def score_comments(article)
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ class EmailDigest
|
|||
private
|
||||
|
||||
def get_users
|
||||
User.registered.where(email_digest_periodic: true).where.not(email: "")
|
||||
User.registered.joins(:notification_setting)
|
||||
.where(notification_setting: { email_digest_periodic: true })
|
||||
.where.not(email: "")
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ class EmailDigestArticleCollector
|
|||
return [] unless should_receive_email?
|
||||
|
||||
articles = if user_has_followings?
|
||||
experience_level_rating = (@user.experience_level || 5)
|
||||
experience_level_rating = (@user.setting.experience_level || 5)
|
||||
experience_level_rating_min = experience_level_rating - 3.6
|
||||
experience_level_rating_max = experience_level_rating + 3.6
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ module Feeds
|
|||
published: false
|
||||
date: #{@item.published}
|
||||
tags: #{get_tags}
|
||||
canonical_url: #{@user.feed_mark_canonical ? @feed_source_url : ''}
|
||||
canonical_url: #{@user.setting.feed_mark_canonical ? @feed_source_url : ''}
|
||||
---
|
||||
|
||||
#{assemble_body_markdown}
|
||||
|
|
@ -64,7 +64,7 @@ module Feeds
|
|||
def thorough_parsing(content, feed_url)
|
||||
html_doc = Nokogiri::HTML(content)
|
||||
|
||||
find_and_replace_possible_links!(html_doc) if @user.feed_referential_link
|
||||
find_and_replace_possible_links!(html_doc) if @user.setting.feed_referential_link
|
||||
find_and_replace_picture_tags_with_img!(html_doc)
|
||||
|
||||
if feed_url&.include?("medium.com")
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ module Feeds
|
|||
|
||||
def initialize(users: nil, earlier_than: nil)
|
||||
# using nil here to avoid an unnecessary table count to check presence
|
||||
@users = users || User.with_feed
|
||||
@users = users || User.where(id: Users::Setting.with_feed.select(:user_id))
|
||||
@earlier_than = earlier_than
|
||||
|
||||
# NOTE: should these be configurable? Currently they are the result of empiric
|
||||
|
|
@ -64,7 +64,7 @@ module Feeds
|
|||
|
||||
# TODO: put this in separate service object
|
||||
def fetch_feeds(batch_of_users)
|
||||
data = batch_of_users.pluck(:id, :feed_url)
|
||||
data = batch_of_users.joins(:setting).pluck(:id, "users_settings.feed_url")
|
||||
|
||||
result = Parallel.map(data, in_threads: num_fetchers) do |user_id, url|
|
||||
cleaned_url = url.to_s.strip
|
||||
|
|
@ -149,7 +149,7 @@ module Feeds
|
|||
e,
|
||||
feeds_import_info: {
|
||||
username: user.username,
|
||||
feed_url: user.feed_url,
|
||||
feed_url: user.setting&.feed_url,
|
||||
item_count: item_count_error(feed),
|
||||
error: "Feeds::Import::CreateArticleError:#{item.url}"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ module Mailchimp
|
|||
gibbon.lists(Settings::General.mailchimp_newsletter_id).members(target_md5_email).upsert(
|
||||
body: {
|
||||
email_address: user.email,
|
||||
status: user.email_newsletter ? "subscribed" : "unsubscribed",
|
||||
status: user.notification_setting.email_newsletter ? "subscribed" : "unsubscribed",
|
||||
merge_fields: {
|
||||
NAME: user.name.to_s,
|
||||
USERNAME: user.username.to_s,
|
||||
|
|
@ -35,7 +35,7 @@ module Mailchimp
|
|||
ARTICLES: user.articles.size,
|
||||
COMMENTS: user.comments.size,
|
||||
ONBOARD_PK: user.onboarding_package_requested.to_s,
|
||||
EXPERIENCE: user.experience_level || 666
|
||||
EXPERIENCE: user.setting.experience_level || 666
|
||||
}
|
||||
},
|
||||
)
|
||||
|
|
@ -68,7 +68,7 @@ module Mailchimp
|
|||
return false unless Settings::General.mailchimp_community_moderators_id.present? && user.has_role?(:trusted)
|
||||
|
||||
success = false
|
||||
status = user.email_community_mod_newsletter ? "subscribed" : "unsubscribed"
|
||||
status = user.notification_setting.email_community_mod_newsletter ? "subscribed" : "unsubscribed"
|
||||
begin
|
||||
gibbon.lists(Settings::General.mailchimp_community_moderators_id).members(target_md5_email).upsert(
|
||||
body: {
|
||||
|
|
@ -98,7 +98,7 @@ module Mailchimp
|
|||
tag_ids = user.roles.where(name: "tag_moderator").pluck(:resource_id)
|
||||
tag_names = Tag.where(id: tag_ids).pluck(:name)
|
||||
|
||||
status = user.email_tag_mod_newsletter ? "subscribed" : "unsubscribed"
|
||||
status = user.notification_setting.email_tag_mod_newsletter ? "subscribed" : "unsubscribed"
|
||||
|
||||
begin
|
||||
gibbon.lists(Settings::General.mailchimp_tag_moderators_id).members(target_md5_email).upsert(
|
||||
|
|
|
|||
|
|
@ -33,9 +33,9 @@ module Moderator
|
|||
def remove_mod_roles
|
||||
@user.remove_role(:trusted)
|
||||
@user.remove_role(:tag_moderator)
|
||||
@user.update(email_tag_mod_newsletter: false)
|
||||
@user.notification_setting.update(email_tag_mod_newsletter: false)
|
||||
Mailchimp::Bot.new(user).manage_tag_moderator_list
|
||||
@user.update(email_community_mod_newsletter: false)
|
||||
@user.notification_setting.update(email_community_mod_newsletter: false)
|
||||
Mailchimp::Bot.new(user).manage_community_moderator_list
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@ module Notifications
|
|||
SUPPORTED = [Comment].freeze
|
||||
|
||||
def self.available_moderators
|
||||
User.with_role(:trusted)
|
||||
.where("last_moderation_notification < ?", MODERATORS_AVAILABILITY_DELAY.ago)
|
||||
.where(mod_roundrobin_notifications: true)
|
||||
User.with_role(:trusted).joins(:notification_setting)
|
||||
.where("last_moderation_notification < ?", 3.days.ago)
|
||||
.where(notification_setting: { mod_roundrobin_notifications: true })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@ module Notifications
|
|||
end
|
||||
|
||||
# Send PNs using Rpush - respecting users' notificaton delivery settings
|
||||
targets = User.where(id: user_ids, mobile_comment_notifications: true).ids
|
||||
targets = User.joins(:notification_setting)
|
||||
.where(notification_setting: { mobile_comment_notifications: true }).ids
|
||||
|
||||
PushNotifications::Send.call(
|
||||
user_ids: targets,
|
||||
title: "@#{comment.user.username}",
|
||||
|
|
|
|||
|
|
@ -24,8 +24,10 @@ module Profiles
|
|||
def initialize(user, updated_attributes)
|
||||
@user = user
|
||||
@profile = user.profile
|
||||
@users_setting = user.setting
|
||||
@updated_profile_attributes = updated_attributes[:profile] || {}
|
||||
@updated_user_attributes = updated_attributes[:user].to_h || {}
|
||||
@updated_users_setting_attributes = updated_attributes[:users_setting].to_h || {}
|
||||
@errors = []
|
||||
@success = false
|
||||
end
|
||||
|
|
@ -38,6 +40,7 @@ module Profiles
|
|||
else
|
||||
errors.concat(@profile.errors.full_messages)
|
||||
errors.concat(@user.errors.full_messages)
|
||||
errors.concat(@users_setting.errors.full_messages)
|
||||
Honeycomb.add_field("error", errors_as_sentence)
|
||||
Honeycomb.add_field("errored", true)
|
||||
end
|
||||
|
|
@ -62,6 +65,7 @@ module Profiles
|
|||
Profile.transaction do
|
||||
update_profile
|
||||
@user.update!(@updated_user_attributes)
|
||||
@users_setting.update!(@updated_users_setting_attributes)
|
||||
end
|
||||
true
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
|
|
|
|||
|
|
@ -49,7 +49,9 @@ module TagModerators
|
|||
end
|
||||
|
||||
def add_tag_mod_role(user, tag)
|
||||
user.update(email_tag_mod_newsletter: true) unless user.email_tag_mod_newsletter?
|
||||
unless user.notification_setting.email_tag_mod_newsletter?
|
||||
user.notification_setting.update(email_tag_mod_newsletter: true)
|
||||
end
|
||||
user.add_role(:tag_moderator, tag)
|
||||
Rails.cache.delete("user-#{user.id}/tag_moderators_list")
|
||||
return unless tag_mod_newsletter_enabled?
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ module TagModerators
|
|||
return if user.has_role?(:trusted) || user.suspended?
|
||||
|
||||
user.add_role(:trusted)
|
||||
user.update(email_community_mod_newsletter: true)
|
||||
user.notification_setting.update(email_community_mod_newsletter: true)
|
||||
Rails.cache.delete("user-#{user.id}/has_trusted_role")
|
||||
NotifyMailer.with(user: user).trusted_role_email.deliver_now
|
||||
return unless community_mod_newsletter_enabled?
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ module TagModerators
|
|||
class Remove
|
||||
def self.call(user, tag)
|
||||
user.remove_role(:tag_moderator, tag)
|
||||
user.update(email_tag_mod_newsletter: false) if user.email_tag_mod_newsletter?
|
||||
if user.notification_setting.email_tag_mod_newsletter?
|
||||
user.notification_setting.update(email_tag_mod_newsletter: false)
|
||||
end
|
||||
Rails.cache.delete("user-#{user.id}/tag_moderators_list")
|
||||
return unless tag_mod_newsletter_enabled?
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
<center style="margin-top:50px;">
|
||||
<em>
|
||||
<% if @user.experience_level == nil %>
|
||||
<% if @user.setting.experience_level.nil? %>
|
||||
<b>
|
||||
You can now add your experience level to your account in order to receive more relevant content.
|
||||
</b>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
<div class="grid gap-6 m:grid-cols-2">
|
||||
<div class="grid gap-6">
|
||||
<% account.bg_color_hex = user_colors(account)[:bg] if account.bg_color_hex.blank? %>
|
||||
<% account.text_color_hex = user_colors(account)[:text] if account.text_color_hex.blank? %>
|
||||
<% if account.class.name == "User" %>
|
||||
<% account.setting.brand_color1 = user_colors(account)[:bg] if account.setting && account.setting.brand_color1.blank? %>
|
||||
<% account.setting.brand_color2 = user_colors(account)[:text] if account.setting && account.setting.brand_color2.blank? %>
|
||||
<% else %>
|
||||
<% account.bg_color_hex = user_colors(account)[:bg] if account.bg_color_hex.blank? %>
|
||||
<% account.text_color_hex = user_colors(account)[:text] if account.text_color_hex.blank? %>
|
||||
<% end %>
|
||||
<div class="crayons-field">
|
||||
<%= f.label :bg_color_hex, "Brand color 1", class: "crayons-field__label" %>
|
||||
<p class="crayons-field__description">Used for backgrounds, borders etc.</p>
|
||||
|
|
@ -15,7 +20,11 @@
|
|||
</div>
|
||||
|
||||
<div class="crayons-field">
|
||||
<% account.bg_color_hex = user_colors(account)[:text] if account.text_color_hex.blank? %>
|
||||
<% if account.class.name == "User" %>
|
||||
<% account.setting.brand_color1 = user_colors(account)[:text] if account.setting && account.setting.brand_color2.blank? %>
|
||||
<% else %>
|
||||
<% account.bg_color_hex = user_colors(account)[:text] if account.text_color_hex.blank? %>
|
||||
<% end %>
|
||||
<%= f.label :text_color_hex, "Brand color 2", class: "crayons-field__label" %>
|
||||
<p class="crayons-field__description">Used for texts (usually put on <em>Brand color 1</em>).</p>
|
||||
<div class="flex items-center">
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<% if actor.class.name == "User" %>
|
||||
<div class="user-metadata-details">
|
||||
<ul class="user-metadata-details-inner">
|
||||
<% if actor.email_public && context != "sidebar" %>
|
||||
<% if actor.setting.display_email_on_profile && context != "sidebar" %>
|
||||
<li>
|
||||
<div class="key">
|
||||
Email
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
<%= javascript_packs_with_chunks_tag "stickySaveFooter", defer: true %>
|
||||
|
||||
<%= form_for @user, html: { id: "ux-customization-form", class: "sticky-footer-form" } do |f| %>
|
||||
<%= form_for @users_setting,
|
||||
url: users_settings_path,
|
||||
html: { id: "ux-customization-form", class: "sticky-footer-form" } do |f| %>
|
||||
<%= f.hidden_field :user_id, value: @users_setting.user_id, id: nil %>
|
||||
<%= f.hidden_field :tab, value: @tab, id: nil %>
|
||||
|
||||
<div class="crayons-card crayons-card--content-rows">
|
||||
|
|
@ -11,7 +14,7 @@
|
|||
<fieldset class="crayons-field">
|
||||
<legend class="crayons-field__label">Site Theme</legend>
|
||||
<div class="theme-selector-field grid gap-4 grid-cols-2 l:grid-cols-3">
|
||||
<% %w[default night_theme minimal_light_theme pink_theme ten_x_hacker_theme].each do |theme| %>
|
||||
<% Users::Setting.config_themes.keys.each do |theme| %>
|
||||
<%= render partial: "theme_selector", locals: { f: f, theme: theme } %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
@ -20,7 +23,7 @@
|
|||
<div class="crayons-field">
|
||||
<%= f.label :config_font, "Base Reading Font", class: "crayons-field__label" %>
|
||||
<div class="theme-selector-field grid gap-4 grid-cols-2 l:grid-cols-3">
|
||||
<% User::FONTS.each do |font| %>
|
||||
<% Users::Setting.config_fonts.keys.each do |font| %>
|
||||
<%= render partial: "font_selector", locals: { f: f, font: font } %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
@ -29,7 +32,7 @@
|
|||
<div class="crayons-field">
|
||||
<%= f.label :config_navbar, "Site Navbar", class: "crayons-field__label" %>
|
||||
<div class="theme-selector-field grid gap-4 grid-cols-2 l:grid-cols-3">
|
||||
<% %w[default static].each do |navbar| %>
|
||||
<% Users::Setting.config_navbars.keys.each do |navbar| %>
|
||||
<%= render partial: "navbar_selector", locals: { f: f, navbar: navbar } %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
@ -43,7 +46,7 @@
|
|||
<div class="crayons-field">
|
||||
<%= f.label :editor_version, "Editor Version", class: "crayons-field__label" %>
|
||||
<div class="theme-selector-field grid gap-4 grid-cols-2 l:grid-cols-3">
|
||||
<% %w[v1 v2].each do |version| %>
|
||||
<% Users::Setting.editor_versions.keys.each do |version| %>
|
||||
<%= render partial: "editor_selector", locals: { f: f, version: version } %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<label class="rich-selector crayons-field crayons-field--radio editor-selector--<%= version %>">
|
||||
<%= f.radio_button :editor_version, version, checked: @user.editor_version == version, class: "crayons-radio" %>
|
||||
<%= f.radio_button :editor_version, version, checked: @users_setting.editor_version == version, class: "crayons-radio" %>
|
||||
<div class="crayons-field__label">
|
||||
<% if version == "v1" %>
|
||||
Basic markdown
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<label>
|
||||
<%= f.radio_button :experience_level, exp, checked: @user.experience_level == exp.to_i, class: "rich-selector-radio" %>
|
||||
<%= f.radio_button :experience_level, exp, checked: @users_setting.experience_level == exp.to_i, class: "rich-selector-radio" %>
|
||||
<div class="rich-selector rich-selector--experience align-center my-2 mx-1 fw-medium fs-l px-0 s:px-1">
|
||||
<p><%= i + 1 %></p>
|
||||
<p class="fs-xs m:fs-s fw-normal"><%= label[i] %></p>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<h2 class="crayons-subtitle-1">
|
||||
Connect settings
|
||||
</h2>
|
||||
<%= form_for(@user, html: { id: nil, class: "grid gap-6" }) do |f| %>
|
||||
<%= form_for(@users_setting, url: users_settings_path, html: { id: nil, class: "grid gap-6" }) do |f| %>
|
||||
<div class="crayons-field">
|
||||
<%= f.label :inbox_type, "Inbox privacy", class: "crayons-field__label" %>
|
||||
<p class="crayons-field__description">Open your inbox to messages from people you don't follow or keep your inbox private to mutual follows.</p>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<label class="rich-selector crayons-field crayons-field--radio font-selector--<%= font.tr("_", "-") %>">
|
||||
<%= f.radio_button :config_font, font, checked: @user.decorate.config_font_name == font, class: "crayons-radio" %>
|
||||
<%= f.radio_button :config_font, font, checked: @users_setting.resolved_font_name == font, class: "crayons-radio" %>
|
||||
<div class="crayons-field__label fw-normal">
|
||||
<%= font.tr("_", " ") %>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<label class="rich-selector crayons-field crayons-field--radio navbar-selector--<%= navbar %>">
|
||||
<%= f.radio_button :config_navbar, navbar, checked: @user.config_navbar == navbar, class: "crayons-radio" %>
|
||||
<%= f.radio_button :config_navbar, navbar, checked: @users_setting.config_navbar == navbar, class: "crayons-radio" %>
|
||||
<div class="crayons-field__label">
|
||||
<% if navbar == "default" %>
|
||||
Fixed to window
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<%= form_for @user do |f| %>
|
||||
<%= form_for @users_notification_setting, url: users_notification_settings_path do |f| %>
|
||||
<div class="crayons-card crayons-card--content-rows">
|
||||
<header>
|
||||
<h2 class="crayons-subtitle-1">
|
||||
|
|
@ -105,7 +105,6 @@
|
|||
<% end %>
|
||||
|
||||
<div class="crayons-card crayons-card--content-rows">
|
||||
<%= f.hidden_field :tab, value: @tab %>
|
||||
<button type="submit" class="crayons-btn">Save</button>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
<%= f.text_field "user[email]", class: "crayons-textfield", placeholder: "john.doe@example.com", value: @user.email %>
|
||||
</div>
|
||||
|
||||
<div class="crayons-field crayons-field--checkbox">
|
||||
<%= f.check_box "users_setting[display_email_on_profile]", class: "crayons-checkbox", checked: @user.setting.display_email_on_profile %>
|
||||
<%= f.label :display_email_on_profile, for: "users_setting[display_email_on_profile]", class: "crayons-field__label" %>
|
||||
</div>
|
||||
|
||||
<div class="crayons-field">
|
||||
<%= f.label :username, for: "user[username]", class: "crayons-field__label" %>
|
||||
<%= f.text_field "user[username]", maxlength: 30, class: "crayons-textfield", placeholder: "johndoe", value: @user.username %>
|
||||
|
|
@ -84,6 +89,7 @@
|
|||
<% ProfileFieldGroup.non_empty_groups.each do |group| %>
|
||||
<% next if group.name == "Basic" # TODO: @citizen428 Remove this after user settings work (email field) %>
|
||||
<% next if group.name == "Links" # TODO: [@jacobherrington] Remove this when we drop social links %>
|
||||
<% next if group.name == "Branding" # TODO: [@msarit @jacobherrington] May be further refactored later as part of Profiles work %>
|
||||
<div class="crayons-card crayons-card--content-rows">
|
||||
<h2><%= group.name %></h2>
|
||||
<% if group.description.present? %>
|
||||
|
|
@ -91,6 +97,7 @@
|
|||
<% end %>
|
||||
|
||||
<% group.profile_fields.each do |field| %>
|
||||
<% next if field.attribute_name == "display_email_on_profile" %>
|
||||
<div class="crayons-field <%= "crayons-field--checkbox" if field.input_type == "check_box" %>">
|
||||
<% if field["input_type"] == "check_box" %>
|
||||
<%= f.public_send(field["input_type"],
|
||||
|
|
@ -132,6 +139,44 @@
|
|||
</div>
|
||||
<% end %>
|
||||
|
||||
<% users_setting = @user.setting %>
|
||||
<div class="crayons-card crayons-card--content-rows">
|
||||
<h2>Branding</h2>
|
||||
<div class="crayons-field ">
|
||||
<label class="crayons-field__label" for="users_setting[brand_color1]">
|
||||
Brand color 1
|
||||
</label>
|
||||
<div class="flex items-center w-100 m:w-50">
|
||||
<%= f.public_send("text_field", "users_setting[brand_color1]",
|
||||
value: users_setting.public_send("brand_color1"),
|
||||
placeholder: "#000000",
|
||||
class: "crayons-textfield js-color-field") %>
|
||||
<%= f.public_send("color_field",
|
||||
"users_setting[brand_color1]",
|
||||
value: users_setting.public_send("brand_color1"),
|
||||
class: "crayons-color-selector js-color-field ml-2") %>
|
||||
</div>
|
||||
<p class="crayons-field__description">Used for backgrounds, borders etc.</p>
|
||||
</div>
|
||||
|
||||
<div class="crayons-field ">
|
||||
<label class="crayons-field__label" for="users_setting[brand_color1]">
|
||||
Brand color 2
|
||||
</label>
|
||||
<div class="flex items-center w-100 m:w-50">
|
||||
<%= f.public_send("text_field", "users_setting[brand_color2]",
|
||||
value: users_setting.public_send("brand_color2"),
|
||||
placeholder: "#000000",
|
||||
class: "crayons-textfield js-color-field") %>
|
||||
<%= f.public_send("color_field",
|
||||
"users_setting[brand_color2]",
|
||||
value: users_setting.public_send("brand_color2"),
|
||||
class: "crayons-color-selector js-color-field ml-2") %>
|
||||
</div>
|
||||
<p class="crayons-field__description">Used for texts (usually put on Brand color 1).</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="save-footer crayons-card crayons-card--content-rows">
|
||||
<button type="submit" class="crayons-btn">Save Profile Information</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
<h2 class="crayons-subtitle-1">
|
||||
Publishing to <%= community_name %> from RSS
|
||||
</h2>
|
||||
<% if @user.feed_url.present? %>
|
||||
<%= form_for(@user) do |f| %>
|
||||
<%= f.hidden_field :feed_url, value: @user.feed_url %>
|
||||
<% if @users_setting.feed_url.present? %>
|
||||
<%= form_for @users_setting, url: users_settings_path do |f| %>
|
||||
<%= f.hidden_field :feed_url, value: @users_setting.feed_url %>
|
||||
<%= f.hidden_field :tab, value: @tab %>
|
||||
<div class="flex items-center">
|
||||
<button type="submit" class="crayons-btn crayons-btn--secondary">Fetch feed now</button>
|
||||
<p>Last Fetched: <time id="rss-fetch-time" datetime="<%= @user.feed_fetched_at.iso8601 %>"></time></p>
|
||||
<p class="pl-2">Last Fetched: <time id="rss-fetch-time" datetime="<%= @users_setting.user.feed_fetched_at.iso8601 %>"></time></p>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
|
@ -30,7 +30,7 @@
|
|||
<p><em>By submitting your RSS Feed URL, you agree that you own and/or have permission to syndicate the associated content.</em></p>
|
||||
</div>
|
||||
|
||||
<%= form_for @user, html: { class: "grid gap-6" } do |f| %>
|
||||
<%= form_for @users_setting, url: users_settings_path, html: { class: "grid gap-6" } do |f| %>
|
||||
<div class="crayons-field">
|
||||
<%= f.label :feed_url, "RSS Feed URL", class: "crayons-field__label" %>
|
||||
<%= f.url_field :feed_url, placeholder: "https://yoursite.com/feed", class: "crayons-textfield" %>
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
</div>
|
||||
|
||||
<%= f.hidden_field :tab, value: @tab, id: nil %>
|
||||
<% button_text = @user.feed_url.present? ? "Save Feed Settings" : "Submit Feed Settings" %>
|
||||
<% button_text = @users_setting.feed_url.present? ? "Save Feed Settings" : "Submit Feed Settings" %>
|
||||
<button class="crayons-btn w-max" type="submit"><%= button_text %></button>
|
||||
<% end %>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<label class="rich-selector crayons-field crayons-field--radio p-4 theme-selector theme-selector--<%= theme.tr("_", "-") %>" title="<%= theme.titlecase %>">
|
||||
<%= f.radio_button :config_theme, theme, checked: @user.config_theme == theme, class: "crayons-radio" %>
|
||||
<%= f.radio_button :config_theme, theme, checked: @users_setting.config_theme == theme, class: "crayons-radio" %>
|
||||
<div class="crayons-field__label">
|
||||
<%= theme.titlecase %>
|
||||
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@
|
|||
<%= local_date(@user.created_at) %>
|
||||
</span>
|
||||
|
||||
<% if @user.email_public %>
|
||||
<% if @user.setting.display_email_on_profile %>
|
||||
<a href="mailto:<%= @user.email %>" class="profile-header__meta__item">
|
||||
<%= inline_svg_tag("email.svg", class: "crayons-icon mr-2 shrink-0", aria: true, title: "Email address") %>
|
||||
<%= @user.email %>
|
||||
|
|
@ -114,11 +114,11 @@
|
|||
</div>
|
||||
|
||||
<div class="crayons-modal__box__body">
|
||||
<% if @user.inbox_guidelines.present? %>
|
||||
<% if @user.setting.inbox_guidelines.present? %>
|
||||
<p class="fw-bold mb-2">@<%= @user.username %>'s guidelines:</p>
|
||||
<p class="mb-6"><%= @user.inbox_guidelines %></p>
|
||||
<p class="mb-6"><%= @user.setting.inbox_guidelines %></p>
|
||||
<% end %>
|
||||
<form id="new-message-form" class="message-form mb-4" data-info='{"id":<%= @user.id %>,"className":"<%= @user.class.name %>","username":"<%= @user.username %>", "showChat":"<%= @user.inbox_type %>"}'>
|
||||
<form id="new-message-form" class="message-form mb-4" data-info='{"id":<%= @user.id %>,"className":"<%= @user.class.name %>","username":"<%= @user.username %>", "showChat":"<%= @user.setting.inbox_type %>"}'>
|
||||
<textarea id="new-message" rows="4" cols="70" placeholder="Enter your message here..." class="crayons-textfield"></textarea>
|
||||
<button type="submit" class="submit-message crayons-btn">Send</button>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ module Emails
|
|||
|
||||
def perform(user_id)
|
||||
user = User.find_by(id: user_id)
|
||||
return unless user&.email_digest_periodic? && user&.registered?
|
||||
return unless user&.notification_setting&.email_digest_periodic? && user&.registered?
|
||||
|
||||
articles = EmailDigestArticleCollector.new(user).articles_to_send
|
||||
return unless articles.any?
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
class MigrateUserNotificationSettingsWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
SQL = <<~SQL.freeze
|
||||
WITH notification_settings_data AS (
|
||||
SELECT
|
||||
users.id AS user_id,
|
||||
COALESCE(email_badge_notifications, true),
|
||||
COALESCE(email_comment_notifications, true),
|
||||
COALESCE(email_community_mod_newsletter, false),
|
||||
COALESCE(email_connect_messages, true),
|
||||
COALESCE(email_digest_periodic, false),
|
||||
COALESCE(email_follower_notifications, true),
|
||||
COALESCE(email_membership_newsletter, false),
|
||||
COALESCE(email_mention_notifications, true),
|
||||
COALESCE(email_newsletter, false),
|
||||
COALESCE(email_tag_mod_newsletter, false),
|
||||
COALESCE(email_unread_notifications, true),
|
||||
COALESCE(mobile_comment_notifications, true),
|
||||
COALESCE(mod_roundrobin_notifications, true),
|
||||
COALESCE(reaction_notifications, true),
|
||||
COALESCE(welcome_notifications, true),
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM users
|
||||
WHERE users.id = $1
|
||||
)
|
||||
INSERT INTO users_notification_settings (user_id, email_badge_notifications, email_comment_notifications, email_community_mod_newsletter, email_connect_messages, email_digest_periodic, email_follower_notifications, email_membership_newsletter, email_mention_notifications, email_newsletter, email_tag_mod_newsletter, email_unread_notifications, mobile_comment_notifications, mod_roundrobin_notifications, reaction_notifications, welcome_notifications, created_at, updated_at)
|
||||
SELECT * FROM notification_settings_data
|
||||
ON CONFLICT (user_id) DO UPDATE
|
||||
SET email_badge_notifications = EXCLUDED.email_badge_notifications,
|
||||
email_comment_notifications = EXCLUDED.email_comment_notifications,
|
||||
email_community_mod_newsletter = EXCLUDED.email_community_mod_newsletter,
|
||||
email_connect_messages = EXCLUDED.email_connect_messages,
|
||||
email_digest_periodic = EXCLUDED.email_digest_periodic,
|
||||
email_follower_notifications = EXCLUDED.email_follower_notifications,
|
||||
email_membership_newsletter = EXCLUDED.email_membership_newsletter,
|
||||
email_mention_notifications = EXCLUDED.email_mention_notifications,
|
||||
email_newsletter = EXCLUDED.email_newsletter,
|
||||
email_tag_mod_newsletter = EXCLUDED.email_tag_mod_newsletter,
|
||||
email_unread_notifications = EXCLUDED.email_unread_notifications,
|
||||
mobile_comment_notifications = EXCLUDED.mobile_comment_notifications,
|
||||
mod_roundrobin_notifications = EXCLUDED.mod_roundrobin_notifications,
|
||||
reaction_notifications = EXCLUDED.reaction_notifications,
|
||||
welcome_notifications = EXCLUDED.welcome_notifications,
|
||||
updated_at = NOW()
|
||||
SQL
|
||||
|
||||
def perform(user_id)
|
||||
Users::Setting.connection.exec_insert SQL, nil, [user_id]
|
||||
end
|
||||
end
|
||||
|
|
@ -1,83 +0,0 @@
|
|||
class MigrateUserSettingsWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
SQL = <<~SQL.freeze
|
||||
WITH settings_data AS (
|
||||
SELECT
|
||||
data ->> 'brand_color1' AS brand_color1,
|
||||
data ->> 'brand_color2' AS brand_color2,
|
||||
CASE WHEN config_font='default' THEN 0
|
||||
WHEN config_font='comic_sans' THEN 1
|
||||
WHEN config_font='monospace' THEN 2
|
||||
WHEN config_font='open_dyslexic' THEN 3
|
||||
WHEN config_font='sans_serif' THEN 4
|
||||
WHEN config_font='serif' THEN 5
|
||||
ELSE 0
|
||||
END
|
||||
config_font,
|
||||
CASE WHEN config_navbar='default_navbar' THEN 0
|
||||
WHEN config_navbar='static_navbar' THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
config_navbar,
|
||||
CASE WHEN config_theme='default_theme' THEN 0
|
||||
WHEN config_theme='minimal_light_theme' THEN 1
|
||||
WHEN config_theme='night_theme' THEN 2
|
||||
WHEN config_theme='pink_theme' THEN 3
|
||||
WHEN config_theme='ten_x_hacker_theme' THEN 4
|
||||
ELSE 0
|
||||
END
|
||||
config_theme,
|
||||
COALESCE(display_announcements, true),
|
||||
COALESCE((data ->> 'display_email_on_profile')::boolean, false) as display_email_on_profile,
|
||||
COALESCE(display_sponsors, true),
|
||||
CASE WHEN editor_version='v2' THEN 0
|
||||
WHEN editor_version='v1' THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
editor_version,
|
||||
experience_level,
|
||||
COALESCE(feed_mark_canonical, false),
|
||||
COALESCE(feed_referential_link, true),
|
||||
feed_url,
|
||||
inbox_guidelines,
|
||||
CASE WHEN inbox_type='private' THEN 0
|
||||
WHEN inbox_type='open' THEN 1
|
||||
ELSE 0
|
||||
END
|
||||
inbox_type,
|
||||
COALESCE(permit_adjacent_sponsors, true),
|
||||
users.id AS user_id,
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM users
|
||||
JOIN profiles
|
||||
ON profiles.user_id = users.id
|
||||
WHERE users.id = $1
|
||||
)
|
||||
INSERT INTO users_settings (brand_color1, brand_color2, config_font, config_navbar, config_theme, display_announcements, display_email_on_profile, display_sponsors, editor_version, experience_level, feed_mark_canonical, feed_referential_link, feed_url, inbox_guidelines, inbox_type, permit_adjacent_sponsors, user_id, created_at, updated_at)
|
||||
SELECT * FROM settings_data
|
||||
ON CONFLICT (user_id) DO UPDATE
|
||||
SET brand_color1 = EXCLUDED.brand_color1,
|
||||
brand_color2 = EXCLUDED.brand_color2,
|
||||
config_font = EXCLUDED.config_font,
|
||||
config_navbar = EXCLUDED.config_navbar,
|
||||
config_theme = EXCLUDED.config_theme,
|
||||
display_announcements = EXCLUDED.display_announcements,
|
||||
display_email_on_profile = EXCLUDED.display_email_on_profile,
|
||||
display_sponsors = EXCLUDED.display_sponsors,
|
||||
editor_version = EXCLUDED.editor_version,
|
||||
experience_level = EXCLUDED.experience_level,
|
||||
feed_mark_canonical = EXCLUDED.feed_mark_canonical,
|
||||
feed_referential_link = EXCLUDED.feed_referential_link,
|
||||
feed_url = EXCLUDED.feed_url,
|
||||
inbox_guidelines = EXCLUDED.inbox_guidelines,
|
||||
inbox_type = EXCLUDED.inbox_type,
|
||||
permit_adjacent_sponsors = EXCLUDED.permit_adjacent_sponsors,
|
||||
updated_at = NOW()
|
||||
SQL
|
||||
|
||||
def perform(user_id)
|
||||
Users::Setting.connection.exec_insert SQL, nil, [user_id]
|
||||
end
|
||||
end
|
||||
|
|
@ -20,7 +20,7 @@ module Notifications
|
|||
return false unless receiver
|
||||
return true if receiver_klass == "Organization"
|
||||
|
||||
receiver.reaction_notifications
|
||||
receiver.notification_setting.reaction_notifications
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -82,6 +82,11 @@ Rails.application.configure do
|
|||
Bullet.add_whitelist(type: :unused_eager_loading, class_name: "Comment", association: :user)
|
||||
# NOTE: @citizen428 Temporarily ignoring this while working out user - profile relationship
|
||||
Bullet.add_whitelist(type: :n_plus_one_query, class_name: "User", association: :profile)
|
||||
Bullet.add_whitelist(type: :n_plus_one_query, class_name: "User", association: :setting)
|
||||
Bullet.add_whitelist(type: :n_plus_one_query, class_name: "User", association: :notification_setting)
|
||||
# @mstruve: These occur during setting updates, not sure how since we are only dealing with single setting records
|
||||
Bullet.add_whitelist(type: :n_plus_one_query, class_name: "Users::Setting", association: :user)
|
||||
Bullet.add_whitelist(type: :n_plus_one_query, class_name: "Users::NotificationSetting", association: :user)
|
||||
end
|
||||
end
|
||||
# rubocop:enable Metrics/BlockLength
|
||||
|
|
|
|||
|
|
@ -136,6 +136,10 @@ Rails.application.routes.draw do
|
|||
resources :devices, only: %i[create destroy]
|
||||
end
|
||||
end
|
||||
namespace :users do
|
||||
resource :settings, only: %i[update]
|
||||
resource :notification_settings, only: %i[update]
|
||||
end
|
||||
resources :users, only: %i[update]
|
||||
resources :reactions, only: %i[index create]
|
||||
resources :response_templates, only: %i[index create edit update destroy]
|
||||
|
|
@ -227,6 +231,8 @@ Rails.application.routes.draw do
|
|||
post "/notification_subscriptions/:notifiable_type/:notifiable_id", to: "notification_subscriptions#upsert"
|
||||
patch "/onboarding_update", to: "users#onboarding_update"
|
||||
patch "/onboarding_checkbox_update", to: "users#onboarding_checkbox_update"
|
||||
patch "/onboarding_notifications_checkbox_update",
|
||||
to: "users/notification_settings#onboarding_notifications_checkbox_update"
|
||||
get "email_subscriptions/unsubscribe"
|
||||
post "/chat_channels/:id/moderate", to: "chat_channels#moderate"
|
||||
post "/chat_channels/:id/open", to: "chat_channels#open"
|
||||
|
|
|
|||
|
|
@ -75,8 +75,6 @@ users_in_random_order = seeder.create_if_none(User, num_users) do
|
|||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
website_url: Faker::Internet.url,
|
||||
twitter_username: Faker::Internet.username(specifier: name),
|
||||
email_comment_notifications: false,
|
||||
email_follower_notifications: false,
|
||||
# Emails limited to 50 characters
|
||||
email: Faker::Internet.email(name: name, separators: "+", domain: Faker::Internet.domain_word.first(20)),
|
||||
confirmed_at: Time.current,
|
||||
|
|
@ -142,8 +140,6 @@ seeder.create_if_doesnt_exist(User, "email", "admin@forem.local") do
|
|||
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
|
||||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
website_url: Faker::Internet.url,
|
||||
email_comment_notifications: false,
|
||||
email_follower_notifications: false,
|
||||
confirmed_at: Time.current,
|
||||
password: "password",
|
||||
password_confirmation: "password",
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
module DataUpdateScripts
|
||||
class MigrateRelevantFieldsFromUsersToUsersSettings
|
||||
def run
|
||||
User.ids.each do |id|
|
||||
MigrateUserSettingsWorker.perform_async id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
module DataUpdateScripts
|
||||
class MigrateRelevantFieldsFromUsersToUsersNotificationSettings
|
||||
def run
|
||||
User.ids.each do |id|
|
||||
MigrateUserNotificationSettingsWorker.perform_async id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
module DataUpdateScripts
|
||||
class RemoveNotificationSettingMigrationScripts
|
||||
SCRIPTS_TO_REMOVE = %w[
|
||||
20210423155327_migrate_relevant_fields_from_users_to_users_settings
|
||||
20210503174302_migrate_relevant_fields_from_users_to_users_notification_settings
|
||||
].freeze
|
||||
|
||||
def run
|
||||
DataUpdateScript.delete_by(file_name: SCRIPTS_TO_REMOVE)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -2,7 +2,7 @@ require "rails_helper"
|
|||
|
||||
RSpec.describe UserDecorator, type: :decorator do
|
||||
let(:saved_user) { create(:user) }
|
||||
let(:user) { build(:user) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
context "with serialization" do
|
||||
it "serializes both the decorated object IDs and decorated methods" do
|
||||
|
|
@ -54,120 +54,104 @@ RSpec.describe UserDecorator, type: :decorator do
|
|||
|
||||
describe "#darker_color" do
|
||||
it "returns a darker version of the assigned color if colors are blank" do
|
||||
saved_user.assign_attributes(bg_color_hex: "", text_color_hex: "")
|
||||
saved_user.setting.update(brand_color1: "", brand_color2: "")
|
||||
expect(saved_user.decorate.darker_color).to be_present
|
||||
end
|
||||
|
||||
it "returns a darker version of the color if bg_color_hex is present" do
|
||||
saved_user.assign_attributes(bg_color_hex: "#dddddd", text_color_hex: "#ffffff")
|
||||
it "returns a darker version of the color if brand_color1 is present" do
|
||||
saved_user.setting.update(brand_color1: "#dddddd", brand_color2: "#ffffff")
|
||||
expect(saved_user.decorate.darker_color).to eq("#c2c2c2")
|
||||
end
|
||||
|
||||
it "returns an adjusted darker version of the color" do
|
||||
saved_user.assign_attributes(bg_color_hex: "#dddddd", text_color_hex: "#ffffff")
|
||||
saved_user.setting.update(brand_color1: "#dddddd", brand_color2: "#ffffff")
|
||||
expect(saved_user.decorate.darker_color(0.3)).to eq("#424242")
|
||||
end
|
||||
|
||||
it "returns an adjusted lighter version of the color if adjustment is over 1.0" do
|
||||
saved_user.assign_attributes(bg_color_hex: "#dddddd", text_color_hex: "#ffffff")
|
||||
saved_user.setting.update(brand_color1: "#dddddd", brand_color2: "#ffffff")
|
||||
expect(saved_user.decorate.darker_color(1.1)).to eq("#f3f3f3")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#enriched_colors" do
|
||||
it "returns assigned colors if bg_color_hex is blank" do
|
||||
saved_user.assign_attributes(bg_color_hex: "")
|
||||
it "returns assigned colors if brand_color1 is blank" do
|
||||
saved_user.setting.update(brand_color1: "")
|
||||
expect(saved_user.decorate.enriched_colors[:bg]).to be_present
|
||||
expect(saved_user.decorate.enriched_colors[:text]).to be_present
|
||||
end
|
||||
|
||||
it "returns assigned colors if text_color_hex is blank" do
|
||||
saved_user.assign_attributes(text_color_hex: "")
|
||||
it "returns assigned colors if brand_color2 is blank" do
|
||||
saved_user.setting.update(brand_color2: "")
|
||||
expect(saved_user.decorate.enriched_colors[:bg]).to be_present
|
||||
expect(saved_user.decorate.enriched_colors[:text]).to be_present
|
||||
end
|
||||
|
||||
it "returns bg_color_hex and assigned text_color_hex if text_color_hex is blank" do
|
||||
saved_user.assign_attributes(bg_color_hex: "#dddddd", text_color_hex: "")
|
||||
it "returns brand_color1 and assigned brand_color2 if brand_color2 is blank" do
|
||||
saved_user.setting.update(brand_color1: "#dddddd", brand_color2: "")
|
||||
expect(saved_user.decorate.enriched_colors[:bg]).to be_present
|
||||
expect(saved_user.decorate.enriched_colors[:text]).to be_present
|
||||
end
|
||||
|
||||
it "returns text_color_hex and assigned bg_color_hex if bg_color_hex is blank" do
|
||||
saved_user.assign_attributes(bg_color_hex: "", text_color_hex: "#ffffff")
|
||||
it "returns brand_color2 and assigned brand_color1 if brand_color1 is blank" do
|
||||
saved_user.setting.update(brand_color1: "", brand_color2: "#ffffff")
|
||||
expect(saved_user.decorate.enriched_colors[:bg]).to be_present
|
||||
expect(saved_user.decorate.enriched_colors[:text]).to be_present
|
||||
end
|
||||
|
||||
it "returns bg_color_hex and text_color_hex if both are present" do
|
||||
saved_user.assign_attributes(bg_color_hex: "#dddddd", text_color_hex: "#fffff3")
|
||||
it "returns brand_color1 and brand_color2 if both are present" do
|
||||
saved_user.setting.update(brand_color1: "#dddddd", brand_color2: "#fffff3")
|
||||
expect(saved_user.decorate.enriched_colors).to eq(bg: "#dddddd", text: "#fffff3")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#config_font_name" do
|
||||
it "replaces 'default' with font configured for the site in Settings::General" do
|
||||
expect(user.config_font).to eq("default")
|
||||
%w[sans_serif serif open_dyslexic].each do |font|
|
||||
allow(Settings::UserExperience).to receive(:default_font).and_return(font)
|
||||
expect(user.decorate.config_font_name).to eq(font)
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't replace the user's custom selected font" do
|
||||
user_comic_sans = create(:user, config_font: "comic_sans")
|
||||
allow(Settings::UserExperience).to receive(:default_font).and_return("open_dyslexic")
|
||||
expect(user_comic_sans.decorate.config_font_name).to eq("comic_sans")
|
||||
end
|
||||
end
|
||||
|
||||
describe "#config_body_class" do
|
||||
it "creates proper body class with defaults" do
|
||||
expected_result = %W[
|
||||
default sans-serif-article-body
|
||||
trusted-status-#{user.trusted} #{user.config_navbar}-header
|
||||
trusted-status-#{user.trusted} #{user.setting.config_navbar}-header
|
||||
].join(" ")
|
||||
expect(user.decorate.config_body_class).to eq(expected_result)
|
||||
end
|
||||
|
||||
it "creates proper body class with sans serif config" do
|
||||
user.config_font = "sans_serif"
|
||||
user.setting.config_font = "sans_serif"
|
||||
expected_result = %W[
|
||||
default sans-serif-article-body
|
||||
trusted-status-#{user.trusted} #{user.config_navbar}-header
|
||||
trusted-status-#{user.trusted} #{user.setting.config_navbar}-header
|
||||
].join(" ")
|
||||
expect(user.decorate.config_body_class).to eq(expected_result)
|
||||
end
|
||||
|
||||
it "creates proper body class with night theme" do
|
||||
user.config_theme = "night_theme"
|
||||
user.setting.config_theme = "night_theme"
|
||||
expected_result = %W[
|
||||
night-theme sans-serif-article-body
|
||||
trusted-status-#{user.trusted} #{user.config_navbar}-header
|
||||
trusted-status-#{user.trusted} #{user.setting.config_navbar}-header
|
||||
].join(" ")
|
||||
expect(user.decorate.config_body_class).to eq(expected_result)
|
||||
end
|
||||
|
||||
it "creates proper body class with pink theme" do
|
||||
user.config_theme = "pink_theme"
|
||||
user.setting.config_theme = "pink_theme"
|
||||
expected_result = %W[
|
||||
pink-theme sans-serif-article-body
|
||||
trusted-status-#{user.trusted} #{user.config_navbar}-header
|
||||
trusted-status-#{user.trusted} #{user.setting.config_navbar}-header
|
||||
].join(" ")
|
||||
expect(user.decorate.config_body_class).to eq(expected_result)
|
||||
end
|
||||
|
||||
it "creates proper body class with minimal light theme" do
|
||||
user.config_theme = "minimal_light_theme"
|
||||
user.setting.config_theme = "minimal_light_theme"
|
||||
expected_result = %W[
|
||||
minimal-light-theme sans-serif-article-body
|
||||
trusted-status-#{user.trusted} #{user.config_navbar}-header
|
||||
trusted-status-#{user.trusted} #{user.setting.config_navbar}-header
|
||||
].join(" ")
|
||||
expect(user.decorate.config_body_class).to eq(expected_result)
|
||||
end
|
||||
|
||||
it "works with static navbar" do
|
||||
user.config_navbar = "static"
|
||||
user.setting.config_navbar = "static"
|
||||
expected_result = %W[
|
||||
default sans-serif-article-body
|
||||
trusted-status-#{user.trusted} static-header
|
||||
|
|
@ -192,17 +176,17 @@ RSpec.describe UserDecorator, type: :decorator do
|
|||
|
||||
describe "#dark_theme?" do
|
||||
it "determines dark theme if night theme" do
|
||||
user.config_theme = "night_theme"
|
||||
user.setting.config_theme = "night_theme"
|
||||
expect(user.decorate.dark_theme?).to be(true)
|
||||
end
|
||||
|
||||
it "determines dark theme if ten x hacker" do
|
||||
user.config_theme = "ten_x_hacker_theme"
|
||||
user.setting.config_theme = "ten_x_hacker_theme"
|
||||
expect(user.decorate.dark_theme?).to be(true)
|
||||
end
|
||||
|
||||
it "determines not dark theme if not one of the dark themes" do
|
||||
user.config_theme = "default"
|
||||
user.setting.config_theme = "default"
|
||||
expect(user.decorate.dark_theme?).to be(false)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -17,10 +17,8 @@ FactoryBot.define do
|
|||
saw_onboarding { true }
|
||||
checked_code_of_conduct { true }
|
||||
checked_terms_and_conditions { true }
|
||||
display_announcements { true }
|
||||
registered_at { Time.current }
|
||||
signup_cta_variant { "navbar_basic" }
|
||||
email_digest_periodic { false }
|
||||
|
||||
trait :with_identity do
|
||||
transient { identities { Authentication::Providers.available } }
|
||||
|
|
@ -164,8 +162,10 @@ FactoryBot.define do
|
|||
end
|
||||
|
||||
trait :with_newsletters do
|
||||
email_newsletter { true }
|
||||
email_digest_periodic { true }
|
||||
after(:create) do |user|
|
||||
Users::NotificationSetting.find_by(user_id: user.id)
|
||||
.update_columns(email_newsletter: true, email_digest_periodic: true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
19
spec/factories/users_notification_settings.rb
Normal file
19
spec/factories/users_notification_settings.rb
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
FactoryBot.define do
|
||||
factory :users_notification_setting, class: "Users::NotificationSetting" do
|
||||
email_badge_notifications { true }
|
||||
email_comment_notifications { true }
|
||||
email_community_mod_newsletter { false }
|
||||
email_connect_messages { true }
|
||||
email_digest_periodic { false }
|
||||
email_follower_notifications { true }
|
||||
email_membership_newsletter { false }
|
||||
email_mention_notifications { true }
|
||||
email_newsletter { false }
|
||||
email_tag_mod_newsletter { false }
|
||||
email_unread_notifications { true }
|
||||
mobile_comment_notifications { true }
|
||||
mod_roundrobin_notifications { true }
|
||||
reaction_notifications { true }
|
||||
welcome_notifications { true }
|
||||
end
|
||||
end
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
require "rails_helper"
|
||||
require Rails.root.join(
|
||||
"lib/data_update_scripts/20210503174302_migrate_relevant_fields_from_users_to_users_notification_settings.rb",
|
||||
)
|
||||
|
||||
describe DataUpdateScripts::MigrateRelevantFieldsFromUsersToUsersNotificationSettings, sidekiq: :inline do
|
||||
let(:users_notification_setting) { Users::NotificationSetting.last }
|
||||
|
||||
before do
|
||||
Users::NotificationSetting.destroy_all
|
||||
User.destroy_all
|
||||
end
|
||||
|
||||
# NOTE: an after_commit in User model creates a users_notification_settings record for users
|
||||
# So in these specs, we destroy these users_notification_settings records
|
||||
# so that he migration script creates them instead
|
||||
context "when migrating data" do
|
||||
it "sets the expected number of records" do
|
||||
create_list(:user, 3)
|
||||
Users::NotificationSetting.destroy_all
|
||||
|
||||
expect do
|
||||
described_class.new.run
|
||||
end.to change(Users::NotificationSetting, :count).from(0).to(3)
|
||||
end
|
||||
|
||||
it "sets the correct User records" do
|
||||
create(:user, welcome_notifications: true)
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_notification_setting.welcome_notifications).to be(true)
|
||||
end
|
||||
|
||||
it "sets the correct types" do
|
||||
create(:user)
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_notification_setting.reaction_notifications).to be_in([true, false])
|
||||
end
|
||||
|
||||
it "sets a fallback value for values that are null" do
|
||||
user = create(:user)
|
||||
user.notification_setting.destroy
|
||||
user.update_columns(email_newsletter: nil)
|
||||
user.reload
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_notification_setting.email_newsletter).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
it "assigns updated_at and created_at timestamps that are more current than the original values" do
|
||||
user = create(:user, created_at: 1.minute.ago, updated_at: 1.minute.ago)
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_notification_setting.created_at).to be > user.created_at
|
||||
expect(users_notification_setting.updated_at).to be > user.updated_at
|
||||
end
|
||||
|
||||
context "when the user id exists in both the users_notification_settings and users tables" do
|
||||
it "replaces the users_notification_settings values with values from the users table" do
|
||||
user = create(:user, email_newsletter: true)
|
||||
user_id = user.id
|
||||
user.notification_setting.destroy
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_notification_setting.user_id).to eq(user_id)
|
||||
expect(users_notification_setting.email_newsletter).to be(true)
|
||||
|
||||
user.update_columns(email_newsletter: false)
|
||||
user.notification_setting.destroy
|
||||
|
||||
described_class.new.run
|
||||
users_notification_setting.reload
|
||||
|
||||
expect(users_notification_setting.user_id).to eq(user_id)
|
||||
expect(users_notification_setting.email_newsletter).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,133 +0,0 @@
|
|||
require "rails_helper"
|
||||
require Rails.root.join(
|
||||
"lib/data_update_scripts/20210423155327_migrate_relevant_fields_from_users_to_users_settings.rb",
|
||||
)
|
||||
|
||||
describe DataUpdateScripts::MigrateRelevantFieldsFromUsersToUsersSettings, sidekiq: :inline do
|
||||
let(:users_setting) { Users::Setting.last }
|
||||
|
||||
before do
|
||||
Users::Setting.destroy_all
|
||||
Profile.destroy_all
|
||||
User.destroy_all
|
||||
end
|
||||
|
||||
# NOTE: an after_commit in User model creates a users_notification_settings record for users
|
||||
# So in these specs, we destroy these users_notification_settings records
|
||||
# so that he migration script creates them instead
|
||||
context "when migrating data" do
|
||||
it "sets the expected number of records" do
|
||||
create_list(:user, 3)
|
||||
Users::Setting.destroy_all
|
||||
|
||||
expect do
|
||||
described_class.new.run
|
||||
end.to change(Users::Setting, :count).from(0).to(3)
|
||||
end
|
||||
|
||||
it "sets the correct User records" do
|
||||
user = create(:user, config_font: "sans_serif")
|
||||
user.setting.destroy
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.config_font).to eq("sans_serif")
|
||||
end
|
||||
|
||||
it "sets the correct Profile records" do
|
||||
profile = create(
|
||||
:profile,
|
||||
:with_DEV_info,
|
||||
user: create(:user, :without_profile),
|
||||
display_email_on_profile: true,
|
||||
)
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.display_email_on_profile).to eq(profile.display_email_on_profile)
|
||||
end
|
||||
|
||||
it "sets the correct types" do
|
||||
create(:user)
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.permit_adjacent_sponsors).to be_in([true, false])
|
||||
end
|
||||
|
||||
it "casts data (display_email_on_profile) to the correct types (boolean)" do
|
||||
create(
|
||||
:profile,
|
||||
:with_DEV_info,
|
||||
user: create(:user, :without_profile),
|
||||
display_email_on_profile: true,
|
||||
)
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.display_email_on_profile).to be_in([true, false])
|
||||
end
|
||||
|
||||
it "sets a fallback value for values that are null" do
|
||||
user = create(:user)
|
||||
user.setting.destroy
|
||||
user.update_columns(display_announcements: nil)
|
||||
user.reload
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.display_announcements).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context "when migrating enum settings" do
|
||||
it "assigns the correct mapping for enum settings" do
|
||||
user = create(:user, config_font: "monospace")
|
||||
user.setting.destroy
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.monospace_font?).to be(true)
|
||||
end
|
||||
|
||||
it "assigns the fallback value when the value passed does not have an enum defined" do
|
||||
user = create(:user)
|
||||
user.setting.destroy
|
||||
user.update_columns(config_font: "fake_font")
|
||||
user.reload
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.default?).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
it "assigns updated_at and created_at timestamps that are more current than the original values" do
|
||||
user = create(:user, created_at: 1.minute.ago, updated_at: 1.minute.ago)
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.created_at).to be > user.created_at
|
||||
expect(users_setting.updated_at).to be > user.updated_at
|
||||
end
|
||||
|
||||
context "when the user id exists in both the users_settings and users tables" do
|
||||
it "replaces the users_settings values with values from the users table" do
|
||||
user = create(:user, display_announcements: true)
|
||||
user_id = user.id
|
||||
|
||||
described_class.new.run
|
||||
|
||||
expect(users_setting.user_id).to eq(user_id)
|
||||
expect(users_setting.display_announcements).to be(true)
|
||||
|
||||
user.update_columns(display_announcements: false)
|
||||
|
||||
described_class.new.run
|
||||
users_setting.reload
|
||||
|
||||
expect(users_setting.user_id).to eq(user_id)
|
||||
expect(users_setting.display_announcements).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
require "rails_helper"
|
||||
require Rails.root.join(
|
||||
"lib/data_update_scripts/20210701134702_remove_notification_setting_migration_scripts.rb",
|
||||
)
|
||||
|
||||
describe DataUpdateScripts::RemoveNotificationSettingMigrationScripts do
|
||||
it "removes scripts correctly from the DB", :aggregate_failures do
|
||||
described_class::SCRIPTS_TO_REMOVE.each do |script_name|
|
||||
create(:data_update_script, file_name: script_name)
|
||||
end
|
||||
|
||||
described_class.new.run
|
||||
|
||||
described_class::SCRIPTS_TO_REMOVE.each do |script_name|
|
||||
expect(DataUpdateScript.exists?(file_name: script_name)).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe DigestMailer, type: :mailer do
|
||||
let(:user) { build_stubbed(:user) }
|
||||
let(:user) { create(:user) }
|
||||
let(:article) { build_stubbed(:article) }
|
||||
|
||||
describe "#digest_email" do
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ RSpec.describe Follow, type: :model do
|
|||
|
||||
it "sends an email notification" do
|
||||
allow(ForemInstance).to receive(:smtp_enabled?).and_return(true)
|
||||
user_2.update_column(:email_follower_notifications, true)
|
||||
user_2.notification_setting.update(email_follower_notifications: true)
|
||||
expect do
|
||||
Sidekiq::Testing.inline! do
|
||||
described_class.create!(follower: user, followable: user_2)
|
||||
|
|
|
|||
|
|
@ -144,7 +144,8 @@ RSpec.describe Message, type: :model do
|
|||
|
||||
it "does not send email if user has email_messages turned off" do
|
||||
chat_channel.update_column(:channel_type, "direct")
|
||||
user2.update_columns(updated_at: 1.day.ago, email_connect_messages: false)
|
||||
user2.update_columns(updated_at: 1.day.ago)
|
||||
user2.notification_setting.update_columns(email_connect_messages: false)
|
||||
user2.chat_channel_memberships.last.update_column(:last_opened_at, 2.days.ago)
|
||||
|
||||
expect do
|
||||
|
|
|
|||
|
|
@ -179,10 +179,7 @@ RSpec.describe User, type: :model do
|
|||
it { is_expected.to allow_value(nil).for(:payment_pointer) }
|
||||
it { is_expected.to allow_value("").for(:payment_pointer) }
|
||||
|
||||
it { is_expected.to validate_inclusion_of(:inbox_type).in_array(%w[open private]) }
|
||||
|
||||
it { is_expected.to validate_length_of(:email).is_at_most(50).allow_nil }
|
||||
it { is_expected.to validate_length_of(:inbox_guidelines).is_at_most(250).allow_nil }
|
||||
it { is_expected.to validate_length_of(:name).is_at_most(100).is_at_least(1) }
|
||||
it { is_expected.to validate_length_of(:password).is_at_most(100).is_at_least(8) }
|
||||
it { is_expected.to validate_length_of(:username).is_at_most(30).is_at_least(2) }
|
||||
|
|
@ -192,9 +189,6 @@ RSpec.describe User, type: :model do
|
|||
it { is_expected.to validate_presence_of(:blocked_by_count) }
|
||||
it { is_expected.to validate_presence_of(:blocking_others_count) }
|
||||
it { is_expected.to validate_presence_of(:comments_count) }
|
||||
it { is_expected.to validate_presence_of(:config_font) }
|
||||
it { is_expected.to validate_presence_of(:config_navbar) }
|
||||
it { is_expected.to validate_presence_of(:config_theme) }
|
||||
it { is_expected.to validate_presence_of(:credits_count) }
|
||||
it { is_expected.to validate_presence_of(:following_orgs_count) }
|
||||
it { is_expected.to validate_presence_of(:following_tags_count) }
|
||||
|
|
@ -277,26 +271,6 @@ RSpec.describe User, type: :model do
|
|||
expect(user.errors[:base].to_s).to include("could not be saved. Rate limit reached")
|
||||
expect(limiter).to have_received(:track_limit_by_action).with(:user_update).twice
|
||||
end
|
||||
|
||||
context "when validating feed_url", vcr: true do
|
||||
it "is valid with no feed_url" do
|
||||
user.feed_url = nil
|
||||
|
||||
expect(user).to be_valid
|
||||
end
|
||||
|
||||
it "is not valid with an invalid feed_url", vcr: { cassette_name: "feeds_validate_url_invalid" } do
|
||||
user.feed_url = "http://example.com"
|
||||
|
||||
expect(user).not_to be_valid
|
||||
end
|
||||
|
||||
it "is valid with a valid feed_url", vcr: { cassette_name: "feeds_import_medium_vaidehi" } do
|
||||
user.feed_url = "https://medium.com/feed/@vaidehijoshi"
|
||||
|
||||
expect(user).to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when callbacks are triggered before validation" do
|
||||
|
|
@ -350,42 +324,6 @@ RSpec.describe User, type: :model do
|
|||
end
|
||||
end
|
||||
|
||||
describe "#config_theme" do
|
||||
it "accepts valid theme" do
|
||||
user.config_theme = "night theme"
|
||||
expect(user).to be_valid
|
||||
end
|
||||
|
||||
it "does not accept invalid theme" do
|
||||
user.config_theme = "no night mode"
|
||||
expect(user).not_to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
describe "#config_font" do
|
||||
it "accepts valid font" do
|
||||
user.config_font = "sans serif"
|
||||
expect(user).to be_valid
|
||||
end
|
||||
|
||||
it "does not accept invalid font" do
|
||||
user.config_font = "goobledigook"
|
||||
expect(user).not_to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
describe "#config_navbar" do
|
||||
it "accepts valid navbar" do
|
||||
user.config_navbar = "static"
|
||||
expect(user).to be_valid
|
||||
end
|
||||
|
||||
it "does not accept invalid navbar" do
|
||||
user.config_navbar = "not valid navbar input"
|
||||
expect(user).not_to be_valid
|
||||
end
|
||||
end
|
||||
|
||||
context "when the past value is relevant" do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
|
|
@ -663,50 +601,52 @@ RSpec.describe User, type: :model do
|
|||
end
|
||||
|
||||
it "creates proper body class with defaults" do
|
||||
classes = "default sans-serif-article-body trusted-status-#{user.trusted} #{user.config_navbar}-header"
|
||||
classes = "default sans-serif-article-body trusted-status-#{user.trusted} #{user.setting.config_navbar}-header"
|
||||
expect(user.decorate.config_body_class).to eq(classes)
|
||||
end
|
||||
|
||||
it "determines dark theme if night theme" do
|
||||
user.config_theme = "night_theme"
|
||||
user.setting.config_theme = "night_theme"
|
||||
expect(user.decorate.dark_theme?).to eq(true)
|
||||
end
|
||||
|
||||
it "determines dark theme if ten x hacker" do
|
||||
user.config_theme = "ten_x_hacker_theme"
|
||||
user.setting.config_theme = "ten_x_hacker_theme"
|
||||
expect(user.decorate.dark_theme?).to eq(true)
|
||||
end
|
||||
|
||||
it "determines not dark theme if not one of the dark themes" do
|
||||
user.config_theme = "default"
|
||||
user.setting.config_theme = "default"
|
||||
expect(user.decorate.dark_theme?).to eq(false)
|
||||
end
|
||||
|
||||
it "creates proper body class with sans serif config" do
|
||||
user.config_font = "sans_serif"
|
||||
user.setting.config_font = "sans_serif"
|
||||
|
||||
classes = "default sans-serif-article-body trusted-status-#{user.trusted} #{user.config_navbar}-header"
|
||||
classes = "default sans-serif-article-body trusted-status-#{user.trusted} #{user.setting.config_navbar}-header"
|
||||
expect(user.decorate.config_body_class).to eq(classes)
|
||||
end
|
||||
|
||||
it "creates proper body class with open dyslexic config" do
|
||||
user.config_font = "open_dyslexic"
|
||||
user.setting.config_font = "open_dyslexic"
|
||||
|
||||
classes = "default open-dyslexic-article-body trusted-status-#{user.trusted} #{user.config_navbar}-header"
|
||||
classes = "default open-dyslexic-article-body trusted-status-#{user.trusted} #{user.setting.config_navbar}-header"
|
||||
expect(user.decorate.config_body_class).to eq(classes)
|
||||
end
|
||||
|
||||
it "creates proper body class with night theme" do
|
||||
user.config_theme = "night_theme"
|
||||
user.setting.config_theme = "night_theme"
|
||||
|
||||
classes = "night-theme sans-serif-article-body trusted-status-#{user.trusted} #{user.config_navbar}-header"
|
||||
# rubocop:disable Layout/LineLength
|
||||
classes = "night-theme sans-serif-article-body trusted-status-#{user.trusted} #{user.setting.config_navbar}-header"
|
||||
# rubocop:enable Layout/LineLength
|
||||
expect(user.decorate.config_body_class).to eq(classes)
|
||||
end
|
||||
|
||||
it "creates proper body class with pink theme" do
|
||||
user.config_theme = "pink_theme"
|
||||
user.setting.config_theme = "pink_theme"
|
||||
|
||||
classes = "pink-theme sans-serif-article-body trusted-status-#{user.trusted} #{user.config_navbar}-header"
|
||||
classes = "pink-theme sans-serif-article-body trusted-status-#{user.trusted} #{user.setting.config_navbar}-header"
|
||||
expect(user.decorate.config_body_class).to eq(classes)
|
||||
end
|
||||
end
|
||||
|
|
@ -788,12 +728,12 @@ RSpec.describe User, type: :model do
|
|||
end
|
||||
|
||||
it "returns false if user opted out from follower notifications" do
|
||||
user.assign_attributes(email_follower_notifications: false)
|
||||
user.notification_setting.update(email_follower_notifications: false)
|
||||
expect(user.receives_follower_email_notifications?).to be(false)
|
||||
end
|
||||
|
||||
it "returns true if user opted in from follower notifications and has an email" do
|
||||
user.assign_attributes(email_follower_notifications: true)
|
||||
user.notification_setting.update(email_follower_notifications: true)
|
||||
expect(user.receives_follower_email_notifications?).to be(true)
|
||||
end
|
||||
end
|
||||
|
|
@ -896,14 +836,5 @@ RSpec.describe User, type: :model do
|
|||
# Changes were also persisted in the users table
|
||||
expect(user.reload.available_for).to eq "profile migrations"
|
||||
end
|
||||
|
||||
it "propagates changes of mapped attributes to the profile model", :aggregate_failures do
|
||||
expect do
|
||||
user.update(bg_color_hex: "#abcdef")
|
||||
end.to change { user.profile.reload.brand_color1 }.to("#abcdef")
|
||||
|
||||
# Changes were also persisted in the users table
|
||||
expect(user.reload.bg_color_hex).to eq "#abcdef"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
require "rails_helper"
|
||||
# rubocop:disable Layout/LineLength
|
||||
RSpec.describe Users::Setting, type: :model do
|
||||
let(:user) { create(:user) }
|
||||
let(:setting) { described_class.find_by(user_id: user.id) }
|
||||
|
||||
describe "validations" do
|
||||
subject { setting }
|
||||
|
||||
let(:user) { create(:user) }
|
||||
let(:setting) { user.setting }
|
||||
|
||||
it { is_expected.to validate_length_of(:inbox_guidelines).is_at_most(250).allow_nil }
|
||||
it { is_expected.to define_enum_for(:inbox_type).with_values(private: 0, open: 1).with_suffix(:inbox) }
|
||||
it { is_expected.to define_enum_for(:config_font).with_values(default: 0, comic_sans: 1, monospace: 2, open_dyslexic: 3, sans_serif: 4, serif: 5).with_suffix(:font) }
|
||||
|
|
@ -48,6 +48,43 @@ RSpec.describe Users::Setting, type: :model do
|
|||
expect { setting.config_navbar = 10 }.to raise_error(ArgumentError)
|
||||
end
|
||||
end
|
||||
|
||||
context "when validating feed_url", vcr: true do
|
||||
it "is valid with no feed_url" do
|
||||
setting.feed_url = nil
|
||||
|
||||
expect(setting).to be_valid
|
||||
end
|
||||
|
||||
it "is not valid with an invalid feed_url", vcr: { cassette_name: "feeds_validate_url_invalid" } do
|
||||
setting.feed_url = "http://example.com"
|
||||
|
||||
expect(setting).not_to be_valid
|
||||
end
|
||||
|
||||
it "is valid with a valid feed_url", vcr: { cassette_name: "feeds_import_medium_vaidehi" } do
|
||||
setting.feed_url = "https://medium.com/feed/@vaidehijoshi"
|
||||
|
||||
expect(setting).to be_valid
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#resolved_font_name" do
|
||||
it "replaces 'default' with font configured for the site in Settings::General" do
|
||||
expect(setting.config_font).to eq("default")
|
||||
%w[sans_serif serif open_dyslexic].each do |font|
|
||||
allow(Settings::UserExperience).to receive(:default_font).and_return(font)
|
||||
expect(setting.resolved_font_name).to eq(font)
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't replace the user's custom selected font" do
|
||||
user_comic_sans = create(:user)
|
||||
user_comic_sans.setting.update(config_font: "comic_sans")
|
||||
allow(Settings::UserExperience).to receive(:default_font).and_return("open_dyslexic")
|
||||
expect(user_comic_sans.setting.resolved_font_name).to eq("comic_sans")
|
||||
end
|
||||
end
|
||||
end
|
||||
# rubocop:enable Layout/LineLength
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@ require "rails_helper"
|
|||
|
||||
RSpec.describe "ChatChannels", type: :request do
|
||||
let(:user) { create(:user) }
|
||||
let(:user_open_inbox) { create(:user, inbox_type: "open") }
|
||||
let(:user_open_inbox) do
|
||||
u = create(:user)
|
||||
u.setting.update(inbox_type: "open")
|
||||
u
|
||||
end
|
||||
let(:user_closed_inbox) { create(:user) }
|
||||
let(:test_subject) { create(:user) }
|
||||
let(:chat_channel) { create(:chat_channel) }
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe "EmailSubscriptions", type: :request do
|
||||
let(:user) { build(:user) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
allow(User).to receive(:find).and_return(user)
|
||||
|
|
@ -24,7 +24,7 @@ RSpec.describe "EmailSubscriptions", type: :request do
|
|||
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)
|
||||
expect(user.notification_setting.email_mention_notifications).to be(false)
|
||||
end
|
||||
|
||||
it "handles error properly" do
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ RSpec.describe "IncomingWebhooks::MailchimpUnsubscribesController", type: :reque
|
|||
|
||||
expect do
|
||||
post "/incoming_webhooks/mailchimp/#{secret}/unsubscribe", params: params
|
||||
end.to change { user.reload.email_newsletter }.from(true).to(false)
|
||||
end.to change { user.reload.notification_setting.email_newsletter }.from(true).to(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -537,7 +537,7 @@ RSpec.describe "NotificationsIndex", type: :request do
|
|||
|
||||
before do
|
||||
user.add_role(:trusted)
|
||||
user.update(mod_roundrobin_notifications: false)
|
||||
user.notification_setting.update(mod_roundrobin_notifications: false)
|
||||
sign_in user
|
||||
sidekiq_perform_enqueued_jobs do
|
||||
Notification.send_moderation_notification(comment)
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ RSpec.describe "Reactions", type: :request do
|
|||
|
||||
context "when creating readinglist" do
|
||||
before do
|
||||
user.update_column(:experience_level, 8)
|
||||
user.setting.update_column(:experience_level, 8)
|
||||
sign_in user
|
||||
post "/reactions", params: {
|
||||
reactable_id: article.id,
|
||||
|
|
|
|||
93
spec/requests/user/user_notification_settings_spec.rb
Normal file
93
spec/requests/user/user_notification_settings_spec.rb
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe "UserNotificationSettings", type: :request do
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before { sign_in user }
|
||||
|
||||
describe "PUT /update/:id" do
|
||||
it "disables reaction notifications (in both users and notification_settings tables)" do
|
||||
expect(user.notification_setting.reaction_notifications).to be(true)
|
||||
|
||||
expect do
|
||||
put users_notification_settings_path(user.notification_setting.id),
|
||||
params: { users_notification_setting: { tab: "notifications", reaction_notifications: 0 } }
|
||||
end.to change { user.notification_setting.reload.reaction_notifications }.from(true).to(false)
|
||||
end
|
||||
|
||||
it "enables community-success notifications" do
|
||||
put users_notification_settings_path(user.notification_setting.id),
|
||||
params: { users_notification_setting: { tab: "notifications", mod_roundrobin_notifications: 1 } }
|
||||
expect(user.reload.subscribed_to_mod_roundrobin_notifications?).to be(true)
|
||||
end
|
||||
|
||||
it "disables community-success notifications" do
|
||||
put users_notification_settings_path(user.notification_setting.id),
|
||||
params: { users_notification_setting: { tab: "notifications", mod_roundrobin_notifications: 0 } }
|
||||
expect(user.reload.subscribed_to_mod_roundrobin_notifications?).to be(false)
|
||||
end
|
||||
|
||||
it "can toggle welcome notifications" do
|
||||
put users_notification_settings_path(user.notification_setting.id),
|
||||
params: { users_notification_setting: { tab: "notifications", welcome_notifications: 0 } }
|
||||
expect(user.reload.subscribed_to_welcome_notifications?).to be(false)
|
||||
|
||||
put users_notification_settings_path(user.notification_setting.id),
|
||||
params: { users_notification_setting: { tab: "notifications", welcome_notifications: 1 } }
|
||||
expect(user.reload.subscribed_to_welcome_notifications?).to be(true)
|
||||
end
|
||||
|
||||
it "returns error message if settings can't be saved" do
|
||||
put users_notification_settings_path(user.notification_setting.id),
|
||||
params: { users_notification_setting: { tab: "notifications", email_digest_periodic: nil } }
|
||||
|
||||
expect(flash[:error]).not_to be_blank
|
||||
end
|
||||
end
|
||||
|
||||
describe "PATCH /onboarding_notifications_checkbox_update" do
|
||||
before { sign_in user }
|
||||
|
||||
it "updates onboarding checkbox" do
|
||||
user.update_column(:saw_onboarding, false)
|
||||
|
||||
expect do
|
||||
patch onboarding_notifications_checkbox_update_path(format: :json),
|
||||
params: { notifications: { tab: "notifications", email_newsletter: 1 } }
|
||||
end.to change { user.notification_setting.reload.email_newsletter }.from(false).to(true)
|
||||
expect(user.saw_onboarding).to be(true)
|
||||
end
|
||||
|
||||
it "can toggle email_newsletter" do
|
||||
expect do
|
||||
patch onboarding_notifications_checkbox_update_path(format: :json),
|
||||
params: { notifications: { tab: "notifications", email_newsletter: 1 } }
|
||||
end.to change { user.notification_setting.reload.email_newsletter }.from(false).to(true)
|
||||
|
||||
expect do
|
||||
patch onboarding_notifications_checkbox_update_path(format: :json),
|
||||
params: { notifications: { tab: "notifications", email_newsletter: 0 } }
|
||||
end.to change { user.notification_setting.reload.email_newsletter }.from(true).to(false)
|
||||
end
|
||||
|
||||
it "can toggle email_digest_periodic" do
|
||||
expect do
|
||||
patch onboarding_notifications_checkbox_update_path(format: :json),
|
||||
params: { notifications: { tab: "notifications", email_digest_periodic: 1 } }
|
||||
end.to change { user.notification_setting.reload.email_digest_periodic }.from(false).to(true)
|
||||
|
||||
expect do
|
||||
patch onboarding_notifications_checkbox_update_path(format: :json),
|
||||
params: { notifications: { tab: "notifications", email_digest_periodic: 0 } }
|
||||
end.to change { user.notification_setting.reload.email_digest_periodic }.from(true).to(false)
|
||||
end
|
||||
|
||||
it "returns 422 status and errors if errors occur" do
|
||||
patch onboarding_notifications_checkbox_update_path(format: :json),
|
||||
params: { notifications: { tab: "notifications", email_digest_periodic: nil } }
|
||||
|
||||
expect(response.status).to eq(422)
|
||||
expect(response.parsed_body["errors"]).not_to be_blank
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -238,44 +238,16 @@ RSpec.describe "UserSettings", type: :request do
|
|||
expect(user.reload.profile_updated_at).to be > 2.minutes.ago
|
||||
end
|
||||
|
||||
it "disables reaction notifications (in both users and notification_settings tables)" do
|
||||
expect(user.notification_setting.reaction_notifications).to be(true)
|
||||
|
||||
expect do
|
||||
put "/users/#{user.id}", params: { user: { tab: "notifications", reaction_notifications: 0 } }
|
||||
end.to change { user.reload.reaction_notifications }.from(true).to(false)
|
||||
|
||||
expect(user.notification_setting.reload.reaction_notifications).to be(false)
|
||||
end
|
||||
|
||||
it "enables community-success notifications" do
|
||||
put "/users/#{user.id}", params: { user: { tab: "notifications", mod_roundrobin_notifications: 1 } }
|
||||
expect(user.reload.subscribed_to_mod_roundrobin_notifications?).to be(true)
|
||||
end
|
||||
|
||||
it "updates the users announcement display preferences (in both users and user_settings tables)" do
|
||||
expect(user.setting.display_announcements).to be(true)
|
||||
|
||||
expect do
|
||||
put "/users/#{user.id}", params: { user: { tab: "misc", display_announcements: 0 } }
|
||||
end.to change { user.reload.display_announcements }.from(true).to(false)
|
||||
put users_settings_path(user.setting.id), params: { users_setting: { display_announcements: 0 } }
|
||||
end.to change { user.setting.reload.display_announcements }.from(true).to(false)
|
||||
|
||||
expect(user.setting.reload.display_announcements).to be(false)
|
||||
end
|
||||
|
||||
it "disables community-success notifications" do
|
||||
put "/users/#{user.id}", params: { user: { tab: "notifications", mod_roundrobin_notifications: 0 } }
|
||||
expect(user.reload.subscribed_to_mod_roundrobin_notifications?).to be(false)
|
||||
end
|
||||
|
||||
it "can toggle welcome notifications" do
|
||||
put "/users/#{user.id}", params: { user: { tab: "notifications", welcome_notifications: 0 } }
|
||||
expect(user.reload.subscribed_to_welcome_notifications?).to be(false)
|
||||
|
||||
put "/users/#{user.id}", params: { user: { tab: "notifications", welcome_notifications: 1 } }
|
||||
expect(user.reload.subscribed_to_welcome_notifications?).to be(true)
|
||||
end
|
||||
|
||||
it "updates username to too short username" do
|
||||
put "/users/#{user.id}", params: { user: { tab: "profile", username: "h" } }
|
||||
expect(response.body).to include("Username is too short")
|
||||
|
|
@ -338,12 +310,16 @@ RSpec.describe "UserSettings", type: :request do
|
|||
|
||||
context "when requesting a fetch of the feed", vcr: { cassette_name: "feeds_import_medium_vaidehi" } do
|
||||
let(:feed_url) { "https://medium.com/feed/@vaidehijoshi" }
|
||||
let(:user) { create(:user, feed_url: feed_url) }
|
||||
let(:user) do
|
||||
u = create(:user)
|
||||
u.setting.update(feed_url: feed_url)
|
||||
u
|
||||
end
|
||||
|
||||
it "invokes Feeds::ImportArticlesWorker" do
|
||||
allow(Feeds::ImportArticlesWorker).to receive(:perform_async).with(user.id)
|
||||
|
||||
put user_path(user.id), params: { user: { feed_url: feed_url } }
|
||||
put users_settings_path(user.setting.id), params: { users_setting: { feed_url: feed_url } }
|
||||
|
||||
expect(Feeds::ImportArticlesWorker).to have_received(:perform_async).with(user.id)
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ RSpec.describe "UserShow", type: :request do
|
|||
:profile,
|
||||
:with_DEV_info,
|
||||
user: create(:user, :without_profile),
|
||||
display_email_on_profile: true,
|
||||
)
|
||||
end
|
||||
let(:user) { profile.user }
|
||||
|
|
@ -19,6 +18,7 @@ RSpec.describe "UserShow", type: :request do
|
|||
|
||||
# rubocop:disable RSpec/ExampleLength
|
||||
it "renders the proper JSON-LD for a user" do
|
||||
user.setting.update(display_email_on_profile: true)
|
||||
get user.path
|
||||
text = Nokogiri::HTML(response.body).at('script[type="application/ld+json"]').text
|
||||
response_json = JSON.parse(text)
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@ RSpec.describe ArticleWithVideoCreationService, type: :service do
|
|||
describe "#create!" do
|
||||
it "works" do
|
||||
Timecop.travel(3.weeks.ago)
|
||||
user = create(:user, editor_version: "v1")
|
||||
user = create(:user)
|
||||
user.setting.update(editor_version: "v1")
|
||||
Timecop.return
|
||||
test = build_stubbed(:article, user: user, video: link).attributes.symbolize_keys
|
||||
article = described_class.new(test, user).create!
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ RSpec.describe Articles::Builder, type: :service do
|
|||
end
|
||||
|
||||
context "when tag" do
|
||||
let(:user) { create(:user, editor_version: "v1") }
|
||||
let(:user) { create(:user) }
|
||||
let(:tag) { create(:tag) }
|
||||
let(:correct_attributes) do
|
||||
{
|
||||
|
|
@ -100,6 +100,7 @@ RSpec.describe Articles::Builder, type: :service do
|
|||
end
|
||||
|
||||
it "initializes an article with the correct attributes and does not need authorization" do
|
||||
user.setting.update(editor_version: "v1")
|
||||
subject, needs_authorization = described_class.call(user, tag, prefill)
|
||||
|
||||
expect(subject).to be_an_instance_of(Article)
|
||||
|
|
@ -126,7 +127,7 @@ RSpec.describe Articles::Builder, type: :service do
|
|||
end
|
||||
|
||||
context "when user_editor_v1" do
|
||||
let(:user) { create(:user, editor_version: "v1") }
|
||||
let(:user) { create(:user) }
|
||||
let(:correct_attributes) do
|
||||
body = "---\ntitle: \npublished: false\ndescription: \ntags: " \
|
||||
"\n//cover_image: https://direct_url_to_image.jpg\n---\n\n"
|
||||
|
|
@ -139,6 +140,7 @@ RSpec.describe Articles::Builder, type: :service do
|
|||
end
|
||||
|
||||
it "initializes an article with the correct attributes and does not need authorization" do
|
||||
user.setting.update(editor_version: "v1")
|
||||
subject, needs_authorization = described_class.call(user, tag, prefill)
|
||||
|
||||
expect(subject).to be_an_instance_of(Article)
|
||||
|
|
|
|||
|
|
@ -282,7 +282,11 @@ RSpec.describe Articles::Feeds::LargeForemExperimental, type: :service do
|
|||
let(:article) { create(:article, experience_level_rating: 7) }
|
||||
|
||||
context "when user has a further experience level" do
|
||||
let(:user) { create(:user, experience_level: 1) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
user.setting.update(experience_level: 1)
|
||||
end
|
||||
|
||||
it "returns negative of (absolute value of the difference between article and user experience) divided by 2" do
|
||||
expect(feed.score_experience_level(article)).to eq(-3)
|
||||
|
|
@ -295,7 +299,11 @@ RSpec.describe Articles::Feeds::LargeForemExperimental, type: :service do
|
|||
end
|
||||
|
||||
context "when user has a closer experience level" do
|
||||
let(:user) { create(:user, experience_level: 9) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
user.setting.update(experience_level: 9)
|
||||
end
|
||||
|
||||
it "returns negative of (absolute value of the difference between article and user experience) divided by 2" do
|
||||
expect(feed.score_experience_level(article)).to eq(-1)
|
||||
|
|
@ -303,7 +311,11 @@ RSpec.describe Articles::Feeds::LargeForemExperimental, type: :service do
|
|||
end
|
||||
|
||||
context "when the user does not have an experience level set" do
|
||||
let(:user) { create(:user, experience_level: nil) }
|
||||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
user.setting.update(experience_level: nil)
|
||||
end
|
||||
|
||||
it "uses a value of 5 for user experience level" do
|
||||
expect(feed.score_experience_level(article)).to eq(-1)
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ RSpec.describe Authentication::Authenticator, type: :service do
|
|||
expect(user.password).to be_present
|
||||
expect(user.signup_cta_variant).to be_nil
|
||||
expect(user.saw_onboarding).to be(false)
|
||||
expect(user.editor_version).to eq("v2")
|
||||
expect(user.setting.editor_version).to eq("v2")
|
||||
end
|
||||
|
||||
it "sets the correct sign up cta variant" do
|
||||
|
|
@ -244,7 +244,7 @@ RSpec.describe Authentication::Authenticator, type: :service do
|
|||
expect(user.password).to be_present
|
||||
expect(user.signup_cta_variant).to be_nil
|
||||
expect(user.saw_onboarding).to be(false)
|
||||
expect(user.editor_version).to eq("v2")
|
||||
expect(user.setting.editor_version).to eq("v2")
|
||||
end
|
||||
|
||||
it "sets the correct sign up cta variant" do
|
||||
|
|
@ -445,7 +445,7 @@ RSpec.describe Authentication::Authenticator, type: :service do
|
|||
expect(user.password).to be_present
|
||||
expect(user.signup_cta_variant).to be_nil
|
||||
expect(user.saw_onboarding).to be(false)
|
||||
expect(user.editor_version).to eq("v2")
|
||||
expect(user.setting.editor_version).to eq("v2")
|
||||
end
|
||||
|
||||
it "sets the correct sign up cta variant" do
|
||||
|
|
@ -534,7 +534,7 @@ RSpec.describe Authentication::Authenticator, type: :service do
|
|||
expect(user.password).to be_present
|
||||
expect(user.signup_cta_variant).to be_nil
|
||||
expect(user.saw_onboarding).to be(false)
|
||||
expect(user.editor_version).to eq("v2")
|
||||
expect(user.setting.editor_version).to eq("v2")
|
||||
end
|
||||
|
||||
it "sets the correct sign up cta variant" do
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ RSpec.describe Broadcasts::WelcomeNotification::Generator, type: :service do
|
|||
let(:user) { create(:user, :with_identity, identities: ["github"], created_at: 1.week.ago) }
|
||||
|
||||
it "does not send a notification to an unsubscribed user" do
|
||||
user.update!(welcome_notifications: false)
|
||||
user.notification_setting.update!(welcome_notifications: false)
|
||||
expect do
|
||||
sidekiq_perform_enqueued_jobs { described_class.call(user.id) }
|
||||
end.to not_change(user.notifications, :count)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@ require "rails_helper"
|
|||
RSpec.describe EmailDigest, type: :service do
|
||||
describe "::send_digest_email" do
|
||||
it "enqueues Emails::SendUserDigestWorker" do
|
||||
user = create(:user, email_digest_periodic: true)
|
||||
user = create(:user)
|
||||
user.notification_setting.update(email_digest_periodic: true)
|
||||
allow(Emails::SendUserDigestWorker).to receive(:perform_async)
|
||||
described_class.send_periodic_digest_email
|
||||
expect(Emails::SendUserDigestWorker).to have_received(:perform_async).with(user.id)
|
||||
|
|
@ -11,7 +12,8 @@ RSpec.describe EmailDigest, type: :service do
|
|||
|
||||
it "performs job inline if community is DEV" do
|
||||
allow(ForemInstance).to receive(:dev_to?).and_return(true)
|
||||
user = create(:user, email_digest_periodic: true)
|
||||
user = create(:user)
|
||||
user.notification_setting.update(email_digest_periodic: true)
|
||||
worker = Emails::SendUserDigestWorker.new
|
||||
allow(worker).to receive(:perform)
|
||||
allow(Emails::SendUserDigestWorker).to receive(:new).and_return(worker)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe Feeds::AssembleArticleMarkdown, type: :service do
|
||||
let(:user) { create(:user, feed_mark_canonical: true) }
|
||||
let(:user) do
|
||||
u = create(:user)
|
||||
u.setting.update(feed_mark_canonical: true)
|
||||
u
|
||||
end
|
||||
let(:feed_source_url) { "https://feed.source/url" }
|
||||
let(:feed) { instance_double("Feedjira::Parser::RSS", url: "https://feed.source/") }
|
||||
let(:title) { "A title" }
|
||||
|
|
|
|||
|
|
@ -5,13 +5,14 @@ RSpec.describe Feeds::Import, type: :service, vcr: true do
|
|||
let(:nonmedium_link) { "https://circleci.com/blog/feed.xml" }
|
||||
let(:nonpermanent_link) { "https://medium.com/feed/@macsiri/" }
|
||||
|
||||
describe ".call" do
|
||||
before do
|
||||
[link, nonmedium_link, nonpermanent_link].each do |feed_url|
|
||||
create(:user, feed_url: feed_url)
|
||||
end
|
||||
before do
|
||||
[link, nonmedium_link, nonpermanent_link].each do |feed_url|
|
||||
user = create(:user)
|
||||
user.setting.update(feed_url: feed_url)
|
||||
end
|
||||
end
|
||||
|
||||
describe ".call" do
|
||||
# TODO: We could probably improve these tests by parsing against the items in the feed rather than hardcoding
|
||||
it "fetch only articles from a feed_url", vcr: { cassette_name: "feeds_import" } do
|
||||
num_articles = described_class.call
|
||||
|
|
@ -34,14 +35,14 @@ RSpec.describe Feeds::Import, type: :service, vcr: true do
|
|||
it "parses correctly", vcr: { cassette_name: "feeds_import" } do
|
||||
expect do
|
||||
described_class.call
|
||||
end.to change(User.find_by(feed_url: nonpermanent_link).articles, :count).by(1)
|
||||
end.to change(Users::Setting.find_by(feed_url: nonpermanent_link).user.articles, :count).by(1)
|
||||
end
|
||||
|
||||
it "sets feed_fetched_at to the current time", vcr: { cassette_name: "feeds_import" } do
|
||||
Timecop.freeze(Time.current) do
|
||||
described_class.call
|
||||
|
||||
user = User.find_by(feed_url: nonpermanent_link)
|
||||
user = Users::Setting.find_by(feed_url: nonpermanent_link).user
|
||||
feed_fetched_at = user.feed_fetched_at
|
||||
expect(feed_fetched_at.to_i).to eq(Time.current.to_i)
|
||||
end
|
||||
|
|
@ -103,28 +104,25 @@ RSpec.describe Feeds::Import, type: :service, vcr: true do
|
|||
context "with an explicit set of users", vcr: { cassette_name: "feeds_import" } do
|
||||
# TODO: We could probably improve these tests by parsing against the items in the feed rather than hardcoding
|
||||
it "accepts a subset of users" do
|
||||
num_articles = described_class.call(users: User.with_feed.limit(1))
|
||||
num_articles = described_class.call(users: User.where(id: Users::Setting.with_feed.select(:user_id)).limit(1))
|
||||
|
||||
expect(num_articles).to eq(10)
|
||||
end
|
||||
|
||||
it "imports no articles if given users are without feed" do
|
||||
create(:user, feed_url: nil)
|
||||
user = create(:user)
|
||||
user.setting.update(feed_url: nil)
|
||||
|
||||
expect(described_class.call(users: User.where(feed_url: nil))).to eq(0)
|
||||
# rubocop:disable Layout/LineLength
|
||||
expect(described_class.call(users: User.where(id: Users::Setting.where(feed_url: nil).select(:user_id)))).to eq(0)
|
||||
# rubocop:enable Layout/LineLength
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when refetching" do
|
||||
before do
|
||||
[link, nonmedium_link, nonpermanent_link].each do |feed_url|
|
||||
create(:user, feed_url: feed_url)
|
||||
end
|
||||
end
|
||||
|
||||
it "does refetch same user over and over by default", vcr: { cassette_name: "feeds_import_multiple_times" } do
|
||||
user = User.find_by(feed_url: nonpermanent_link)
|
||||
user = Users::Setting.find_by(feed_url: nonpermanent_link).user
|
||||
|
||||
Timecop.freeze(Time.current) do
|
||||
user.update_columns(feed_fetched_at: Time.current)
|
||||
|
|
@ -175,7 +173,8 @@ RSpec.describe Feeds::Import, type: :service, vcr: true do
|
|||
# checking its invocation is a shortcut to testing the functionality.
|
||||
allow(Article).to receive(:find_by).and_call_original
|
||||
|
||||
create(:user, feed_url: nonpermanent_link, feed_referential_link: false)
|
||||
user = create(:user)
|
||||
user.setting.update(feed_url: nonpermanent_link, feed_referential_link: false)
|
||||
|
||||
described_class.call
|
||||
|
||||
|
|
@ -185,7 +184,8 @@ RSpec.describe Feeds::Import, type: :service, vcr: true do
|
|||
|
||||
describe "feeds parsing and regressions" do
|
||||
it "parses https://medium.com/feed/@dvirsegal correctly", vcr: { cassette_name: "rss_reader_dvirsegal" } do
|
||||
user = create(:user, feed_url: "https://medium.com/feed/@dvirsegal")
|
||||
user = create(:user)
|
||||
user.setting.update(feed_url: "https://medium.com/feed/@dvirsegal")
|
||||
|
||||
expect do
|
||||
described_class.call(users: User.where(id: user.id))
|
||||
|
|
@ -193,7 +193,8 @@ RSpec.describe Feeds::Import, type: :service, vcr: true do
|
|||
end
|
||||
|
||||
it "converts/replaces <picture> tags to <img>", vcr: { cassette_name: "rss_reader_swimburger" } do
|
||||
user = create(:user, feed_url: "https://swimburger.net/atom.xml")
|
||||
user = create(:user)
|
||||
user.setting.update(feed_url: "https://swimburger.net/atom.xml")
|
||||
|
||||
expect do
|
||||
described_class.call(users: User.where(id: user.id))
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ RSpec.describe Mailchimp::Bot, type: :service do
|
|||
ARTICLES: user.articles.size,
|
||||
COMMENTS: user.comments.size,
|
||||
ONBOARD_PK: user.onboarding_package_requested.to_s,
|
||||
EXPERIENCE: user.experience_level || 666
|
||||
EXPERIENCE: user.setting.experience_level || 666
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -50,20 +50,21 @@ RSpec.describe Mailchimp::Bot, type: :service do
|
|||
|
||||
describe "#upsert_to_newsletter" do
|
||||
it "sends proper information" do
|
||||
user.notification_setting.update(email_newsletter: true)
|
||||
described_class.new(user).upsert_to_newsletter
|
||||
expect(my_gibbon_client).to have_received(:upsert).with(matcher)
|
||||
end
|
||||
|
||||
it "unsubscribes properly" do
|
||||
user.update(email_newsletter: false)
|
||||
user.notification_setting.update(email_newsletter: false)
|
||||
described_class.new(user).upsert_to_newsletter
|
||||
expect(my_gibbon_client).to have_received(:upsert)
|
||||
.with(hash_including(body: hash_including(status: "unsubscribed")))
|
||||
end
|
||||
|
||||
it "subscribes properly" do
|
||||
user.update(email_newsletter: false)
|
||||
user.update(email_newsletter: true)
|
||||
user.notification_setting.update(email_newsletter: false)
|
||||
user.notification_setting.update(email_newsletter: true)
|
||||
described_class.new(user).upsert_to_newsletter
|
||||
expect(my_gibbon_client).to have_received(:upsert)
|
||||
.with(hash_including(body: hash_including(status: "subscribed")))
|
||||
|
|
@ -78,7 +79,7 @@ RSpec.describe Mailchimp::Bot, type: :service do
|
|||
end
|
||||
|
||||
it "tries to resubscribe the user if the user has previously been subscribed" do
|
||||
user.update(email_newsletter: false)
|
||||
user.notification_setting.update(email_newsletter: false)
|
||||
mailchimp_bot = described_class.new(user)
|
||||
mc_error =
|
||||
Gibbon::MailChimpError.new("Error", status_code: 400, title: "Member In Compliance State")
|
||||
|
|
@ -101,7 +102,7 @@ RSpec.describe Mailchimp::Bot, type: :service do
|
|||
end
|
||||
|
||||
it "sends proper information" do
|
||||
user.update(email_community_mod_newsletter: true)
|
||||
user.notification_setting.update(email_community_mod_newsletter: true)
|
||||
user.add_role(:trusted)
|
||||
Settings::General.mailchimp_community_moderators_id = "something"
|
||||
described_class.new(user).manage_community_moderator_list
|
||||
|
|
@ -124,7 +125,7 @@ RSpec.describe Mailchimp::Bot, type: :service do
|
|||
end
|
||||
|
||||
it "sends proper information" do
|
||||
user.update(email_tag_mod_newsletter: true)
|
||||
user.notification_setting.update(email_tag_mod_newsletter: true)
|
||||
user.add_role(:tag_moderator, tag)
|
||||
described_class.new(user).manage_tag_moderator_list
|
||||
expect(my_gibbon_client).to have_received(:upsert)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe Notifications::Moderation::Send, type: :service do
|
||||
let(:last_moderation_time) { Time.zone.now - Notifications::Moderation::MODERATORS_AVAILABILITY_DELAY - 3.hours }
|
||||
let(:last_moderation_time) { Time.zone.now - Notifications::Moderation::MODERATORS_AVAILABILITY_DELAY - 1.week }
|
||||
let(:dev_account) { create(:user) }
|
||||
let(:user) { create(:user) }
|
||||
let(:article) { create(:article, user_id: user.id) }
|
||||
|
|
@ -10,7 +10,8 @@ RSpec.describe Notifications::Moderation::Send, type: :service do
|
|||
let(:moderator) { available_moderators.first }
|
||||
|
||||
before do
|
||||
create(:user, :trusted, last_moderation_notification: last_moderation_time)
|
||||
u = create(:user, :trusted, last_moderation_notification: last_moderation_time)
|
||||
u.notification_setting.update(mod_roundrobin_notifications: true)
|
||||
allow(User).to receive(:dev_account).and_return(dev_account)
|
||||
# Creating a comment calls moderation job which itself call moderation service
|
||||
Comment.skip_callback(:commit, :after, :send_to_moderator)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,6 @@ RSpec.describe TagModerators::AddTrustedRole, type: :service do
|
|||
it "signs the user up for the community mods newsletter" do
|
||||
expect do
|
||||
described_class.call(user)
|
||||
end.to change { user.reload.email_community_mod_newsletter }.from(false).to(true)
|
||||
end.to change { user.reload.notification_setting.email_community_mod_newsletter }.from(false).to(true)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -31,8 +31,6 @@ seeder.create_if_doesnt_exist(User, "email", "admin@forem.local") do
|
|||
username: "Admin_McAdmin",
|
||||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
website_url: Faker::Internet.url,
|
||||
email_comment_notifications: false,
|
||||
email_follower_notifications: false,
|
||||
confirmed_at: Time.current,
|
||||
password: "password",
|
||||
password_confirmation: "password",
|
||||
|
|
@ -40,6 +38,10 @@ seeder.create_if_doesnt_exist(User, "email", "admin@forem.local") do
|
|||
checked_code_of_conduct: true,
|
||||
checked_terms_and_conditions: true,
|
||||
)
|
||||
user.notification_setting.update(
|
||||
email_comment_notifications: false,
|
||||
email_follower_notifications: false,
|
||||
)
|
||||
|
||||
user.profile.update({
|
||||
summary: "Admin user summary",
|
||||
|
|
@ -76,15 +78,13 @@ end
|
|||
##############################################################################
|
||||
|
||||
seeder.create_if_doesnt_exist(User, "email", "change-password-user@forem.com") do
|
||||
User.create!(
|
||||
user = User.create!(
|
||||
name: "Change Password User",
|
||||
email: "change-password-user@forem.com",
|
||||
username: "changepassworduser",
|
||||
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
|
||||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
website_url: Faker::Internet.url,
|
||||
email_comment_notifications: false,
|
||||
email_follower_notifications: false,
|
||||
confirmed_at: Time.current,
|
||||
password: "password",
|
||||
password_confirmation: "password",
|
||||
|
|
@ -92,42 +92,48 @@ seeder.create_if_doesnt_exist(User, "email", "change-password-user@forem.com") d
|
|||
checked_code_of_conduct: true,
|
||||
checked_terms_and_conditions: true,
|
||||
)
|
||||
user.notification_setting.update(
|
||||
email_comment_notifications: false,
|
||||
email_follower_notifications: false,
|
||||
)
|
||||
user
|
||||
end
|
||||
|
||||
##############################################################################
|
||||
|
||||
seeder.create_if_doesnt_exist(User, "email", "article-editor-v1-user@forem.com") do
|
||||
User.create!(
|
||||
user = User.create!(
|
||||
name: "Article Editor v1 User",
|
||||
email: "article-editor-v1-user@forem.local",
|
||||
username: "article_editor_v1_user",
|
||||
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
|
||||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
website_url: Faker::Internet.url,
|
||||
email_comment_notifications: false,
|
||||
email_follower_notifications: false,
|
||||
confirmed_at: Time.current,
|
||||
password: "password",
|
||||
password_confirmation: "password",
|
||||
saw_onboarding: true,
|
||||
checked_code_of_conduct: true,
|
||||
checked_terms_and_conditions: true,
|
||||
editor_version: "v1",
|
||||
)
|
||||
user.setting.update(editor_version: "v1")
|
||||
user.notification_setting.update(
|
||||
email_comment_notifications: false,
|
||||
email_follower_notifications: false,
|
||||
)
|
||||
user
|
||||
end
|
||||
|
||||
##############################################################################
|
||||
|
||||
seeder.create_if_doesnt_exist(User, "email", "article-editor-v2-user@forem.com") do
|
||||
User.create!(
|
||||
user = User.create!(
|
||||
name: "Article Editor v2 User",
|
||||
email: "article-editor-v2-user@forem.local",
|
||||
username: "article_editor_v2_user",
|
||||
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
|
||||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
website_url: Faker::Internet.url,
|
||||
email_comment_notifications: false,
|
||||
email_follower_notifications: false,
|
||||
confirmed_at: Time.current,
|
||||
password: "password",
|
||||
password_confirmation: "password",
|
||||
|
|
@ -135,20 +141,23 @@ seeder.create_if_doesnt_exist(User, "email", "article-editor-v2-user@forem.com")
|
|||
checked_code_of_conduct: true,
|
||||
checked_terms_and_conditions: true,
|
||||
)
|
||||
user.notification_setting.update(
|
||||
email_comment_notifications: false,
|
||||
email_follower_notifications: false,
|
||||
)
|
||||
user
|
||||
end
|
||||
|
||||
##############################################################################
|
||||
|
||||
chat_user_1 = seeder.create_if_doesnt_exist(User, "email", "chat-user-1@forem.local") do
|
||||
User.create!(
|
||||
user = User.create!(
|
||||
name: "Chat user 1",
|
||||
email: "chat-user-1@forem.local",
|
||||
username: "chat_user_1",
|
||||
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
|
||||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
website_url: Faker::Internet.url,
|
||||
email_comment_notifications: false,
|
||||
email_follower_notifications: false,
|
||||
confirmed_at: Time.current,
|
||||
password: "password",
|
||||
password_confirmation: "password",
|
||||
|
|
@ -156,20 +165,23 @@ chat_user_1 = seeder.create_if_doesnt_exist(User, "email", "chat-user-1@forem.lo
|
|||
checked_code_of_conduct: true,
|
||||
checked_terms_and_conditions: true,
|
||||
)
|
||||
user.notification_setting.update(
|
||||
email_comment_notifications: false,
|
||||
email_follower_notifications: false,
|
||||
)
|
||||
user
|
||||
end
|
||||
|
||||
##############################################################################
|
||||
|
||||
chat_user_2 = seeder.create_if_doesnt_exist(User, "email", "chat-user-2@forem.local") do
|
||||
User.create!(
|
||||
user = User.create!(
|
||||
name: "Chat user 2",
|
||||
email: "chat-user-2@forem.local",
|
||||
username: "chat_user_2",
|
||||
summary: Faker::Lorem.paragraph_by_chars(number: 199, supplemental: false),
|
||||
profile_image: File.open(Rails.root.join("app/assets/images/#{rand(1..40)}.png")),
|
||||
website_url: Faker::Internet.url,
|
||||
email_comment_notifications: false,
|
||||
email_follower_notifications: false,
|
||||
confirmed_at: Time.current,
|
||||
password: "password",
|
||||
password_confirmation: "password",
|
||||
|
|
@ -177,6 +189,11 @@ chat_user_2 = seeder.create_if_doesnt_exist(User, "email", "chat-user-2@forem.lo
|
|||
checked_code_of_conduct: true,
|
||||
checked_terms_and_conditions: true,
|
||||
)
|
||||
user.notification_setting.update(
|
||||
email_comment_notifications: false,
|
||||
email_follower_notifications: false,
|
||||
)
|
||||
user
|
||||
end
|
||||
|
||||
##############################################################################
|
||||
|
|
|
|||
|
|
@ -3,7 +3,11 @@ require "rails_helper"
|
|||
RSpec.describe "Creating an article with the editor", type: :system do
|
||||
include_context "with runkit_tag"
|
||||
|
||||
let(:user) { create(:user, editor_version: "v1") }
|
||||
let(:user) do
|
||||
u = create(:user)
|
||||
u.setting.update(editor_version: "v1")
|
||||
u
|
||||
end
|
||||
let!(:template) { file_fixture("article_published.txt").read }
|
||||
let!(:template_with_runkit_tag) do
|
||||
file_fixture("article_with_runkit_tag.txt").read
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ RSpec.describe "User visits a homepage", type: :system do
|
|||
|
||||
context "when opting-out of announcements" do
|
||||
before do
|
||||
user.update!(display_announcements: false)
|
||||
user.setting.update!(display_announcements: false)
|
||||
create(:announcement_broadcast, active: true)
|
||||
get "/async_info/base_data" # Explicitly ensure broadcast data is loaded before doing any checks
|
||||
visit "/"
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ RSpec.describe "User edits their extensions", type: :system, js: true do
|
|||
stub_request(:get, "https://medium.com/feed/alkdmksadksa")
|
||||
.to_return(status: 200, body: "not an xml feed")
|
||||
|
||||
fill_in "user[feed_url]", with: "https://medium.com/feed/alkdmksadksa"
|
||||
fill_in "users_setting[feed_url]", with: "https://medium.com/feed/alkdmksadksa"
|
||||
click_on "Submit Feed Settings"
|
||||
|
||||
expect(page).to have_text("Feed url is not a valid RSS/Atom feed")
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ RSpec.describe "Using the editor", type: :system do
|
|||
end
|
||||
|
||||
describe "Submitting an article with v1 editor", js: true do
|
||||
before { user.update!(editor_version: "v1") }
|
||||
before { user.setting.update!(editor_version: "v1") }
|
||||
|
||||
it "fill out form and submit", cloudinary: true do
|
||||
fill_markdown_with(read_from_file(raw_text))
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ RSpec.describe "rendering locals in a partial", type: :view do
|
|||
context "when comment is low-quality" do
|
||||
it "renders the comment with low-quality marker" do
|
||||
allow(Settings::General).to receive(:mascot_image_url).and_return("https://i.imgur.com/fKYKgo4.png")
|
||||
comment = build_stubbed(:comment, processed_html: "hi", score: CommentDecorator::LOW_QUALITY_THRESHOLD - 100)
|
||||
article = build_stubbed(:article)
|
||||
comment = create(:comment, processed_html: "hi", score: CommentDecorator::LOW_QUALITY_THRESHOLD - 100)
|
||||
article = create(:article)
|
||||
|
||||
render "comments/comment",
|
||||
comment: comment,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@ require "rails_helper"
|
|||
|
||||
RSpec.describe Emails::SendUserDigestWorker, type: :worker do
|
||||
let(:worker) { subject }
|
||||
let(:user) { create(:user, email_digest_periodic: true) }
|
||||
let(:user) do
|
||||
u = create(:user)
|
||||
u.notification_setting.update(email_digest_periodic: true)
|
||||
u
|
||||
end
|
||||
let(:author) { create(:user) }
|
||||
let(:mailer) { double }
|
||||
let(:message_delivery) { double }
|
||||
|
|
@ -31,7 +35,7 @@ RSpec.describe Emails::SendUserDigestWorker, type: :worker do
|
|||
|
||||
it "does not send email when user does not have email_digest_periodic" do
|
||||
create_list(:article, 3, user_id: author.id, public_reactions_count: 20, score: 20)
|
||||
user.update_column(:email_digest_periodic, false)
|
||||
user.notification_setting.update_column(:email_digest_periodic, false)
|
||||
worker.perform(user.id)
|
||||
|
||||
expect(DigestMailer).not_to have_received(:with)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue