Add autofollow option to invites (#7805)
* Add autofollow option to invites * Trigger CodeClimate rebuild
This commit is contained in:
		
							parent
							
								
									915f02ce8f
								
							
						
					
					
						commit
						907694df6a
					
				
					 12 changed files with 108 additions and 18 deletions
				
			
		|  | @ -3,6 +3,7 @@ | ||||||
| class Auth::RegistrationsController < Devise::RegistrationsController | class Auth::RegistrationsController < Devise::RegistrationsController | ||||||
|   layout :determine_layout |   layout :determine_layout | ||||||
| 
 | 
 | ||||||
|  |   before_action :set_invite, only: [:new, :create] | ||||||
|   before_action :check_enabled_registrations, only: [:new, :create] |   before_action :check_enabled_registrations, only: [:new, :create] | ||||||
|   before_action :configure_sign_up_params, only: [:create] |   before_action :configure_sign_up_params, only: [:create] | ||||||
|   before_action :set_sessions, only: [:edit, :update] |   before_action :set_sessions, only: [:edit, :update] | ||||||
|  | @ -51,7 +52,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def allowed_registrations? |   def allowed_registrations? | ||||||
|     Setting.open_registrations || (invite_code.present? && Invite.find_by(code: invite_code)&.valid_for_use?) |     Setting.open_registrations || @invite&.valid_for_use? | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def invite_code |   def invite_code | ||||||
|  | @ -68,6 +69,10 @@ class Auth::RegistrationsController < Devise::RegistrationsController | ||||||
|     @instance_presenter = InstancePresenter.new |     @instance_presenter = InstancePresenter.new | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|  |   def set_invite | ||||||
|  |     @invite = invite_code.present? ? Invite.find_by(code: invite_code) : nil | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|   def determine_layout |   def determine_layout | ||||||
|     %w(edit update).include?(action_name) ? 'admin' : 'auth' |     %w(edit update).include?(action_name) ? 'admin' : 'auth' | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  | @ -11,7 +11,7 @@ class InvitesController < ApplicationController | ||||||
|     authorize :invite, :create? |     authorize :invite, :create? | ||||||
| 
 | 
 | ||||||
|     @invites = invites |     @invites = invites | ||||||
|     @invite  = Invite.new(expires_in: 1.day.to_i) |     @invite  = Invite.new | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def create |   def create | ||||||
|  | @ -42,6 +42,6 @@ class InvitesController < ApplicationController | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def resource_params |   def resource_params | ||||||
|     params.require(:invite).permit(:max_uses, :expires_in) |     params.require(:invite).permit(:max_uses, :expires_in, :autofollow) | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -458,23 +458,31 @@ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .account-card { | .account-card { | ||||||
|   padding: 14px 10px; |  | ||||||
|   background: $simple-background-color; |  | ||||||
|   border-radius: 4px; |   border-radius: 4px; | ||||||
|   text-align: left; |   text-align: left; | ||||||
|   box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); |   box-shadow: 0 0 15px rgba($base-shadow-color, 0.2); | ||||||
|  |   background: $simple-background-color; | ||||||
| 
 | 
 | ||||||
|   .detailed-status__display-name { |   &__header { | ||||||
|  |     background-size: cover; | ||||||
|  |     background-position: center center; | ||||||
|  |     height: 90px; | ||||||
|  |     border-radius: 4px 4px 0 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   & > .detailed-status__display-name { | ||||||
|     display: block; |     display: block; | ||||||
|     overflow: hidden; |     overflow: hidden; | ||||||
|     margin-bottom: 15px; |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |     padding: 10px; | ||||||
| 
 | 
 | ||||||
|     &:last-child { |     &:last-child { | ||||||
|       margin-bottom: 0; |       margin-bottom: 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     & > div { |     & > div:first-child { | ||||||
|       float: left; |       flex: 0 0 auto; | ||||||
|       margin-right: 10px; |       margin-right: 10px; | ||||||
|       width: 48px; |       width: 48px; | ||||||
|       height: 48px; |       height: 48px; | ||||||
|  | @ -483,9 +491,11 @@ | ||||||
|     .avatar { |     .avatar { | ||||||
|       display: block; |       display: block; | ||||||
|       border-radius: 4px; |       border-radius: 4px; | ||||||
|  |       margin: 0; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     .display-name { |     .display-name { | ||||||
|  |       flex: 1 0 auto; | ||||||
|       display: block; |       display: block; | ||||||
|       max-width: 100%; |       max-width: 100%; | ||||||
|       overflow: hidden; |       overflow: hidden; | ||||||
|  | @ -493,6 +503,10 @@ | ||||||
|       text-overflow: ellipsis; |       text-overflow: ellipsis; | ||||||
|       cursor: default; |       cursor: default; | ||||||
| 
 | 
 | ||||||
|  |       & > .detailed-status__display-name { | ||||||
|  |         margin-bottom: 0; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|       strong { |       strong { | ||||||
|         font-weight: 500; |         font-weight: 500; | ||||||
|         color: $ui-base-color; |         color: $ui-base-color; | ||||||
|  | @ -519,9 +533,28 @@ | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   .account__header__content { |   .counter { | ||||||
|     font-size: 14px; |     box-sizing: border-box; | ||||||
|     color: $inverted-text-color; |     flex: 0 0 auto; | ||||||
|  |     color: $light-text-color; | ||||||
|  |     padding: 0 10px; | ||||||
|  |     cursor: default; | ||||||
|  |     text-align: center; | ||||||
|  |     position: relative; | ||||||
|  |     line-height: 24px; | ||||||
|  | 
 | ||||||
|  |     .counter-label { | ||||||
|  |       font-size: 12px; | ||||||
|  |       display: block; | ||||||
|  |       text-transform: uppercase; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     .counter-number { | ||||||
|  |       font-weight: 500; | ||||||
|  |       font-size: 16px; | ||||||
|  |       color: $inverted-text-color; | ||||||
|  |       font-family: 'mastodon-font-display', sans-serif; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ | ||||||
| #  uses       :integer          default(0), not null | #  uses       :integer          default(0), not null | ||||||
| #  created_at :datetime         not null | #  created_at :datetime         not null | ||||||
| #  updated_at :datetime         not null | #  updated_at :datetime         not null | ||||||
|  | #  autofollow :boolean          default(FALSE), not null | ||||||
| # | # | ||||||
| 
 | 
 | ||||||
| class Invite < ApplicationRecord | class Invite < ApplicationRecord | ||||||
|  |  | ||||||
|  | @ -2,13 +2,25 @@ | ||||||
| 
 | 
 | ||||||
| class BootstrapTimelineService < BaseService | class BootstrapTimelineService < BaseService | ||||||
|   def call(source_account) |   def call(source_account) | ||||||
|     bootstrap_timeline_accounts.each do |target_account| |     @source_account = source_account | ||||||
|       FollowService.new.call(source_account, target_account) | 
 | ||||||
|     end |     autofollow_inviter! | ||||||
|  |     autofollow_bootstrap_timeline_accounts! | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   private |   private | ||||||
| 
 | 
 | ||||||
|  |   def autofollow_inviter! | ||||||
|  |     return unless @source_account&.user&.invite&.autofollow? | ||||||
|  |     FollowService.new.call(@source_account, @source_account.user.invite.user.account) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def autofollow_bootstrap_timeline_accounts! | ||||||
|  |     bootstrap_timeline_accounts.each do |target_account| | ||||||
|  |       FollowService.new.call(@source_account, target_account) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|   def bootstrap_timeline_accounts |   def bootstrap_timeline_accounts | ||||||
|     return @bootstrap_timeline_accounts if defined?(@bootstrap_timeline_accounts) |     return @bootstrap_timeline_accounts if defined?(@bootstrap_timeline_accounts) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -7,6 +7,11 @@ | ||||||
| = simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| | = simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| | ||||||
|   = render 'shared/error_messages', object: resource |   = render 'shared/error_messages', object: resource | ||||||
| 
 | 
 | ||||||
|  |   - if @invite.present? && @invite.autofollow? | ||||||
|  |     .fields-group{ style: 'margin-bottom: 30px' } | ||||||
|  |       %p.hint{ style: 'text-align: center' }= t('invites.invited_by') | ||||||
|  |       = render 'authorize_follows/card', account: @invite.user.account | ||||||
|  | 
 | ||||||
|   = f.simple_fields_for :account do |ff| |   = f.simple_fields_for :account do |ff| | ||||||
|     .input-with-append |     .input-with-append | ||||||
|       = ff.input :username, autofocus: true, placeholder: t('simple_form.labels.defaults.username'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off' } |       = ff.input :username, autofocus: true, placeholder: t('simple_form.labels.defaults.username'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off' } | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| .account-card | .account-card | ||||||
|  |   .account-card__header{ style: "background-image: url(#{account.header.url(:original)})" } | ||||||
|   .detailed-status__display-name |   .detailed-status__display-name | ||||||
|     %div |     %div | ||||||
|       = image_tag account.avatar.url(:original), alt: '', width: 48, height: 48, class: 'avatar' |       = image_tag account.avatar.url(:original), alt: '', width: 48, height: 48, class: 'avatar' | ||||||
|  | @ -9,5 +10,14 @@ | ||||||
|         %strong.emojify= display_name(account, custom_emojify: true) |         %strong.emojify= display_name(account, custom_emojify: true) | ||||||
|         %span @#{account.acct} |         %span @#{account.acct} | ||||||
| 
 | 
 | ||||||
|   - if account.note? |     .counter | ||||||
|     .account__header__content.emojify= Formatter.instance.simplified_format(account) |       %span.counter-number= number_to_human account.statuses_count, strip_insignificant_zeros: true | ||||||
|  |       %span.counter-label= t('accounts.posts') | ||||||
|  | 
 | ||||||
|  |     .counter | ||||||
|  |       %span.counter-number= number_to_human account.following_count, strip_insignificant_zeros: true | ||||||
|  |       %span.counter-label= t('accounts.following') | ||||||
|  | 
 | ||||||
|  |     .counter | ||||||
|  |       %span.counter-number= number_to_human account.followers_count, strip_insignificant_zeros: true | ||||||
|  |       %span.counter-label= t('accounts.followers') | ||||||
|  |  | ||||||
|  | @ -5,5 +5,8 @@ | ||||||
|     = f.input :max_uses, wrapper: :with_label, collection: [1, 5, 10, 25, 50, 100], label_method: lambda { |num| I18n.t('invites.max_uses', count: num) }, prompt: I18n.t('invites.max_uses_prompt') |     = f.input :max_uses, wrapper: :with_label, collection: [1, 5, 10, 25, 50, 100], label_method: lambda { |num| I18n.t('invites.max_uses', count: num) }, prompt: I18n.t('invites.max_uses_prompt') | ||||||
|     = f.input :expires_in, wrapper: :with_label, collection: [30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week].map(&:to_i), label_method: lambda { |i| I18n.t("invites.expires_in.#{i}") }, prompt: I18n.t('invites.expires_in_prompt') |     = f.input :expires_in, wrapper: :with_label, collection: [30.minutes, 1.hour, 6.hours, 12.hours, 1.day, 1.week].map(&:to_i), label_method: lambda { |i| I18n.t("invites.expires_in.#{i}") }, prompt: I18n.t('invites.expires_in_prompt') | ||||||
| 
 | 
 | ||||||
|  |   .fields-group | ||||||
|  |     = f.input :autofollow, wrapper: :with_label | ||||||
|  | 
 | ||||||
|   .actions |   .actions | ||||||
|     = f.button :button, t('invites.generate'), type: :submit |     = f.button :button, t('invites.generate'), type: :submit | ||||||
|  |  | ||||||
|  | @ -514,6 +514,7 @@ en: | ||||||
|       '86400': 1 day |       '86400': 1 day | ||||||
|     expires_in_prompt: Never |     expires_in_prompt: Never | ||||||
|     generate: Generate |     generate: Generate | ||||||
|  |     invited_by: 'You were invited by:' | ||||||
|     max_uses: |     max_uses: | ||||||
|       one: 1 use |       one: 1 use | ||||||
|       other: "%{count} uses" |       other: "%{count} uses" | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ en: | ||||||
|   simple_form: |   simple_form: | ||||||
|     hints: |     hints: | ||||||
|       defaults: |       defaults: | ||||||
|  |         autofollow: People who sign up through the invite will automatically follow you | ||||||
|         avatar: PNG, GIF or JPG. At most 2MB. Will be downscaled to 400x400px |         avatar: PNG, GIF or JPG. At most 2MB. Will be downscaled to 400x400px | ||||||
|         bot: This account mainly performs automated actions and might not be monitored |         bot: This account mainly performs automated actions and might not be monitored | ||||||
|         digest: Only sent after a long period of inactivity and only if you have received any personal messages in your absence |         digest: Only sent after a long period of inactivity and only if you have received any personal messages in your absence | ||||||
|  | @ -30,6 +31,7 @@ en: | ||||||
|           name: Label |           name: Label | ||||||
|           value: Content |           value: Content | ||||||
|       defaults: |       defaults: | ||||||
|  |         autofollow: Invite to follow your account | ||||||
|         avatar: Avatar |         avatar: Avatar | ||||||
|         bot: This is a bot account |         bot: This is a bot account | ||||||
|         confirm_new_password: Confirm new password |         confirm_new_password: Confirm new password | ||||||
|  |  | ||||||
							
								
								
									
										17
									
								
								db/migrate/20180615122121_add_autofollow_to_invites.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								db/migrate/20180615122121_add_autofollow_to_invites.rb
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | ||||||
|  | require Rails.root.join('lib', 'mastodon', 'migration_helpers') | ||||||
|  | 
 | ||||||
|  | class AddAutofollowToInvites < ActiveRecord::Migration[5.2] | ||||||
|  |   include Mastodon::MigrationHelpers | ||||||
|  | 
 | ||||||
|  |   disable_ddl_transaction! | ||||||
|  | 
 | ||||||
|  |   def change | ||||||
|  |     safety_assured do | ||||||
|  |       add_column_with_default :invites, :autofollow, :bool, default: false, allow_null: false | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def down | ||||||
|  |     remove_column :invites, :autofollow | ||||||
|  |   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: 2018_06_09_104432) do | ActiveRecord::Schema.define(version: 2018_06_15_122121) 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" | ||||||
|  | @ -219,6 +219,7 @@ ActiveRecord::Schema.define(version: 2018_06_09_104432) do | ||||||
|     t.integer "uses", default: 0, null: false |     t.integer "uses", default: 0, null: false | ||||||
|     t.datetime "created_at", null: false |     t.datetime "created_at", null: false | ||||||
|     t.datetime "updated_at", null: false |     t.datetime "updated_at", null: false | ||||||
|  |     t.boolean "autofollow", default: false, null: false | ||||||
|     t.index ["code"], name: "index_invites_on_code", unique: true |     t.index ["code"], name: "index_invites_on_code", unique: true | ||||||
|     t.index ["user_id"], name: "index_invites_on_user_id" |     t.index ["user_id"], name: "index_invites_on_user_id" | ||||||
|   end |   end | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue