Change user settings to be stored in a more optimal way (#23630)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
parent
e7c3e55874
commit
a9b5598c97
36 changed files with 817 additions and 525 deletions
|
@ -185,3 +185,11 @@ Style/TrailingCommaInHashLiteral:
|
||||||
|
|
||||||
Style/SymbolArray:
|
Style/SymbolArray:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
|
# Reason: Prefer less intendation in conditional assignments
|
||||||
|
# https://docs.rubocop.org/rubocop/cops_style.html#styleredundantbegin
|
||||||
|
Style/RedundantBegin:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
RSpec/NamedSubject:
|
||||||
|
EnforcedStyle: named_only
|
||||||
|
|
|
@ -259,6 +259,7 @@ Metrics/ModuleLength:
|
||||||
- 'app/helpers/jsonld_helper.rb'
|
- 'app/helpers/jsonld_helper.rb'
|
||||||
- 'app/helpers/statuses_helper.rb'
|
- 'app/helpers/statuses_helper.rb'
|
||||||
- 'app/models/concerns/account_interactions.rb'
|
- 'app/models/concerns/account_interactions.rb'
|
||||||
|
- 'app/models/concerns/has_user_settings.rb'
|
||||||
|
|
||||||
# Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters.
|
# Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters.
|
||||||
Metrics/ParameterLists:
|
Metrics/ParameterLists:
|
||||||
|
|
|
@ -13,7 +13,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
|
||||||
def update
|
def update
|
||||||
@account = current_account
|
@account = current_account
|
||||||
UpdateAccountService.new.call(@account, account_params, raise_error: true)
|
UpdateAccountService.new.call(@account, account_params, raise_error: true)
|
||||||
UserSettingsDecorator.new(current_user).update(user_settings_params) if user_settings_params
|
current_user.update(user_params) if user_params
|
||||||
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
|
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
|
||||||
render json: @account, serializer: REST::CredentialAccountSerializer
|
render json: @account, serializer: REST::CredentialAccountSerializer
|
||||||
end
|
end
|
||||||
|
@ -34,15 +34,17 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_settings_params
|
def user_params
|
||||||
return nil if params[:source].blank?
|
return nil if params[:source].blank?
|
||||||
|
|
||||||
source_params = params.require(:source)
|
source_params = params.require(:source)
|
||||||
|
|
||||||
{
|
{
|
||||||
'setting_default_privacy' => source_params.fetch(:privacy, @account.user.setting_default_privacy),
|
settings_attributes: {
|
||||||
'setting_default_sensitive' => source_params.fetch(:sensitive, @account.user.setting_default_sensitive),
|
default_privacy: source_params.fetch(:privacy, @account.user.setting_default_privacy),
|
||||||
'setting_default_language' => source_params.fetch(:language, @account.user.setting_default_language),
|
default_sensitive: source_params.fetch(:sensitive, @account.user.setting_default_sensitive),
|
||||||
|
default_language: source_params.fetch(:language, @account.user.setting_default_language),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,8 +4,6 @@ class Settings::PreferencesController < Settings::BaseController
|
||||||
def show; end
|
def show; end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
user_settings.update(user_settings_params.to_h)
|
|
||||||
|
|
||||||
if current_user.update(user_params)
|
if current_user.update(user_params)
|
||||||
I18n.locale = current_user.locale
|
I18n.locale = current_user.locale
|
||||||
redirect_to after_update_redirect_path, notice: I18n.t('generic.changes_saved_msg')
|
redirect_to after_update_redirect_path, notice: I18n.t('generic.changes_saved_msg')
|
||||||
|
@ -20,43 +18,7 @@ class Settings::PreferencesController < Settings::BaseController
|
||||||
settings_preferences_path
|
settings_preferences_path
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_settings
|
|
||||||
UserSettingsDecorator.new(current_user)
|
|
||||||
end
|
|
||||||
|
|
||||||
def user_params
|
def user_params
|
||||||
params.require(:user).permit(
|
params.require(:user).permit(:locale, chosen_languages: [], settings_attributes: UserSettings.keys)
|
||||||
:locale,
|
|
||||||
chosen_languages: []
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def user_settings_params
|
|
||||||
params.require(:user).permit(
|
|
||||||
:setting_default_privacy,
|
|
||||||
:setting_default_sensitive,
|
|
||||||
:setting_default_language,
|
|
||||||
:setting_unfollow_modal,
|
|
||||||
:setting_boost_modal,
|
|
||||||
:setting_delete_modal,
|
|
||||||
:setting_auto_play_gif,
|
|
||||||
:setting_display_media,
|
|
||||||
:setting_expand_spoilers,
|
|
||||||
:setting_reduce_motion,
|
|
||||||
:setting_disable_swiping,
|
|
||||||
:setting_system_font_ui,
|
|
||||||
:setting_noindex,
|
|
||||||
:setting_theme,
|
|
||||||
:setting_aggregate_reblogs,
|
|
||||||
:setting_show_application,
|
|
||||||
:setting_advanced_layout,
|
|
||||||
:setting_use_blurhash,
|
|
||||||
:setting_use_pending_items,
|
|
||||||
:setting_trends,
|
|
||||||
:setting_crop_images,
|
|
||||||
:setting_always_send_emails,
|
|
||||||
notification_emails: %i(follow follow_request reblog favourite mention report pending_account trending_tag appeal),
|
|
||||||
interactions: %i(must_be_follower must_be_following must_be_following_dm)
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,155 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class UserSettingsDecorator
|
|
||||||
attr_reader :user, :settings
|
|
||||||
|
|
||||||
def initialize(user)
|
|
||||||
@user = user
|
|
||||||
end
|
|
||||||
|
|
||||||
def update(settings)
|
|
||||||
@settings = settings
|
|
||||||
process_update
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def process_update
|
|
||||||
user.settings['notification_emails'] = merged_notification_emails if change?('notification_emails')
|
|
||||||
user.settings['interactions'] = merged_interactions if change?('interactions')
|
|
||||||
user.settings['default_privacy'] = default_privacy_preference if change?('setting_default_privacy')
|
|
||||||
user.settings['default_sensitive'] = default_sensitive_preference if change?('setting_default_sensitive')
|
|
||||||
user.settings['default_language'] = default_language_preference if change?('setting_default_language')
|
|
||||||
user.settings['unfollow_modal'] = unfollow_modal_preference if change?('setting_unfollow_modal')
|
|
||||||
user.settings['boost_modal'] = boost_modal_preference if change?('setting_boost_modal')
|
|
||||||
user.settings['delete_modal'] = delete_modal_preference if change?('setting_delete_modal')
|
|
||||||
user.settings['auto_play_gif'] = auto_play_gif_preference if change?('setting_auto_play_gif')
|
|
||||||
user.settings['display_media'] = display_media_preference if change?('setting_display_media')
|
|
||||||
user.settings['expand_spoilers'] = expand_spoilers_preference if change?('setting_expand_spoilers')
|
|
||||||
user.settings['reduce_motion'] = reduce_motion_preference if change?('setting_reduce_motion')
|
|
||||||
user.settings['disable_swiping'] = disable_swiping_preference if change?('setting_disable_swiping')
|
|
||||||
user.settings['system_font_ui'] = system_font_ui_preference if change?('setting_system_font_ui')
|
|
||||||
user.settings['noindex'] = noindex_preference if change?('setting_noindex')
|
|
||||||
user.settings['theme'] = theme_preference if change?('setting_theme')
|
|
||||||
user.settings['aggregate_reblogs'] = aggregate_reblogs_preference if change?('setting_aggregate_reblogs')
|
|
||||||
user.settings['show_application'] = show_application_preference if change?('setting_show_application')
|
|
||||||
user.settings['advanced_layout'] = advanced_layout_preference if change?('setting_advanced_layout')
|
|
||||||
user.settings['use_blurhash'] = use_blurhash_preference if change?('setting_use_blurhash')
|
|
||||||
user.settings['use_pending_items'] = use_pending_items_preference if change?('setting_use_pending_items')
|
|
||||||
user.settings['trends'] = trends_preference if change?('setting_trends')
|
|
||||||
user.settings['crop_images'] = crop_images_preference if change?('setting_crop_images')
|
|
||||||
user.settings['always_send_emails'] = always_send_emails_preference if change?('setting_always_send_emails')
|
|
||||||
end
|
|
||||||
|
|
||||||
def merged_notification_emails
|
|
||||||
user.settings['notification_emails'].merge coerced_settings('notification_emails').to_h
|
|
||||||
end
|
|
||||||
|
|
||||||
def merged_interactions
|
|
||||||
user.settings['interactions'].merge coerced_settings('interactions').to_h
|
|
||||||
end
|
|
||||||
|
|
||||||
def default_privacy_preference
|
|
||||||
settings['setting_default_privacy']
|
|
||||||
end
|
|
||||||
|
|
||||||
def default_sensitive_preference
|
|
||||||
boolean_cast_setting 'setting_default_sensitive'
|
|
||||||
end
|
|
||||||
|
|
||||||
def unfollow_modal_preference
|
|
||||||
boolean_cast_setting 'setting_unfollow_modal'
|
|
||||||
end
|
|
||||||
|
|
||||||
def boost_modal_preference
|
|
||||||
boolean_cast_setting 'setting_boost_modal'
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete_modal_preference
|
|
||||||
boolean_cast_setting 'setting_delete_modal'
|
|
||||||
end
|
|
||||||
|
|
||||||
def system_font_ui_preference
|
|
||||||
boolean_cast_setting 'setting_system_font_ui'
|
|
||||||
end
|
|
||||||
|
|
||||||
def auto_play_gif_preference
|
|
||||||
boolean_cast_setting 'setting_auto_play_gif'
|
|
||||||
end
|
|
||||||
|
|
||||||
def display_media_preference
|
|
||||||
settings['setting_display_media']
|
|
||||||
end
|
|
||||||
|
|
||||||
def expand_spoilers_preference
|
|
||||||
boolean_cast_setting 'setting_expand_spoilers'
|
|
||||||
end
|
|
||||||
|
|
||||||
def reduce_motion_preference
|
|
||||||
boolean_cast_setting 'setting_reduce_motion'
|
|
||||||
end
|
|
||||||
|
|
||||||
def disable_swiping_preference
|
|
||||||
boolean_cast_setting 'setting_disable_swiping'
|
|
||||||
end
|
|
||||||
|
|
||||||
def noindex_preference
|
|
||||||
boolean_cast_setting 'setting_noindex'
|
|
||||||
end
|
|
||||||
|
|
||||||
def show_application_preference
|
|
||||||
boolean_cast_setting 'setting_show_application'
|
|
||||||
end
|
|
||||||
|
|
||||||
def theme_preference
|
|
||||||
settings['setting_theme']
|
|
||||||
end
|
|
||||||
|
|
||||||
def default_language_preference
|
|
||||||
settings['setting_default_language']
|
|
||||||
end
|
|
||||||
|
|
||||||
def aggregate_reblogs_preference
|
|
||||||
boolean_cast_setting 'setting_aggregate_reblogs'
|
|
||||||
end
|
|
||||||
|
|
||||||
def advanced_layout_preference
|
|
||||||
boolean_cast_setting 'setting_advanced_layout'
|
|
||||||
end
|
|
||||||
|
|
||||||
def use_blurhash_preference
|
|
||||||
boolean_cast_setting 'setting_use_blurhash'
|
|
||||||
end
|
|
||||||
|
|
||||||
def use_pending_items_preference
|
|
||||||
boolean_cast_setting 'setting_use_pending_items'
|
|
||||||
end
|
|
||||||
|
|
||||||
def trends_preference
|
|
||||||
boolean_cast_setting 'setting_trends'
|
|
||||||
end
|
|
||||||
|
|
||||||
def crop_images_preference
|
|
||||||
boolean_cast_setting 'setting_crop_images'
|
|
||||||
end
|
|
||||||
|
|
||||||
def always_send_emails_preference
|
|
||||||
boolean_cast_setting 'setting_always_send_emails'
|
|
||||||
end
|
|
||||||
|
|
||||||
def boolean_cast_setting(key)
|
|
||||||
ActiveModel::Type::Boolean.new.cast(settings[key])
|
|
||||||
end
|
|
||||||
|
|
||||||
def coerced_settings(key)
|
|
||||||
coerce_values settings.fetch(key, {})
|
|
||||||
end
|
|
||||||
|
|
||||||
def coerce_values(params_hash)
|
|
||||||
params_hash.transform_values { |x| ActiveModel::Type::Boolean.new.cast(x) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def change?(key)
|
|
||||||
!settings[key].nil?
|
|
||||||
end
|
|
||||||
end
|
|
19
app/lib/user_settings_serializer.rb
Normal file
19
app/lib/user_settings_serializer.rb
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class UserSettingsSerializer
|
||||||
|
def self.load(value)
|
||||||
|
json = begin
|
||||||
|
if value.blank?
|
||||||
|
{}
|
||||||
|
else
|
||||||
|
Oj.load(value, symbol_keys: true)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
UserSettings.new(json)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.dump(value)
|
||||||
|
Oj.dump(value.as_json)
|
||||||
|
end
|
||||||
|
end
|
141
app/models/concerns/has_user_settings.rb
Normal file
141
app/models/concerns/has_user_settings.rb
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module HasUserSettings
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
serialize :settings, UserSettingsSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
def settings_attributes=(attributes)
|
||||||
|
settings.update(attributes)
|
||||||
|
end
|
||||||
|
|
||||||
|
def prefers_noindex?
|
||||||
|
settings['noindex']
|
||||||
|
end
|
||||||
|
|
||||||
|
def preferred_posting_language
|
||||||
|
valid_locale_cascade(settings['default_language'], locale, I18n.locale)
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_auto_play_gif
|
||||||
|
settings['web.auto_play']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_default_sensitive
|
||||||
|
settings['default_sensitive']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_unfollow_modal
|
||||||
|
settings['web.unfollow_modal']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_boost_modal
|
||||||
|
settings['web.reblog_modal']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_delete_modal
|
||||||
|
settings['web.delete_modal']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_reduce_motion
|
||||||
|
settings['web.reduce_motion']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_system_font_ui
|
||||||
|
settings['web.use_system_font']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_noindex
|
||||||
|
settings['noindex']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_theme
|
||||||
|
settings['theme']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_display_media
|
||||||
|
settings['web.display_media']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_expand_spoilers
|
||||||
|
settings['web.expand_content_warnings']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_default_language
|
||||||
|
settings['default_language']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_aggregate_reblogs
|
||||||
|
settings['aggregate_reblogs']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_show_application
|
||||||
|
settings['show_application']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_advanced_layout
|
||||||
|
settings['web.advanced_layout']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_use_blurhash
|
||||||
|
settings['web.use_blurhash']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_use_pending_items
|
||||||
|
settings['web.use_pending_items']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_trends
|
||||||
|
settings['web.trends']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_crop_images
|
||||||
|
settings['web.crop_images']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_disable_swiping
|
||||||
|
settings['web.disable_swiping']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_always_send_emails
|
||||||
|
settings['always_send_emails']
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting_default_privacy
|
||||||
|
settings['default_privacy'] || (account.locked? ? 'private' : 'public')
|
||||||
|
end
|
||||||
|
|
||||||
|
def allows_report_emails?
|
||||||
|
settings['notification_emails.report']
|
||||||
|
end
|
||||||
|
|
||||||
|
def allows_pending_account_emails?
|
||||||
|
settings['notification_emails.pending_account']
|
||||||
|
end
|
||||||
|
|
||||||
|
def allows_appeal_emails?
|
||||||
|
settings['notification_emails.appeal']
|
||||||
|
end
|
||||||
|
|
||||||
|
def allows_trends_review_emails?
|
||||||
|
settings['notification_emails.trends']
|
||||||
|
end
|
||||||
|
|
||||||
|
def aggregates_reblogs?
|
||||||
|
settings['aggregate_reblogs']
|
||||||
|
end
|
||||||
|
|
||||||
|
def shows_application?
|
||||||
|
settings['show_application']
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_all_media?
|
||||||
|
settings['web.display_media'] == 'show_all'
|
||||||
|
end
|
||||||
|
|
||||||
|
def hide_all_media?
|
||||||
|
settings['web.display_media'] == 'hide_all'
|
||||||
|
end
|
||||||
|
end
|
|
@ -39,10 +39,11 @@
|
||||||
# webauthn_id :string
|
# webauthn_id :string
|
||||||
# sign_up_ip :inet
|
# sign_up_ip :inet
|
||||||
# role_id :bigint(8)
|
# role_id :bigint(8)
|
||||||
|
# settings :text
|
||||||
#
|
#
|
||||||
|
|
||||||
class User < ApplicationRecord
|
class User < ApplicationRecord
|
||||||
self.ignored_columns = %w(
|
self.ignored_columns += %w(
|
||||||
remember_created_at
|
remember_created_at
|
||||||
remember_token
|
remember_token
|
||||||
current_sign_in_ip
|
current_sign_in_ip
|
||||||
|
@ -51,9 +52,9 @@ class User < ApplicationRecord
|
||||||
filtered_languages
|
filtered_languages
|
||||||
)
|
)
|
||||||
|
|
||||||
include Settings::Extend
|
|
||||||
include Redisable
|
include Redisable
|
||||||
include LanguagesHelper
|
include LanguagesHelper
|
||||||
|
include HasUserSettings
|
||||||
|
|
||||||
# The home and list feeds will be stored in Redis for this amount
|
# The home and list feeds will be stored in Redis for this amount
|
||||||
# of time, and status fan-out to followers will include only people
|
# of time, and status fan-out to followers will include only people
|
||||||
|
@ -132,13 +133,6 @@ class User < ApplicationRecord
|
||||||
|
|
||||||
has_many :session_activations, dependent: :destroy
|
has_many :session_activations, dependent: :destroy
|
||||||
|
|
||||||
delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal,
|
|
||||||
:reduce_motion, :system_font_ui, :noindex, :theme, :display_media,
|
|
||||||
:expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
|
|
||||||
:advanced_layout, :use_blurhash, :use_pending_items, :trends, :crop_images,
|
|
||||||
:disable_swiping, :always_send_emails,
|
|
||||||
to: :settings, prefix: :setting, allow_nil: false
|
|
||||||
|
|
||||||
delegate :can?, to: :role
|
delegate :can?, to: :role
|
||||||
|
|
||||||
attr_reader :invite_code
|
attr_reader :invite_code
|
||||||
|
@ -302,42 +296,6 @@ class User < ApplicationRecord
|
||||||
save!
|
save!
|
||||||
end
|
end
|
||||||
|
|
||||||
def prefers_noindex?
|
|
||||||
setting_noindex
|
|
||||||
end
|
|
||||||
|
|
||||||
def preferred_posting_language
|
|
||||||
valid_locale_cascade(settings.default_language, locale, I18n.locale)
|
|
||||||
end
|
|
||||||
|
|
||||||
def setting_default_privacy
|
|
||||||
settings.default_privacy || (account.locked? ? 'private' : 'public')
|
|
||||||
end
|
|
||||||
|
|
||||||
def allows_report_emails?
|
|
||||||
settings.notification_emails['report']
|
|
||||||
end
|
|
||||||
|
|
||||||
def allows_pending_account_emails?
|
|
||||||
settings.notification_emails['pending_account']
|
|
||||||
end
|
|
||||||
|
|
||||||
def allows_appeal_emails?
|
|
||||||
settings.notification_emails['appeal']
|
|
||||||
end
|
|
||||||
|
|
||||||
def allows_trends_review_emails?
|
|
||||||
settings.notification_emails['trending_tag']
|
|
||||||
end
|
|
||||||
|
|
||||||
def aggregates_reblogs?
|
|
||||||
@aggregates_reblogs ||= settings.aggregate_reblogs
|
|
||||||
end
|
|
||||||
|
|
||||||
def shows_application?
|
|
||||||
@shows_application ||= settings.show_application
|
|
||||||
end
|
|
||||||
|
|
||||||
def token_for_app(app)
|
def token_for_app(app)
|
||||||
return nil if app.nil? || app.owner != self
|
return nil if app.nil? || app.owner != self
|
||||||
|
|
||||||
|
@ -417,14 +375,6 @@ class User < ApplicationRecord
|
||||||
send_reset_password_instructions
|
send_reset_password_instructions
|
||||||
end
|
end
|
||||||
|
|
||||||
def show_all_media?
|
|
||||||
setting_display_media == 'show_all'
|
|
||||||
end
|
|
||||||
|
|
||||||
def hide_all_media?
|
|
||||||
setting_display_media == 'hide_all'
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def send_devise_notification(notification, *args, **kwargs)
|
def send_devise_notification(notification, *args, **kwargs)
|
||||||
|
@ -494,7 +444,8 @@ class User < ApplicationRecord
|
||||||
def sanitize_languages
|
def sanitize_languages
|
||||||
return if chosen_languages.nil?
|
return if chosen_languages.nil?
|
||||||
|
|
||||||
chosen_languages.reject!(&:blank?)
|
chosen_languages.compact_blank!
|
||||||
|
|
||||||
self.chosen_languages = nil if chosen_languages.empty?
|
self.chosen_languages = nil if chosen_languages.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
99
app/models/user_settings.rb
Normal file
99
app/models/user_settings.rb
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class UserSettings
|
||||||
|
class Error < StandardError; end
|
||||||
|
class KeyError < Error; end
|
||||||
|
|
||||||
|
include UserSettings::DSL
|
||||||
|
include UserSettings::Glue
|
||||||
|
|
||||||
|
setting :always_send_emails, default: false
|
||||||
|
setting :aggregate_reblogs, default: true
|
||||||
|
setting :theme, default: -> { ::Setting.theme }
|
||||||
|
setting :noindex, default: -> { ::Setting.noindex }
|
||||||
|
setting :show_application, default: true
|
||||||
|
setting :default_language, default: nil
|
||||||
|
setting :default_sensitive, default: false
|
||||||
|
setting :default_privacy, default: nil
|
||||||
|
|
||||||
|
namespace :web do
|
||||||
|
setting :crop_images, default: true
|
||||||
|
setting :advanced_layout, default: false
|
||||||
|
setting :trends, default: true
|
||||||
|
setting :use_blurhash, default: true
|
||||||
|
setting :use_pending_items, default: false
|
||||||
|
setting :use_system_font, default: false
|
||||||
|
setting :disable_swiping, default: false
|
||||||
|
setting :delete_modal, default: true
|
||||||
|
setting :reblog_modal, default: false
|
||||||
|
setting :unfollow_modal, default: true
|
||||||
|
setting :reduce_motion, default: false
|
||||||
|
setting :expand_content_warnings, default: false
|
||||||
|
setting :display_media, default: 'default', in: %w(default show_all hide_all)
|
||||||
|
setting :auto_play, default: false
|
||||||
|
end
|
||||||
|
|
||||||
|
namespace :notification_emails do
|
||||||
|
setting :follow, default: true
|
||||||
|
setting :reblog, default: false
|
||||||
|
setting :favourite, default: false
|
||||||
|
setting :mention, default: true
|
||||||
|
setting :follow_request, default: true
|
||||||
|
setting :report, default: true
|
||||||
|
setting :pending_account, default: true
|
||||||
|
setting :trends, default: true
|
||||||
|
setting :appeal, default: true
|
||||||
|
end
|
||||||
|
|
||||||
|
namespace :interactions do
|
||||||
|
setting :must_be_follower, default: false
|
||||||
|
setting :must_be_following, default: false
|
||||||
|
setting :must_be_following_dm, default: false
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(original_hash)
|
||||||
|
@original_hash = original_hash || {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def [](key)
|
||||||
|
key = key.to_sym
|
||||||
|
|
||||||
|
raise KeyError, "Undefined setting: #{key}" unless self.class.definition_for?(key)
|
||||||
|
|
||||||
|
if @original_hash.key?(key)
|
||||||
|
@original_hash[key]
|
||||||
|
else
|
||||||
|
self.class.definition_for(key).default_value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def []=(key, value)
|
||||||
|
key = key.to_sym
|
||||||
|
|
||||||
|
raise KeyError, "Undefined setting: #{key}" unless self.class.definition_for?(key)
|
||||||
|
|
||||||
|
typecast_value = self.class.definition_for(key).type_cast(value)
|
||||||
|
|
||||||
|
if typecast_value.nil?
|
||||||
|
@original_hash.delete(key)
|
||||||
|
else
|
||||||
|
@original_hash[key] = typecast_value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(params)
|
||||||
|
params.each do |k, v|
|
||||||
|
self[k] = v unless v.nil?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
keys.each do |key|
|
||||||
|
define_method(key) do
|
||||||
|
self[key]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def as_json
|
||||||
|
@original_hash
|
||||||
|
end
|
||||||
|
end
|
37
app/models/user_settings/dsl.rb
Normal file
37
app/models/user_settings/dsl.rb
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module UserSettings::DSL
|
||||||
|
module ClassMethods
|
||||||
|
def setting(key, options = {})
|
||||||
|
@definitions ||= {}
|
||||||
|
|
||||||
|
UserSettings::Setting.new(key, options).tap do |s|
|
||||||
|
@definitions[s.key] = s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def namespace(key, &block)
|
||||||
|
@definitions ||= {}
|
||||||
|
|
||||||
|
UserSettings::Namespace.new(key).configure(&block).tap do |n|
|
||||||
|
@definitions.merge!(n.definitions)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def keys
|
||||||
|
@definitions.keys
|
||||||
|
end
|
||||||
|
|
||||||
|
def definition_for(key)
|
||||||
|
@definitions[key.to_sym]
|
||||||
|
end
|
||||||
|
|
||||||
|
def definition_for?(key)
|
||||||
|
@definitions.key?(key.to_sym)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.included(base)
|
||||||
|
base.extend ClassMethods
|
||||||
|
end
|
||||||
|
end
|
23
app/models/user_settings/glue.rb
Normal file
23
app/models/user_settings/glue.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module UserSettings::Glue
|
||||||
|
def to_model
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_key
|
||||||
|
''
|
||||||
|
end
|
||||||
|
|
||||||
|
def persisted?
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def type_for_attribute(key)
|
||||||
|
self.class.definition_for(key)&.type
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_attribute?(key) # rubocop:disable Naming/PredicateName
|
||||||
|
self.class.definition_for?(key)
|
||||||
|
end
|
||||||
|
end
|
21
app/models/user_settings/namespace.rb
Normal file
21
app/models/user_settings/namespace.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class UserSettings::Namespace
|
||||||
|
attr_reader :name, :definitions
|
||||||
|
|
||||||
|
def initialize(name)
|
||||||
|
@name = name.to_sym
|
||||||
|
@definitions = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def configure(&block)
|
||||||
|
instance_eval(&block)
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
|
def setting(key, options = {})
|
||||||
|
UserSettings::Setting.new(key, options.merge(namespace: name)).tap do |s|
|
||||||
|
@definitions[s.key] = s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
48
app/models/user_settings/setting.rb
Normal file
48
app/models/user_settings/setting.rb
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class UserSettings::Setting
|
||||||
|
attr_reader :name, :namespace, :in
|
||||||
|
|
||||||
|
def initialize(name, options = {})
|
||||||
|
@name = name.to_sym
|
||||||
|
@default_value = options[:default]
|
||||||
|
@namespace = options[:namespace]
|
||||||
|
@in = options[:in]
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_value
|
||||||
|
if @default_value.respond_to?(:call)
|
||||||
|
@default_value.call
|
||||||
|
else
|
||||||
|
@default_value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def type
|
||||||
|
if @default_value.is_a?(TrueClass) || @default_value.is_a?(FalseClass)
|
||||||
|
ActiveModel::Type::Boolean.new
|
||||||
|
else
|
||||||
|
ActiveModel::Type::String.new
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def type_cast(value)
|
||||||
|
if type.respond_to?(:cast)
|
||||||
|
type.cast(value)
|
||||||
|
else
|
||||||
|
value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_a
|
||||||
|
[key, default_value]
|
||||||
|
end
|
||||||
|
|
||||||
|
def key
|
||||||
|
if namespace
|
||||||
|
"#{namespace}.#{name}".to_sym
|
||||||
|
else
|
||||||
|
name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -3,6 +3,11 @@
|
||||||
class NotifyService < BaseService
|
class NotifyService < BaseService
|
||||||
include Redisable
|
include Redisable
|
||||||
|
|
||||||
|
NON_EMAIL_TYPES = %i(
|
||||||
|
admin.report
|
||||||
|
admin.sign_up
|
||||||
|
).freeze
|
||||||
|
|
||||||
def call(recipient, type, activity)
|
def call(recipient, type, activity)
|
||||||
@recipient = recipient
|
@recipient = recipient
|
||||||
@activity = activity
|
@activity = activity
|
||||||
|
@ -36,11 +41,11 @@ class NotifyService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def optional_non_follower?
|
def optional_non_follower?
|
||||||
@recipient.user.settings.interactions['must_be_follower'] && !@notification.from_account.following?(@recipient)
|
@recipient.user.settings['interactions.must_be_follower'] && !@notification.from_account.following?(@recipient)
|
||||||
end
|
end
|
||||||
|
|
||||||
def optional_non_following?
|
def optional_non_following?
|
||||||
@recipient.user.settings.interactions['must_be_following'] && !following_sender?
|
@recipient.user.settings['interactions.must_be_following'] && !following_sender?
|
||||||
end
|
end
|
||||||
|
|
||||||
def message?
|
def message?
|
||||||
|
@ -82,7 +87,7 @@ class NotifyService < BaseService
|
||||||
|
|
||||||
def optional_non_following_and_direct?
|
def optional_non_following_and_direct?
|
||||||
direct_message? &&
|
direct_message? &&
|
||||||
@recipient.user.settings.interactions['must_be_following_dm'] &&
|
@recipient.user.settings['interactions.must_be_following_dm'] &&
|
||||||
!following_sender? &&
|
!following_sender? &&
|
||||||
!response_to_recipient?
|
!response_to_recipient?
|
||||||
end
|
end
|
||||||
|
@ -171,6 +176,6 @@ class NotifyService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def send_email_for_notification_type?
|
def send_email_for_notification_type?
|
||||||
@recipient.user.settings.notification_emails[@notification.type.to_s]
|
NON_EMAIL_TYPES.exclude?(@notification.type) && @recipient.user.settings["notification_emails.#{@notification.type}"]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,57 +9,58 @@
|
||||||
.fields-group.fields-row__column.fields-row__column-6
|
.fields-group.fields-row__column.fields-row__column-6
|
||||||
= f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| native_locale_name(locale) }, selected: I18n.locale, hint: false
|
= f.input :locale, collection: I18n.available_locales, wrapper: :with_label, include_blank: false, label_method: lambda { |locale| native_locale_name(locale) }, selected: I18n.locale, hint: false
|
||||||
.fields-group.fields-row__column.fields-row__column-6
|
.fields-group.fields-row__column.fields-row__column-6
|
||||||
= f.input :setting_theme, collection: Themes.instance.names, label_method: lambda { |theme| I18n.t("themes.#{theme}", default: theme) }, wrapper: :with_label, include_blank: false, hint: false
|
= f.simple_fields_for :settings, current_user.settings do |ff|
|
||||||
|
= ff.input :theme, collection: Themes.instance.names, label_method: lambda { |theme| I18n.t("themes.#{theme}", default: theme) }, wrapper: :with_label, include_blank: false, hint: false
|
||||||
|
|
||||||
- unless I18n.locale == :en
|
- unless I18n.locale == :en
|
||||||
.flash-message.translation-prompt
|
.flash-message.translation-prompt
|
||||||
#{t 'appearance.localization.body'} #{content_tag(:a, t('appearance.localization.guide_link_text'), href: t('appearance.localization.guide_link'), target: '_blank', rel: 'noopener')}
|
#{t 'appearance.localization.body'} #{content_tag(:a, t('appearance.localization.guide_link_text'), href: t('appearance.localization.guide_link'), target: '_blank', rel: 'noopener')}
|
||||||
|
|
||||||
|
= f.simple_fields_for :settings, current_user.settings do |ff|
|
||||||
%h4= t 'appearance.advanced_web_interface'
|
%h4= t 'appearance.advanced_web_interface'
|
||||||
|
|
||||||
%p.hint= t 'appearance.advanced_web_interface_hint'
|
%p.hint= t 'appearance.advanced_web_interface_hint'
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :setting_advanced_layout, as: :boolean, wrapper: :with_label, hint: false
|
= ff.input :'web.advanced_layout', wrapper: :with_label, hint: false, label: I18n.t('simple_form.labels.defaults.setting_advanced_layout')
|
||||||
|
|
||||||
%h4= t 'appearance.animations_and_accessibility'
|
%h4= t 'appearance.animations_and_accessibility'
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :setting_use_pending_items, as: :boolean, wrapper: :with_label
|
= ff.input :'web.use_pending_items', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_use_pending_items'), hint: I18n.t('simple_form.hints.defaults.setting_use_pending_items')
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :setting_auto_play_gif, as: :boolean, wrapper: :with_label, recommended: true
|
= ff.input :'web.auto_play', wrapper: :with_label, recommended: true, label: I18n.t('simple_form.labels.defaults.setting_auto_play_gif')
|
||||||
= f.input :setting_reduce_motion, as: :boolean, wrapper: :with_label
|
= ff.input :'web.reduce_motion', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_reduce_motion')
|
||||||
= f.input :setting_disable_swiping, as: :boolean, wrapper: :with_label
|
= ff.input :'web.disable_swiping', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_disable_swiping')
|
||||||
= f.input :setting_system_font_ui, as: :boolean, wrapper: :with_label
|
= ff.input :'web.use_system_font', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_system_font_ui')
|
||||||
|
|
||||||
%h4= t 'appearance.toot_layout'
|
%h4= t 'appearance.toot_layout'
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :setting_crop_images, as: :boolean, wrapper: :with_label
|
= ff.input :'web.crop_images', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_crop_images')
|
||||||
|
|
||||||
%h4= t 'appearance.discovery'
|
%h4= t 'appearance.discovery'
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :setting_trends, as: :boolean, wrapper: :with_label
|
= ff.input :'web.trends', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_trends')
|
||||||
|
|
||||||
%h4= t 'appearance.confirmation_dialogs'
|
%h4= t 'appearance.confirmation_dialogs'
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :setting_unfollow_modal, as: :boolean, wrapper: :with_label
|
= ff.input :'web.unfollow_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_unfollow_modal')
|
||||||
= f.input :setting_boost_modal, as: :boolean, wrapper: :with_label
|
= ff.input :'web.reblog_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_boost_modal')
|
||||||
= f.input :setting_delete_modal, as: :boolean, wrapper: :with_label
|
= ff.input :'web.delete_modal', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_delete_modal')
|
||||||
|
|
||||||
%h4= t 'appearance.sensitive_content'
|
%h4= t 'appearance.sensitive_content'
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :setting_display_media, collection: ['default', 'show_all', 'hide_all'], label_method: lambda { |item| t("simple_form.hints.defaults.setting_display_media_#{item}") }, hint: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label
|
= ff.input :'web.display_media', collection: ['default', 'show_all', 'hide_all'],label_method: lambda { |item| t("simple_form.hints.defaults.setting_display_media_#{item}") }, hint: false, as: :radio_buttons, collection_wrapper_tag: 'ul', item_wrapper_tag: 'li', wrapper: :with_floating_label, label: I18n.t('simple_form.labels.defaults.setting_display_media')
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :setting_use_blurhash, as: :boolean, wrapper: :with_label
|
= ff.input :'web.use_blurhash', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_use_blurhash'), hint: I18n.t('simple_form.hints.defaults.setting_use_blurhash')
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :setting_expand_spoilers, as: :boolean, wrapper: :with_label
|
= ff.input :'web.expand_content_warnings', wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_expand_spoilers')
|
||||||
|
|
||||||
.actions
|
.actions
|
||||||
= f.button :button, t('generic.save_changes'), type: :submit
|
= f.button :button, t('generic.save_changes'), type: :submit
|
||||||
|
|
|
@ -11,25 +11,25 @@
|
||||||
|
|
||||||
%p.hint= t 'notifications.email_events_hint'
|
%p.hint= t 'notifications.email_events_hint'
|
||||||
|
|
||||||
|
= f.simple_fields_for :settings, current_user.settings do |ff|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.simple_fields_for :notification_emails, hash_to_object(current_user.settings.notification_emails) do |ff|
|
= ff.input :'notification_emails.follow', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.follow')
|
||||||
= ff.input :follow, as: :boolean, wrapper: :with_label
|
= ff.input :'notification_emails.follow_request', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.follow_request')
|
||||||
= ff.input :follow_request, as: :boolean, wrapper: :with_label
|
= ff.input :'notification_emails.reblog', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.reblog')
|
||||||
= ff.input :reblog, as: :boolean, wrapper: :with_label
|
= ff.input :'notification_emails.favourite', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.favourite')
|
||||||
= ff.input :favourite, as: :boolean, wrapper: :with_label
|
= ff.input :'notification_emails.mention', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.mention')
|
||||||
= ff.input :mention, as: :boolean, wrapper: :with_label
|
= ff.input :'notification_emails.report', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.report') if current_user.can?(:manage_reports)
|
||||||
= ff.input :report, as: :boolean, wrapper: :with_label if current_user.can?(:manage_reports)
|
= ff.input :'notification_emails.appeal', as: :boolean, wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.appeal') if current_user.can?(:manage_appeals)
|
||||||
= ff.input :appeal, as: :boolean, wrapper: :with_label if current_user.can?(:manage_appeals)
|
= ff.input :'notification_emails.pending_account', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.pending_account') if current_user.can?(:manage_users)
|
||||||
= ff.input :pending_account, as: :boolean, wrapper: :with_label if current_user.can?(:manage_users)
|
= ff.input :'notification_emails.trends', wrapper: :with_label, label: I18n.t('simple_form.labels.notification_emails.trending_tag') if current_user.can?(:manage_taxonomies)
|
||||||
= ff.input :trending_tag, as: :boolean, wrapper: :with_label if current_user.can?(:manage_taxonomies)
|
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :setting_always_send_emails, as: :boolean, wrapper: :with_label
|
= ff.input :always_send_emails, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_always_send_emails'), hint: I18n.t('simple_form.hints.defaults.setting_always_send_emails')
|
||||||
|
|
||||||
%h4= t 'notifications.other_settings'
|
%h4= t 'notifications.other_settings'
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.simple_fields_for :interactions, hash_to_object(current_user.settings.interactions) do |ff|
|
= f.simple_fields_for :settings, current_user.settings do |ff|
|
||||||
= ff.input :must_be_follower, as: :boolean, wrapper: :with_label
|
= ff.input :'interactions.must_be_follower', wrapper: :with_label, label: I18n.t('simple_form.labels.interactions.must_be_follower')
|
||||||
= ff.input :must_be_following, as: :boolean, wrapper: :with_label
|
= ff.input :'interactions.must_be_following', wrapper: :with_label, label: I18n.t('simple_form.labels.interactions.must_be_following')
|
||||||
= ff.input :must_be_following_dm, as: :boolean, wrapper: :with_label
|
= ff.input :'interactions.must_be_following_dm', wrapper: :with_label, label: I18n.t('simple_form.labels.interactions.must_be_following_dm')
|
||||||
|
|
|
@ -7,26 +7,27 @@
|
||||||
= simple_form_for current_user, url: settings_preferences_other_path, html: { method: :put, id: 'edit_preferences' } do |f|
|
= simple_form_for current_user, url: settings_preferences_other_path, html: { method: :put, id: 'edit_preferences' } do |f|
|
||||||
= render 'shared/error_messages', object: current_user
|
= render 'shared/error_messages', object: current_user
|
||||||
|
|
||||||
|
= f.simple_fields_for :settings, current_user.settings do |ff|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :setting_noindex, as: :boolean, wrapper: :with_label
|
= ff.input :noindex, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_noindex'), hint: I18n.t('simple_form.hints.defaults.setting_noindex')
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :setting_aggregate_reblogs, as: :boolean, wrapper: :with_label, recommended: true
|
= ff.input :aggregate_reblogs, wrapper: :with_label, recommended: true, label: I18n.t('simple_form.labels.defaults.setting_aggregate_reblogs'), hint: I18n.t('simple_form.hints.defaults.setting_aggregate_reblogs')
|
||||||
|
|
||||||
%h4= t 'preferences.posting_defaults'
|
%h4= t 'preferences.posting_defaults'
|
||||||
|
|
||||||
.fields-row
|
.fields-row
|
||||||
.fields-group.fields-row__column.fields-row__column-6
|
.fields-group.fields-row__column.fields-row__column-6
|
||||||
= f.input :setting_default_privacy, collection: Status.selectable_visibilities, wrapper: :with_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), I18n.t("statuses.visibilities.#{visibility}_long")], ' - ') }, required: false, hint: false
|
= ff.input :default_privacy, collection: Status.selectable_visibilities, wrapper: :with_label, include_blank: false, label_method: lambda { |visibility| safe_join([I18n.t("statuses.visibilities.#{visibility}"), I18n.t("statuses.visibilities.#{visibility}_long")], ' - ') }, required: false, hint: false, label: I18n.t('simple_form.labels.defaults.setting_default_privacy')
|
||||||
|
|
||||||
.fields-group.fields-row__column.fields-row__column-6
|
.fields-group.fields-row__column.fields-row__column-6
|
||||||
= f.input :setting_default_language, collection: [nil] + filterable_languages, wrapper: :with_label, label_method: lambda { |locale| locale.nil? ? I18n.t('statuses.default_language') : native_locale_name(locale) }, required: false, include_blank: false, hint: false
|
= ff.input :default_language, collection: [nil] + filterable_languages, wrapper: :with_label, label_method: lambda { |locale| locale.nil? ? I18n.t('statuses.default_language') : native_locale_name(locale) }, required: false, include_blank: false, hint: false, label: I18n.t('simple_form.labels.defaults.setting_default_language')
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :setting_default_sensitive, as: :boolean, wrapper: :with_label
|
= ff.input :default_sensitive, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_default_sensitive'), hint: I18n.t('simple_form.hints.defaults.setting_default_sensitive')
|
||||||
|
|
||||||
.fields-group
|
.fields-group
|
||||||
= f.input :setting_show_application, as: :boolean, wrapper: :with_label, recommended: true
|
= ff.input :show_application, wrapper: :with_label, recommended: true, label: I18n.t('simple_form.labels.defaults.setting_show_application'), hint: I18n.t('simple_form.hints.defaults.setting_show_application')
|
||||||
|
|
||||||
%h4= t 'preferences.public_timelines'
|
%h4= t 'preferences.public_timelines'
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ ActiveSupport::Inflector.inflections(:en) do |inflect|
|
||||||
inflect.acronym 'URL'
|
inflect.acronym 'URL'
|
||||||
inflect.acronym 'ASCII'
|
inflect.acronym 'ASCII'
|
||||||
inflect.acronym 'DeepL'
|
inflect.acronym 'DeepL'
|
||||||
|
inflect.acronym 'DSL'
|
||||||
|
|
||||||
inflect.singular 'data', 'data'
|
inflect.singular 'data', 'data'
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,48 +12,14 @@ defaults: &defaults
|
||||||
registrations_mode: 'open'
|
registrations_mode: 'open'
|
||||||
profile_directory: true
|
profile_directory: true
|
||||||
closed_registrations_message: ''
|
closed_registrations_message: ''
|
||||||
open_deletion: true
|
|
||||||
min_invite_role: 'admin'
|
|
||||||
timeline_preview: true
|
timeline_preview: true
|
||||||
show_staff_badge: true
|
show_staff_badge: true
|
||||||
default_sensitive: false
|
|
||||||
unfollow_modal: false
|
|
||||||
boost_modal: false
|
|
||||||
delete_modal: true
|
|
||||||
auto_play_gif: false
|
|
||||||
display_media: 'default'
|
|
||||||
expand_spoilers: false
|
|
||||||
preview_sensitive_media: false
|
preview_sensitive_media: false
|
||||||
reduce_motion: false
|
|
||||||
disable_swiping: false
|
|
||||||
show_application: true
|
|
||||||
system_font_ui: false
|
|
||||||
noindex: false
|
noindex: false
|
||||||
theme: 'default'
|
theme: 'default'
|
||||||
aggregate_reblogs: true
|
|
||||||
advanced_layout: false
|
|
||||||
use_blurhash: true
|
|
||||||
use_pending_items: false
|
|
||||||
trends: true
|
trends: true
|
||||||
trends_as_landing_page: true
|
trends_as_landing_page: true
|
||||||
trendable_by_default: false
|
trendable_by_default: false
|
||||||
crop_images: true
|
|
||||||
notification_emails:
|
|
||||||
follow: true
|
|
||||||
reblog: false
|
|
||||||
favourite: false
|
|
||||||
mention: true
|
|
||||||
follow_request: true
|
|
||||||
digest: true
|
|
||||||
report: true
|
|
||||||
pending_account: true
|
|
||||||
trending_tag: true
|
|
||||||
appeal: true
|
|
||||||
always_send_emails: false
|
|
||||||
interactions:
|
|
||||||
must_be_follower: false
|
|
||||||
must_be_following: false
|
|
||||||
must_be_following_dm: false
|
|
||||||
reserved_usernames:
|
reserved_usernames:
|
||||||
- admin
|
- admin
|
||||||
- support
|
- support
|
||||||
|
|
7
db/migrate/20230215074327_add_settings_to_users.rb
Normal file
7
db/migrate/20230215074327_add_settings_to_users.rb
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddSettingsToUsers < ActiveRecord::Migration[6.1]
|
||||||
|
def change
|
||||||
|
add_column :users, :settings, :text
|
||||||
|
end
|
||||||
|
end
|
84
db/migrate/20230215074423_move_user_settings.rb
Normal file
84
db/migrate/20230215074423_move_user_settings.rb
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class MoveUserSettings < ActiveRecord::Migration[6.1]
|
||||||
|
class User < ApplicationRecord; end
|
||||||
|
|
||||||
|
MAPPING = {
|
||||||
|
default_privacy: 'default_privacy',
|
||||||
|
default_sensitive: 'web.default_sensitive',
|
||||||
|
default_language: 'default_language',
|
||||||
|
noindex: 'noindex',
|
||||||
|
theme: 'theme',
|
||||||
|
trends: 'web.trends',
|
||||||
|
unfollow_modal: 'web.unfollow_modal',
|
||||||
|
boost_modal: 'web.reblog_modal',
|
||||||
|
delete_modal: 'web.delete_modal',
|
||||||
|
auto_play_gif: 'web.auto_play',
|
||||||
|
display_media: 'web.display_media',
|
||||||
|
expand_spoilers: 'web.expand_content_warnings',
|
||||||
|
reduce_motion: 'web.reduce_motion',
|
||||||
|
disable_swiping: 'web.disable_swiping',
|
||||||
|
show_application: 'show_application',
|
||||||
|
system_font_ui: 'web.use_system_font',
|
||||||
|
aggregate_reblogs: 'aggregate_reblogs',
|
||||||
|
advanced_layout: 'web.advanced_layout',
|
||||||
|
use_blurhash: 'web.use_blurhash',
|
||||||
|
use_pending_items: 'web.use_pending_items',
|
||||||
|
crop_images: 'web.crop_images',
|
||||||
|
notification_emails: {
|
||||||
|
follow: 'notification_emails.follow',
|
||||||
|
reblog: 'notification_emails.reblog',
|
||||||
|
favourite: 'notification_emails.favourite',
|
||||||
|
mention: 'notification_emails.mention',
|
||||||
|
follow_request: 'notification_emails.follow_request',
|
||||||
|
report: 'notification_emails.report',
|
||||||
|
pending_account: 'notification_emails.pending_account',
|
||||||
|
trending_tag: 'notification_emails.trends',
|
||||||
|
appeal: 'notification_emails.appeal',
|
||||||
|
}.freeze,
|
||||||
|
always_send_emails: 'always_send_emails',
|
||||||
|
interactions: {
|
||||||
|
must_be_follower: 'interactions.must_be_follower',
|
||||||
|
must_be_following: 'interactions.must_be_following',
|
||||||
|
must_be_following_dm: 'interactions.must_be_following_dm',
|
||||||
|
}.freeze,
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
class LegacySetting < ApplicationRecord
|
||||||
|
self.table_name = 'settings'
|
||||||
|
|
||||||
|
def var
|
||||||
|
self[:var]&.to_sym
|
||||||
|
end
|
||||||
|
|
||||||
|
def value
|
||||||
|
YAML.safe_load(self[:value], permitted_classes: [ActiveSupport::HashWithIndifferentAccess]) if self[:value].present?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def up
|
||||||
|
User.find_each do |user|
|
||||||
|
previous_settings = LegacySetting.where(thing_type: 'User', thing_id: user.id).index_by(&:var)
|
||||||
|
|
||||||
|
user_settings = {}
|
||||||
|
|
||||||
|
MAPPING.each do |legacy_key, new_key|
|
||||||
|
value = previous_settings[legacy_key]&.value
|
||||||
|
|
||||||
|
next if value.blank?
|
||||||
|
|
||||||
|
if value.is_a?(Hash)
|
||||||
|
value.each do |nested_key, nested_value|
|
||||||
|
user_settings[MAPPING[legacy_key][nested_key.to_sym]] = nested_value
|
||||||
|
end
|
||||||
|
else
|
||||||
|
user_settings[new_key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
user.update_column('settings', Oj.dump(user_settings)) # rubocop:disable Rails/SkipsModelValidations
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def down; end
|
||||||
|
end
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2022_12_06_114142) do
|
ActiveRecord::Schema.define(version: 2023_02_15_074423) do
|
||||||
|
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
|
@ -1060,6 +1060,7 @@ ActiveRecord::Schema.define(version: 2022_12_06_114142) do
|
||||||
t.inet "sign_up_ip"
|
t.inet "sign_up_ip"
|
||||||
t.boolean "skip_sign_in_token"
|
t.boolean "skip_sign_in_token"
|
||||||
t.bigint "role_id"
|
t.bigint "role_id"
|
||||||
|
t.text "settings"
|
||||||
t.index ["account_id"], name: "index_users_on_account_id"
|
t.index ["account_id"], name: "index_users_on_account_id"
|
||||||
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
|
t.index ["confirmation_token"], name: "index_users_on_confirmation_token", unique: true
|
||||||
t.index ["created_by_application_id"], name: "index_users_on_created_by_application_id", where: "(created_by_application_id IS NOT NULL)"
|
t.index ["created_by_application_id"], name: "index_users_on_created_by_application_id", where: "(created_by_application_id IS NOT NULL)"
|
||||||
|
|
|
@ -54,7 +54,7 @@ namespace :tests do
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
unless User.find(1).settings.notification_emails['favourite'] == true && User.find(1).settings.notification_emails['mention'] == false
|
unless User.find(1).settings['notification_emails.favourite'] == true && User.find(1).settings['notification_emails.mention'] == false
|
||||||
puts 'User settings not kept as expected'
|
puts 'User settings not kept as expected'
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
|
|
|
@ -46,6 +46,7 @@ describe Api::V1::Accounts::CredentialsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates account info' do
|
it 'updates account info' do
|
||||||
|
user.reload
|
||||||
user.account.reload
|
user.account.reload
|
||||||
|
|
||||||
expect(user.account.display_name).to eq("Alice Isn't Dead")
|
expect(user.account.display_name).to eq("Alice Isn't Dead")
|
||||||
|
|
|
@ -88,21 +88,19 @@ describe ApplicationController, type: :controller do
|
||||||
|
|
||||||
it 'returns instances\'s default theme when user didn\'t set theme' do
|
it 'returns instances\'s default theme when user didn\'t set theme' do
|
||||||
current_user = Fabricate(:user)
|
current_user = Fabricate(:user)
|
||||||
|
current_user.settings.update(theme: 'contrast', noindex: false)
|
||||||
|
current_user.save
|
||||||
sign_in current_user
|
sign_in current_user
|
||||||
|
|
||||||
allow(Setting).to receive(:[]).with('theme').and_return 'contrast'
|
|
||||||
allow(Setting).to receive(:[]).with('noindex').and_return false
|
|
||||||
|
|
||||||
expect(controller.view_context.current_theme).to eq 'contrast'
|
expect(controller.view_context.current_theme).to eq 'contrast'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns user\'s theme when it is set' do
|
it 'returns user\'s theme when it is set' do
|
||||||
current_user = Fabricate(:user)
|
current_user = Fabricate(:user)
|
||||||
current_user.settings['theme'] = 'mastodon-light'
|
current_user.settings.update(theme: 'mastodon-light')
|
||||||
|
current_user.save
|
||||||
sign_in current_user
|
sign_in current_user
|
||||||
|
|
||||||
allow(Setting).to receive(:[]).with('theme').and_return 'contrast'
|
|
||||||
|
|
||||||
expect(controller.view_context.current_theme).to eq 'mastodon-light'
|
expect(controller.view_context.current_theme).to eq 'mastodon-light'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,20 +20,22 @@ describe Settings::Preferences::NotificationsController do
|
||||||
|
|
||||||
describe 'PUT #update' do
|
describe 'PUT #update' do
|
||||||
it 'updates notifications settings' do
|
it 'updates notifications settings' do
|
||||||
user.settings['notification_emails'] = user.settings['notification_emails'].merge('follow' => false)
|
user.settings.update('notification_emails.follow': false, 'interactions.must_be_follower': true)
|
||||||
user.settings['interactions'] = user.settings['interactions'].merge('must_be_follower' => true)
|
user.save
|
||||||
|
|
||||||
put :update, params: {
|
put :update, params: {
|
||||||
user: {
|
user: {
|
||||||
notification_emails: { follow: '1' },
|
settings_attributes: {
|
||||||
interactions: { must_be_follower: '0' },
|
'notification_emails.follow': '1',
|
||||||
|
'interactions.must_be_follower': '0',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(response).to redirect_to(settings_preferences_notifications_path)
|
expect(response).to redirect_to(settings_preferences_notifications_path)
|
||||||
user.reload
|
user.reload
|
||||||
expect(user.settings['notification_emails']['follow']).to be true
|
expect(user.settings['notification_emails.follow']).to be true
|
||||||
expect(user.settings['interactions']['must_be_follower']).to be false
|
expect(user.settings['interactions.must_be_follower']).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,20 +29,22 @@ describe Settings::Preferences::OtherController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'updates user settings' do
|
it 'updates user settings' do
|
||||||
user.settings['boost_modal'] = false
|
user.settings.update('web.reblog_modal': false, 'web.delete_modal': true)
|
||||||
user.settings['delete_modal'] = true
|
user.save
|
||||||
|
|
||||||
put :update, params: {
|
put :update, params: {
|
||||||
user: {
|
user: {
|
||||||
setting_boost_modal: '1',
|
settings_attributes: {
|
||||||
setting_delete_modal: '0',
|
'web.reblog_modal': '1',
|
||||||
|
'web.delete_modal': '0',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(response).to redirect_to(settings_preferences_other_path)
|
expect(response).to redirect_to(settings_preferences_other_path)
|
||||||
user.reload
|
user.reload
|
||||||
expect(user.settings['boost_modal']).to be true
|
expect(user.settings['web.reblog_modal']).to be true
|
||||||
expect(user.settings['delete_modal']).to be false
|
expect(user.settings['web.delete_modal']).to be false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Settings::Extend do
|
|
||||||
class User
|
|
||||||
include Settings::Extend
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#settings' do
|
|
||||||
it 'sets @settings as an instance of Settings::ScopedSettings' do
|
|
||||||
user = Fabricate(:user)
|
|
||||||
expect(user.settings).to be_a Settings::ScopedSettings
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,35 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Settings::ScopedSettings do
|
|
||||||
let(:object) { Fabricate(:user) }
|
|
||||||
let(:scoped_setting) { described_class.new(object) }
|
|
||||||
let(:val) { 'whatever' }
|
|
||||||
let(:methods) { %i(auto_play_gif default_sensitive unfollow_modal boost_modal delete_modal reduce_motion system_font_ui noindex theme) }
|
|
||||||
|
|
||||||
describe '.initialize' do
|
|
||||||
it 'sets @object' do
|
|
||||||
scoped_setting = described_class.new(object)
|
|
||||||
expect(scoped_setting.instance_variable_get(:@object)).to be object
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#method_missing' do
|
|
||||||
it 'sets scoped_setting.method_name = val' do
|
|
||||||
methods.each do |key|
|
|
||||||
scoped_setting.send("#{key}=", val)
|
|
||||||
expect(scoped_setting.send(key)).to eq val
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#[]= and #[]' do
|
|
||||||
it 'sets [key] = val' do
|
|
||||||
methods.each do |key|
|
|
||||||
scoped_setting[key] = val
|
|
||||||
expect(scoped_setting[key]).to eq val
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,84 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
describe UserSettingsDecorator do
|
|
||||||
describe 'update' do
|
|
||||||
let(:user) { Fabricate(:user) }
|
|
||||||
let(:settings) { described_class.new(user) }
|
|
||||||
|
|
||||||
it 'updates the user settings value for email notifications' do
|
|
||||||
values = { 'notification_emails' => { 'follow' => '1' } }
|
|
||||||
|
|
||||||
settings.update(values)
|
|
||||||
expect(user.settings['notification_emails']['follow']).to be true
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates the user settings value for interactions' do
|
|
||||||
values = { 'interactions' => { 'must_be_follower' => '0' } }
|
|
||||||
|
|
||||||
settings.update(values)
|
|
||||||
expect(user.settings['interactions']['must_be_follower']).to be false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates the user settings value for privacy' do
|
|
||||||
values = { 'setting_default_privacy' => 'public' }
|
|
||||||
|
|
||||||
settings.update(values)
|
|
||||||
expect(user.settings['default_privacy']).to eq 'public'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates the user settings value for sensitive' do
|
|
||||||
values = { 'setting_default_sensitive' => '1' }
|
|
||||||
|
|
||||||
settings.update(values)
|
|
||||||
expect(user.settings['default_sensitive']).to be true
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates the user settings value for unfollow modal' do
|
|
||||||
values = { 'setting_unfollow_modal' => '0' }
|
|
||||||
|
|
||||||
settings.update(values)
|
|
||||||
expect(user.settings['unfollow_modal']).to be false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates the user settings value for boost modal' do
|
|
||||||
values = { 'setting_boost_modal' => '1' }
|
|
||||||
|
|
||||||
settings.update(values)
|
|
||||||
expect(user.settings['boost_modal']).to be true
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates the user settings value for delete toot modal' do
|
|
||||||
values = { 'setting_delete_modal' => '0' }
|
|
||||||
|
|
||||||
settings.update(values)
|
|
||||||
expect(user.settings['delete_modal']).to be false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates the user settings value for gif auto play' do
|
|
||||||
values = { 'setting_auto_play_gif' => '0' }
|
|
||||||
|
|
||||||
settings.update(values)
|
|
||||||
expect(user.settings['auto_play_gif']).to be false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'updates the user settings value for system font in UI' do
|
|
||||||
values = { 'setting_system_font_ui' => '0' }
|
|
||||||
|
|
||||||
settings.update(values)
|
|
||||||
expect(user.settings['system_font_ui']).to be false
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'decoerces setting values before applying' do
|
|
||||||
values = {
|
|
||||||
'setting_delete_modal' => 'false',
|
|
||||||
'setting_boost_modal' => 'true',
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.update(values)
|
|
||||||
expect(user.settings['delete_modal']).to be false
|
|
||||||
expect(user.settings['boost_modal']).to be true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
25
spec/models/user_settings/namespace_spec.rb
Normal file
25
spec/models/user_settings/namespace_spec.rb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe UserSettings::Namespace do
|
||||||
|
subject { described_class.new(name) }
|
||||||
|
|
||||||
|
let(:name) { :foo }
|
||||||
|
|
||||||
|
describe '#setting' do
|
||||||
|
before do
|
||||||
|
subject.setting :bar, default: 'baz'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'adds setting to definitions' do
|
||||||
|
expect(subject.definitions[:'foo.bar']).to have_attributes(name: :bar, namespace: :foo, default_value: 'baz')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#definitions' do
|
||||||
|
it 'returns a hash' do
|
||||||
|
expect(subject.definitions).to be_a Hash
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
74
spec/models/user_settings/setting_spec.rb
Normal file
74
spec/models/user_settings/setting_spec.rb
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe UserSettings::Setting do
|
||||||
|
subject { described_class.new(name, options) }
|
||||||
|
|
||||||
|
let(:name) { :foo }
|
||||||
|
let(:options) { { default: default, namespace: namespace } }
|
||||||
|
let(:default) { false }
|
||||||
|
let(:namespace) { nil }
|
||||||
|
|
||||||
|
describe '#default_value' do
|
||||||
|
context 'when default value is a primitive value' do
|
||||||
|
it 'returns default value' do
|
||||||
|
expect(subject.default_value).to eq default
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when default value is a proc' do
|
||||||
|
let(:default) { -> { 'bar' } }
|
||||||
|
|
||||||
|
it 'returns value from proc' do
|
||||||
|
expect(subject.default_value).to eq 'bar'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#type' do
|
||||||
|
it 'returns a type' do
|
||||||
|
expect(subject.type).to be_a ActiveModel::Type::Value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#type_cast' do
|
||||||
|
context 'when default value is a boolean' do
|
||||||
|
let(:default) { false }
|
||||||
|
|
||||||
|
it 'returns boolean' do
|
||||||
|
expect(subject.type_cast('1')).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when default value is a string' do
|
||||||
|
let(:default) { '' }
|
||||||
|
|
||||||
|
it 'returns string' do
|
||||||
|
expect(subject.type_cast(1)).to eq '1'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_a' do
|
||||||
|
it 'returns an array' do
|
||||||
|
expect(subject.to_a).to eq [name, default]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#key' do
|
||||||
|
context 'when there is no namespace' do
|
||||||
|
it 'returnsn a symbol' do
|
||||||
|
expect(subject.key).to eq :foo
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when there is a namespace' do
|
||||||
|
let(:namespace) { :bar }
|
||||||
|
|
||||||
|
it 'returns a symbol' do
|
||||||
|
expect(subject.key).to eq :'bar.foo'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
110
spec/models/user_settings_spec.rb
Normal file
110
spec/models/user_settings_spec.rb
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe UserSettings do
|
||||||
|
subject { described_class.new(json) }
|
||||||
|
|
||||||
|
let(:json) { {} }
|
||||||
|
|
||||||
|
describe '#[]' do
|
||||||
|
context 'when setting is not set' do
|
||||||
|
it 'returns default value' do
|
||||||
|
expect(subject[:always_send_emails]).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when setting is set' do
|
||||||
|
let(:json) { { default_language: 'fr' } }
|
||||||
|
|
||||||
|
it 'returns value' do
|
||||||
|
expect(subject[:default_language]).to eq 'fr'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when setting was not defined' do
|
||||||
|
it 'raises error' do
|
||||||
|
expect { subject[:foo] }.to raise_error UserSettings::KeyError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#[]=' do
|
||||||
|
context 'when value matches type' do
|
||||||
|
before do
|
||||||
|
subject[:always_send_emails] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates value' do
|
||||||
|
expect(subject[:always_send_emails]).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when value needs to be type-cast' do
|
||||||
|
before do
|
||||||
|
subject[:always_send_emails] = '1'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates value with a type-cast' do
|
||||||
|
expect(subject[:always_send_emails]).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#update' do
|
||||||
|
before do
|
||||||
|
subject.update(always_send_emails: true, default_language: 'fr', default_privacy: nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates values' do
|
||||||
|
expect(subject[:always_send_emails]).to be true
|
||||||
|
expect(subject[:default_language]).to eq 'fr'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not set values that are nil' do
|
||||||
|
expect(subject.as_json).to_not include(default_privacy: nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#as_json' do
|
||||||
|
let(:json) { { default_language: 'fr' } }
|
||||||
|
|
||||||
|
it 'returns hash' do
|
||||||
|
expect(subject.as_json).to eq json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.keys' do
|
||||||
|
it 'returns an array' do
|
||||||
|
expect(described_class.keys).to be_a Array
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.definition_for' do
|
||||||
|
context 'when key is defined' do
|
||||||
|
it 'returns a setting' do
|
||||||
|
expect(described_class.definition_for(:always_send_emails)).to be_a UserSettings::Setting
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when key is not defined' do
|
||||||
|
it 'returns nil' do
|
||||||
|
expect(described_class.definition_for(:foo)).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.definition_for?' do
|
||||||
|
context 'when key is defined' do
|
||||||
|
it 'returns true' do
|
||||||
|
expect(described_class.definition_for?(:always_send_emails)).to be true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when key is not defined' do
|
||||||
|
it 'returns false' do
|
||||||
|
expect(described_class.definition_for?(:foo)).to be false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -313,9 +313,9 @@ RSpec.describe User, type: :model do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'settings' do
|
describe 'settings' do
|
||||||
it 'is instance of Settings::ScopedSettings' do
|
it 'is instance of UserSettings' do
|
||||||
user = Fabricate(:user)
|
user = Fabricate(:user)
|
||||||
expect(user.settings).to be_a Settings::ScopedSettings
|
expect(user.settings).to be_a UserSettings
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -379,16 +379,6 @@ RSpec.describe User, type: :model do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it_behaves_like 'Settings-extended' do
|
|
||||||
def create!
|
|
||||||
User.create!(account: Fabricate(:account, user: nil), email: 'foo@mastodon.space', password: 'abcd1234', agreement: true)
|
|
||||||
end
|
|
||||||
|
|
||||||
def fabricate
|
|
||||||
Fabricate(:user)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe 'token_for_app' do
|
describe 'token_for_app' do
|
||||||
let(:user) { Fabricate(:user) }
|
let(:user) { Fabricate(:user) }
|
||||||
let(:app) { Fabricate(:application, owner: user) }
|
let(:app) { Fabricate(:application, owner: user) }
|
||||||
|
|
|
@ -54,7 +54,8 @@ RSpec.describe NotifyService, type: :service do
|
||||||
let(:type) { :mention }
|
let(:type) { :mention }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
user.settings.interactions = user.settings.interactions.merge('must_be_following_dm' => enabled)
|
user.settings.update('interactions.must_be_following_dm': enabled)
|
||||||
|
user.save
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'if recipient is supposed to be following sender' do
|
context 'if recipient is supposed to be following sender' do
|
||||||
|
@ -155,8 +156,8 @@ RSpec.describe NotifyService, type: :service do
|
||||||
before do
|
before do
|
||||||
ActionMailer::Base.deliveries.clear
|
ActionMailer::Base.deliveries.clear
|
||||||
|
|
||||||
notification_emails = user.settings.notification_emails
|
user.settings.update('notification_emails.follow': enabled)
|
||||||
user.settings.notification_emails = notification_emails.merge('follow' => enabled)
|
user.save
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when email notification is enabled' do
|
context 'when email notification is enabled' do
|
||||||
|
|
|
@ -96,7 +96,8 @@ RSpec.describe ReportService, type: :service do
|
||||||
|
|
||||||
before do
|
before do
|
||||||
ActionMailer::Base.deliveries.clear
|
ActionMailer::Base.deliveries.clear
|
||||||
source_account.user.settings.notification_emails['report'] = true
|
source_account.user.settings['notification_emails.report'] = true
|
||||||
|
source_account.user.save
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not send an e-mail' do
|
it 'does not send an e-mail' do
|
||||||
|
|
Loading…
Reference in a new issue