Merge pull request #1310 from ThibG/glitch-soc/merge-upstream
Merge upstream changes
This commit is contained in:
		
						commit
						7061b36c17
					
				
					 75 changed files with 605 additions and 556 deletions
				
			
		
							
								
								
									
										14
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								Gemfile
									
									
									
									
									
								
							|  | @ -69,7 +69,7 @@ gem 'nilsimsa', git: 'https://github.com/witgo/nilsimsa', ref: 'fd184883048b922b | |||
| gem 'nokogiri', '~> 1.10' | ||||
| gem 'nsa', '~> 0.2' | ||||
| gem 'oj', '~> 3.10' | ||||
| gem 'ox', '~> 2.12' | ||||
| gem 'ox', '~> 2.13' | ||||
| gem 'parslet' | ||||
| gem 'parallel', '~> 1.19' | ||||
| gem 'posix-spawn', git: 'https://github.com/rtomayko/posix-spawn', ref: '58465d2e213991f8afb13b984854a49fcdcc980c' | ||||
|  | @ -94,7 +94,7 @@ gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie' | |||
| gem 'stoplight', '~> 2.2.0' | ||||
| gem 'strong_migrations', '~> 0.6' | ||||
| gem 'tty-command', '~> 0.9', require: false | ||||
| gem 'tty-prompt', '~> 0.20', require: false | ||||
| gem 'tty-prompt', '~> 0.21', require: false | ||||
| gem 'twitter-text', '~> 1.14' | ||||
| gem 'tzinfo-data', '~> 1.2019' | ||||
| gem 'webpacker', '~> 4.2' | ||||
|  | @ -112,7 +112,7 @@ group :development, :test do | |||
|   gem 'i18n-tasks', '~> 0.9', require: false | ||||
|   gem 'pry-byebug', '~> 3.8' | ||||
|   gem 'pry-rails', '~> 0.3' | ||||
|   gem 'rspec-rails', '~> 3.9' | ||||
|   gem 'rspec-rails', '~> 4.0' | ||||
| end | ||||
| 
 | ||||
| group :production, :test do | ||||
|  | @ -122,19 +122,19 @@ end | |||
| group :test do | ||||
|   gem 'capybara', '~> 3.31' | ||||
|   gem 'climate_control', '~> 0.2' | ||||
|   gem 'faker', '~> 2.10' | ||||
|   gem 'faker', '~> 2.11' | ||||
|   gem 'microformats', '~> 4.2' | ||||
|   gem 'rails-controller-testing', '~> 1.0' | ||||
|   gem 'rspec-sidekiq', '~> 3.0' | ||||
|   gem 'simplecov', '~> 0.18', require: false | ||||
|   gem 'webmock', '~> 3.8' | ||||
|   gem 'parallel_tests', '~> 2.30' | ||||
|   gem 'parallel_tests', '~> 2.32' | ||||
| end | ||||
| 
 | ||||
| group :development do | ||||
|   gem 'active_record_query_trace', '~> 1.7' | ||||
|   gem 'annotate', '~> 3.0' | ||||
|   gem 'better_errors', '~> 2.5' | ||||
|   gem 'better_errors', '~> 2.6' | ||||
|   gem 'binding_of_caller', '~> 0.7' | ||||
|   gem 'bullet', '~> 6.1' | ||||
|   gem 'letter_opener', '~> 1.7' | ||||
|  | @ -142,7 +142,7 @@ group :development do | |||
|   gem 'memory_profiler' | ||||
|   gem 'rubocop', '~> 0.79', require: false | ||||
|   gem 'rubocop-rails', '~> 2.4', require: false | ||||
|   gem 'brakeman', '~> 4.7', require: false | ||||
|   gem 'brakeman', '~> 4.8', require: false | ||||
|   gem 'bundler-audit', '~> 0.6', require: false | ||||
| 
 | ||||
|   gem 'capistrano', '~> 3.12' | ||||
|  |  | |||
							
								
								
									
										66
									
								
								Gemfile.lock
									
									
									
									
									
								
							
							
						
						
									
										66
									
								
								Gemfile.lock
									
									
									
									
									
								
							|  | @ -108,7 +108,7 @@ GEM | |||
|     aws-sigv4 (1.1.1) | ||||
|       aws-eventstream (~> 1.0, >= 1.0.2) | ||||
|     bcrypt (3.1.12) | ||||
|     better_errors (2.5.1) | ||||
|     better_errors (2.6.0) | ||||
|       coderay (>= 1.0.0) | ||||
|       erubi (>= 1.0.0) | ||||
|       rack (>= 0.9.0) | ||||
|  | @ -116,9 +116,9 @@ GEM | |||
|       debug_inspector (>= 0.0.1) | ||||
|     blurhash (0.1.4) | ||||
|       ffi (~> 1.10.0) | ||||
|     bootsnap (1.4.5) | ||||
|     bootsnap (1.4.6) | ||||
|       msgpack (~> 1.0) | ||||
|     brakeman (4.7.2) | ||||
|     brakeman (4.8.0) | ||||
|     browser (4.0.0) | ||||
|     builder (3.2.4) | ||||
|     bullet (6.1.0) | ||||
|  | @ -166,7 +166,7 @@ GEM | |||
|     cocaine (0.5.8) | ||||
|       climate_control (>= 0.0.3, < 1.0) | ||||
|     coderay (1.1.2) | ||||
|     concurrent-ruby (1.1.5) | ||||
|     concurrent-ruby (1.1.6) | ||||
|     connection_pool (2.2.2) | ||||
|     crack (0.4.3) | ||||
|       safe_yaml (~> 1.0.0) | ||||
|  | @ -218,7 +218,7 @@ GEM | |||
|       tzinfo | ||||
|     excon (0.71.0) | ||||
|     fabrication (2.21.0) | ||||
|     faker (2.10.1) | ||||
|     faker (2.11.0) | ||||
|       i18n (>= 1.6, < 2) | ||||
|     faraday (0.17.3) | ||||
|       multipart-post (>= 1.2, < 3) | ||||
|  | @ -299,19 +299,19 @@ GEM | |||
|       terminal-table (>= 1.5.1) | ||||
|     idn-ruby (0.1.0) | ||||
|     ipaddress (0.8.3) | ||||
|     iso-639 (0.2.8) | ||||
|     iso-639 (0.3.5) | ||||
|     jaro_winkler (1.5.4) | ||||
|     jmespath (1.4.0) | ||||
|     json (2.3.0) | ||||
|     json-canonicalization (0.2.0) | ||||
|     json-ld (3.1.1) | ||||
|     json-ld (3.1.2) | ||||
|       htmlentities (~> 4.3) | ||||
|       json-canonicalization (~> 0.2) | ||||
|       link_header (~> 0.0, >= 0.0.8) | ||||
|       multi_json (~> 1.14) | ||||
|       rack (~> 2.0) | ||||
|       rdf (~> 3.1) | ||||
|     json-ld-preloaded (3.1.1) | ||||
|     json-ld-preloaded (3.1.2) | ||||
|       json-ld (~> 3.1) | ||||
|       rdf (~> 3.1) | ||||
|     jsonapi-renderer (0.2.2) | ||||
|  | @ -365,7 +365,7 @@ GEM | |||
|     mini_mime (1.0.2) | ||||
|     mini_portile2 (2.4.0) | ||||
|     minitest (5.14.0) | ||||
|     msgpack (1.3.1) | ||||
|     msgpack (1.3.3) | ||||
|     multi_json (1.14.1) | ||||
|     multipart-post (2.1.1) | ||||
|     necromancer (0.5.1) | ||||
|  | @ -383,7 +383,7 @@ GEM | |||
|       concurrent-ruby (~> 1.0, >= 1.0.2) | ||||
|       sidekiq (>= 3.5) | ||||
|       statsd-ruby (~> 1.4, >= 1.4.0) | ||||
|     oj (3.10.3) | ||||
|     oj (3.10.5) | ||||
|     omniauth (1.9.1) | ||||
|       hashie (>= 3.4.6) | ||||
|       rack (>= 1.6.2, < 3) | ||||
|  | @ -395,7 +395,7 @@ GEM | |||
|       omniauth (~> 1.3, >= 1.3.2) | ||||
|       ruby-saml (~> 1.7) | ||||
|     orm_adapter (0.5.0) | ||||
|     ox (2.12.1) | ||||
|     ox (2.13.2) | ||||
|     paperclip (6.0.0) | ||||
|       activemodel (>= 4.2.0) | ||||
|       activesupport (>= 4.2.0) | ||||
|  | @ -406,7 +406,7 @@ GEM | |||
|       av (~> 0.9.0) | ||||
|       paperclip (>= 2.5.2) | ||||
|     parallel (1.19.1) | ||||
|     parallel_tests (2.30.1) | ||||
|     parallel_tests (2.32.0) | ||||
|       parallel | ||||
|     parser (2.7.0.5) | ||||
|       ast (~> 2.4.0) | ||||
|  | @ -414,7 +414,7 @@ GEM | |||
|     pastel (0.7.3) | ||||
|       equatable (~> 0.6) | ||||
|       tty-color (~> 0.5) | ||||
|     pg (1.2.2) | ||||
|     pg (1.2.3) | ||||
|     pghero (2.4.1) | ||||
|       activerecord (>= 5) | ||||
|     pkg-config (1.4.1) | ||||
|  | @ -531,14 +531,14 @@ GEM | |||
|     rspec-mocks (3.9.1) | ||||
|       diff-lcs (>= 1.2.0, < 2.0) | ||||
|       rspec-support (~> 3.9.0) | ||||
|     rspec-rails (3.9.1) | ||||
|       actionpack (>= 3.0) | ||||
|       activesupport (>= 3.0) | ||||
|       railties (>= 3.0) | ||||
|       rspec-core (~> 3.9.0) | ||||
|       rspec-expectations (~> 3.9.0) | ||||
|       rspec-mocks (~> 3.9.0) | ||||
|       rspec-support (~> 3.9.0) | ||||
|     rspec-rails (4.0.0) | ||||
|       actionpack (>= 4.2) | ||||
|       activesupport (>= 4.2) | ||||
|       railties (>= 4.2) | ||||
|       rspec-core (~> 3.9) | ||||
|       rspec-expectations (~> 3.9) | ||||
|       rspec-mocks (~> 3.9) | ||||
|       rspec-support (~> 3.9) | ||||
|     rspec-sidekiq (3.0.3) | ||||
|       rspec-core (~> 3.0, >= 3.0.0) | ||||
|       sidekiq (>= 2.4.0) | ||||
|  | @ -577,7 +577,7 @@ GEM | |||
|       sidekiq (>= 3) | ||||
|       thwait | ||||
|       tilt (>= 1.4.0) | ||||
|     sidekiq-unique-jobs (6.0.20) | ||||
|     sidekiq-unique-jobs (6.0.21) | ||||
|       concurrent-ruby (~> 1.0, >= 1.0.5) | ||||
|       sidekiq (>= 4.0, < 7.0) | ||||
|       thor (~> 0) | ||||
|  | @ -616,11 +616,11 @@ GEM | |||
|     thread_safe (0.3.6) | ||||
|     thwait (0.1.0) | ||||
|     tilt (2.0.10) | ||||
|     tty-color (0.5.0) | ||||
|     tty-color (0.5.1) | ||||
|     tty-command (0.9.0) | ||||
|       pastel (~> 0.7.0) | ||||
|     tty-cursor (0.7.0) | ||||
|     tty-prompt (0.20.0) | ||||
|     tty-cursor (0.7.1) | ||||
|     tty-prompt (0.21.0) | ||||
|       necromancer (~> 0.5.0) | ||||
|       pastel (~> 0.7.0) | ||||
|       tty-reader (~> 0.7.0) | ||||
|  | @ -628,7 +628,7 @@ GEM | |||
|       tty-cursor (~> 0.7) | ||||
|       tty-screen (~> 0.7) | ||||
|       wisper (~> 2.0.0) | ||||
|     tty-screen (0.7.0) | ||||
|     tty-screen (0.7.1) | ||||
|     twitter-text (1.14.7) | ||||
|       unf (~> 0.1.0) | ||||
|     tzinfo (1.2.6) | ||||
|  | @ -669,11 +669,11 @@ DEPENDENCIES | |||
|   addressable (~> 2.7) | ||||
|   annotate (~> 3.0) | ||||
|   aws-sdk-s3 (~> 1.61) | ||||
|   better_errors (~> 2.5) | ||||
|   better_errors (~> 2.6) | ||||
|   binding_of_caller (~> 0.7) | ||||
|   blurhash (~> 0.1) | ||||
|   bootsnap (~> 1.4) | ||||
|   brakeman (~> 4.7) | ||||
|   brakeman (~> 4.8) | ||||
|   browser | ||||
|   bullet (~> 6.1) | ||||
|   bundler-audit (~> 0.6) | ||||
|  | @ -696,7 +696,7 @@ DEPENDENCIES | |||
|   dotenv-rails (~> 2.7) | ||||
|   e2mmap (~> 0.1.0) | ||||
|   fabrication (~> 2.21) | ||||
|   faker (~> 2.10) | ||||
|   faker (~> 2.11) | ||||
|   fast_blank (~> 1.0) | ||||
|   fastimage | ||||
|   fog-core (<= 2.1.0) | ||||
|  | @ -734,11 +734,11 @@ DEPENDENCIES | |||
|   omniauth (~> 1.9) | ||||
|   omniauth-cas (~> 1.1) | ||||
|   omniauth-saml (~> 1.10) | ||||
|   ox (~> 2.12) | ||||
|   ox (~> 2.13) | ||||
|   paperclip (~> 6.0) | ||||
|   paperclip-av-transcoder (~> 0.6) | ||||
|   parallel (~> 1.19) | ||||
|   parallel_tests (~> 2.30) | ||||
|   parallel_tests (~> 2.32) | ||||
|   parslet | ||||
|   pg (~> 1.2) | ||||
|   pghero (~> 2.4) | ||||
|  | @ -763,7 +763,7 @@ DEPENDENCIES | |||
|   redis-namespace (~> 1.7) | ||||
|   redis-rails (~> 5.0) | ||||
|   rqrcode (~> 1.1) | ||||
|   rspec-rails (~> 3.9) | ||||
|   rspec-rails (~> 4.0) | ||||
|   rspec-sidekiq (~> 3.0) | ||||
|   rubocop (~> 0.79) | ||||
|   rubocop-rails (~> 2.4) | ||||
|  | @ -785,7 +785,7 @@ DEPENDENCIES | |||
|   thor (~> 0.20) | ||||
|   thwait (~> 0.1.0) | ||||
|   tty-command (~> 0.9) | ||||
|   tty-prompt (~> 0.20) | ||||
|   tty-prompt (~> 0.21) | ||||
|   twitter-text (~> 1.14) | ||||
|   tzinfo-data (~> 1.2019) | ||||
|   webmock (~> 3.8) | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ class Api::V1::StatusesController < Api::BaseController | |||
|   before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only:   [:create, :destroy] | ||||
|   before_action :require_user!, except:  [:show, :context] | ||||
|   before_action :set_status, only:       [:show, :context] | ||||
|   before_action :set_thread, only:       [:create] | ||||
| 
 | ||||
|   override_rate_limit_headers :create, family: :statuses | ||||
| 
 | ||||
|  | @ -36,7 +37,7 @@ class Api::V1::StatusesController < Api::BaseController | |||
|   def create | ||||
|     @status = PostStatusService.new.call(current_user.account, | ||||
|                                          text: status_params[:status], | ||||
|                                          thread: status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id]), | ||||
|                                          thread: @thread, | ||||
|                                          media_ids: status_params[:media_ids], | ||||
|                                          sensitive: status_params[:sensitive], | ||||
|                                          spoiler_text: status_params[:spoiler_text], | ||||
|  | @ -70,6 +71,12 @@ class Api::V1::StatusesController < Api::BaseController | |||
|     raise ActiveRecord::RecordNotFound | ||||
|   end | ||||
| 
 | ||||
|   def set_thread | ||||
|     @thread = status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id]) | ||||
|   rescue ActiveRecord::RecordNotFound | ||||
|     render json: { error: I18n.t('statuses.errors.in_reply_not_found') }, status: 404 | ||||
|   end | ||||
| 
 | ||||
|   def status_params | ||||
|     params.permit( | ||||
|       :status, | ||||
|  |  | |||
|  | @ -29,6 +29,6 @@ class Settings::ImportsController < Settings::BaseController | |||
|   end | ||||
| 
 | ||||
|   def import_params | ||||
|     params.require(:import).permit(:data, :type) | ||||
|     params.require(:import).permit(:data, :type, :mode) | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -370,6 +370,7 @@ export function fetchFollowersFail(id, error) { | |||
|     type: FOLLOWERS_FETCH_FAIL, | ||||
|     id, | ||||
|     error, | ||||
|     skipNotFound: true, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
|  | @ -456,6 +457,7 @@ export function fetchFollowingFail(id, error) { | |||
|     type: FOLLOWING_FETCH_FAIL, | ||||
|     id, | ||||
|     error, | ||||
|     skipNotFound: true, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
|  | @ -545,6 +547,7 @@ export function fetchRelationshipsFail(error) { | |||
|     type: RELATIONSHIPS_FETCH_FAIL, | ||||
|     error, | ||||
|     skipLoading: true, | ||||
|     skipNotFound: true, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -34,11 +34,11 @@ export function showAlert(title = messages.unexpectedTitle, message = messages.u | |||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function showAlertForError(error) { | ||||
| export function showAlertForError(error, skipNotFound = false) { | ||||
|   if (error.response) { | ||||
|     const { data, status, statusText, headers } = error.response; | ||||
| 
 | ||||
|     if (status === 404 || status === 410) { | ||||
|     if (skipNotFound && (status === 404 || status === 410)) { | ||||
|       // Skip these errors as they are reflected in the UI
 | ||||
|       return { type: ALERT_NOOP }; | ||||
|     } | ||||
|  |  | |||
|  | @ -27,4 +27,5 @@ export const fetchAccountIdentityProofsFail = (accountId, err) => ({ | |||
|   type: IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL, | ||||
|   accountId, | ||||
|   err, | ||||
|   skipNotFound: true, | ||||
| }); | ||||
|  |  | |||
|  | @ -165,6 +165,7 @@ export function expandTimelineFail(timeline, error, isLoadingMore) { | |||
|     timeline, | ||||
|     error, | ||||
|     skipLoading: !isLoadingMore, | ||||
|     skipNotFound: timeline.startsWith('account:'), | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -127,15 +127,7 @@ class Poll extends ImmutablePureComponent { | |||
| 
 | ||||
|     return ( | ||||
|       <li key={option.get('title')}> | ||||
|         {showResults && ( | ||||
|           <Motion defaultStyle={{ width: 0 }} style={{ width: spring(percent, { stiffness: 180, damping: 12 }) }}> | ||||
|             {({ width }) => | ||||
|               <span className={classNames('poll__chart', { leading })} style={{ width: `${width}%` }} /> | ||||
|             } | ||||
|           </Motion> | ||||
|         )} | ||||
| 
 | ||||
|         <label className={classNames('poll__text', { selectable: !showResults })}> | ||||
|         <label className={classNames('poll__option', { selectable: !showResults })}> | ||||
|           <input | ||||
|             name='vote-options' | ||||
|             type={poll.get('multiple') ? 'checkbox' : 'radio'} | ||||
|  | @ -157,12 +149,26 @@ class Poll extends ImmutablePureComponent { | |||
|             /> | ||||
|           )} | ||||
|           {showResults && <span className='poll__number'> | ||||
|             {!!voted && <Icon id='check' className='poll__vote__mark' title={intl.formatMessage(messages.voted)} />} | ||||
|             {Math.round(percent)}% | ||||
|           </span>} | ||||
| 
 | ||||
|           <span dangerouslySetInnerHTML={{ __html: titleEmojified }} /> | ||||
|           <span | ||||
|             className='poll__option__text' | ||||
|             dangerouslySetInnerHTML={{ __html: titleEmojified }} | ||||
|           /> | ||||
| 
 | ||||
|           {!!voted && <span className='poll__voted'> | ||||
|             <Icon id='check' className='poll__voted__mark' title={intl.formatMessage(messages.voted)} /> | ||||
|           </span>} | ||||
|         </label> | ||||
| 
 | ||||
|         {showResults && ( | ||||
|           <Motion defaultStyle={{ width: 0 }} style={{ width: spring(percent, { stiffness: 180, damping: 12 }) }}> | ||||
|             {({ width }) => | ||||
|               <span className={classNames('poll__chart', { leading })} style={{ width: `${width}%` }} /> | ||||
|             } | ||||
|           </Motion> | ||||
|         )} | ||||
|       </li> | ||||
|     ); | ||||
|   } | ||||
|  |  | |||
|  | @ -62,7 +62,7 @@ class Option extends React.PureComponent { | |||
| 
 | ||||
|     return ( | ||||
|       <li> | ||||
|         <label className='poll__text editable'> | ||||
|         <label className='poll__option editable'> | ||||
|           <span className={classNames('poll__input', { checkbox: isPollMultiple })} /> | ||||
| 
 | ||||
|           <AutosuggestInput | ||||
|  |  | |||
|  | @ -195,7 +195,7 @@ class Conversation extends ImmutablePureComponent { | |||
|     return ( | ||||
|       <HotKeys handlers={handlers}> | ||||
|         <div className={classNames('conversation focusable muted', { 'conversation--unread': unread })} tabIndex='0'> | ||||
|           <div className='conversation__avatar'> | ||||
|           <div className='conversation__avatar' onClick={this.handleClick} role='presentation'> | ||||
|             <AvatarComposite accounts={accounts} size={48} /> | ||||
|           </div> | ||||
| 
 | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ export default function errorsMiddleware() { | |||
|       const isFail = new RegExp(`${defaultFailSuffix}$`, 'g'); | ||||
| 
 | ||||
|       if (action.type.match(isFail)) { | ||||
|         dispatch(showAlertForError(action.error)); | ||||
|         dispatch(showAlertForError(action.error, action.skipNotFound)); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -344,7 +344,6 @@ export default function compose(state = initialState, action) { | |||
|     }); | ||||
|   case COMPOSE_SPOILERNESS_CHANGE: | ||||
|     return state.withMutations(map => { | ||||
|       map.set('spoiler_text', ''); | ||||
|       map.set('spoiler', !state.get('spoiler')); | ||||
|       map.set('idempotencyKey', uuid()); | ||||
| 
 | ||||
|  |  | |||
|  | @ -51,7 +51,6 @@ | |||
|     @include avatar-radius; | ||||
|     overflow: hidden; | ||||
|     position: relative; | ||||
|     cursor: default; | ||||
| 
 | ||||
|     & div { | ||||
|       @include avatar-radius; | ||||
|  |  | |||
|  | @ -1515,6 +1515,7 @@ | |||
|     padding: 10px; | ||||
|     padding-top: 12px; | ||||
|     position: relative; | ||||
|     cursor: pointer; | ||||
|   } | ||||
| 
 | ||||
|   &__unread { | ||||
|  |  | |||
|  | @ -331,13 +331,11 @@ | |||
|     } | ||||
| 
 | ||||
|     .display-name { | ||||
|       color: $light-text-color; | ||||
| 
 | ||||
|       strong { | ||||
|         color: $inverted-text-color; | ||||
|       } | ||||
| 
 | ||||
|       span { | ||||
|         color: $lighter-text-color; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     .status__content { | ||||
|  |  | |||
|  | @ -14,20 +14,18 @@ | |||
|   } | ||||
| 
 | ||||
|   &__chart { | ||||
|     position: absolute; | ||||
|     top: 0; | ||||
|     left: 0; | ||||
|     height: 100%; | ||||
|     display: inline-block; | ||||
|     border-radius: 4px; | ||||
|     background: darken($ui-primary-color, 14%); | ||||
|     display: block; | ||||
|     background: darken($ui-primary-color, 5%); | ||||
|     height: 5px; | ||||
|     min-width: 1%; | ||||
| 
 | ||||
|     &.leading { | ||||
|       background: $ui-highlight-color; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   &__text { | ||||
|   &__option { | ||||
|     position: relative; | ||||
|     display: flex; | ||||
|     padding: 6px 0; | ||||
|  | @ -35,6 +33,13 @@ | |||
|     cursor: default; | ||||
|     overflow: hidden; | ||||
| 
 | ||||
|     &__text { | ||||
|       display: inline-block; | ||||
|       word-wrap: break-word; | ||||
|       overflow-wrap: break-word; | ||||
|       max-width: calc(100% - 45px - 25px); | ||||
|     } | ||||
| 
 | ||||
|     input[type=radio], | ||||
|     input[type=checkbox] { | ||||
|       display: none; | ||||
|  | @ -119,19 +124,18 @@ | |||
| 
 | ||||
|   &__number { | ||||
|     display: inline-block; | ||||
|     width: 52px; | ||||
|     width: 45px; | ||||
|     font-weight: 700; | ||||
|     padding: 0 10px; | ||||
|     padding-left: 8px; | ||||
|     text-align: right; | ||||
|     margin-top: auto; | ||||
|     margin-bottom: auto; | ||||
|     flex: 0 0 52px; | ||||
|     flex: 0 0 45px; | ||||
|   } | ||||
| 
 | ||||
|   &__vote__mark { | ||||
|     float: left; | ||||
|     line-height: 18px; | ||||
|   &__voted { | ||||
|     padding: 0 5px; | ||||
|     display: inline-block; | ||||
| 
 | ||||
|     &__mark { | ||||
|       font-size: 18px; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   &__footer { | ||||
|  | @ -208,7 +212,7 @@ | |||
|     display: flex; | ||||
|     align-items: center; | ||||
| 
 | ||||
|     .poll__text { | ||||
|     .poll__option { | ||||
|       flex: 0 0 auto; | ||||
|       width: calc(100% - (23px + 6px)); | ||||
|       margin-right: 6px; | ||||
|  |  | |||
|  | @ -396,6 +396,7 @@ export function fetchFollowersFail(id, error) { | |||
|     type: FOLLOWERS_FETCH_FAIL, | ||||
|     id, | ||||
|     error, | ||||
|     skipNotFound: true, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
|  | @ -482,6 +483,7 @@ export function fetchFollowingFail(id, error) { | |||
|     type: FOLLOWING_FETCH_FAIL, | ||||
|     id, | ||||
|     error, | ||||
|     skipNotFound: true, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
|  | @ -571,6 +573,7 @@ export function fetchRelationshipsFail(error) { | |||
|     type: RELATIONSHIPS_FETCH_FAIL, | ||||
|     error, | ||||
|     skipLoading: true, | ||||
|     skipNotFound: true, | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -34,11 +34,11 @@ export function showAlert(title = messages.unexpectedTitle, message = messages.u | |||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export function showAlertForError(error) { | ||||
| export function showAlertForError(error, skipNotFound = false) { | ||||
|   if (error.response) { | ||||
|     const { data, status, statusText, headers } = error.response; | ||||
| 
 | ||||
|     if (status === 404 || status === 410) { | ||||
|     if (skipNotFound && (status === 404 || status === 410)) { | ||||
|       // Skip these errors as they are reflected in the UI
 | ||||
|       return { type: ALERT_NOOP }; | ||||
|     } | ||||
|  |  | |||
|  | @ -27,4 +27,5 @@ export const fetchAccountIdentityProofsFail = (accountId, err) => ({ | |||
|   type: IDENTITY_PROOFS_ACCOUNT_FETCH_FAIL, | ||||
|   accountId, | ||||
|   err, | ||||
|   skipNotFound: true, | ||||
| }); | ||||
|  |  | |||
|  | @ -149,6 +149,7 @@ export function expandTimelineFail(timeline, error, isLoadingMore) { | |||
|     timeline, | ||||
|     error, | ||||
|     skipLoading: !isLoadingMore, | ||||
|     skipNotFound: timeline.startsWith('account:'), | ||||
|   }; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -76,8 +76,9 @@ class ColumnHeader extends React.PureComponent { | |||
| 
 | ||||
|   handlePin = () => { | ||||
|     if (!this.props.pinned) { | ||||
|       this.historyBack(); | ||||
|       this.context.router.history.replace('/'); | ||||
|     } | ||||
| 
 | ||||
|     this.props.onPin(); | ||||
|   } | ||||
| 
 | ||||
|  |  | |||
|  | @ -127,15 +127,7 @@ class Poll extends ImmutablePureComponent { | |||
| 
 | ||||
|     return ( | ||||
|       <li key={option.get('title')}> | ||||
|         {showResults && ( | ||||
|           <Motion defaultStyle={{ width: 0 }} style={{ width: spring(percent, { stiffness: 180, damping: 12 }) }}> | ||||
|             {({ width }) => | ||||
|               <span className={classNames('poll__chart', { leading })} style={{ width: `${width}%` }} /> | ||||
|             } | ||||
|           </Motion> | ||||
|         )} | ||||
| 
 | ||||
|         <label className={classNames('poll__text', { selectable: !showResults })}> | ||||
|         <label className={classNames('poll__option', { selectable: !showResults })}> | ||||
|           <input | ||||
|             name='vote-options' | ||||
|             type={poll.get('multiple') ? 'checkbox' : 'radio'} | ||||
|  | @ -157,12 +149,26 @@ class Poll extends ImmutablePureComponent { | |||
|             /> | ||||
|           )} | ||||
|           {showResults && <span className='poll__number'> | ||||
|             {!!voted && <Icon id='check' className='poll__vote__mark' title={intl.formatMessage(messages.voted)} />} | ||||
|             {Math.round(percent)}% | ||||
|           </span>} | ||||
| 
 | ||||
|           <span dangerouslySetInnerHTML={{ __html: titleEmojified }} /> | ||||
|           <span | ||||
|             className='poll__option__text' | ||||
|             dangerouslySetInnerHTML={{ __html: titleEmojified }} | ||||
|           /> | ||||
| 
 | ||||
|           {!!voted && <span className='poll__voted'> | ||||
|             <Icon id='check' className='poll__voted__mark' title={intl.formatMessage(messages.voted)} /> | ||||
|           </span>} | ||||
|         </label> | ||||
| 
 | ||||
|         {showResults && ( | ||||
|           <Motion defaultStyle={{ width: 0 }} style={{ width: spring(percent, { stiffness: 180, damping: 12 }) }}> | ||||
|             {({ width }) => | ||||
|               <span className={classNames('poll__chart', { leading })} style={{ width: `${width}%` }} /> | ||||
|             } | ||||
|           </Motion> | ||||
|         )} | ||||
|       </li> | ||||
|     ); | ||||
|   } | ||||
|  |  | |||
|  | @ -432,16 +432,10 @@ class Status extends ImmutablePureComponent { | |||
|               </a> | ||||
|             </div> | ||||
| 
 | ||||
|             <StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} collapsable onCollapsedToggle={this.handleCollapsedToggle} /> | ||||
|             <StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} showThread={showThread} onExpandedToggle={this.handleExpandedToggle} collapsable onCollapsedToggle={this.handleCollapsedToggle} /> | ||||
| 
 | ||||
|             {media} | ||||
| 
 | ||||
|             {showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) && ( | ||||
|               <button className='status__content__read-more-button' onClick={this.handleClick}> | ||||
|                 <FormattedMessage id='status.show_thread' defaultMessage='Show thread' /> | ||||
|               </button> | ||||
|             )} | ||||
| 
 | ||||
|             <StatusActionBar status={status} account={account} {...other} /> | ||||
|           </div> | ||||
|         </div> | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ export default class StatusContent extends React.PureComponent { | |||
|   static propTypes = { | ||||
|     status: ImmutablePropTypes.map.isRequired, | ||||
|     expanded: PropTypes.bool, | ||||
|     showThread: PropTypes.bool, | ||||
|     onExpandedToggle: PropTypes.func, | ||||
|     onClick: PropTypes.func, | ||||
|     collapsable: PropTypes.bool, | ||||
|  | @ -181,6 +182,7 @@ export default class StatusContent extends React.PureComponent { | |||
| 
 | ||||
|     const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden; | ||||
|     const renderReadMore = this.props.onClick && status.get('collapsed'); | ||||
|     const renderViewThread = this.props.showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id']); | ||||
| 
 | ||||
|     const content = { __html: status.get('contentHtml') }; | ||||
|     const spoilerContent = { __html: status.get('spoilerHtml') }; | ||||
|  | @ -195,6 +197,12 @@ export default class StatusContent extends React.PureComponent { | |||
|       directionStyle.direction = 'rtl'; | ||||
|     } | ||||
| 
 | ||||
|     const showThreadButton = ( | ||||
|       <button className='status__content__read-more-button' onClick={this.props.onClick}> | ||||
|         <FormattedMessage id='status.show_thread' defaultMessage='Show thread' /> | ||||
|       </button> | ||||
|     ); | ||||
| 
 | ||||
|     const readMoreButton = ( | ||||
|       <button className='status__content__read-more-button' onClick={this.props.onClick} key='read-more'> | ||||
|         <FormattedMessage id='status.read_more' defaultMessage='Read more' /><Icon id='angle-right' fixedWidth /> | ||||
|  | @ -229,6 +237,8 @@ export default class StatusContent extends React.PureComponent { | |||
|           <div tabIndex={!hidden ? 0 : null} className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''}`} style={directionStyle} dangerouslySetInnerHTML={content} /> | ||||
| 
 | ||||
|           {!hidden && !!status.get('poll') && <PollContainer pollId={status.get('poll')} />} | ||||
| 
 | ||||
|           {renderViewThread && showThreadButton} | ||||
|         </div> | ||||
|       ); | ||||
|     } else if (this.props.onClick) { | ||||
|  | @ -237,6 +247,8 @@ export default class StatusContent extends React.PureComponent { | |||
|           <div className='status__content__text status__content__text--visible' style={directionStyle} dangerouslySetInnerHTML={content} /> | ||||
| 
 | ||||
|           {!!status.get('poll') && <PollContainer pollId={status.get('poll')} />} | ||||
| 
 | ||||
|           {renderViewThread && showThreadButton} | ||||
|         </div>, | ||||
|       ]; | ||||
| 
 | ||||
|  | @ -251,6 +263,8 @@ export default class StatusContent extends React.PureComponent { | |||
|           <div className='status__content__text status__content__text--visible' style={directionStyle} dangerouslySetInnerHTML={content} /> | ||||
| 
 | ||||
|           {!!status.get('poll') && <PollContainer pollId={status.get('poll')} />} | ||||
| 
 | ||||
|           {renderViewThread && showThreadButton} | ||||
|         </div> | ||||
|       ); | ||||
|     } | ||||
|  |  | |||
|  | @ -75,7 +75,7 @@ class Option extends React.PureComponent { | |||
| 
 | ||||
|     return ( | ||||
|       <li> | ||||
|         <label className='poll__text editable'> | ||||
|         <label className='poll__option editable'> | ||||
|           <span | ||||
|             className={classNames('poll__input', { checkbox: isPollMultiple })} | ||||
|             onClick={this.handleToggleMultiple} | ||||
|  |  | |||
|  | @ -160,7 +160,7 @@ class Conversation extends ImmutablePureComponent { | |||
|     return ( | ||||
|       <HotKeys handlers={handlers}> | ||||
|         <div className={classNames('conversation focusable muted', { 'conversation--unread': unread })} tabIndex='0'> | ||||
|           <div className='conversation__avatar'> | ||||
|           <div className='conversation__avatar' onClick={this.handleClick} role='presentation'> | ||||
|             <AvatarComposite accounts={accounts} size={48} /> | ||||
|           </div> | ||||
| 
 | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ export default function errorsMiddleware() { | |||
|       const isFail = new RegExp(`${defaultFailSuffix}$`, 'g'); | ||||
| 
 | ||||
|       if (action.type.match(isFail)) { | ||||
|         dispatch(showAlertForError(action.error)); | ||||
|         dispatch(showAlertForError(action.error, action.skipNotFound)); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -261,7 +261,6 @@ export default function compose(state = initialState, action) { | |||
|     }); | ||||
|   case COMPOSE_SPOILERNESS_CHANGE: | ||||
|     return state.withMutations(map => { | ||||
|       map.set('spoiler_text', ''); | ||||
|       map.set('spoiler', !state.get('spoiler')); | ||||
|       map.set('idempotencyKey', uuid()); | ||||
| 
 | ||||
|  |  | |||
|  | @ -142,7 +142,7 @@ html { | |||
| } | ||||
| 
 | ||||
| .compose-form__autosuggest-wrapper, | ||||
| .poll__text input[type="text"], | ||||
| .poll__option input[type="text"], | ||||
| .compose-form .spoiler-input__input, | ||||
| .compose-form__poll-wrapper select, | ||||
| .search__input, | ||||
|  |  | |||
|  | @ -1028,13 +1028,11 @@ | |||
|     } | ||||
| 
 | ||||
|     .display-name { | ||||
|       color: $light-text-color; | ||||
| 
 | ||||
|       strong { | ||||
|         color: $inverted-text-color; | ||||
|       } | ||||
| 
 | ||||
|       span { | ||||
|         color: $light-text-color; | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     .status__content { | ||||
|  | @ -1333,7 +1331,6 @@ | |||
|     border-radius: 50%; | ||||
|     overflow: hidden; | ||||
|     position: relative; | ||||
|     cursor: default; | ||||
| 
 | ||||
|     & > div { | ||||
|       float: left; | ||||
|  | @ -6570,6 +6567,7 @@ noscript { | |||
|     padding: 10px; | ||||
|     padding-top: 12px; | ||||
|     position: relative; | ||||
|     cursor: pointer; | ||||
|   } | ||||
| 
 | ||||
|   &__unread { | ||||
|  |  | |||
|  | @ -8,20 +8,18 @@ | |||
|   } | ||||
| 
 | ||||
|   &__chart { | ||||
|     position: absolute; | ||||
|     top: 0; | ||||
|     left: 0; | ||||
|     height: 100%; | ||||
|     display: inline-block; | ||||
|     border-radius: 4px; | ||||
|     background: darken($ui-primary-color, 14%); | ||||
|     display: block; | ||||
|     background: darken($ui-primary-color, 5%); | ||||
|     height: 5px; | ||||
|     min-width: 1%; | ||||
| 
 | ||||
|     &.leading { | ||||
|       background: $ui-highlight-color; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   &__text { | ||||
|   &__option { | ||||
|     position: relative; | ||||
|     display: flex; | ||||
|     padding: 6px 0; | ||||
|  | @ -29,6 +27,13 @@ | |||
|     cursor: default; | ||||
|     overflow: hidden; | ||||
| 
 | ||||
|     &__text { | ||||
|       display: inline-block; | ||||
|       word-wrap: break-word; | ||||
|       overflow-wrap: break-word; | ||||
|       max-width: calc(100% - 45px - 25px); | ||||
|     } | ||||
| 
 | ||||
|     input[type=radio], | ||||
|     input[type=checkbox] { | ||||
|       display: none; | ||||
|  | @ -112,19 +117,18 @@ | |||
| 
 | ||||
|   &__number { | ||||
|     display: inline-block; | ||||
|     width: 52px; | ||||
|     width: 45px; | ||||
|     font-weight: 700; | ||||
|     padding: 0 10px; | ||||
|     padding-left: 8px; | ||||
|     text-align: right; | ||||
|     margin-top: auto; | ||||
|     margin-bottom: auto; | ||||
|     flex: 0 0 52px; | ||||
|     flex: 0 0 45px; | ||||
|   } | ||||
| 
 | ||||
|   &__vote__mark { | ||||
|     float: left; | ||||
|     line-height: 18px; | ||||
|   &__voted { | ||||
|     padding: 0 5px; | ||||
|     display: inline-block; | ||||
| 
 | ||||
|     &__mark { | ||||
|       font-size: 18px; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   &__footer { | ||||
|  | @ -199,7 +203,7 @@ | |||
|     display: flex; | ||||
|     align-items: center; | ||||
| 
 | ||||
|     .poll__text { | ||||
|     .poll__option { | ||||
|       flex: 0 0 auto; | ||||
|       width: calc(100% - (23px + 6px)); | ||||
|       margin-right: 6px; | ||||
|  |  | |||
|  | @ -64,7 +64,8 @@ class ImportService < BaseService | |||
|   end | ||||
| 
 | ||||
|   def import_relationships!(action, undo_action, overwrite_scope, limit, extra_fields = {}) | ||||
|     items = @data.take(limit).map { |row| [row['Account address']&.strip, Hash[extra_fields.map { |key, header| [key, row[header]&.strip] }]] }.reject { |(id, _)| id.blank? } | ||||
|     local_domain_suffix = "@#{Rails.configuration.x.local_domain}" | ||||
|     items = @data.take(limit).map { |row| [row['Account address']&.strip&.delete_suffix(local_domain_suffix), Hash[extra_fields.map { |key, header| [key, row[header]&.strip] }]] }.reject { |(id, _)| id.blank? } | ||||
| 
 | ||||
|     if @import.overwrite? | ||||
|       presence_hash = items.each_with_object({}) { |(id, extra), mapping| mapping[id] = [true, extra] } | ||||
|  |  | |||
|  | @ -10,10 +10,10 @@ class SearchService < BaseService | |||
|     @resolve = options[:resolve] || false | ||||
| 
 | ||||
|     default_results.tap do |results| | ||||
|       next if @query.blank? | ||||
|       next if @query.blank? || @limit.zero? | ||||
| 
 | ||||
|       if url_query? | ||||
|         results.merge!(url_resource_results) unless url_resource.nil? || (@options[:type].present? && url_resource_symbol != @options[:type].to_sym) | ||||
|         results.merge!(url_resource_results) unless url_resource.nil? || @offset.positive? || (@options[:type].present? && url_resource_symbol != @options[:type].to_sym) | ||||
|       elsif @query.present? | ||||
|         results[:accounts] = perform_accounts_search! if account_searchable? | ||||
|         results[:statuses] = perform_statuses_search! if full_text_searchable? | ||||
|  |  | |||
|  | @ -8,16 +8,16 @@ | |||
|       %li | ||||
|         - if show_results | ||||
|           - percent = total_votes_count > 0 ? 100 * option.votes_count / total_votes_count : 0 | ||||
|           %span.poll__chart{ style: "width: #{percent}%" } | ||||
| 
 | ||||
|           %label.poll__text>< | ||||
|           %label.poll__option>< | ||||
|             %span.poll__number>< | ||||
|               - if own_votes.include?(index) | ||||
|                 %i.poll__vote__mark.fa.fa-check | ||||
|                 %i.poll__voted__mark.fa.fa-check | ||||
|               = percent.round | ||||
|             = Formatter.instance.format_poll_option(status, option, autoplay: autoplay) | ||||
| 
 | ||||
|             %span.poll__chart{ style: "width: #{percent}%" } | ||||
|         - else | ||||
|           %label.poll__text>< | ||||
|           %label.poll__option>< | ||||
|             %span.poll__input{ class: poll.multiple? ? 'checkbox' : nil}>< | ||||
|             = Formatter.instance.format_poll_option(status, option, autoplay: autoplay) | ||||
|   .poll__footer | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ class ActivityPub::DistributePollUpdateWorker | |||
|   include Sidekiq::Worker | ||||
|   include Payloadable | ||||
| 
 | ||||
|   sidekiq_options queue: 'push', unique: :until_executed, retry: 0 | ||||
|   sidekiq_options queue: 'push', lock: :until_executed, retry: 0 | ||||
| 
 | ||||
|   def perform(status_id) | ||||
|     @status  = Status.find(status_id) | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| class ActivityPub::SynchronizeFeaturedCollectionWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options queue: 'pull', unique: :until_executed | ||||
|   sidekiq_options queue: 'pull', lock: :until_executed | ||||
| 
 | ||||
|   def perform(account_id) | ||||
|     ActivityPub::FetchFeaturedCollectionService.new.call(Account.find(account_id)) | ||||
|  |  | |||
|  | @ -1,9 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class AfterRemoteFollowRequestWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options queue: 'pull', retry: 5 | ||||
| 
 | ||||
|   def perform(follow_request_id); end | ||||
| end | ||||
|  | @ -1,9 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class AfterRemoteFollowWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options queue: 'pull', retry: 5 | ||||
| 
 | ||||
|   def perform(follow_id); end | ||||
| end | ||||
|  | @ -1,9 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class NotificationWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options queue: 'push', retry: 5 | ||||
| 
 | ||||
|   def perform(xml, source_account_id, target_account_id); end | ||||
| end | ||||
|  | @ -3,7 +3,7 @@ | |||
| class PollExpirationNotifyWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options unique: :until_executed | ||||
|   sidekiq_options lock: :until_executed | ||||
| 
 | ||||
|   def perform(poll_id) | ||||
|     poll = Poll.find(poll_id) | ||||
|  |  | |||
|  | @ -1,9 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class ProcessingWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options backtrace: true | ||||
| 
 | ||||
|   def perform(account_id, body); end | ||||
| end | ||||
|  | @ -3,7 +3,7 @@ | |||
| class PublishScheduledStatusWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options unique: :until_executed | ||||
|   sidekiq_options lock: :until_executed | ||||
| 
 | ||||
|   def perform(scheduled_status_id) | ||||
|     scheduled_status = ScheduledStatus.find(scheduled_status_id) | ||||
|  |  | |||
|  | @ -1,9 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class Pubsubhubbub::ConfirmationWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options queue: 'push', retry: false | ||||
| 
 | ||||
|   def perform(subscription_id, mode, secret = nil, lease_seconds = nil); end | ||||
| end | ||||
|  | @ -1,9 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class Pubsubhubbub::DeliveryWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options queue: 'push', retry: 3, dead: false | ||||
| 
 | ||||
|   def perform(subscription_id, payload); end | ||||
| end | ||||
|  | @ -1,9 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class Pubsubhubbub::DistributionWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options queue: 'push' | ||||
| 
 | ||||
|   def perform(stream_entry_ids); end | ||||
| end | ||||
|  | @ -1,9 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class Pubsubhubbub::RawDistributionWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options queue: 'push' | ||||
| 
 | ||||
|   def perform(xml, source_account_id); end | ||||
| end | ||||
|  | @ -1,9 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class Pubsubhubbub::SubscribeWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options queue: 'push', retry: 10, unique: :until_executed, dead: false | ||||
| 
 | ||||
|   def perform(account_id); end | ||||
| end | ||||
|  | @ -1,9 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class Pubsubhubbub::UnsubscribeWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options queue: 'push', retry: false, unique: :until_executed, dead: false | ||||
| 
 | ||||
|   def perform(account_id); end | ||||
| end | ||||
|  | @ -3,7 +3,7 @@ | |||
| class RegenerationWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options unique: :until_executed | ||||
|   sidekiq_options lock: :until_executed | ||||
| 
 | ||||
|   def perform(account_id, _ = :home) | ||||
|     account = Account.find(account_id) | ||||
|  |  | |||
|  | @ -1,9 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class RemoteProfileUpdateWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options queue: 'pull' | ||||
| 
 | ||||
|   def perform(account_id, body, resubscribe); end | ||||
| end | ||||
|  | @ -3,7 +3,7 @@ | |||
| class ResolveAccountWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options queue: 'pull', unique: :until_executed | ||||
|   sidekiq_options queue: 'pull', lock: :until_executed | ||||
| 
 | ||||
|   def perform(uri) | ||||
|     ResolveAccountService.new.call(uri) | ||||
|  |  | |||
|  | @ -1,9 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class SalmonWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options backtrace: true | ||||
| 
 | ||||
|   def perform(account_id, body); end | ||||
| end | ||||
|  | @ -3,7 +3,7 @@ | |||
| class Scheduler::BackupCleanupScheduler | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options unique: :until_executed, retry: 0 | ||||
|   sidekiq_options lock: :until_executed, retry: 0 | ||||
| 
 | ||||
|   def perform | ||||
|     old_backups.reorder(nil).find_each(&:destroy!) | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| class Scheduler::DoorkeeperCleanupScheduler | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options unique: :until_executed, retry: 0 | ||||
|   sidekiq_options lock: :until_executed, retry: 0 | ||||
| 
 | ||||
|   def perform | ||||
|     Doorkeeper::AccessToken.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| class Scheduler::EmailScheduler | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options unique: :until_executed, retry: 0 | ||||
|   sidekiq_options lock: :until_executed, retry: 0 | ||||
| 
 | ||||
|   FREQUENCY      = 7.days.freeze | ||||
|   SIGN_IN_OFFSET = 1.day.freeze | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ class Scheduler::FeedCleanupScheduler | |||
|   include Sidekiq::Worker | ||||
|   include Redisable | ||||
| 
 | ||||
|   sidekiq_options unique: :until_executed, retry: 0 | ||||
|   sidekiq_options lock: :until_executed, retry: 0 | ||||
| 
 | ||||
|   def perform | ||||
|     clean_home_feeds! | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ class Scheduler::IpCleanupScheduler | |||
| 
 | ||||
|   RETENTION_PERIOD = 1.year | ||||
| 
 | ||||
|   sidekiq_options unique: :until_executed, retry: 0 | ||||
|   sidekiq_options lock: :until_executed, retry: 0 | ||||
| 
 | ||||
|   def perform | ||||
|     time_ago = RETENTION_PERIOD.ago | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| class Scheduler::MediaCleanupScheduler | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options unique: :until_executed, retry: 0 | ||||
|   sidekiq_options lock: :until_executed, retry: 0 | ||||
| 
 | ||||
|   def perform | ||||
|     unattached_media.find_each(&:destroy) | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| class Scheduler::PgheroScheduler | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options unique: :until_executed, retry: 0 | ||||
|   sidekiq_options lock: :until_executed, retry: 0 | ||||
| 
 | ||||
|   def perform | ||||
|     PgHero.capture_space_stats | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| class Scheduler::ScheduledStatusesScheduler | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options unique: :until_executed, retry: 0 | ||||
|   sidekiq_options lock: :until_executed, retry: 0 | ||||
| 
 | ||||
|   def perform | ||||
|     publish_scheduled_statuses! | ||||
|  |  | |||
|  | @ -1,9 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class Scheduler::SubscriptionsCleanupScheduler | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options unique: :until_executed, retry: 0 | ||||
| 
 | ||||
|   def perform; end | ||||
| end | ||||
|  | @ -1,9 +0,0 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class Scheduler::SubscriptionsScheduler | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options unique: :until_executed, retry: 0 | ||||
| 
 | ||||
|   def perform; end | ||||
| end | ||||
|  | @ -3,7 +3,7 @@ | |||
| class Scheduler::TrendingTagsScheduler | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options unique: :until_executed, retry: 0 | ||||
|   sidekiq_options lock: :until_executed, retry: 0 | ||||
| 
 | ||||
|   def perform | ||||
|     TrendingTags.update! if Setting.trends | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| class Scheduler::UserCleanupScheduler | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options unique: :until_executed, retry: 0 | ||||
|   sidekiq_options lock: :until_executed, retry: 0 | ||||
| 
 | ||||
|   def perform | ||||
|     User.where('confirmed_at is NULL AND confirmation_sent_at <= ?', 2.days.ago).reorder(nil).find_in_batches do |batch| | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| class VerifyAccountLinksWorker | ||||
|   include Sidekiq::Worker | ||||
| 
 | ||||
|   sidekiq_options queue: 'pull', retry: false, unique: :until_executed | ||||
|   sidekiq_options queue: 'pull', retry: false, lock: :until_executed | ||||
| 
 | ||||
|   def perform(account_id) | ||||
|     account = Account.find(account_id) | ||||
|  |  | |||
|  | @ -37,6 +37,7 @@ if Rails.env.production? | |||
|     p.style_src       :self, :unsafe_inline, assets_host | ||||
|     p.media_src       :self, :data, *data_hosts | ||||
|     p.frame_src       :self, :https | ||||
|     p.child_src       :self, :blob, assets_host | ||||
|     p.worker_src      :self, :blob, assets_host | ||||
|     p.connect_src     :self, :blob, :data, Rails.configuration.x.streaming_api_base_url, *data_hosts | ||||
|     p.manifest_src    :self, assets_host | ||||
|  |  | |||
|  | @ -42,6 +42,7 @@ class Rack::Attack | |||
|     /auth/sign_in | ||||
|     /auth | ||||
|     /auth/password | ||||
|     /auth/confirmation | ||||
|   ).freeze | ||||
| 
 | ||||
|   PROTECTED_PATHS_REGEX = Regexp.union(PROTECTED_PATHS.map { |path| /\A#{Regexp.escape(path)}/ }) | ||||
|  |  | |||
|  | @ -13,6 +13,11 @@ Sidekiq.configure_server do |config| | |||
|   config.server_middleware do |chain| | ||||
|     chain.add SidekiqErrorHandler | ||||
|   end | ||||
| 
 | ||||
|   config.death_handlers << lambda do |job, _ex| | ||||
|     digest = job['lock_digest'] | ||||
|     SidekiqUniqueJobs::Digests.delete_by_digest(digest) if digest | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| Sidekiq.configure_client do |config| | ||||
|  |  | |||
|  | @ -1093,6 +1093,8 @@ en: | |||
|     disallowed_hashtags: | ||||
|       one: 'contained a disallowed hashtag: %{tags}' | ||||
|       other: 'contained the disallowed hashtags: %{tags}' | ||||
|     errors: | ||||
|       in_reply_not_found: The status you are trying to reply to does not appear to exist. | ||||
|     language_detection: Automatically detect language | ||||
|     open_in_web: Open in web | ||||
|     over_character_limit: character limit of %{max} exceeded | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'sidekiq/web' | ||||
| require 'sidekiq_unique_jobs/web' | ||||
| require 'sidekiq-scheduler/web' | ||||
| 
 | ||||
| Sidekiq::Web.set :session_secret, Rails.application.secrets[:secret_key_base] | ||||
|  |  | |||
|  | @ -115,7 +115,7 @@ module Mastodon | |||
|       when :filesystem | ||||
|         require 'find' | ||||
| 
 | ||||
|         root_path = ENV.fetch('RAILS_ROOT_PATH', File.join(':rails_root', 'public', 'system')).gsub(':rails_root', Rails.root.to_s) | ||||
|         root_path = ENV.fetch('PAPERCLIP_ROOT_PATH', File.join(':rails_root', 'public', 'system')).gsub(':rails_root', Rails.root.to_s) | ||||
| 
 | ||||
|         Find.find(File.join(*[root_path, prefix].compact)) do |path| | ||||
|           next if File.directory?(path) | ||||
|  |  | |||
							
								
								
									
										18
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								package.json
									
									
									
									
									
								
							|  | @ -65,17 +65,17 @@ | |||
|     "@babel/plugin-proposal-decorators": "^7.8.3", | ||||
|     "@babel/plugin-transform-react-inline-elements": "^7.9.0", | ||||
|     "@babel/plugin-transform-runtime": "^7.9.0", | ||||
|     "@babel/preset-env": "^7.8.3", | ||||
|     "@babel/preset-react": "^7.8.3", | ||||
|     "@babel/preset-env": "^7.9.0", | ||||
|     "@babel/preset-react": "^7.9.4", | ||||
|     "@babel/runtime": "^7.8.4", | ||||
|     "@clusterws/cws": "^0.17.3", | ||||
|     "@gamestdio/websocket": "^0.3.2", | ||||
|     "array-includes": "^3.1.1", | ||||
|     "arrow-key-navigation": "^1.1.0", | ||||
|     "atrament": "0.2.4", | ||||
|     "autoprefixer": "^9.7.4", | ||||
|     "autoprefixer": "^9.7.5", | ||||
|     "axios": "^0.19.2", | ||||
|     "babel-loader": "^8.0.6", | ||||
|     "babel-loader": "^8.1.0", | ||||
|     "babel-plugin-lodash": "^3.3.4", | ||||
|     "babel-plugin-preval": "^5.0.0", | ||||
|     "babel-plugin-react-intl": "^3.4.1", | ||||
|  | @ -127,7 +127,7 @@ | |||
|     "prop-types": "^15.5.10", | ||||
|     "punycode": "^2.1.0", | ||||
|     "@rails/ujs": "^6.0.2", | ||||
|     "react": "^16.12.0", | ||||
|     "react": "^16.13.1", | ||||
|     "react-dom": "^16.13.0", | ||||
|     "react-hotkeys": "^1.1.4", | ||||
|     "react-immutable-proptypes": "^2.2.0", | ||||
|  | @ -157,13 +157,13 @@ | |||
|     "sass": "^1.26.3", | ||||
|     "sass-loader": "^8.0.2", | ||||
|     "stacktrace-js": "^2.0.2", | ||||
|     "stringz": "^2.0.0", | ||||
|     "stringz": "^2.1.0", | ||||
|     "substring-trie": "^1.0.2", | ||||
|     "terser-webpack-plugin": "^2.3.5", | ||||
|     "tesseract.js": "^2.0.0-alpha.16", | ||||
|     "throng": "^4.0.0", | ||||
|     "tiny-queue": "^0.2.1", | ||||
|     "uuid": "^3.4.0", | ||||
|     "uuid": "^7.0.2", | ||||
|     "wavesurfer.js": "^3.3.1", | ||||
|     "webpack": "^4.42.1", | ||||
|     "webpack-assets-manifest": "^3.1.1", | ||||
|  | @ -174,7 +174,7 @@ | |||
|   }, | ||||
|   "devDependencies": { | ||||
|     "babel-eslint": "^10.0.3", | ||||
|     "babel-jest": "^25.1.0", | ||||
|     "babel-jest": "^25.2.4", | ||||
|     "enzyme": "^3.11.0", | ||||
|     "enzyme-adapter-react-16": "^1.15.2", | ||||
|     "eslint": "^6.8.0", | ||||
|  | @ -188,6 +188,6 @@ | |||
|     "react-test-renderer": "^16.13.0", | ||||
|     "sass-lint": "^1.13.1", | ||||
|     "webpack-dev-server": "^3.10.3", | ||||
|     "yargs": "^15.1.0" | ||||
|     "yargs": "^15.3.1" | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -91,10 +91,6 @@ RSpec.describe ImportService, type: :service do | |||
| 
 | ||||
|     let(:csv) { attachment_fixture('mute-imports.txt') } | ||||
| 
 | ||||
|     before do | ||||
|       allow(NotificationWorker).to receive(:perform_async) | ||||
|     end | ||||
| 
 | ||||
|     describe 'when no accounts are followed' do | ||||
|       let(:import) { Import.create(account: account, type: 'following', data: csv) } | ||||
|       it 'follows the listed accounts, including boosts' do | ||||
|  | @ -135,10 +131,6 @@ RSpec.describe ImportService, type: :service do | |||
| 
 | ||||
|     let(:csv) { attachment_fixture('new-following-imports.txt') } | ||||
| 
 | ||||
|     before do | ||||
|       allow(NotificationWorker).to receive(:perform_async) | ||||
|     end | ||||
| 
 | ||||
|     describe 'when no accounts are followed' do | ||||
|       let(:import) { Import.create(account: account, type: 'following', data: csv) } | ||||
|       it 'follows the listed accounts, respecting boosts' do | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue