Compare commits
	
		
			69 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 72f9eab3d6 | ||
|  | 0b7a0d15c7 | ||
|  | 80b3ca0f6f | ||
|  | 45afdf1781 | ||
|  | 79b34a0fa2 | ||
|  | 872a0d5bd8 | ||
|  | 01421999ae | ||
|  | 0b888acfd4 | ||
|  | 238de58e65 | ||
|  | 7233ac07d2 | ||
|  | b1e03197fa | ||
|  | 7be53a10b0 | ||
|  | a0de3222dd | ||
|  | 540b3f37ae | ||
|  | 852b48295f | ||
|  | 9b3b40df66 | ||
|  | d799921c75 | ||
|  | e56404be41 | ||
|  | 7badad7797 | ||
|  | 59797ee233 | ||
|  | fbe7756da6 | ||
|  | 0a103c7749 | ||
|  | fb16c37d2a | ||
|  | 6f244ba82c | ||
|  | ea75ae2d1f | ||
|  | acb982fc66 | ||
|  | eed7484cd6 | ||
|  | 02194838dd | ||
|  | 3323b4173e | ||
|  | 9a28052e92 | ||
|  | e6fd4bea35 | ||
|  | 5276c0a090 | ||
|  | 7861c5f108 | ||
|  | 3987bd18a4 | ||
|  | 74c1c9ec01 | ||
|  | 537d2939b1 | ||
|  | 2091ae92be | ||
|  | dcc614f869 | ||
|  | ed867eca9d | ||
|  | 08e4c78e78 | ||
|  | 704053d221 | ||
|  | 35b84985a8 | ||
|  | d41f0b66cc | ||
|  | 921b781909 | ||
|  | 6f5c0afe93 | ||
|  | eec6095e02 | ||
|  | 9f04b0d4b1 | ||
|  | 628358aeea | ||
|  | c235711ffe | ||
|  | ff6ca8bdc6 | ||
|  | dbda87c31f | ||
|  | e4a241abef | ||
|  | 93555182c3 | ||
|  | 0eff42d688 | ||
|  | 1d92b90be9 | ||
|  | da809f9eec | ||
|  | c4d36d024c | ||
|  | 9e97fbf0af | ||
|  | 10f6793fd0 | ||
|  | a594139115 | ||
|  | 95bd85d9e8 | ||
|  | 8d51ce4290 | ||
|  | 06636c6eca | ||
|  | e9822a4e4e | ||
|  | 9a61b0ef22 | ||
|  | d872902997 | ||
|  | 5ec25ff3e1 | ||
|  | 49e296e1b0 | ||
|  | 7347d4f8bb | 
| @@ -27,6 +27,7 @@ plugins: | |||||||
|     enabled: true |     enabled: true | ||||||
|   eslint: |   eslint: | ||||||
|     enabled: true |     enabled: true | ||||||
|  |     channel: eslint-4 | ||||||
|   rubocop: |   rubocop: | ||||||
|     enabled: true |     enabled: true | ||||||
|   scss-lint: |   scss-lint: | ||||||
|   | |||||||
| @@ -17,11 +17,9 @@ plugins: | |||||||
| parserOptions: | parserOptions: | ||||||
|   sourceType: module |   sourceType: module | ||||||
|   ecmaFeatures: |   ecmaFeatures: | ||||||
|     arrowFunctions: true |     experimentalObjectRestSpread: true | ||||||
|     jsx: true |     jsx: true | ||||||
|     destructuring: true |   ecmaVersion: 2018 | ||||||
|     modules: true |  | ||||||
|     spread: true |  | ||||||
|  |  | ||||||
| settings: | settings: | ||||||
|   import/extensions: |   import/extensions: | ||||||
| @@ -109,6 +107,7 @@ rules: | |||||||
|   react/self-closing-comp: error |   react/self-closing-comp: error | ||||||
|  |  | ||||||
|   jsx-a11y/accessible-emoji: warn |   jsx-a11y/accessible-emoji: warn | ||||||
|  |   jsx-a11y/alt-text: warn | ||||||
|   jsx-a11y/anchor-has-content: warn |   jsx-a11y/anchor-has-content: warn | ||||||
|   jsx-a11y/aria-activedescendant-has-tabindex: warn |   jsx-a11y/aria-activedescendant-has-tabindex: warn | ||||||
|   jsx-a11y/aria-props: warn |   jsx-a11y/aria-props: warn | ||||||
| @@ -119,16 +118,22 @@ rules: | |||||||
|   jsx-a11y/href-no-hash: warn |   jsx-a11y/href-no-hash: warn | ||||||
|   jsx-a11y/html-has-lang: warn |   jsx-a11y/html-has-lang: warn | ||||||
|   jsx-a11y/iframe-has-title: warn |   jsx-a11y/iframe-has-title: warn | ||||||
|   jsx-a11y/img-has-alt: warn |  | ||||||
|   jsx-a11y/img-redundant-alt: warn |   jsx-a11y/img-redundant-alt: warn | ||||||
|  |   jsx-a11y/interactive-supports-focus: warn | ||||||
|   jsx-a11y/label-has-for: off |   jsx-a11y/label-has-for: off | ||||||
|   jsx-a11y/mouse-events-have-key-events: warn |   jsx-a11y/mouse-events-have-key-events: warn | ||||||
|   jsx-a11y/no-access-key: warn |   jsx-a11y/no-access-key: warn | ||||||
|   jsx-a11y/no-distracting-elements: warn |   jsx-a11y/no-distracting-elements: warn | ||||||
|  |   jsx-a11y/no-noninteractive-element-interactions: | ||||||
|  |   - warn | ||||||
|  |   - handlers: | ||||||
|  |     - onClick | ||||||
|   jsx-a11y/no-onchange: warn |   jsx-a11y/no-onchange: warn | ||||||
|   jsx-a11y/no-redundant-roles: warn |   jsx-a11y/no-redundant-roles: warn | ||||||
|   jsx-a11y/onclick-has-focus: warn |   jsx-a11y/no-static-element-interactions: | ||||||
|   jsx-a11y/onclick-has-role: warn |   - warn | ||||||
|  |   - handlers: | ||||||
|  |     - onClick | ||||||
|   jsx-a11y/role-has-required-aria-props: warn |   jsx-a11y/role-has-required-aria-props: warn | ||||||
|   jsx-a11y/role-supports-aria-props: off |   jsx-a11y/role-supports-aria-props: off | ||||||
|   jsx-a11y/scope: warn |   jsx-a11y/scope: warn | ||||||
|   | |||||||
| @@ -1 +1 @@ | |||||||
| 2.4.2 | 2.5.0 | ||||||
|   | |||||||
| @@ -9,6 +9,9 @@ cache: | |||||||
|   - tmp/cache/babel-loader |   - tmp/cache/babel-loader | ||||||
| dist: trusty | dist: trusty | ||||||
| sudo: required | sudo: required | ||||||
|  | branches: | ||||||
|  |   only: | ||||||
|  |   - master | ||||||
|  |  | ||||||
| notifications: | notifications: | ||||||
|   email: false |   email: false | ||||||
| @@ -37,20 +40,20 @@ addons: | |||||||
|     - yarn |     - yarn | ||||||
|  |  | ||||||
| rvm: | rvm: | ||||||
|   - 2.3.4 |  | ||||||
|   - 2.4.2 |   - 2.4.2 | ||||||
|  |   - 2.5.0 | ||||||
|  |  | ||||||
| services: | services: | ||||||
|   - redis-server |   - redis-server | ||||||
|  |  | ||||||
| install: | install: | ||||||
|  |   - gem update --system | ||||||
|   - nvm install |   - nvm install | ||||||
|   - bundle install --path=vendor/bundle --without development production --retry=3 --jobs=16 |   - bundle install --path=vendor/bundle --without development production --retry=3 --jobs=16 | ||||||
|   - yarn install |   - yarn install | ||||||
|  |  | ||||||
| before_script: | before_script: | ||||||
|   - bundle exec rake parallel:create parallel:load_schema parallel:prepare |   - ./bin/rails parallel:create parallel:load_schema parallel:prepare assets:precompile | ||||||
|   - bundle exec rails assets:precompile |  | ||||||
|   - ln -s /usr/bin/x86_64-linux-gnu-g++-6 "$HOME/g++" |   - ln -s /usr/bin/x86_64-linux-gnu-g++-6 "$HOME/g++" | ||||||
|  |  | ||||||
| script: | script: | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| FROM ruby:2.4.2-alpine3.6 | FROM ruby:2.5.0-alpine3.7 | ||||||
|  |  | ||||||
| LABEL maintainer="https://github.com/tootsuite/mastodon" \ | LABEL maintainer="https://github.com/tootsuite/mastodon" \ | ||||||
|       description="A GNU Social-compatible microblogging server" |       description="A GNU Social-compatible microblogging server" | ||||||
| @@ -40,6 +40,7 @@ RUN apk -U upgrade \ | |||||||
|     protobuf \ |     protobuf \ | ||||||
|     su-exec \ |     su-exec \ | ||||||
|     tini \ |     tini \ | ||||||
|  |     tzdata \ | ||||||
|  && update-ca-certificates \ |  && update-ca-certificates \ | ||||||
|  && mkdir -p /tmp/src /opt \ |  && mkdir -p /tmp/src /opt \ | ||||||
|  && wget -O yarn.tar.gz "https://github.com/yarnpkg/yarn/releases/download/v$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \ |  && wget -O yarn.tar.gz "https://github.com/yarnpkg/yarn/releases/download/v$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \ | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								Gemfile
									
									
									
									
									
								
							
							
						
						| @@ -1,7 +1,7 @@ | |||||||
| # frozen_string_literal: true | # frozen_string_literal: true | ||||||
|  |  | ||||||
| source 'https://rubygems.org' | source 'https://rubygems.org' | ||||||
| ruby '>= 2.3.0', '< 2.5.0' | ruby '>= 2.3.0', '< 2.6.0' | ||||||
|  |  | ||||||
| gem 'pkg-config', '~> 1.2' | gem 'pkg-config', '~> 1.2' | ||||||
|  |  | ||||||
| @@ -28,7 +28,7 @@ gem 'browser' | |||||||
| gem 'charlock_holmes', '~> 0.7.5' | gem 'charlock_holmes', '~> 0.7.5' | ||||||
| gem 'iso-639' | gem 'iso-639' | ||||||
| gem 'cld3', '~> 3.2.0' | gem 'cld3', '~> 3.2.0' | ||||||
| gem 'devise', '~> 4.3' | gem 'devise', '~> 4.4' | ||||||
| gem 'devise-two-factor', '~> 3.0' | gem 'devise-two-factor', '~> 3.0' | ||||||
| gem 'doorkeeper', '~> 4.2' | gem 'doorkeeper', '~> 4.2' | ||||||
| gem 'fast_blank', '~> 1.0' | gem 'fast_blank', '~> 1.0' | ||||||
| @@ -49,6 +49,7 @@ gem 'oj', '~> 3.3' | |||||||
| gem 'ostatus2', '~> 2.0' | gem 'ostatus2', '~> 2.0' | ||||||
| gem 'ox', '~> 2.8' | gem 'ox', '~> 2.8' | ||||||
| gem 'pundit', '~> 1.1' | gem 'pundit', '~> 1.1' | ||||||
|  | gem 'premailer-rails' | ||||||
| gem 'rack-attack', '~> 5.0' | gem 'rack-attack', '~> 5.0' | ||||||
| gem 'rack-cors', '~> 0.4', require: 'rack/cors' | gem 'rack-cors', '~> 0.4', require: 'rack/cors' | ||||||
| gem 'rack-timeout', '~> 0.4' | gem 'rack-timeout', '~> 0.4' | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								Gemfile.lock
									
									
									
									
									
								
							
							
						
						| @@ -110,7 +110,7 @@ GEM | |||||||
|       activesupport |       activesupport | ||||||
|     charlock_holmes (0.7.5) |     charlock_holmes (0.7.5) | ||||||
|     chunky_png (1.3.8) |     chunky_png (1.3.8) | ||||||
|     cld3 (3.2.1) |     cld3 (3.2.2) | ||||||
|       ffi (>= 1.1.0, < 1.10.0) |       ffi (>= 1.1.0, < 1.10.0) | ||||||
|     climate_control (0.2.0) |     climate_control (0.2.0) | ||||||
|     cocaine (0.5.8) |     cocaine (0.5.8) | ||||||
| @@ -122,8 +122,10 @@ GEM | |||||||
|     crack (0.4.3) |     crack (0.4.3) | ||||||
|       safe_yaml (~> 1.0.0) |       safe_yaml (~> 1.0.0) | ||||||
|     crass (1.0.3) |     crass (1.0.3) | ||||||
|  |     css_parser (1.6.0) | ||||||
|  |       addressable | ||||||
|     debug_inspector (0.0.3) |     debug_inspector (0.0.3) | ||||||
|     devise (4.3.0) |     devise (4.4.0) | ||||||
|       bcrypt (~> 3.0) |       bcrypt (~> 3.0) | ||||||
|       orm_adapter (~> 0.1) |       orm_adapter (~> 0.1) | ||||||
|       railties (>= 4.1.0, < 5.2) |       railties (>= 4.1.0, < 5.2) | ||||||
| @@ -324,6 +326,13 @@ GEM | |||||||
|       activerecord |       activerecord | ||||||
|     pkg-config (1.2.8) |     pkg-config (1.2.8) | ||||||
|     powerpack (0.1.1) |     powerpack (0.1.1) | ||||||
|  |     premailer (1.11.1) | ||||||
|  |       addressable | ||||||
|  |       css_parser (>= 1.6.0) | ||||||
|  |       htmlentities (>= 4.0.0) | ||||||
|  |     premailer-rails (1.10.1) | ||||||
|  |       actionmailer (>= 3, < 6) | ||||||
|  |       premailer (~> 1.7, >= 1.7.9) | ||||||
|     pry (0.11.3) |     pry (0.11.3) | ||||||
|       coderay (~> 1.1.0) |       coderay (~> 1.1.0) | ||||||
|       method_source (~> 0.9.0) |       method_source (~> 0.9.0) | ||||||
| @@ -559,7 +568,7 @@ DEPENDENCIES | |||||||
|   charlock_holmes (~> 0.7.5) |   charlock_holmes (~> 0.7.5) | ||||||
|   cld3 (~> 3.2.0) |   cld3 (~> 3.2.0) | ||||||
|   climate_control (~> 0.2) |   climate_control (~> 0.2) | ||||||
|   devise (~> 4.3) |   devise (~> 4.4) | ||||||
|   devise-two-factor (~> 3.0) |   devise-two-factor (~> 3.0) | ||||||
|   doorkeeper (~> 4.2) |   doorkeeper (~> 4.2) | ||||||
|   dotenv-rails (~> 2.2) |   dotenv-rails (~> 2.2) | ||||||
| @@ -600,6 +609,7 @@ DEPENDENCIES | |||||||
|   pg (~> 0.20) |   pg (~> 0.20) | ||||||
|   pghero (~> 1.7) |   pghero (~> 1.7) | ||||||
|   pkg-config (~> 1.2) |   pkg-config (~> 1.2) | ||||||
|  |   premailer-rails | ||||||
|   pry-rails (~> 0.3) |   pry-rails (~> 0.3) | ||||||
|   puma (~> 3.10) |   puma (~> 3.10) | ||||||
|   pundit (~> 1.1) |   pundit (~> 1.1) | ||||||
| @@ -639,7 +649,7 @@ DEPENDENCIES | |||||||
|   webpush |   webpush | ||||||
|  |  | ||||||
| RUBY VERSION | RUBY VERSION | ||||||
|    ruby 2.4.2p198 |    ruby 2.5.0p0 | ||||||
|  |  | ||||||
| BUNDLED WITH | BUNDLED WITH | ||||||
|    1.16.1 |    1.16.1 | ||||||
|   | |||||||
| @@ -1,22 +0,0 @@ | |||||||
| # frozen_string_literal: true |  | ||||||
|  |  | ||||||
| class ActivityPub::FollowsController < Api::BaseController |  | ||||||
|   include SignatureVerification |  | ||||||
|  |  | ||||||
|   def show |  | ||||||
|     render json: follow_request, |  | ||||||
|            serializer: ActivityPub::FollowSerializer, |  | ||||||
|            adapter: ActivityPub::Adapter, |  | ||||||
|            content_type: 'application/activity+json' |  | ||||||
|   end |  | ||||||
|  |  | ||||||
|   private |  | ||||||
|  |  | ||||||
|   def follow_request |  | ||||||
|     FollowRequest.includes(:account).references(:account).find_by!( |  | ||||||
|       id: params.require(:id), |  | ||||||
|       accounts: { domain: nil, username: params.require(:account_username) }, |  | ||||||
|       target_account: signed_request_account |  | ||||||
|     ) |  | ||||||
|   end |  | ||||||
| end |  | ||||||
| @@ -6,8 +6,8 @@ class Api::BaseController < ApplicationController | |||||||
|  |  | ||||||
|   include RateLimitHeaders |   include RateLimitHeaders | ||||||
|  |  | ||||||
|   skip_before_action :verify_authenticity_token |  | ||||||
|   skip_before_action :store_current_location |   skip_before_action :store_current_location | ||||||
|  |   protect_from_forgery with: :null_session | ||||||
|  |  | ||||||
|   rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e| |   rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e| | ||||||
|     render json: { error: e.to_s }, status: 422 |     render json: { error: e.to_s }, status: 422 | ||||||
|   | |||||||
| @@ -21,9 +21,9 @@ class Api::V1::Instances::ActivityController < Api::BaseController | |||||||
|  |  | ||||||
|       weeks << { |       weeks << { | ||||||
|         week: week.to_time.to_i.to_s, |         week: week.to_time.to_i.to_s, | ||||||
|         statuses: Redis.current.get("activity:statuses:local:#{week_id}") || 0, |         statuses: Redis.current.get("activity:statuses:local:#{week_id}") || '0', | ||||||
|         logins: Redis.current.pfcount("activity:logins:#{week_id}"), |         logins: Redis.current.pfcount("activity:logins:#{week_id}").to_s, | ||||||
|         registrations: Redis.current.get("activity:accounts:local:#{week_id}") || 0, |         registrations: Redis.current.get("activity:accounts:local:#{week_id}") || '0', | ||||||
|       } |       } | ||||||
|     end |     end | ||||||
|  |  | ||||||
|   | |||||||
| @@ -9,7 +9,11 @@ class Api::V1::Timelines::HomeController < Api::BaseController | |||||||
|  |  | ||||||
|   def show |   def show | ||||||
|     @statuses = load_statuses |     @statuses = load_statuses | ||||||
|     render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id) |  | ||||||
|  |     render json: @statuses, | ||||||
|  |            each_serializer: REST::StatusSerializer, | ||||||
|  |            relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id), | ||||||
|  |            status: regeneration_in_progress? ? 206 : 200 | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   private |   private | ||||||
| @@ -57,4 +61,8 @@ class Api::V1::Timelines::HomeController < Api::BaseController | |||||||
|   def pagination_since_id |   def pagination_since_id | ||||||
|     @statuses.first.id |     @statuses.first.id | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   def regeneration_in_progress? | ||||||
|  |     Redis.current.exists("account:#{current_account.id}:regeneration") | ||||||
|  |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ class Api::Web::PushSubscriptionsController < Api::BaseController | |||||||
|   respond_to :json |   respond_to :json | ||||||
|  |  | ||||||
|   before_action :require_user! |   before_action :require_user! | ||||||
|  |   protect_from_forgery with: :exception | ||||||
|  |  | ||||||
|   def create |   def create | ||||||
|     params.require(:subscription).require(:endpoint) |     params.require(:subscription).require(:endpoint) | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ class ApplicationController < ActionController::Base | |||||||
|   private |   private | ||||||
|  |  | ||||||
|   def https_enabled? |   def https_enabled? | ||||||
|     Rails.env.production? && ENV['LOCAL_HTTPS'] == 'true' |     Rails.env.production? | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   def store_current_location |   def store_current_location | ||||||
| @@ -124,15 +124,15 @@ class ApplicationController < ActionController::Base | |||||||
|  |  | ||||||
|   def render_cached_json(cache_key, **options) |   def render_cached_json(cache_key, **options) | ||||||
|     options[:expires_in] ||= 3.minutes |     options[:expires_in] ||= 3.minutes | ||||||
|     options[:public]     ||= true |  | ||||||
|     cache_key              = cache_key.join(':') if cache_key.is_a?(Enumerable) |     cache_key              = cache_key.join(':') if cache_key.is_a?(Enumerable) | ||||||
|  |     cache_public           = options.key?(:public) ? options.delete(:public) : true | ||||||
|     content_type           = options.delete(:content_type) || 'application/json' |     content_type           = options.delete(:content_type) || 'application/json' | ||||||
|  |  | ||||||
|     data = Rails.cache.fetch(cache_key, { raw: true }.merge(options)) do |     data = Rails.cache.fetch(cache_key, { raw: true }.merge(options)) do | ||||||
|       yield.to_json |       yield.to_json | ||||||
|     end |     end | ||||||
|  |  | ||||||
|     expires_in options[:expires_in], public: options[:public] |     expires_in options[:expires_in], public: cache_public | ||||||
|     render json: data, content_type: content_type |     render json: data, content_type: content_type | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ module UserTrackingConcern | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def regenerate_feed! |   def regenerate_feed! | ||||||
|     Redis.current.setnx("account:#{current_user.account_id}:regeneration", true) == 1 && Redis.current.expire("account:#{current_user.account_id}:regeneration", 3_600 * 24) |     Redis.current.setnx("account:#{current_user.account_id}:regeneration", true) && Redis.current.expire("account:#{current_user.account_id}:regeneration", 1.day.seconds) | ||||||
|     RegenerationWorker.perform_async(current_user.account_id) |     RegenerationWorker.perform_async(current_user.account_id) | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -14,13 +14,14 @@ class SharesController < ApplicationController | |||||||
|   private |   private | ||||||
|  |  | ||||||
|   def initial_state_params |   def initial_state_params | ||||||
|  |     text = [params[:title], params[:text], params[:url]].compact.join(' ') | ||||||
|     { |     { | ||||||
|       settings: Web::Setting.find_by(user: current_user)&.data || {}, |       settings: Web::Setting.find_by(user: current_user)&.data || {}, | ||||||
|       push_subscription: current_account.user.web_push_subscription(current_session), |       push_subscription: current_account.user.web_push_subscription(current_session), | ||||||
|       current_account: current_account, |       current_account: current_account, | ||||||
|       token: current_session.token, |       token: current_session.token, | ||||||
|       admin: Account.find_local(Setting.site_contact_username), |       admin: Account.find_local(Setting.site_contact_username), | ||||||
|       text: params[:text], |       text: text, | ||||||
|     } |     } | ||||||
|   end |   end | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,6 +6,6 @@ module InstanceHelper | |||||||
|   end |   end | ||||||
|  |  | ||||||
|   def site_hostname |   def site_hostname | ||||||
|     Rails.configuration.x.local_domain |     @site_hostname ||= Addressable::URI.parse("//#{Rails.configuration.x.local_domain}").display_uri.host | ||||||
|   end |   end | ||||||
| end | end | ||||||
|   | |||||||
| @@ -39,6 +39,10 @@ module JsonLdHelper | |||||||
|     !json.nil? && equals_or_includes?(json['@context'], ActivityPub::TagManager::CONTEXT) |     !json.nil? && equals_or_includes?(json['@context'], ActivityPub::TagManager::CONTEXT) | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  |   def unsupported_uri_scheme?(uri) | ||||||
|  |     !uri.start_with?('http://', 'https://') | ||||||
|  |   end | ||||||
|  |  | ||||||
|   def canonicalize(json) |   def canonicalize(json) | ||||||
|     graph = RDF::Graph.new << JSON::LD::API.toRdf(json) |     graph = RDF::Graph.new << JSON::LD::API.toRdf(json) | ||||||
|     graph.dump(:normalize) |     graph.dump(:normalize) | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								app/helpers/mailer_helper.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | |||||||
|  | # frozen_string_literal: true | ||||||
|  |  | ||||||
|  | module MailerHelper | ||||||
|  | end | ||||||
| Before Width: | Height: | Size: 142 KiB | 
| Before Width: | Height: | Size: 24 KiB | 
							
								
								
									
										1
									
								
								app/javascript/images/elephant_ui_disappointed.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 11 KiB | 
							
								
								
									
										1
									
								
								app/javascript/images/elephant_ui_greeting.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 17 KiB | 
							
								
								
									
										1
									
								
								app/javascript/images/elephant_ui_plane.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 11 KiB | 
							
								
								
									
										1
									
								
								app/javascript/images/elephant_ui_working.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 8.3 KiB | 
							
								
								
									
										2
									
								
								app/javascript/images/icon_cached.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | |||||||
|  | <?xml version="1.0" encoding="utf-8"?> | ||||||
|  | <svg width="2048" height="1792" viewBox="0 0 2048 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1344 1504q0 13-9.5 22.5t-22.5 9.5h-960q-8 0-13.5-2t-9-7-5.5-8-3-11.5-1-11.5v-600h-192q-26 0-45-19t-19-45q0-24 15-41l320-384q19-22 49-22t49 22l320 384q15 17 15 41 0 26-19 45t-45 19h-192v384h576q16 0 25 11l160 192q7 10 7 21zm640-416q0 24-15 41l-320 384q-20 23-49 23t-49-23l-320-384q-15-17-15-41 0-26 19-45t45-19h192v-384h-576q-16 0-25-12l-160-192q-7-9-7-20 0-13 9.5-22.5t22.5-9.5h960q8 0 13.5 2t9 7 5.5 8 3 11.5 1 11.5v600h192q26 0 45 19t19 45z" fill="#fff"/></svg> | ||||||
| After Width: | Height: | Size: 604 B | 
							
								
								
									
										4
									
								
								app/javascript/images/icon_done.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | |||||||
|  | <svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <path d="M0 0h24v24H0z" fill="none"/> | ||||||
|  |     <path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 16.2z"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 214 B | 
							
								
								
									
										4
									
								
								app/javascript/images/icon_email.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | |||||||
|  | <svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <path d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 4l-8 5-8-5V6l8 5 8-5v2z"/> | ||||||
|  |     <path d="M0 0h24v24H0z" fill="none"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 273 B | 
							
								
								
									
										4
									
								
								app/javascript/images/icon_grade.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | |||||||
|  | <svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z"/> | ||||||
|  |     <path d="M0 0h24v24H0z" fill="none"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 252 B | 
							
								
								
									
										4
									
								
								app/javascript/images/icon_lock_open.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | |||||||
|  | <svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <path d="M0 0h24v24H0z" fill="none"/> | ||||||
|  |     <path d="M12 17c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm6-9h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6h1.9c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm0 12H6V10h12v10z"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 395 B | 
							
								
								
									
										4
									
								
								app/javascript/images/icon_person_add.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | |||||||
|  | <svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <path d="M0 0h24v24H0z" fill="none"/> | ||||||
|  |     <path d="M15 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm-9-2V7H4v3H1v2h3v3h2v-3h3v-2H6zm9 4c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 305 B | 
							
								
								
									
										4
									
								
								app/javascript/images/icon_reply.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,4 @@ | |||||||
|  | <svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> | ||||||
|  |     <path d="M10 9V5l-7 7 7 7v-4.1c5 0 8.5 1.6 11 5.1-1-5-4-10-11-11z"/> | ||||||
|  |     <path d="M0 0h24v24H0z" fill="none"/> | ||||||
|  | </svg> | ||||||
| After Width: | Height: | Size: 220 B | 
							
								
								
									
										1
									
								
								app/javascript/images/logo_transparent.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | |||||||
|  | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 216.4144 232.00976"><path d="M107.86523 0C78.203984.2425 49.672422 3.4535937 33.044922 11.089844c0 0-32.97656262 14.752031-32.97656262 65.082031 0 11.525-.224375 25.306175.140625 39.919925 1.19750002 49.22 9.02375002 97.72843 54.53124962 109.77343 20.9825 5.55375 38.99711 6.71547 53.505856 5.91797 26.31125-1.45875 41.08203-9.38867 41.08203-9.38867l-.86914-19.08984s-18.80171 5.92758-39.91796 5.20508c-20.921254-.7175-43.006879-2.25516-46.390629-27.94141-.3125-2.25625-.46875-4.66938-.46875-7.20313 0 0 20.536953 5.0204 46.564449 6.21289 15.915.73001 30.8393-.93343 45.99805-2.74218 29.07-3.47125 54.38125-21.3818 57.5625-37.74805 5.0125-25.78125 4.59961-62.916015 4.59961-62.916015 0-50.33-32.97461-65.082031-32.97461-65.082031C166.80539 3.4535938 138.255.2425 108.59375 0h-.72852zM74.296875 39.326172c12.355 0 21.710234 4.749297 27.896485 14.248047l6.01367 10.080078 6.01563-10.080078c6.185-9.49875 15.54023-14.248047 27.89648-14.248047 10.6775 0 19.28156 3.753672 25.85156 11.076172 6.36875 7.3225 9.53907 17.218828 9.53907 29.673828v60.941408h-24.14454V81.869141c0-12.46875-5.24453-18.798829-15.73828-18.798829-11.6025 0-17.41797 7.508516-17.41797 22.353516v32.375002H96.207031V85.423828c0-14.845-5.815468-22.353515-17.417969-22.353516-10.49375 0-15.740234 6.330079-15.740234 18.798829v59.148439H38.904297V80.076172c0-12.455 3.171016-22.351328 9.541015-29.673828 6.568751-7.3225 15.172813-11.076172 25.851563-11.076172z" fill="#fff"/></svg> | ||||||
| After Width: | Height: | Size: 1.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/javascript/images/mailer/icon_cached.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 754 B | 
							
								
								
									
										
											BIN
										
									
								
								app/javascript/images/mailer/icon_done.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 279 B | 
							
								
								
									
										
											BIN
										
									
								
								app/javascript/images/mailer/icon_email.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 520 B | 
							
								
								
									
										
											BIN
										
									
								
								app/javascript/images/mailer/icon_grade.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 541 B | 
							
								
								
									
										
											BIN
										
									
								
								app/javascript/images/mailer/icon_lock_open.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 550 B | 
							
								
								
									
										
											BIN
										
									
								
								app/javascript/images/mailer/icon_person_add.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 512 B | 
							
								
								
									
										
											BIN
										
									
								
								app/javascript/images/mailer/icon_reply.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 391 B | 
							
								
								
									
										
											BIN
										
									
								
								app/javascript/images/mailer/logo_full.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.0 KiB | 
							
								
								
									
										
											BIN
										
									
								
								app/javascript/images/mailer/logo_transparent.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 627 B | 
| Before Width: | Height: | Size: 19 KiB | 
| Before Width: | Height: | Size: 32 KiB | 
| Before Width: | Height: | Size: 5.8 KiB | 
| Before Width: | Height: | Size: 3.2 KiB | 
| Before Width: | Height: | Size: 5.1 KiB | 
| @@ -1,4 +1,4 @@ | |||||||
| import axios from 'axios'; | import api from '../../api'; | ||||||
| import { pushNotificationsSetting } from '../../settings'; | import { pushNotificationsSetting } from '../../settings'; | ||||||
| import { setBrowserSupport, setSubscription, clearSubscription } from './setter'; | import { setBrowserSupport, setSubscription, clearSubscription } from './setter'; | ||||||
|  |  | ||||||
| @@ -35,7 +35,7 @@ const subscribe = (registration) => | |||||||
| const unsubscribe = ({ registration, subscription }) => | const unsubscribe = ({ registration, subscription }) => | ||||||
|   subscription ? subscription.unsubscribe().then(() => registration) : registration; |   subscription ? subscription.unsubscribe().then(() => registration) : registration; | ||||||
|  |  | ||||||
| const sendSubscriptionToBackend = (subscription, me) => { | const sendSubscriptionToBackend = (getState, subscription, me) => { | ||||||
|   const params = { subscription }; |   const params = { subscription }; | ||||||
|  |  | ||||||
|   if (me) { |   if (me) { | ||||||
| @@ -45,7 +45,7 @@ const sendSubscriptionToBackend = (subscription, me) => { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   return axios.post('/api/web/push_subscriptions', params).then(response => response.data); |   return api(getState).post('/api/web/push_subscriptions', params).then(response => response.data); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| // Last one checks for payload support: https://web-push-book.gauntface.com/chapter-06/01-non-standards-browsers/#no-payload | // Last one checks for payload support: https://web-push-book.gauntface.com/chapter-06/01-non-standards-browsers/#no-payload | ||||||
| @@ -85,13 +85,13 @@ export function register () { | |||||||
|             } else { |             } else { | ||||||
|               // Something went wrong, try to subscribe again |               // Something went wrong, try to subscribe again | ||||||
|               return unsubscribe({ registration, subscription }).then(subscribe).then( |               return unsubscribe({ registration, subscription }).then(subscribe).then( | ||||||
|                 subscription => sendSubscriptionToBackend(subscription, me)); |                 subscription => sendSubscriptionToBackend(getState, subscription, me)); | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|  |  | ||||||
|           // No subscription, try to subscribe |           // No subscription, try to subscribe | ||||||
|           return subscribe(registration).then( |           return subscribe(registration).then( | ||||||
|             subscription => sendSubscriptionToBackend(subscription, me)); |             subscription => sendSubscriptionToBackend(getState, subscription, me)); | ||||||
|         }) |         }) | ||||||
|         .then(subscription => { |         .then(subscription => { | ||||||
|           // If we got a PushSubscription (and not a subscription object from the backend) |           // If we got a PushSubscription (and not a subscription object from the backend) | ||||||
| @@ -137,7 +137,7 @@ export function saveSettings() { | |||||||
|     const alerts = state.get('alerts'); |     const alerts = state.get('alerts'); | ||||||
|     const data = { alerts }; |     const data = { alerts }; | ||||||
|  |  | ||||||
|     axios.put(`/api/web/push_subscriptions/${subscription.get('id')}`, { |     api(getState).put(`/api/web/push_subscriptions/${subscription.get('id')}`, { | ||||||
|       data, |       data, | ||||||
|     }).then(() => { |     }).then(() => { | ||||||
|       const me = getState().getIn(['meta', 'me']); |       const me = getState().getIn(['meta', 'me']); | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| import axios from 'axios'; | import api from '../api'; | ||||||
| import { debounce } from 'lodash'; | import { debounce } from 'lodash'; | ||||||
|  |  | ||||||
| export const SETTING_CHANGE = 'SETTING_CHANGE'; | export const SETTING_CHANGE = 'SETTING_CHANGE'; | ||||||
| @@ -23,7 +23,7 @@ const debouncedSave = debounce((dispatch, getState) => { | |||||||
|  |  | ||||||
|   const data = getState().get('settings').filter((_, path) => path !== 'saved').toJS(); |   const data = getState().get('settings').filter((_, path) => path !== 'saved').toJS(); | ||||||
|  |  | ||||||
|   axios.put('/api/web/settings', { data }).then(() => dispatch({ type: SETTING_SAVE })); |   api(getState).put('/api/web/settings', { data }).then(() => dispatch({ type: SETTING_SAVE })); | ||||||
| }, 5000, { trailing: true }); | }, 5000, { trailing: true }); | ||||||
|  |  | ||||||
| export function saveSettings() { | export function saveSettings() { | ||||||
|   | |||||||
| @@ -19,13 +19,14 @@ export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT'; | |||||||
|  |  | ||||||
| export const TIMELINE_CONTEXT_UPDATE = 'CONTEXT_UPDATE'; | export const TIMELINE_CONTEXT_UPDATE = 'CONTEXT_UPDATE'; | ||||||
|  |  | ||||||
| export function refreshTimelineSuccess(timeline, statuses, skipLoading, next) { | export function refreshTimelineSuccess(timeline, statuses, skipLoading, next, partial) { | ||||||
|   return { |   return { | ||||||
|     type: TIMELINE_REFRESH_SUCCESS, |     type: TIMELINE_REFRESH_SUCCESS, | ||||||
|     timeline, |     timeline, | ||||||
|     statuses, |     statuses, | ||||||
|     skipLoading, |     skipLoading, | ||||||
|     next, |     next, | ||||||
|  |     partial, | ||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -88,7 +89,7 @@ export function refreshTimeline(timelineId, path, params = {}) { | |||||||
|   return function (dispatch, getState) { |   return function (dispatch, getState) { | ||||||
|     const timeline = getState().getIn(['timelines', timelineId], ImmutableMap()); |     const timeline = getState().getIn(['timelines', timelineId], ImmutableMap()); | ||||||
|  |  | ||||||
|     if (timeline.get('isLoading') || timeline.get('online')) { |     if (timeline.get('isLoading') || (timeline.get('online') && !timeline.get('isPartial'))) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -104,8 +105,12 @@ export function refreshTimeline(timelineId, path, params = {}) { | |||||||
|     dispatch(refreshTimelineRequest(timelineId, skipLoading)); |     dispatch(refreshTimelineRequest(timelineId, skipLoading)); | ||||||
|  |  | ||||||
|     api(getState).get(path, { params }).then(response => { |     api(getState).get(path, { params }).then(response => { | ||||||
|  |       if (response.status === 206) { | ||||||
|  |         dispatch(refreshTimelineSuccess(timelineId, [], skipLoading, null, true)); | ||||||
|  |       } else { | ||||||
|         const next = getLinks(response).refs.find(link => link.rel === 'next'); |         const next = getLinks(response).refs.find(link => link.rel === 'next'); | ||||||
|       dispatch(refreshTimelineSuccess(timelineId, response.data, skipLoading, next ? next.uri : null)); |         dispatch(refreshTimelineSuccess(timelineId, response.data, skipLoading, next ? next.uri : null, false)); | ||||||
|  |       } | ||||||
|     }).catch(error => { |     }).catch(error => { | ||||||
|       dispatch(refreshTimelineFail(timelineId, error, skipLoading)); |       dispatch(refreshTimelineFail(timelineId, error, skipLoading)); | ||||||
|     }); |     }); | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| import axios from 'axios'; | import axios from 'axios'; | ||||||
|  | import ready from './ready'; | ||||||
| import LinkHeader from './link_header'; | import LinkHeader from './link_header'; | ||||||
|  |  | ||||||
| export const getLinks = response => { | export const getLinks = response => { | ||||||
| @@ -11,10 +12,17 @@ export const getLinks = response => { | |||||||
|   return LinkHeader.parse(value); |   return LinkHeader.parse(value); | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | let csrfHeader = {}; | ||||||
|  | function setCSRFHeader() { | ||||||
|  |   const csrfToken = document.querySelector('meta[name=csrf-token]').content; | ||||||
|  |   csrfHeader['X-CSRF-Token'] = csrfToken; | ||||||
|  | } | ||||||
|  | ready(setCSRFHeader); | ||||||
|  |  | ||||||
| export default getState => axios.create({ | export default getState => axios.create({ | ||||||
|   headers: { |   headers: Object.assign(csrfHeader, getState ? { | ||||||
|     'Authorization': `Bearer ${getState().getIn(['meta', 'access_token'], '')}`, |     'Authorization': `Bearer ${getState().getIn(['meta', 'access_token'], '')}`, | ||||||
|   }, |   } : {}), | ||||||
|  |  | ||||||
|   transformResponse: [function (data) { |   transformResponse: [function (data) { | ||||||
|     try { |     try { | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ exports[`<DisplayName /> renders display name + account name 1`] = ` | |||||||
| <span | <span | ||||||
|   className="display-name" |   className="display-name" | ||||||
| > | > | ||||||
|  |   <bdi> | ||||||
|     <strong |     <strong | ||||||
|       className="display-name__html" |       className="display-name__html" | ||||||
|       dangerouslySetInnerHTML={ |       dangerouslySetInnerHTML={ | ||||||
| @@ -12,6 +13,7 @@ exports[`<DisplayName /> renders display name + account name 1`] = ` | |||||||
|         } |         } | ||||||
|       } |       } | ||||||
|     /> |     /> | ||||||
|  |   </bdi> | ||||||
|     |     | ||||||
|   <span |   <span | ||||||
|     className="display-name__account" |     className="display-name__account" | ||||||
|   | |||||||
| @@ -93,7 +93,7 @@ export default class Account extends ImmutablePureComponent { | |||||||
|             {hidingNotificationsButton} |             {hidingNotificationsButton} | ||||||
|           </Fragment> |           </Fragment> | ||||||
|         ); |         ); | ||||||
|       } else if (!account.get('moved')) { |       } else if (!account.get('moved') || following) { | ||||||
|         buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />; |         buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -20,11 +20,11 @@ export default class AttachmentList extends ImmutablePureComponent { | |||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|         <ul className='attachment-list__list'> |         <ul className='attachment-list__list'> | ||||||
|           {media.map(attachment => |           {media.map(attachment => ( | ||||||
|             <li key={attachment.get('id')}> |             <li key={attachment.get('id')}> | ||||||
|               <a href={attachment.get('remote_url')} target='_blank' rel='noopener'>{filename(attachment.get('remote_url'))}</a> |               <a href={attachment.get('remote_url')} target='_blank' rel='noopener'>{filename(attachment.get('remote_url'))}</a> | ||||||
|             </li> |             </li> | ||||||
|           )} |           ))} | ||||||
|         </ul> |         </ul> | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|   | |||||||
| @@ -5,11 +5,11 @@ import PropTypes from 'prop-types'; | |||||||
|  |  | ||||||
| const Collapsable = ({ fullHeight, isVisible, children }) => ( | const Collapsable = ({ fullHeight, isVisible, children }) => ( | ||||||
|   <Motion defaultStyle={{ opacity: !isVisible ? 0 : 100, height: isVisible ? fullHeight : 0 }} style={{ opacity: spring(!isVisible ? 0 : 100), height: spring(!isVisible ? 0 : fullHeight) }}> |   <Motion defaultStyle={{ opacity: !isVisible ? 0 : 100, height: isVisible ? fullHeight : 0 }} style={{ opacity: spring(!isVisible ? 0 : 100), height: spring(!isVisible ? 0 : fullHeight) }}> | ||||||
|     {({ opacity, height }) => |     {({ opacity, height }) => ( | ||||||
|       <div style={{ height: `${height}px`, overflow: 'hidden', opacity: opacity / 100, display: Math.floor(opacity) === 0 ? 'none' : 'block' }}> |       <div style={{ height: `${height}px`, overflow: 'hidden', opacity: opacity / 100, display: Math.floor(opacity) === 0 ? 'none' : 'block' }}> | ||||||
|         {children} |         {children} | ||||||
|       </div> |       </div> | ||||||
|     } |     )} | ||||||
|   </Motion> |   </Motion> | ||||||
| ); | ); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,7 +23,6 @@ export default class ColumnHeader extends React.PureComponent { | |||||||
|     icon: PropTypes.string.isRequired, |     icon: PropTypes.string.isRequired, | ||||||
|     active: PropTypes.bool, |     active: PropTypes.bool, | ||||||
|     multiColumn: PropTypes.bool, |     multiColumn: PropTypes.bool, | ||||||
|     focusable: PropTypes.bool, |  | ||||||
|     showBackButton: PropTypes.bool, |     showBackButton: PropTypes.bool, | ||||||
|     children: PropTypes.node, |     children: PropTypes.node, | ||||||
|     pinned: PropTypes.bool, |     pinned: PropTypes.bool, | ||||||
| @@ -32,10 +31,6 @@ export default class ColumnHeader extends React.PureComponent { | |||||||
|     onClick: PropTypes.func, |     onClick: PropTypes.func, | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   static defaultProps = { |  | ||||||
|     focusable: true, |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   state = { |   state = { | ||||||
|     collapsed: true, |     collapsed: true, | ||||||
|     animating: false, |     animating: false, | ||||||
| @@ -68,7 +63,7 @@ export default class ColumnHeader extends React.PureComponent { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   render () { |   render () { | ||||||
|     const { title, icon, active, children, pinned, onPin, multiColumn, focusable, showBackButton, intl: { formatMessage } } = this.props; |     const { title, icon, active, children, pinned, onPin, multiColumn, showBackButton, intl: { formatMessage } } = this.props; | ||||||
|     const { collapsed, animating } = this.state; |     const { collapsed, animating } = this.state; | ||||||
|  |  | ||||||
|     const wrapperClassName = classNames('column-header__wrapper', { |     const wrapperClassName = classNames('column-header__wrapper', { | ||||||
| @@ -135,11 +130,13 @@ export default class ColumnHeader extends React.PureComponent { | |||||||
|  |  | ||||||
|     return ( |     return ( | ||||||
|       <div className={wrapperClassName}> |       <div className={wrapperClassName}> | ||||||
|         <h1 tabIndex={focusable ? 0 : null} role='button' className={buttonClassName} aria-label={title} onClick={this.handleTitleClick}> |         <h1 className={buttonClassName}> | ||||||
|  |           <button onClick={this.handleTitleClick}> | ||||||
|             <i className={`fa fa-fw fa-${icon} column-header__icon`} /> |             <i className={`fa fa-fw fa-${icon} column-header__icon`} /> | ||||||
|             <span className='column-header__title'> |             <span className='column-header__title'> | ||||||
|               {title} |               {title} | ||||||
|             </span> |             </span> | ||||||
|  |           </button> | ||||||
|  |  | ||||||
|           <div className='column-header__buttons'> |           <div className='column-header__buttons'> | ||||||
|             {backButton} |             {backButton} | ||||||
|   | |||||||
| @@ -12,7 +12,7 @@ export default class DisplayName extends React.PureComponent { | |||||||
|  |  | ||||||
|     return ( |     return ( | ||||||
|       <span className='display-name'> |       <span className='display-name'> | ||||||
|         <strong className='display-name__html' dangerouslySetInnerHTML={displayNameHtml} /> <span className='display-name__account'>@{this.props.account.get('acct')}</span> |         <bdi><strong className='display-name__html' dangerouslySetInnerHTML={displayNameHtml} /></bdi> <span className='display-name__account'>@{this.props.account.get('acct')}</span> | ||||||
|       </span> |       </span> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -93,7 +93,7 @@ export default class IconButton extends React.PureComponent { | |||||||
|  |  | ||||||
|     return ( |     return ( | ||||||
|       <Motion defaultStyle={{ rotate: active ? -360 : 0 }} style={{ rotate: animate ? spring(active ? -360 : 0, { stiffness: 120, damping: 7 }) : 0 }}> |       <Motion defaultStyle={{ rotate: active ? -360 : 0 }} style={{ rotate: animate ? spring(active ? -360 : 0, { stiffness: 120, damping: 7 }) : 0 }}> | ||||||
|         {({ rotate }) => |         {({ rotate }) => ( | ||||||
|           <button |           <button | ||||||
|             aria-label={title} |             aria-label={title} | ||||||
|             aria-pressed={pressed} |             aria-pressed={pressed} | ||||||
| @@ -106,7 +106,7 @@ export default class IconButton extends React.PureComponent { | |||||||
|           > |           > | ||||||
|             <i style={{ transform: `rotate(${rotate}deg)` }} className={`fa fa-fw fa-${icon}`} aria-hidden='true' /> |             <i style={{ transform: `rotate(${rotate}deg)` }} className={`fa fa-fw fa-${icon}`} aria-hidden='true' /> | ||||||
|           </button> |           </button> | ||||||
|         } |         )} | ||||||
|       </Motion> |       </Motion> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -2,9 +2,14 @@ import React from 'react'; | |||||||
| import { FormattedMessage } from 'react-intl'; | import { FormattedMessage } from 'react-intl'; | ||||||
|  |  | ||||||
| const MissingIndicator = () => ( | const MissingIndicator = () => ( | ||||||
|   <div className='missing-indicator'> |   <div className='regeneration-indicator missing-indicator'> | ||||||
|     <div> |     <div> | ||||||
|       <FormattedMessage id='missing_indicator.label' defaultMessage='Not found' /> |       <div className='regeneration-indicator__figure' /> | ||||||
|  |  | ||||||
|  |       <div className='regeneration-indicator__label'> | ||||||
|  |         <FormattedMessage id='missing_indicator.label' tagName='strong' defaultMessage='Not found' /> | ||||||
|  |         <FormattedMessage id='missing_indicator.sublabel' defaultMessage='This resource could not be found' /> | ||||||
|  |       </div> | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| ); | ); | ||||||
|   | |||||||
| @@ -162,7 +162,7 @@ export default class Status extends ImmutablePureComponent { | |||||||
|       prepend = ( |       prepend = ( | ||||||
|         <div className='status__prepend'> |         <div className='status__prepend'> | ||||||
|           <div className='status__prepend-icon-wrapper'><i className='fa fa-fw fa-retweet status__prepend-icon' /></div> |           <div className='status__prepend-icon-wrapper'><i className='fa fa-fw fa-retweet status__prepend-icon' /></div> | ||||||
|           <FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handleAccountClick} data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} className='status__display-name muted'><strong dangerouslySetInnerHTML={display_name_html} /></a> }} /> |           <FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handleAccountClick} data-id={status.getIn(['account', 'id'])} href={status.getIn(['account', 'url'])} className='status__display-name muted'><bdi><strong dangerouslySetInnerHTML={display_name_html} /></bdi></a> }} /> | ||||||
|         </div> |         </div> | ||||||
|       ); |       ); | ||||||
|  |  | ||||||
| @@ -178,14 +178,16 @@ export default class Status extends ImmutablePureComponent { | |||||||
|  |  | ||||||
|         media = ( |         media = ( | ||||||
|           <Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} > |           <Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} > | ||||||
|             {Component => <Component |             {Component => ( | ||||||
|  |               <Component | ||||||
|                 preview={video.get('preview_url')} |                 preview={video.get('preview_url')} | ||||||
|                 src={video.get('url')} |                 src={video.get('url')} | ||||||
|                 width={239} |                 width={239} | ||||||
|                 height={110} |                 height={110} | ||||||
|                 sensitive={status.get('sensitive')} |                 sensitive={status.get('sensitive')} | ||||||
|                 onOpenVideo={this.handleOpenVideo} |                 onOpenVideo={this.handleOpenVideo} | ||||||
|             />} |               /> | ||||||
|  |             )} | ||||||
|           </Bundle> |           </Bundle> | ||||||
|         ); |         ); | ||||||
|       } else { |       } else { | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ import PropTypes from 'prop-types'; | |||||||
| import StatusContainer from '../containers/status_container'; | import StatusContainer from '../containers/status_container'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import ScrollableList from './scrollable_list'; | import ScrollableList from './scrollable_list'; | ||||||
|  | import { FormattedMessage } from 'react-intl'; | ||||||
|  |  | ||||||
| export default class StatusList extends ImmutablePureComponent { | export default class StatusList extends ImmutablePureComponent { | ||||||
|  |  | ||||||
| @@ -16,6 +17,7 @@ export default class StatusList extends ImmutablePureComponent { | |||||||
|     trackScroll: PropTypes.bool, |     trackScroll: PropTypes.bool, | ||||||
|     shouldUpdateScroll: PropTypes.func, |     shouldUpdateScroll: PropTypes.func, | ||||||
|     isLoading: PropTypes.bool, |     isLoading: PropTypes.bool, | ||||||
|  |     isPartial: PropTypes.bool, | ||||||
|     hasMore: PropTypes.bool, |     hasMore: PropTypes.bool, | ||||||
|     prepend: PropTypes.node, |     prepend: PropTypes.node, | ||||||
|     emptyMessage: PropTypes.node, |     emptyMessage: PropTypes.node, | ||||||
| @@ -49,7 +51,22 @@ export default class StatusList extends ImmutablePureComponent { | |||||||
|  |  | ||||||
|   render () { |   render () { | ||||||
|     const { statusIds, ...other }  = this.props; |     const { statusIds, ...other }  = this.props; | ||||||
|     const { isLoading } = other; |     const { isLoading, isPartial } = other; | ||||||
|  |  | ||||||
|  |     if (isPartial) { | ||||||
|  |       return ( | ||||||
|  |         <div className='regeneration-indicator'> | ||||||
|  |           <div> | ||||||
|  |             <div className='regeneration-indicator__figure' /> | ||||||
|  |  | ||||||
|  |             <div className='regeneration-indicator__label'> | ||||||
|  |               <FormattedMessage id='regeneration_indicator.label' tagName='strong' defaultMessage='Loading…' /> | ||||||
|  |               <FormattedMessage id='regeneration_indicator.sublabel' defaultMessage='Your home feed is being prepared!' /> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       ); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     const scrollableContent = (isLoading || statusIds.size > 0) ? ( |     const scrollableContent = (isLoading || statusIds.size > 0) ? ( | ||||||
|       statusIds.map((statusId) => ( |       statusIds.map((statusId) => ( | ||||||
|   | |||||||
| @@ -41,7 +41,7 @@ class Avatar extends ImmutablePureComponent { | |||||||
|  |  | ||||||
|     return ( |     return ( | ||||||
|       <Motion defaultStyle={{ radius: 90 }} style={{ radius: spring(isHovered ? 30 : 90, { stiffness: 180, damping: 12 }) }}> |       <Motion defaultStyle={{ radius: 90 }} style={{ radius: spring(isHovered ? 30 : 90, { stiffness: 180, damping: 12 }) }}> | ||||||
|         {({ radius }) => |         {({ radius }) => ( | ||||||
|           <a |           <a | ||||||
|             href={account.get('url')} |             href={account.get('url')} | ||||||
|             className='account__header__avatar' |             className='account__header__avatar' | ||||||
| @@ -56,7 +56,7 @@ class Avatar extends ImmutablePureComponent { | |||||||
|           > |           > | ||||||
|             <span style={{ display: 'none' }}>{account.get('acct')}</span> |             <span style={{ display: 'none' }}>{account.get('acct')}</span> | ||||||
|           </a> |           </a> | ||||||
|         } |         )} | ||||||
|       </Motion> |       </Motion> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| @@ -103,7 +103,7 @@ export default class Header extends ImmutablePureComponent { | |||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if (account.get('moved')) { |     if (account.get('moved') && !account.getIn(['relationship', 'following'])) { | ||||||
|       actionBtn = ''; |       actionBtn = ''; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -94,12 +94,12 @@ export default class AccountGallery extends ImmutablePureComponent { | |||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|             <div className='account-gallery__container'> |             <div className='account-gallery__container'> | ||||||
|               {medias.map(media => |               {medias.map(media => ( | ||||||
|                 <MediaItem |                 <MediaItem | ||||||
|                   key={media.get('id')} |                   key={media.get('id')} | ||||||
|                   media={media} |                   media={media} | ||||||
|                 /> |                 /> | ||||||
|               )} |               ))} | ||||||
|               {loadMore} |               {loadMore} | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|   | |||||||
| @@ -34,7 +34,7 @@ export default class MovedNote extends ImmutablePureComponent { | |||||||
|       <div className='account__moved-note'> |       <div className='account__moved-note'> | ||||||
|         <div className='account__moved-note__message'> |         <div className='account__moved-note__message'> | ||||||
|           <div className='account__moved-note__icon-wrapper'><i className='fa fa-fw fa-suitcase account__moved-note__icon' /></div> |           <div className='account__moved-note__icon-wrapper'><i className='fa fa-fw fa-suitcase account__moved-note__icon' /></div> | ||||||
|           <FormattedMessage id='account.moved_to' defaultMessage='{name} has moved to:' values={{ name: <strong dangerouslySetInnerHTML={displayNameHtml} /> }} /> |           <FormattedMessage id='account.moved_to' defaultMessage='{name} has moved to:' values={{ name: <bdi><strong dangerouslySetInnerHTML={displayNameHtml} /></bdi> }} /> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|         <a href={to.get('url')} onClick={this.handleAccountClick} className='detailed-status__display-name'> |         <a href={to.get('url')} onClick={this.handleAccountClick} className='detailed-status__display-name'> | ||||||
|   | |||||||
| @@ -72,7 +72,7 @@ class PrivacyDropdownMenu extends React.PureComponent { | |||||||
|       <Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}> |       <Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}> | ||||||
|         {({ opacity, scaleX, scaleY }) => ( |         {({ opacity, scaleX, scaleY }) => ( | ||||||
|           <div className='privacy-dropdown__dropdown' style={{ ...style, opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }} ref={this.setRef}> |           <div className='privacy-dropdown__dropdown' style={{ ...style, opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }} ref={this.setRef}> | ||||||
|             {items.map(item => |             {items.map(item => ( | ||||||
|               <div role='button' tabIndex='0' key={item.value} data-index={item.value} onKeyDown={this.handleClick} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })}> |               <div role='button' tabIndex='0' key={item.value} data-index={item.value} onKeyDown={this.handleClick} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })}> | ||||||
|                 <div className='privacy-dropdown__option__icon'> |                 <div className='privacy-dropdown__option__icon'> | ||||||
|                   <i className={`fa fa-fw fa-${item.icon}`} /> |                   <i className={`fa fa-fw fa-${item.icon}`} /> | ||||||
| @@ -83,7 +83,7 @@ class PrivacyDropdownMenu extends React.PureComponent { | |||||||
|                   {item.meta} |                   {item.meta} | ||||||
|                 </div> |                 </div> | ||||||
|               </div> |               </div> | ||||||
|             )} |             ))} | ||||||
|           </div> |           </div> | ||||||
|         )} |         )} | ||||||
|       </Motion> |       </Motion> | ||||||
|   | |||||||
| @@ -40,11 +40,11 @@ export default class SearchResults extends ImmutablePureComponent { | |||||||
|       count += results.get('hashtags').size; |       count += results.get('hashtags').size; | ||||||
|       hashtags = ( |       hashtags = ( | ||||||
|         <div className='search-results__section'> |         <div className='search-results__section'> | ||||||
|           {results.get('hashtags').map(hashtag => |           {results.get('hashtags').map(hashtag => ( | ||||||
|             <Link key={hashtag} className='search-results__hashtag' to={`/timelines/tag/${hashtag}`}> |             <Link key={hashtag} className='search-results__hashtag' to={`/timelines/tag/${hashtag}`}> | ||||||
|               #{hashtag} |               #{hashtag} | ||||||
|             </Link> |             </Link> | ||||||
|           )} |           ))} | ||||||
|         </div> |         </div> | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -94,15 +94,15 @@ export default class Compose extends React.PureComponent { | |||||||
|           <div className='drawer__inner' onFocus={this.onFocus}> |           <div className='drawer__inner' onFocus={this.onFocus}> | ||||||
|             <NavigationContainer onClose={this.onBlur} /> |             <NavigationContainer onClose={this.onBlur} /> | ||||||
|             <ComposeFormContainer /> |             <ComposeFormContainer /> | ||||||
|             <div className='mastodon' /> |             {multiColumn && <div className='mastodon' />} | ||||||
|           </div> |           </div> | ||||||
|  |  | ||||||
|           <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}> |           <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}> | ||||||
|             {({ x }) => |             {({ x }) => ( | ||||||
|               <div className='drawer__inner darker' style={{ transform: `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}> |               <div className='drawer__inner darker' style={{ transform: `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}> | ||||||
|                 <SearchResultsContainer /> |                 <SearchResultsContainer /> | ||||||
|               </div> |               </div> | ||||||
|             } |             )} | ||||||
|           </Motion> |           </Motion> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|   | |||||||
| @@ -168,8 +168,8 @@ export const urlRegex = (function() { | |||||||
|           '#{validGeneralUrlPathChars}*'    + |           '#{validGeneralUrlPathChars}*'    + | ||||||
|         ')'                                 + |         ')'                                 + | ||||||
|       ')'                                   + |       ')'                                   + | ||||||
|     '\\)' |     '\\)', | ||||||
|   , 'i'); |     'i'); | ||||||
|   // Valid end-of-path chracters (so /foo. does not gobble the period). |   // Valid end-of-path chracters (so /foo. does not gobble the period). | ||||||
|   // 1. Allow =&# for empty URL parameters and other URL-join artifacts |   // 1. Allow =&# for empty URL parameters and other URL-join artifacts | ||||||
|   regexen.validUrlPathEndingChars = regexSupplant(/[^#{spaces_group}\(\)\?!\*';:=\,\.\$%\[\]#{pd}~&\|@]|(?:#{validUrlBalancedParens})/i); |   regexen.validUrlPathEndingChars = regexSupplant(/[^#{spaces_group}\(\)\?!\*';:=\,\.\$%\[\]#{pd}~&\|@]|(?:#{validUrlBalancedParens})/i); | ||||||
| @@ -190,7 +190,7 @@ export const urlRegex = (function() { | |||||||
|       '(?::(#{validPortNumber}))?'                               + // $4 Port number (optional) |       '(?::(#{validPortNumber}))?'                               + // $4 Port number (optional) | ||||||
|       '(\\/#{validUrlPath}*)?'                                   + // $5 URL Path |       '(\\/#{validUrlPath}*)?'                                   + // $5 URL Path | ||||||
|       '(\\?#{validUrlQueryChars}*#{validUrlQueryEndingChars})?'  + // $6 Query String |       '(\\?#{validUrlQueryChars}*#{validUrlQueryEndingChars})?'  + // $6 Query String | ||||||
|     ')' |     ')', | ||||||
|   , 'gi'); |     'gi'); | ||||||
|   return regexen.validUrl; |   return regexen.validUrl; | ||||||
| }()); | }()); | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| import { expandHomeTimeline } from '../../actions/timelines'; | import { expandHomeTimeline, refreshHomeTimeline } from '../../actions/timelines'; | ||||||
| import PropTypes from 'prop-types'; | import PropTypes from 'prop-types'; | ||||||
| import StatusListContainer from '../ui/containers/status_list_container'; | import StatusListContainer from '../ui/containers/status_list_container'; | ||||||
| import Column from '../../components/column'; | import Column from '../../components/column'; | ||||||
| @@ -16,6 +16,7 @@ const messages = defineMessages({ | |||||||
|  |  | ||||||
| const mapStateToProps = state => ({ | const mapStateToProps = state => ({ | ||||||
|   hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0, |   hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0, | ||||||
|  |   isPartial: state.getIn(['timelines', 'home', 'isPartial'], false), | ||||||
| }); | }); | ||||||
|  |  | ||||||
| @connect(mapStateToProps) | @connect(mapStateToProps) | ||||||
| @@ -26,6 +27,7 @@ export default class HomeTimeline extends React.PureComponent { | |||||||
|     dispatch: PropTypes.func.isRequired, |     dispatch: PropTypes.func.isRequired, | ||||||
|     intl: PropTypes.object.isRequired, |     intl: PropTypes.object.isRequired, | ||||||
|     hasUnread: PropTypes.bool, |     hasUnread: PropTypes.bool, | ||||||
|  |     isPartial: PropTypes.bool, | ||||||
|     columnId: PropTypes.string, |     columnId: PropTypes.string, | ||||||
|     multiColumn: PropTypes.bool, |     multiColumn: PropTypes.bool, | ||||||
|   }; |   }; | ||||||
| @@ -57,6 +59,39 @@ export default class HomeTimeline extends React.PureComponent { | |||||||
|     this.props.dispatch(expandHomeTimeline()); |     this.props.dispatch(expandHomeTimeline()); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   componentDidMount () { | ||||||
|  |     this._checkIfReloadNeeded(false, this.props.isPartial); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   componentDidUpdate (prevProps) { | ||||||
|  |     this._checkIfReloadNeeded(prevProps.isPartial, this.props.isPartial); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   componentWillUnmount () { | ||||||
|  |     this._stopPolling(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   _checkIfReloadNeeded (wasPartial, isPartial) { | ||||||
|  |     const { dispatch } = this.props; | ||||||
|  |  | ||||||
|  |     if (wasPartial === isPartial) { | ||||||
|  |       return; | ||||||
|  |     } else if (!wasPartial && isPartial) { | ||||||
|  |       this.polling = setInterval(() => { | ||||||
|  |         dispatch(refreshHomeTimeline()); | ||||||
|  |       }, 3000); | ||||||
|  |     } else if (wasPartial && !isPartial) { | ||||||
|  |       this._stopPolling(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   _stopPolling () { | ||||||
|  |     if (this.polling) { | ||||||
|  |       clearInterval(this.polling); | ||||||
|  |       this.polling = null; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   render () { |   render () { | ||||||
|     const { intl, hasUnread, columnId, multiColumn } = this.props; |     const { intl, hasUnread, columnId, multiColumn } = this.props; | ||||||
|     const pinned = !!columnId; |     const pinned = !!columnId; | ||||||
|   | |||||||
| @@ -66,11 +66,11 @@ export default class ListEditor extends ImmutablePureComponent { | |||||||
|           {showSearch && <div role='button' tabIndex='-1' className='drawer__backdrop' onClick={onClear} />} |           {showSearch && <div role='button' tabIndex='-1' className='drawer__backdrop' onClick={onClear} />} | ||||||
|  |  | ||||||
|           <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}> |           <Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}> | ||||||
|             {({ x }) => |             {({ x }) => ( | ||||||
|               <div className='drawer__inner backdrop' style={{ transform: x === 0 ? null : `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}> |               <div className='drawer__inner backdrop' style={{ transform: x === 0 ? null : `translateX(${x}%)`, visibility: x === -100 ? 'hidden' : 'visible' }}> | ||||||
|                 {searchAccountIds.map(accountId => <Account key={accountId} accountId={accountId} />)} |                 {searchAccountIds.map(accountId => <Account key={accountId} accountId={accountId} />)} | ||||||
|               </div> |               </div> | ||||||
|             } |             )} | ||||||
|           </Motion> |           </Motion> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|   | |||||||
| @@ -120,13 +120,17 @@ export default class ListTimeline extends React.PureComponent { | |||||||
|     if (typeof list === 'undefined') { |     if (typeof list === 'undefined') { | ||||||
|       return ( |       return ( | ||||||
|         <Column> |         <Column> | ||||||
|  |           <div className='scrollable'> | ||||||
|             <LoadingIndicator /> |             <LoadingIndicator /> | ||||||
|  |           </div> | ||||||
|         </Column> |         </Column> | ||||||
|       ); |       ); | ||||||
|     } else if (list === false) { |     } else if (list === false) { | ||||||
|       return ( |       return ( | ||||||
|         <Column> |         <Column> | ||||||
|  |           <div className='scrollable'> | ||||||
|             <MissingIndicator /> |             <MissingIndicator /> | ||||||
|  |           </div> | ||||||
|         </Column> |         </Column> | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -133,7 +133,7 @@ export default class Notification extends ImmutablePureComponent { | |||||||
|     const { notification } = this.props; |     const { notification } = this.props; | ||||||
|     const account          = notification.get('account'); |     const account          = notification.get('account'); | ||||||
|     const displayNameHtml  = { __html: account.get('display_name_html') }; |     const displayNameHtml  = { __html: account.get('display_name_html') }; | ||||||
|     const link             = <Permalink className='notification__display-name' href={account.get('url')} title={account.get('acct')} to={`/accounts/${account.get('id')}`} dangerouslySetInnerHTML={displayNameHtml} />; |     const link             = <bdi><Permalink className='notification__display-name' href={account.get('url')} title={account.get('acct')} to={`/accounts/${account.get('id')}`} dangerouslySetInnerHTML={displayNameHtml} /></bdi>; | ||||||
|  |  | ||||||
|     switch(notification.get('type')) { |     switch(notification.get('type')) { | ||||||
|     case 'follow': |     case 'follow': | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ export default class ColumnHeader extends React.PureComponent { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     return ( |     return ( | ||||||
|       <div role='heading' tabIndex='0' className={`column-header ${active ? 'active' : ''}`} onClick={this.handleClick} id={columnHeaderId || null}> |       <div role='button heading' tabIndex='0' className={`column-header ${active ? 'active' : ''}`} onClick={this.handleClick} id={columnHeaderId || null}> | ||||||
|         {icon} |         {icon} | ||||||
|         {type} |         {type} | ||||||
|       </div> |       </div> | ||||||
|   | |||||||
| @@ -37,6 +37,7 @@ export default class ColumnsArea extends ImmutablePureComponent { | |||||||
|   static propTypes = { |   static propTypes = { | ||||||
|     intl: PropTypes.object.isRequired, |     intl: PropTypes.object.isRequired, | ||||||
|     columns: ImmutablePropTypes.list.isRequired, |     columns: ImmutablePropTypes.list.isRequired, | ||||||
|  |     isModalOpen: PropTypes.bool.isRequired, | ||||||
|     singleColumn: PropTypes.bool, |     singleColumn: PropTypes.bool, | ||||||
|     children: PropTypes.node, |     children: PropTypes.node, | ||||||
|   }; |   }; | ||||||
| @@ -144,7 +145,7 @@ export default class ColumnsArea extends ImmutablePureComponent { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   render () { |   render () { | ||||||
|     const { columns, children, singleColumn } = this.props; |     const { columns, children, singleColumn, isModalOpen } = this.props; | ||||||
|     const { shouldAnimate } = this.state; |     const { shouldAnimate } = this.state; | ||||||
|  |  | ||||||
|     const columnIndex = getIndex(this.context.router.history.location.pathname); |     const columnIndex = getIndex(this.context.router.history.location.pathname); | ||||||
| @@ -159,7 +160,7 @@ export default class ColumnsArea extends ImmutablePureComponent { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     return ( |     return ( | ||||||
|       <div className='columns-area' ref={this.setRef}> |       <div className={`columns-area ${ isModalOpen ? 'unscrollable' : '' }`} ref={this.setRef}> | ||||||
|         {columns.map(column => { |         {columns.map(column => { | ||||||
|           const params = column.get('params', null) === null ? null : column.get('params').toJS(); |           const params = column.get('params', null) === null ? null : column.get('params').toJS(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ import React from 'react'; | |||||||
| import PropTypes from 'prop-types'; | import PropTypes from 'prop-types'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { FormattedMessage, injectIntl } from 'react-intl'; | import { FormattedMessage, injectIntl } from 'react-intl'; | ||||||
| import axios from 'axios'; | import api from '../../../api'; | ||||||
|  |  | ||||||
| @injectIntl | @injectIntl | ||||||
| export default class EmbedModal extends ImmutablePureComponent { | export default class EmbedModal extends ImmutablePureComponent { | ||||||
| @@ -23,7 +23,7 @@ export default class EmbedModal extends ImmutablePureComponent { | |||||||
|  |  | ||||||
|     this.setState({ loading: true }); |     this.setState({ loading: true }); | ||||||
|  |  | ||||||
|     axios.post('/api/web/embed', { url }).then(res => { |     api().post('/api/web/embed', { url }).then(res => { | ||||||
|       this.setState({ loading: false, oembed: res.data }); |       this.setState({ loading: false, oembed: res.data }); | ||||||
|  |  | ||||||
|       const iframeDocument = this.iframe.contentWindow.document; |       const iframeDocument = this.iframe.contentWindow.document; | ||||||
|   | |||||||
| @@ -113,13 +113,11 @@ export default class ModalRoot extends React.PureComponent { | |||||||
|         <div style={{ pointerEvents: visible ? 'auto' : 'none' }}> |         <div style={{ pointerEvents: visible ? 'auto' : 'none' }}> | ||||||
|           <div role='presentation' className='modal-root__overlay' onClick={onClose} /> |           <div role='presentation' className='modal-root__overlay' onClick={onClose} /> | ||||||
|           <div role='dialog' className='modal-root__container'> |           <div role='dialog' className='modal-root__container'> | ||||||
|             { |             {visible && ( | ||||||
|               visible ? |               <BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}> | ||||||
|                 (<BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}> |  | ||||||
|                 {(SpecificComponent) => <SpecificComponent {...props} onClose={onClose} />} |                 {(SpecificComponent) => <SpecificComponent {...props} onClose={onClose} />} | ||||||
|                 </BundleContainer>) : |               </BundleContainer> | ||||||
|               null |             )} | ||||||
|             } |  | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|   | |||||||
| @@ -24,14 +24,23 @@ const messages = defineMessages({ | |||||||
|  |  | ||||||
| const PageOne = ({ acct, domain }) => ( | const PageOne = ({ acct, domain }) => ( | ||||||
|   <div className='onboarding-modal__page onboarding-modal__page-one'> |   <div className='onboarding-modal__page onboarding-modal__page-one'> | ||||||
|     <div style={{ flex: '0 0 auto' }}> |     <div className='onboarding-modal__page-one__lead'> | ||||||
|       <div className='onboarding-modal__page-one__elephant-friend' /> |  | ||||||
|     </div> |  | ||||||
|  |  | ||||||
|     <div> |  | ||||||
|       <h1><FormattedMessage id='onboarding.page_one.welcome' defaultMessage='Welcome to Mastodon!' /></h1> |       <h1><FormattedMessage id='onboarding.page_one.welcome' defaultMessage='Welcome to Mastodon!' /></h1> | ||||||
|       <p><FormattedMessage id='onboarding.page_one.federation' defaultMessage='Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.' /></p> |       <p><FormattedMessage id='onboarding.page_one.federation' defaultMessage='Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.' /></p> | ||||||
|       <p><FormattedMessage id='onboarding.page_one.handle' defaultMessage='You are on {domain}, so your full handle is {handle}' values={{ domain, handle: <strong>@{acct}@{domain}</strong> }} /></p> |     </div> | ||||||
|  |  | ||||||
|  |     <div className='onboarding-modal__page-one__extra'> | ||||||
|  |       <div className='display-case'> | ||||||
|  |         <div className='display-case__label'> | ||||||
|  |           <FormattedMessage id='onboarding.page_one.full_handle' defaultMessage='Your full handle' /> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|  |         <div className='display-case__case'> | ||||||
|  |           @{acct}@{domain} | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <p><FormattedMessage id='onboarding.page_one.handle_hint' defaultMessage='This is what you would tell your friends to search for.' /></p> | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| ); | ); | ||||||
| @@ -46,7 +55,7 @@ const PageTwo = ({ myAccount }) => ( | |||||||
|     <div className='figure non-interactive'> |     <div className='figure non-interactive'> | ||||||
|       <div className='pseudo-drawer'> |       <div className='pseudo-drawer'> | ||||||
|         <NavigationBar account={myAccount} /> |         <NavigationBar account={myAccount} /> | ||||||
|       </div> |  | ||||||
|         <ComposeForm |         <ComposeForm | ||||||
|           text='Awoo! #introductions' |           text='Awoo! #introductions' | ||||||
|           suggestions={ImmutableList()} |           suggestions={ImmutableList()} | ||||||
| @@ -63,6 +72,7 @@ const PageTwo = ({ myAccount }) => ( | |||||||
|           showSearch |           showSearch | ||||||
|         /> |         /> | ||||||
|       </div> |       </div> | ||||||
|  |     </div> | ||||||
|  |  | ||||||
|     <p><FormattedMessage id='onboarding.page_two.compose' defaultMessage='Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.' /></p> |     <p><FormattedMessage id='onboarding.page_two.compose' defaultMessage='Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.' /></p> | ||||||
|   </div> |   </div> | ||||||
| @@ -251,18 +261,12 @@ export default class OnboardingModal extends React.PureComponent { | |||||||
|     const hasMore = currentIndex < pages.length - 1; |     const hasMore = currentIndex < pages.length - 1; | ||||||
|  |  | ||||||
|     const nextOrDoneBtn = hasMore ? ( |     const nextOrDoneBtn = hasMore ? ( | ||||||
|       <button |       <button onClick={this.handleNext} className='onboarding-modal__nav onboarding-modal__next shake-bottom'> | ||||||
|         onClick={this.handleNext} |         <FormattedMessage id='onboarding.next' defaultMessage='Next' /> <i className='fa fa-fw fa-chevron-right' /> | ||||||
|         className='onboarding-modal__nav onboarding-modal__next' |  | ||||||
|       > |  | ||||||
|         <FormattedMessage id='onboarding.next' defaultMessage='Next' /> |  | ||||||
|       </button> |       </button> | ||||||
|     ) : ( |     ) : ( | ||||||
|       <button |       <button onClick={this.handleClose} className='onboarding-modal__nav onboarding-modal__done shake-bottom'> | ||||||
|         onClick={this.handleClose} |         <FormattedMessage id='onboarding.done' defaultMessage='Done' /> <i className='fa fa-fw fa-check' /> | ||||||
|         className='onboarding-modal__nav onboarding-modal__done' |  | ||||||
|       > |  | ||||||
|         <FormattedMessage id='onboarding.done' defaultMessage='Done' /> |  | ||||||
|       </button> |       </button> | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
| @@ -270,9 +274,10 @@ export default class OnboardingModal extends React.PureComponent { | |||||||
|       <div className='modal-root__modal onboarding-modal'> |       <div className='modal-root__modal onboarding-modal'> | ||||||
|         <ReactSwipeableViews index={currentIndex} onChangeIndex={this.handleSwipe} className='onboarding-modal__pager'> |         <ReactSwipeableViews index={currentIndex} onChangeIndex={this.handleSwipe} className='onboarding-modal__pager'> | ||||||
|           {pages.map((page, i) => { |           {pages.map((page, i) => { | ||||||
|             const className = classNames('onboarding-modal__page__wrapper', { |             const className = classNames('onboarding-modal__page__wrapper', `onboarding-modal__page__wrapper-${i}`, { | ||||||
|               'onboarding-modal__page__wrapper--active': i === currentIndex, |               'onboarding-modal__page__wrapper--active': i === currentIndex, | ||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             return ( |             return ( | ||||||
|               <div key={i} className={className}>{page}</div> |               <div key={i} className={className}>{page}</div> | ||||||
|             ); |             ); | ||||||
| @@ -294,6 +299,7 @@ export default class OnboardingModal extends React.PureComponent { | |||||||
|               const className = classNames('onboarding-modal__dot', { |               const className = classNames('onboarding-modal__dot', { | ||||||
|                 active: i === currentIndex, |                 active: i === currentIndex, | ||||||
|               }); |               }); | ||||||
|  |  | ||||||
|               return ( |               return ( | ||||||
|                 <div |                 <div | ||||||
|                   key={`dot-${i}`} |                   key={`dot-${i}`} | ||||||
|   | |||||||
| @@ -37,14 +37,14 @@ export default class UploadArea extends React.PureComponent { | |||||||
|  |  | ||||||
|     return ( |     return ( | ||||||
|       <Motion defaultStyle={{ backgroundOpacity: 0, backgroundScale: 0.95 }} style={{ backgroundOpacity: spring(active ? 1 : 0, { stiffness: 150, damping: 15 }), backgroundScale: spring(active ? 1 : 0.95, { stiffness: 200, damping: 3 }) }}> |       <Motion defaultStyle={{ backgroundOpacity: 0, backgroundScale: 0.95 }} style={{ backgroundOpacity: spring(active ? 1 : 0, { stiffness: 150, damping: 15 }), backgroundScale: spring(active ? 1 : 0.95, { stiffness: 200, damping: 3 }) }}> | ||||||
|         {({ backgroundOpacity, backgroundScale }) => |         {({ backgroundOpacity, backgroundScale }) => ( | ||||||
|           <div className='upload-area' style={{ visibility: active ? 'visible' : 'hidden', opacity: backgroundOpacity }}> |           <div className='upload-area' style={{ visibility: active ? 'visible' : 'hidden', opacity: backgroundOpacity }}> | ||||||
|             <div className='upload-area__drop'> |             <div className='upload-area__drop'> | ||||||
|               <div className='upload-area__background' style={{ transform: `scale(${backgroundScale})` }} /> |               <div className='upload-area__background' style={{ transform: `scale(${backgroundScale})` }} /> | ||||||
|               <div className='upload-area__content'><FormattedMessage id='upload_area.title' defaultMessage='Drag & drop to upload' /></div> |               <div className='upload-area__content'><FormattedMessage id='upload_area.title' defaultMessage='Drag & drop to upload' /></div> | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|         } |         )} | ||||||
|       </Motion> |       </Motion> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ import ColumnsArea from '../components/columns_area'; | |||||||
|  |  | ||||||
| const mapStateToProps = state => ({ | const mapStateToProps = state => ({ | ||||||
|   columns: state.getIn(['settings', 'columns']), |   columns: state.getIn(['settings', 'columns']), | ||||||
|  |   isModalOpen: !!state.get('modal').modalType, | ||||||
| }); | }); | ||||||
|  |  | ||||||
| export default connect(mapStateToProps, null, null, { withRef: true })(ColumnsArea); | export default connect(mapStateToProps, null, null, { withRef: true })(ColumnsArea); | ||||||
|   | |||||||
| @@ -47,6 +47,7 @@ const makeMapStateToProps = () => { | |||||||
|   const mapStateToProps = (state, { timelineId }) => ({ |   const mapStateToProps = (state, { timelineId }) => ({ | ||||||
|     statusIds: getStatusIds(state, { type: timelineId }), |     statusIds: getStatusIds(state, { type: timelineId }), | ||||||
|     isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true), |     isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true), | ||||||
|  |     isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false), | ||||||
|     hasMore: !!state.getIn(['timelines', timelineId, 'next']), |     hasMore: !!state.getIn(['timelines', timelineId, 'next']), | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -67,7 +67,7 @@ | |||||||
|   "confirmations.delete_list.confirm": "Delete", |   "confirmations.delete_list.confirm": "Delete", | ||||||
|   "confirmations.delete_list.message": "هل تود حقا حذف هذه القائمة ؟", |   "confirmations.delete_list.message": "هل تود حقا حذف هذه القائمة ؟", | ||||||
|   "confirmations.domain_block.confirm": "إخفاء إسم النطاق كاملا", |   "confirmations.domain_block.confirm": "إخفاء إسم النطاق كاملا", | ||||||
|   "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", |   "confirmations.domain_block.message": "متأكد من أنك تود حظر إسم النطاق {domain} بالكامل ؟ في غالب الأحيان يُستَحسَن كتم أو حظر بعض الحسابات بدلا من حظر نطاق بالكامل.", | ||||||
|   "confirmations.mute.confirm": "أكتم", |   "confirmations.mute.confirm": "أكتم", | ||||||
|   "confirmations.mute.message": "هل أنت متأكد أنك تريد كتم {name} ؟", |   "confirmations.mute.message": "هل أنت متأكد أنك تريد كتم {name} ؟", | ||||||
|   "confirmations.unfollow.confirm": "إلغاء المتابعة", |   "confirmations.unfollow.confirm": "إلغاء المتابعة", | ||||||
| @@ -92,7 +92,7 @@ | |||||||
|   "empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.", |   "empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.", | ||||||
|   "empty_column.home": "إنك لا تتبع بعد أي شخص إلى حد الآن. زر {public} أو استخدام حقل البحث لكي تبدأ على التعرف على مستخدمين آخرين.", |   "empty_column.home": "إنك لا تتبع بعد أي شخص إلى حد الآن. زر {public} أو استخدام حقل البحث لكي تبدأ على التعرف على مستخدمين آخرين.", | ||||||
|   "empty_column.home.public_timeline": "الخيط العام", |   "empty_column.home.public_timeline": "الخيط العام", | ||||||
|   "empty_column.list": "هذه القائمة فارغة.", |   "empty_column.list": "هذه القائمة فارغة مؤقتا و لكن سوف تمتلئ تدريجيا عندما يبدأ الأعضاء المُنتَمين إليها بنشر تبويقات.", | ||||||
|   "empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.", |   "empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.", | ||||||
|   "empty_column.public": "لا يوجد أي شيء هنا ! قم بنشر شيء ما للعامة، أو إتبع مستخدمين آخرين في الخوادم المثيلة الأخرى لملء خيط المحادثات العام", |   "empty_column.public": "لا يوجد أي شيء هنا ! قم بنشر شيء ما للعامة، أو إتبع مستخدمين آخرين في الخوادم المثيلة الأخرى لملء خيط المحادثات العام", | ||||||
|   "follow_request.authorize": "ترخيص", |   "follow_request.authorize": "ترخيص", | ||||||
| @@ -123,7 +123,7 @@ | |||||||
|   "keyboard_shortcuts.reply": "للردّ", |   "keyboard_shortcuts.reply": "للردّ", | ||||||
|   "keyboard_shortcuts.search": "للتركيز على البحث", |   "keyboard_shortcuts.search": "للتركيز على البحث", | ||||||
|   "keyboard_shortcuts.toot": "لتحرير تبويق جديد", |   "keyboard_shortcuts.toot": "لتحرير تبويق جديد", | ||||||
|   "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", |   "keyboard_shortcuts.unfocus": "لإلغاء التركيز على حقل النص أو نافذة البحث", | ||||||
|   "keyboard_shortcuts.up": "للإنتقال إلى أعلى القائمة", |   "keyboard_shortcuts.up": "للإنتقال إلى أعلى القائمة", | ||||||
|   "lightbox.close": "إغلاق", |   "lightbox.close": "إغلاق", | ||||||
|   "lightbox.next": "التالي", |   "lightbox.next": "التالي", | ||||||
|   | |||||||
| @@ -92,7 +92,7 @@ | |||||||
|   "empty_column.hashtag": "Encara no hi ha res amb aquesta etiqueta.", |   "empty_column.hashtag": "Encara no hi ha res amb aquesta etiqueta.", | ||||||
|   "empty_column.home": "Encara no segueixes ningú. Visita {public} o fes cerca per començar i conèixer altres usuaris.", |   "empty_column.home": "Encara no segueixes ningú. Visita {public} o fes cerca per començar i conèixer altres usuaris.", | ||||||
|   "empty_column.home.public_timeline": "la línia de temps pública", |   "empty_column.home.public_timeline": "la línia de temps pública", | ||||||
|   "empty_column.list": "Encara no hi ha res en aquesta llista.", |   "empty_column.list": "Encara no hi ha res en aquesta llista. Quan els membres d'aquesta llista publiquin nous estats, apareixeran aquí.", | ||||||
|   "empty_column.notifications": "Encara no tens notificacions. Interactua amb altres per iniciar la conversa.", |   "empty_column.notifications": "Encara no tens notificacions. Interactua amb altres per iniciar la conversa.", | ||||||
|   "empty_column.public": "No hi ha res aquí! Escriu alguna cosa públicament o segueix manualment usuaris d'altres instàncies per omplir-ho", |   "empty_column.public": "No hi ha res aquí! Escriu alguna cosa públicament o segueix manualment usuaris d'altres instàncies per omplir-ho", | ||||||
|   "follow_request.authorize": "Autoritzar", |   "follow_request.authorize": "Autoritzar", | ||||||
|   | |||||||
| @@ -174,7 +174,8 @@ | |||||||
|   "onboarding.page_four.home": "The home timeline shows posts from people you follow.", |   "onboarding.page_four.home": "The home timeline shows posts from people you follow.", | ||||||
|   "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.", |   "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.", | ||||||
|   "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.", |   "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.", | ||||||
|   "onboarding.page_one.handle": "You are on {domain}, so your full handle is {handle}", |   "onboarding.page_one.full_handle": "Your full handle", | ||||||
|  |   "onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.", | ||||||
|   "onboarding.page_one.welcome": "Welcome to Mastodon!", |   "onboarding.page_one.welcome": "Welcome to Mastodon!", | ||||||
|   "onboarding.page_six.admin": "Your instance's admin is {admin}.", |   "onboarding.page_six.admin": "Your instance's admin is {admin}.", | ||||||
|   "onboarding.page_six.almost_done": "Almost done...", |   "onboarding.page_six.almost_done": "Almost done...", | ||||||
|   | |||||||
| @@ -7,22 +7,22 @@ | |||||||
|   "account.followers": "پیگیران", |   "account.followers": "پیگیران", | ||||||
|   "account.follows": "پی میگیرد", |   "account.follows": "پی میگیرد", | ||||||
|   "account.follows_you": "پیگیر شماست", |   "account.follows_you": "پیگیر شماست", | ||||||
|   "account.hide_reblogs": "Hide boosts from @{name}", |   "account.hide_reblogs": "پنهان کردن بازبوقهای @{name}", | ||||||
|   "account.media": "رسانه", |   "account.media": "رسانه", | ||||||
|   "account.mention": "نامبردن از @{name}", |   "account.mention": "نامبردن از @{name}", | ||||||
|   "account.moved_to": "{name} has moved to:", |   "account.moved_to": "{name} منتقل شده است به:", | ||||||
|   "account.mute": "بیصدا کردن @{name}", |   "account.mute": "بیصدا کردن @{name}", | ||||||
|   "account.mute_notifications": "Mute notifications from @{name}", |   "account.mute_notifications": "بیصداکردن اعلانها از طرف @{name}", | ||||||
|   "account.posts": "نوشتهها", |   "account.posts": "نوشتهها", | ||||||
|   "account.report": "گزارش @{name}", |   "account.report": "گزارش @{name}", | ||||||
|   "account.requested": "در انتظار پذیرش", |   "account.requested": "در انتظار پذیرش", | ||||||
|   "account.share": "همرسانی نمایهٔ @{name}", |   "account.share": "همرسانی نمایهٔ @{name}", | ||||||
|   "account.show_reblogs": "Show boosts from @{name}", |   "account.show_reblogs": "نشاندادن بازبوقهای  @{name}", | ||||||
|   "account.unblock": "رفع انسداد @{name}", |   "account.unblock": "رفع انسداد @{name}", | ||||||
|   "account.unblock_domain": "رفع پنهانسازی از {domain}", |   "account.unblock_domain": "رفع پنهانسازی از {domain}", | ||||||
|   "account.unfollow": "پایان پیگیری", |   "account.unfollow": "پایان پیگیری", | ||||||
|   "account.unmute": "باصدا کردن @{name}", |   "account.unmute": "باصدا کردن @{name}", | ||||||
|   "account.unmute_notifications": "Unmute notifications from @{name}", |   "account.unmute_notifications": "باصداکردن اعلانها از طرف @{name}", | ||||||
|   "account.view_full_profile": "نمایش نمایهٔ کامل", |   "account.view_full_profile": "نمایش نمایهٔ کامل", | ||||||
|   "boost_modal.combo": "دکمهٔ {combo} را بزنید تا دیگر این را نبینید", |   "boost_modal.combo": "دکمهٔ {combo} را بزنید تا دیگر این را نبینید", | ||||||
|   "bundle_column_error.body": "هنگام بازکردن این بخش خطایی رخ داد.", |   "bundle_column_error.body": "هنگام بازکردن این بخش خطایی رخ داد.", | ||||||
| @@ -36,7 +36,7 @@ | |||||||
|   "column.favourites": "پسندیدهها", |   "column.favourites": "پسندیدهها", | ||||||
|   "column.follow_requests": "درخواستهای پیگیری", |   "column.follow_requests": "درخواستهای پیگیری", | ||||||
|   "column.home": "خانه", |   "column.home": "خانه", | ||||||
|   "column.lists": "Lists", |   "column.lists": "فهرستها", | ||||||
|   "column.mutes": "کاربران بیصداشده", |   "column.mutes": "کاربران بیصداشده", | ||||||
|   "column.notifications": "اعلانها", |   "column.notifications": "اعلانها", | ||||||
|   "column.pins": "نوشتههای ثابت", |   "column.pins": "نوشتههای ثابت", | ||||||
| @@ -65,7 +65,7 @@ | |||||||
|   "confirmations.delete.confirm": "پاک کن", |   "confirmations.delete.confirm": "پاک کن", | ||||||
|   "confirmations.delete.message": "آیا واقعاً میخواهید این نوشته را پاک کنید؟", |   "confirmations.delete.message": "آیا واقعاً میخواهید این نوشته را پاک کنید؟", | ||||||
|   "confirmations.delete_list.confirm": "Delete", |   "confirmations.delete_list.confirm": "Delete", | ||||||
|   "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", |   "confirmations.delete_list.message": "آیا واقعاً میخواهید این فهرست را برای همیشه پاک کنید؟", | ||||||
|   "confirmations.domain_block.confirm": "پنهانسازی کل دامین", |   "confirmations.domain_block.confirm": "پنهانسازی کل دامین", | ||||||
|   "confirmations.domain_block.message": "آیا جدی جدی میخواهید کل دامین {domain} را مسدود کنید؟ بیشتر وقتها مسدودکردن یا بیصداکردن چند حساب کاربری خاص کافی است و توصیه میشود.", |   "confirmations.domain_block.message": "آیا جدی جدی میخواهید کل دامین {domain} را مسدود کنید؟ بیشتر وقتها مسدودکردن یا بیصداکردن چند حساب کاربری خاص کافی است و توصیه میشود.", | ||||||
|   "confirmations.mute.confirm": "بیصدا کن", |   "confirmations.mute.confirm": "بیصدا کن", | ||||||
| @@ -92,7 +92,7 @@ | |||||||
|   "empty_column.hashtag": "هنوز هیچ چیزی با این هشتگ نیست.", |   "empty_column.hashtag": "هنوز هیچ چیزی با این هشتگ نیست.", | ||||||
|   "empty_column.home": "شما هنوز پیگیر کسی نیستید. {public} را ببینید یا چیزی را جستجو کنید تا کاربران دیگر را ببینید.", |   "empty_column.home": "شما هنوز پیگیر کسی نیستید. {public} را ببینید یا چیزی را جستجو کنید تا کاربران دیگر را ببینید.", | ||||||
|   "empty_column.home.public_timeline": "فهرست نوشتههای همهجا", |   "empty_column.home.public_timeline": "فهرست نوشتههای همهجا", | ||||||
|   "empty_column.list": "There is nothing in this list yet.", |   "empty_column.list": "در این فهرست هنوز چیزی نیست. وقتی اعضای این فهرست چیزی بنویسند، اینجا ظاهر خواهد شد.", | ||||||
|   "empty_column.notifications": "هنوز هیچ اعلانی ندارید. به نوشتههای دیگران واکنش نشان دهید تا گفتگو آغاز شود.", |   "empty_column.notifications": "هنوز هیچ اعلانی ندارید. به نوشتههای دیگران واکنش نشان دهید تا گفتگو آغاز شود.", | ||||||
|   "empty_column.public": "اینجا هنوز چیزی نیست! خودتان چیزی بنویسید یا کاربران دیگر را پی بگیرید تا اینجا پر شود", |   "empty_column.public": "اینجا هنوز چیزی نیست! خودتان چیزی بنویسید یا کاربران دیگر را پی بگیرید تا اینجا پر شود", | ||||||
|   "follow_request.authorize": "اجازه دهید", |   "follow_request.authorize": "اجازه دهید", | ||||||
| @@ -108,46 +108,46 @@ | |||||||
|   "home.column_settings.show_reblogs": "نمایش بازبوقها", |   "home.column_settings.show_reblogs": "نمایش بازبوقها", | ||||||
|   "home.column_settings.show_replies": "نمایش پاسخها", |   "home.column_settings.show_replies": "نمایش پاسخها", | ||||||
|   "home.settings": "تنظیمات ستون", |   "home.settings": "تنظیمات ستون", | ||||||
|   "keyboard_shortcuts.back": "to navigate back", |   "keyboard_shortcuts.back": "برای بازگشت", | ||||||
|   "keyboard_shortcuts.boost": "to boost", |   "keyboard_shortcuts.boost": "برای بازبوقیدن", | ||||||
|   "keyboard_shortcuts.column": "to focus a status in one of the columns", |   "keyboard_shortcuts.column": "برای برجستهکردن یک نوشته در یکی از ستونها", | ||||||
|   "keyboard_shortcuts.compose": "to focus the compose textarea", |   "keyboard_shortcuts.compose": "برای فعالکردن کادر نوشتهٔ تازه", | ||||||
|   "keyboard_shortcuts.description": "Description", |   "keyboard_shortcuts.description": "Description", | ||||||
|   "keyboard_shortcuts.down": "to move down in the list", |   "keyboard_shortcuts.down": "برای پایینرفتن در فهرست", | ||||||
|   "keyboard_shortcuts.enter": "to open status", |   "keyboard_shortcuts.enter": "to open status", | ||||||
|   "keyboard_shortcuts.favourite": "to favourite", |   "keyboard_shortcuts.favourite": "برای پسندیدن", | ||||||
|   "keyboard_shortcuts.heading": "Keyboard Shortcuts", |   "keyboard_shortcuts.heading": "Keyboard Shortcuts", | ||||||
|   "keyboard_shortcuts.hotkey": "Hotkey", |   "keyboard_shortcuts.hotkey": "میانبر", | ||||||
|   "keyboard_shortcuts.legend": "to display this legend", |   "keyboard_shortcuts.legend": "برای نمایش این راهنما", | ||||||
|   "keyboard_shortcuts.mention": "to mention author", |   "keyboard_shortcuts.mention": "برای نامبردن از نویسنده", | ||||||
|   "keyboard_shortcuts.reply": "to reply", |   "keyboard_shortcuts.reply": "برای پاسخدادن", | ||||||
|   "keyboard_shortcuts.search": "to focus search", |   "keyboard_shortcuts.search": "برای فعالکردن جستجو", | ||||||
|   "keyboard_shortcuts.toot": "to start a brand new toot", |   "keyboard_shortcuts.toot": "برای آغاز یک بوق تازه", | ||||||
|   "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", |   "keyboard_shortcuts.unfocus": "برای برداشتن توجه از نوشتن/جستجو", | ||||||
|   "keyboard_shortcuts.up": "to move up in the list", |   "keyboard_shortcuts.up": "برای بالا رفتن در فهرست", | ||||||
|   "lightbox.close": "بستن", |   "lightbox.close": "بستن", | ||||||
|   "lightbox.next": "بعدی", |   "lightbox.next": "بعدی", | ||||||
|   "lightbox.previous": "قبلی", |   "lightbox.previous": "قبلی", | ||||||
|   "lists.account.add": "Add to list", |   "lists.account.add": "افزودن به فهرست", | ||||||
|   "lists.account.remove": "Remove from list", |   "lists.account.remove": "پاککردن از فهرست", | ||||||
|   "lists.delete": "Delete list", |   "lists.delete": "حذف فهرست", | ||||||
|   "lists.edit": "Edit list", |   "lists.edit": "ویرایش فهرست", | ||||||
|   "lists.new.create": "Add list", |   "lists.new.create": "افزودن فهرست", | ||||||
|   "lists.new.title_placeholder": "New list title", |   "lists.new.title_placeholder": "نام فهرست تازه", | ||||||
|   "lists.search": "Search among people you follow", |   "lists.search": "بین کسانی که پی میگیرید بگردید", | ||||||
|   "lists.subheading": "Your lists", |   "lists.subheading": "فهرستهای شما", | ||||||
|   "loading_indicator.label": "بارگیری...", |   "loading_indicator.label": "بارگیری...", | ||||||
|   "media_gallery.toggle_visible": "تغییر پیدایی", |   "media_gallery.toggle_visible": "تغییر پیدایی", | ||||||
|   "missing_indicator.label": "پیدا نشد", |   "missing_indicator.label": "پیدا نشد", | ||||||
|   "mute_modal.hide_notifications": "Hide notifications from this user?", |   "mute_modal.hide_notifications": "اعلانهای این کاربر پنهان شود؟", | ||||||
|   "navigation_bar.blocks": "کاربران مسدودشده", |   "navigation_bar.blocks": "کاربران مسدودشده", | ||||||
|   "navigation_bar.community_timeline": "نوشتههای محلی", |   "navigation_bar.community_timeline": "نوشتههای محلی", | ||||||
|   "navigation_bar.edit_profile": "ویرایش نمایه", |   "navigation_bar.edit_profile": "ویرایش نمایه", | ||||||
|   "navigation_bar.favourites": "پسندیدهها", |   "navigation_bar.favourites": "پسندیدهها", | ||||||
|   "navigation_bar.follow_requests": "درخواستهای پیگیری", |   "navigation_bar.follow_requests": "درخواستهای پیگیری", | ||||||
|   "navigation_bar.info": "اطلاعات تکمیلی", |   "navigation_bar.info": "اطلاعات تکمیلی", | ||||||
|   "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts", |   "navigation_bar.keyboard_shortcuts": "میانبرهای صفحهکلید", | ||||||
|   "navigation_bar.lists": "Lists", |   "navigation_bar.lists": "فهرستها", | ||||||
|   "navigation_bar.logout": "خروج", |   "navigation_bar.logout": "خروج", | ||||||
|   "navigation_bar.mutes": "کاربران بیصداشده", |   "navigation_bar.mutes": "کاربران بیصداشده", | ||||||
|   "navigation_bar.pins": "نوشتههای ثابت", |   "navigation_bar.pins": "نوشتههای ثابت", | ||||||
| @@ -174,7 +174,7 @@ | |||||||
|   "onboarding.page_four.home": "ستون «خانه» نوشتههای کسانی را نشان میدهد که شما پی میگیرید.", |   "onboarding.page_four.home": "ستون «خانه» نوشتههای کسانی را نشان میدهد که شما پی میگیرید.", | ||||||
|   "onboarding.page_four.notifications": "ستون «اعلانها» ارتباطهای شما با دیگران را نشان میدهد.", |   "onboarding.page_four.notifications": "ستون «اعلانها» ارتباطهای شما با دیگران را نشان میدهد.", | ||||||
|   "onboarding.page_one.federation": "ماستدون شبکهای از سرورهای مستقل است که با پیوستن به یکدیگر یک شبکهٔ اجتماعی بزرگ را تشکیل میدهند.", |   "onboarding.page_one.federation": "ماستدون شبکهای از سرورهای مستقل است که با پیوستن به یکدیگر یک شبکهٔ اجتماعی بزرگ را تشکیل میدهند.", | ||||||
|   "onboarding.page_one.handle": "شما روی سرور {domain} هستید، بنابراین شناسهٔ کامل شما {handle} است.", |   "onboarding.page_one.handle": "شما روی سرور {domain} هستید، بنابراین شناسهٔ کامل شما {handle} است", | ||||||
|   "onboarding.page_one.welcome": "به ماستدون خوش آمدید!", |   "onboarding.page_one.welcome": "به ماستدون خوش آمدید!", | ||||||
|   "onboarding.page_six.admin": "نشانی مسئول سرور شما {admin} است.", |   "onboarding.page_six.admin": "نشانی مسئول سرور شما {admin} است.", | ||||||
|   "onboarding.page_six.almost_done": "الان تقریباً آمادهاید...", |   "onboarding.page_six.almost_done": "الان تقریباً آمادهاید...", | ||||||
| @@ -199,7 +199,7 @@ | |||||||
|   "privacy.unlisted.short": "فهرستنشده", |   "privacy.unlisted.short": "فهرستنشده", | ||||||
|   "relative_time.days": "{number}d", |   "relative_time.days": "{number}d", | ||||||
|   "relative_time.hours": "{number}h", |   "relative_time.hours": "{number}h", | ||||||
|   "relative_time.just_now": "now", |   "relative_time.just_now": "الان", | ||||||
|   "relative_time.minutes": "{number}m", |   "relative_time.minutes": "{number}m", | ||||||
|   "relative_time.seconds": "{number}s", |   "relative_time.seconds": "{number}s", | ||||||
|   "reply_indicator.cancel": "لغو", |   "reply_indicator.cancel": "لغو", | ||||||
| @@ -222,7 +222,7 @@ | |||||||
|   "status.load_more": "بیشتر نشان بده", |   "status.load_more": "بیشتر نشان بده", | ||||||
|   "status.media_hidden": "تصویر پنهان شده", |   "status.media_hidden": "تصویر پنهان شده", | ||||||
|   "status.mention": "نامبردن از @{name}", |   "status.mention": "نامبردن از @{name}", | ||||||
|   "status.more": "More", |   "status.more": "بیشتر", | ||||||
|   "status.mute": "Mute @{name}", |   "status.mute": "Mute @{name}", | ||||||
|   "status.mute_conversation": "بیصداکردن گفتگو", |   "status.mute_conversation": "بیصداکردن گفتگو", | ||||||
|   "status.open": "این نوشته را باز کن", |   "status.open": "این نوشته را باز کن", | ||||||
| @@ -244,7 +244,7 @@ | |||||||
|   "tabs_bar.home": "خانه", |   "tabs_bar.home": "خانه", | ||||||
|   "tabs_bar.local_timeline": "محلی", |   "tabs_bar.local_timeline": "محلی", | ||||||
|   "tabs_bar.notifications": "اعلانها", |   "tabs_bar.notifications": "اعلانها", | ||||||
|   "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", |   "ui.beforeunload": "اگر از ماستدون خارج شوید پیشنویس شما پاک خواهد شد.", | ||||||
|   "upload_area.title": "برای بارگذاری به اینجا بکشید", |   "upload_area.title": "برای بارگذاری به اینجا بکشید", | ||||||
|   "upload_button.label": "افزودن تصویر", |   "upload_button.label": "افزودن تصویر", | ||||||
|   "upload_form.description": "نوشتهٔ توضیحی برای کمبینایان و نابینایان", |   "upload_form.description": "نوشتهٔ توضیحی برای کمبینایان و نابینایان", | ||||||
|   | |||||||
| @@ -92,7 +92,7 @@ | |||||||
|   "empty_column.hashtag": "Aínda non hai nada con esta etiqueta.", |   "empty_column.hashtag": "Aínda non hai nada con esta etiqueta.", | ||||||
|   "empty_column.home": "A súa liña temporal de inicio está baldeira! Visite {public} ou utilice a busca para atopar outras usuarias.", |   "empty_column.home": "A súa liña temporal de inicio está baldeira! Visite {public} ou utilice a busca para atopar outras usuarias.", | ||||||
|   "empty_column.home.public_timeline": "a liña temporal pública", |   "empty_column.home.public_timeline": "a liña temporal pública", | ||||||
|   "empty_column.list": "Aínda non hai nada en esta lista.", |   "empty_column.list": "Aínda non hai nada en esta lista. Cando as usuarias incluídas na lista publiquen mensaxes, aparecerán aquí.", | ||||||
|   "empty_column.notifications": "Aínda non ten notificacións. Interactúe con outras para iniciar unha conversa.", |   "empty_column.notifications": "Aínda non ten notificacións. Interactúe con outras para iniciar unha conversa.", | ||||||
|   "empty_column.public": "Nada por aquí! Escriba algo de xeito público, ou siga manualmente usuarias de outras instancias para ir enchéndoa", |   "empty_column.public": "Nada por aquí! Escriba algo de xeito público, ou siga manualmente usuarias de outras instancias para ir enchéndoa", | ||||||
|   "follow_request.authorize": "Autorizar", |   "follow_request.authorize": "Autorizar", | ||||||
| @@ -109,7 +109,7 @@ | |||||||
|   "home.column_settings.show_replies": "Mostrar respostas", |   "home.column_settings.show_replies": "Mostrar respostas", | ||||||
|   "home.settings": "Axustes da columna", |   "home.settings": "Axustes da columna", | ||||||
|   "keyboard_shortcuts.back": "voltar atrás", |   "keyboard_shortcuts.back": "voltar atrás", | ||||||
|   "keyboard_shortcuts.boost": "repetir", |   "keyboard_shortcuts.boost": "promover", | ||||||
|   "keyboard_shortcuts.column": "destacar un estado en unha das columnas", |   "keyboard_shortcuts.column": "destacar un estado en unha das columnas", | ||||||
|   "keyboard_shortcuts.compose": "Foco no área de escritura", |   "keyboard_shortcuts.compose": "Foco no área de escritura", | ||||||
|   "keyboard_shortcuts.description": "Descrición", |   "keyboard_shortcuts.description": "Descrición", | ||||||
| @@ -227,8 +227,8 @@ | |||||||
|   "status.mute_conversation": "Acalar conversa", |   "status.mute_conversation": "Acalar conversa", | ||||||
|   "status.open": "Expandir este estado", |   "status.open": "Expandir este estado", | ||||||
|   "status.pin": "Fixar no perfil", |   "status.pin": "Fixar no perfil", | ||||||
|   "status.reblog": "Promocionar", |   "status.reblog": "Promover", | ||||||
|   "status.reblogged_by": "{name} promocionado", |   "status.reblogged_by": "{name} promoveu", | ||||||
|   "status.reply": "Resposta", |   "status.reply": "Resposta", | ||||||
|   "status.replyAll": "Resposta a conversa", |   "status.replyAll": "Resposta a conversa", | ||||||
|   "status.report": "Informar @{name}", |   "status.report": "Informar @{name}", | ||||||
|   | |||||||
| @@ -25,11 +25,11 @@ | |||||||
|   "account.unmute_notifications": "@{name}의 알림 뮤트 해제", |   "account.unmute_notifications": "@{name}의 알림 뮤트 해제", | ||||||
|   "account.view_full_profile": "전체 프로필 보기", |   "account.view_full_profile": "전체 프로필 보기", | ||||||
|   "boost_modal.combo": "다음부터 {combo}를 누르면 이 과정을 건너뛸 수 있습니다.", |   "boost_modal.combo": "다음부터 {combo}를 누르면 이 과정을 건너뛸 수 있습니다.", | ||||||
|   "bundle_column_error.body": "Something went wrong while loading this component.", |   "bundle_column_error.body": "컴포넌트를 불러오는 과정에서 문제가 발생했습니다.", | ||||||
|   "bundle_column_error.retry": "다시 시도", |   "bundle_column_error.retry": "다시 시도", | ||||||
|   "bundle_column_error.title": "네트워크 에러", |   "bundle_column_error.title": "네트워크 에러", | ||||||
|   "bundle_modal_error.close": "닫기", |   "bundle_modal_error.close": "닫기", | ||||||
|   "bundle_modal_error.message": "Something went wrong while loading this component.", |   "bundle_modal_error.message": "컴포넌트를 불러오는 과정에서 문제가 발생했습니다.", | ||||||
|   "bundle_modal_error.retry": "다시 시도", |   "bundle_modal_error.retry": "다시 시도", | ||||||
|   "column.blocks": "차단 중인 사용자", |   "column.blocks": "차단 중인 사용자", | ||||||
|   "column.community": "로컬 타임라인", |   "column.community": "로컬 타임라인", | ||||||
| @@ -50,7 +50,7 @@ | |||||||
|   "column_header.unpin": "고정 해제", |   "column_header.unpin": "고정 해제", | ||||||
|   "column_subheading.navigation": "내비게이션", |   "column_subheading.navigation": "내비게이션", | ||||||
|   "column_subheading.settings": "설정", |   "column_subheading.settings": "설정", | ||||||
|   "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", |   "compose_form.hashtag_warning": "이 툿은 어떤 해시태그로도 검색 되지 않습니다. 전체공개로 게시 된 툿만이 해시태그로 검색 될 수 있습니다.", | ||||||
|   "compose_form.lock_disclaimer": "이 계정은 {locked}로 설정 되어 있지 않습니다. 누구나 이 계정을 팔로우 할 수 있으며, 팔로워 공개의 포스팅을 볼 수 있습니다.", |   "compose_form.lock_disclaimer": "이 계정은 {locked}로 설정 되어 있지 않습니다. 누구나 이 계정을 팔로우 할 수 있으며, 팔로워 공개의 포스팅을 볼 수 있습니다.", | ||||||
|   "compose_form.lock_disclaimer.lock": "비공개", |   "compose_form.lock_disclaimer.lock": "비공개", | ||||||
|   "compose_form.placeholder": "지금 무엇을 하고 있나요?", |   "compose_form.placeholder": "지금 무엇을 하고 있나요?", | ||||||
| @@ -135,7 +135,7 @@ | |||||||
|   "lists.new.create": "리스트 추가", |   "lists.new.create": "리스트 추가", | ||||||
|   "lists.new.title_placeholder": "새 리스트의 이름", |   "lists.new.title_placeholder": "새 리스트의 이름", | ||||||
|   "lists.search": "팔로우 중인 사람들 중에서 찾기", |   "lists.search": "팔로우 중인 사람들 중에서 찾기", | ||||||
|   "lists.subheading": "Your lists", |   "lists.subheading": "당신의 리스트", | ||||||
|   "loading_indicator.label": "불러오는 중...", |   "loading_indicator.label": "불러오는 중...", | ||||||
|   "media_gallery.toggle_visible": "표시 전환", |   "media_gallery.toggle_visible": "표시 전환", | ||||||
|   "missing_indicator.label": "찾을 수 없습니다", |   "missing_indicator.label": "찾을 수 없습니다", | ||||||
| @@ -178,7 +178,7 @@ | |||||||
|   "onboarding.page_one.welcome": "Mastodon에 어서 오세요!", |   "onboarding.page_one.welcome": "Mastodon에 어서 오세요!", | ||||||
|   "onboarding.page_six.admin": "이 인스턴스의 관리자는 {admin}입니다.", |   "onboarding.page_six.admin": "이 인스턴스의 관리자는 {admin}입니다.", | ||||||
|   "onboarding.page_six.almost_done": "이상입니다.", |   "onboarding.page_six.almost_done": "이상입니다.", | ||||||
|   "onboarding.page_six.appetoot": "Bon Appetoot!", |   "onboarding.page_six.appetoot": "본 아페툿!", | ||||||
|   "onboarding.page_six.apps_available": "iOS、Android 또는 다른 플랫폼에서 사용할 수 있는 {apps}이 있습니다.", |   "onboarding.page_six.apps_available": "iOS、Android 또는 다른 플랫폼에서 사용할 수 있는 {apps}이 있습니다.", | ||||||
|   "onboarding.page_six.github": "Mastodon는 오픈 소스 소프트웨어입니다. 버그 보고나 기능 추가 요청, 기여는 {github}에서 할 수 있습니다.", |   "onboarding.page_six.github": "Mastodon는 오픈 소스 소프트웨어입니다. 버그 보고나 기능 추가 요청, 기여는 {github}에서 할 수 있습니다.", | ||||||
|   "onboarding.page_six.guidelines": "커뮤니티 가이드라인", |   "onboarding.page_six.guidelines": "커뮤니티 가이드라인", | ||||||
| @@ -213,7 +213,7 @@ | |||||||
|   "search_popout.tips.text": "단순한 텍스트 검색은 관계된 프로필 이름, 유저 이름 그리고 해시태그를 표시합니다", |   "search_popout.tips.text": "단순한 텍스트 검색은 관계된 프로필 이름, 유저 이름 그리고 해시태그를 표시합니다", | ||||||
|   "search_popout.tips.user": "유저", |   "search_popout.tips.user": "유저", | ||||||
|   "search_results.total": "{count, number}건의 결과", |   "search_results.total": "{count, number}건의 결과", | ||||||
|   "standalone.public_title": "A look inside...", |   "standalone.public_title": "지금 이런 이야기를 하고 있습니다…", | ||||||
|   "status.block": "@{name} 차단", |   "status.block": "@{name} 차단", | ||||||
|   "status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다", |   "status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다", | ||||||
|   "status.delete": "삭제", |   "status.delete": "삭제", | ||||||
| @@ -247,7 +247,7 @@ | |||||||
|   "ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.", |   "ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.", | ||||||
|   "upload_area.title": "드래그 & 드롭으로 업로드", |   "upload_area.title": "드래그 & 드롭으로 업로드", | ||||||
|   "upload_button.label": "미디어 추가", |   "upload_button.label": "미디어 추가", | ||||||
|   "upload_form.description": "Describe for the visually impaired", |   "upload_form.description": "시각장애인을 위한 설명", | ||||||
|   "upload_form.undo": "재시도", |   "upload_form.undo": "재시도", | ||||||
|   "upload_progress.label": "업로드 중...", |   "upload_progress.label": "업로드 중...", | ||||||
|   "video.close": "동영상 닫기", |   "video.close": "동영상 닫기", | ||||||
|   | |||||||
| @@ -100,10 +100,10 @@ | |||||||
|   "getting_started.appsshort": "Apps", |   "getting_started.appsshort": "Apps", | ||||||
|   "getting_started.faq": "FAQ", |   "getting_started.faq": "FAQ", | ||||||
|   "getting_started.heading": "Beginnen", |   "getting_started.heading": "Beginnen", | ||||||
|   "getting_started.open_source_notice": "Mastodon is open-sourcesoftware. Je kunt bijdragen of problemen melden op GitHub via {github}.", |   "getting_started.open_source_notice": "Mastodon is vrije software. Je kunt bijdragen of problemen melden op GitHub via {github}.", | ||||||
|   "getting_started.userguide": "Gebruikersgids", |   "getting_started.userguide": "Gebruikersgids", | ||||||
|   "home.column_settings.advanced": "Geavanceerd", |   "home.column_settings.advanced": "Geavanceerd", | ||||||
|   "home.column_settings.basic": "Basis", |   "home.column_settings.basic": "Algemeen", | ||||||
|   "home.column_settings.filter_regex": "Wegfilteren met reguliere expressies", |   "home.column_settings.filter_regex": "Wegfilteren met reguliere expressies", | ||||||
|   "home.column_settings.show_reblogs": "Boosts tonen", |   "home.column_settings.show_reblogs": "Boosts tonen", | ||||||
|   "home.column_settings.show_replies": "Reacties tonen", |   "home.column_settings.show_replies": "Reacties tonen", | ||||||
| @@ -146,7 +146,7 @@ | |||||||
|   "navigation_bar.favourites": "Favorieten", |   "navigation_bar.favourites": "Favorieten", | ||||||
|   "navigation_bar.follow_requests": "Volgverzoeken", |   "navigation_bar.follow_requests": "Volgverzoeken", | ||||||
|   "navigation_bar.info": "Uitgebreide informatie", |   "navigation_bar.info": "Uitgebreide informatie", | ||||||
|   "navigation_bar.keyboard_shortcuts": "Toetsenbord sneltoetsen", |   "navigation_bar.keyboard_shortcuts": "Sneltoetsen", | ||||||
|   "navigation_bar.lists": "Lijsten", |   "navigation_bar.lists": "Lijsten", | ||||||
|   "navigation_bar.logout": "Afmelden", |   "navigation_bar.logout": "Afmelden", | ||||||
|   "navigation_bar.mutes": "Genegeerde gebruikers", |   "navigation_bar.mutes": "Genegeerde gebruikers", | ||||||
| @@ -180,7 +180,7 @@ | |||||||
|   "onboarding.page_six.almost_done": "Bijna klaar...", |   "onboarding.page_six.almost_done": "Bijna klaar...", | ||||||
|   "onboarding.page_six.appetoot": "Veel succes!", |   "onboarding.page_six.appetoot": "Veel succes!", | ||||||
|   "onboarding.page_six.apps_available": "Er zijn {apps} beschikbaar voor iOS, Android en andere platformen.", |   "onboarding.page_six.apps_available": "Er zijn {apps} beschikbaar voor iOS, Android en andere platformen.", | ||||||
|   "onboarding.page_six.github": "Mastodon kost niets, en is open-source- en vrije software. Je kan bugs melden, nieuwe mogelijkheden aanvragen en als ontwikkelaar meewerken op {github}.", |   "onboarding.page_six.github": "Mastodon kost niets en is vrije software. Je kan bugs melden, nieuwe mogelijkheden aanvragen en als ontwikkelaar meewerken op {github}.", | ||||||
|   "onboarding.page_six.guidelines": "communityrichtlijnen", |   "onboarding.page_six.guidelines": "communityrichtlijnen", | ||||||
|   "onboarding.page_six.read_guidelines": "Vergeet niet de {guidelines} van {domain} te lezen!", |   "onboarding.page_six.read_guidelines": "Vergeet niet de {guidelines} van {domain} te lezen!", | ||||||
|   "onboarding.page_six.various_app": "mobiele apps", |   "onboarding.page_six.various_app": "mobiele apps", | ||||||
|   | |||||||
| @@ -92,7 +92,7 @@ | |||||||
|   "empty_column.hashtag": "Nie ma wpisów oznaczonych tym hashtagiem. Możesz napisać pierwszy!", |   "empty_column.hashtag": "Nie ma wpisów oznaczonych tym hashtagiem. Możesz napisać pierwszy!", | ||||||
|   "empty_column.home": "Nie śledzisz nikogo. Odwiedź publiczną oś czasu lub użyj wyszukiwarki, aby znaleźć interesujące Cię profile.", |   "empty_column.home": "Nie śledzisz nikogo. Odwiedź publiczną oś czasu lub użyj wyszukiwarki, aby znaleźć interesujące Cię profile.", | ||||||
|   "empty_column.home.public_timeline": "publiczna oś czasu", |   "empty_column.home.public_timeline": "publiczna oś czasu", | ||||||
|   "empty_column.list": "Nie ma nic na tej liście.", |   "empty_column.list": "Nie ma nic na tej liście. Kiedy członkowie listy dodadzą nowe wpisy, pojawia się one tutaj.", | ||||||
|   "empty_column.notifications": "Nie masz żadnych powiadomień. Rozpocznij interakcje z innymi użytkownikami.", |   "empty_column.notifications": "Nie masz żadnych powiadomień. Rozpocznij interakcje z innymi użytkownikami.", | ||||||
|   "empty_column.public": "Tu nic nie ma! Napisz coś publicznie, lub dodaj ludzi z innych instancji, aby to wyświetlić.", |   "empty_column.public": "Tu nic nie ma! Napisz coś publicznie, lub dodaj ludzi z innych instancji, aby to wyświetlić.", | ||||||
|   "follow_request.authorize": "Autoryzuj", |   "follow_request.authorize": "Autoryzuj", | ||||||
|   | |||||||
| @@ -50,7 +50,7 @@ | |||||||
|   "column_header.unpin": "Desafixar", |   "column_header.unpin": "Desafixar", | ||||||
|   "column_subheading.navigation": "Navegação", |   "column_subheading.navigation": "Navegação", | ||||||
|   "column_subheading.settings": "Configurações", |   "column_subheading.settings": "Configurações", | ||||||
|   "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", |   "compose_form.hashtag_warning": "Esse toot não será listado em nenhuma hashtag por ser não listado. Somente toots públicos podem ser pesquisados por hashtag.", | ||||||
|   "compose_form.lock_disclaimer": "A sua conta não está {locked}. Qualquer pessoa pode te seguir e visualizar postagens direcionadas a apenas seguidores.", |   "compose_form.lock_disclaimer": "A sua conta não está {locked}. Qualquer pessoa pode te seguir e visualizar postagens direcionadas a apenas seguidores.", | ||||||
|   "compose_form.lock_disclaimer.lock": "trancada", |   "compose_form.lock_disclaimer.lock": "trancada", | ||||||
|   "compose_form.placeholder": "No que você está pensando?", |   "compose_form.placeholder": "No que você está pensando?", | ||||||
| @@ -92,7 +92,7 @@ | |||||||
|   "empty_column.hashtag": "Ainda não há qualquer conteúdo com essa hashtag.", |   "empty_column.hashtag": "Ainda não há qualquer conteúdo com essa hashtag.", | ||||||
|   "empty_column.home": "Você ainda não segue usuário algo. Visite a timeline {public} ou use o buscador para procurar e conhecer outros usuários.", |   "empty_column.home": "Você ainda não segue usuário algo. Visite a timeline {public} ou use o buscador para procurar e conhecer outros usuários.", | ||||||
|   "empty_column.home.public_timeline": "global", |   "empty_column.home.public_timeline": "global", | ||||||
|   "empty_column.list": "Ainda não há nada nesta lista.", |   "empty_column.list": "Ainda não há nada nesta lista. Quando membros dessa lista fizerem novas postagens, elas aparecerão aqui.", | ||||||
|   "empty_column.notifications": "Você ainda não possui notificações. Interaja com outros usuários para começar a conversar.", |   "empty_column.notifications": "Você ainda não possui notificações. Interaja com outros usuários para começar a conversar.", | ||||||
|   "empty_column.public": "Não há nada aqui! Escreva algo publicamente ou siga manualmente usuários de outras instâncias", |   "empty_column.public": "Não há nada aqui! Escreva algo publicamente ou siga manualmente usuários de outras instâncias", | ||||||
|   "follow_request.authorize": "Autorizar", |   "follow_request.authorize": "Autorizar", | ||||||
| @@ -223,7 +223,7 @@ | |||||||
|   "status.media_hidden": "Mídia escondida", |   "status.media_hidden": "Mídia escondida", | ||||||
|   "status.mention": "Mencionar @{name}", |   "status.mention": "Mencionar @{name}", | ||||||
|   "status.more": "Mais", |   "status.more": "Mais", | ||||||
|   "status.mute": "Mute @{name}", |   "status.mute": "Silenciar @{name}", | ||||||
|   "status.mute_conversation": "Silenciar conversa", |   "status.mute_conversation": "Silenciar conversa", | ||||||
|   "status.open": "Expandir", |   "status.open": "Expandir", | ||||||
|   "status.pin": "Fixar no perfil", |   "status.pin": "Fixar no perfil", | ||||||
|   | |||||||
| @@ -35,11 +35,11 @@ | |||||||
|   "column.community": "Local", |   "column.community": "Local", | ||||||
|   "column.favourites": "Favoritos", |   "column.favourites": "Favoritos", | ||||||
|   "column.follow_requests": "Seguidores Pendentes", |   "column.follow_requests": "Seguidores Pendentes", | ||||||
|   "column.home": "Home", |   "column.home": "Início", | ||||||
|   "column.lists": "Listas", |   "column.lists": "Listas", | ||||||
|   "column.mutes": "Utilizadores silenciados", |   "column.mutes": "Utilizadores silenciados", | ||||||
|   "column.notifications": "Notificações", |   "column.notifications": "Notificações", | ||||||
|   "column.pins": "Pinned toot", |   "column.pins": "Posts fixos", | ||||||
|   "column.public": "Global", |   "column.public": "Global", | ||||||
|   "column_back_button.label": "Voltar", |   "column_back_button.label": "Voltar", | ||||||
|   "column_header.hide_settings": "Esconder preferências", |   "column_header.hide_settings": "Esconder preferências", | ||||||
| @@ -47,7 +47,7 @@ | |||||||
|   "column_header.moveRight_settings": "Mover coluna para a direita", |   "column_header.moveRight_settings": "Mover coluna para a direita", | ||||||
|   "column_header.pin": "Fixar", |   "column_header.pin": "Fixar", | ||||||
|   "column_header.show_settings": "Mostrar preferências", |   "column_header.show_settings": "Mostrar preferências", | ||||||
|   "column_header.unpin": "Remover fixar", |   "column_header.unpin": "Desafixar", | ||||||
|   "column_subheading.navigation": "Navegação", |   "column_subheading.navigation": "Navegação", | ||||||
|   "column_subheading.settings": "Preferências", |   "column_subheading.settings": "Preferências", | ||||||
|   "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", |   "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", | ||||||
| @@ -92,7 +92,7 @@ | |||||||
|   "empty_column.hashtag": "Não foram encontradas publicações com essa hashtag.", |   "empty_column.hashtag": "Não foram encontradas publicações com essa hashtag.", | ||||||
|   "empty_column.home": "Ainda não segues qualquer utilizador. Visita {public} ou utiliza a pesquisa para procurar outros utilizadores.", |   "empty_column.home": "Ainda não segues qualquer utilizador. Visita {public} ou utiliza a pesquisa para procurar outros utilizadores.", | ||||||
|   "empty_column.home.public_timeline": "global", |   "empty_column.home.public_timeline": "global", | ||||||
|   "empty_column.list": "Ainda não existem publicações nesta lista.", |   "empty_column.list": "Ainda não existem publicações nesta lista. Quando membros desta lista fizerem novas publicações, elas aparecerão aqui.", | ||||||
|   "empty_column.notifications": "Não tens notificações. Interage com outros utilizadores para iniciar uma conversa.", |   "empty_column.notifications": "Não tens notificações. Interage com outros utilizadores para iniciar uma conversa.", | ||||||
|   "empty_column.public": "Não há nada aqui! Escreve algo publicamente ou segue outros utilizadores para ver aqui os conteúdos públicos", |   "empty_column.public": "Não há nada aqui! Escreve algo publicamente ou segue outros utilizadores para ver aqui os conteúdos públicos", | ||||||
|   "follow_request.authorize": "Autorizar", |   "follow_request.authorize": "Autorizar", | ||||||
| @@ -226,7 +226,7 @@ | |||||||
|   "status.mute": "Mute @{name}", |   "status.mute": "Mute @{name}", | ||||||
|   "status.mute_conversation": "Silenciar conversa", |   "status.mute_conversation": "Silenciar conversa", | ||||||
|   "status.open": "Expandir", |   "status.open": "Expandir", | ||||||
|   "status.pin": "Pin on profile", |   "status.pin": "Fixar no perfil", | ||||||
|   "status.reblog": "Partilhar", |   "status.reblog": "Partilhar", | ||||||
|   "status.reblogged_by": "{name} partilhou", |   "status.reblogged_by": "{name} partilhou", | ||||||
|   "status.reply": "Responder", |   "status.reply": "Responder", | ||||||
| @@ -234,7 +234,7 @@ | |||||||
|   "status.report": "Denunciar @{name}", |   "status.report": "Denunciar @{name}", | ||||||
|   "status.sensitive_toggle": "Clique para ver", |   "status.sensitive_toggle": "Clique para ver", | ||||||
|   "status.sensitive_warning": "Conteúdo sensível", |   "status.sensitive_warning": "Conteúdo sensível", | ||||||
|   "status.share": "Share", |   "status.share": "Compartilhar", | ||||||
|   "status.show_less": "Mostrar menos", |   "status.show_less": "Mostrar menos", | ||||||
|   "status.show_more": "Mostrar mais", |   "status.show_more": "Mostrar mais", | ||||||
|   "status.unmute_conversation": "Deixar de silenciar esta conversa", |   "status.unmute_conversation": "Deixar de silenciar esta conversa", | ||||||
|   | |||||||
| @@ -195,8 +195,8 @@ | |||||||
|   "privacy.private.short": "Iba sledujúci", |   "privacy.private.short": "Iba sledujúci", | ||||||
|   "privacy.public.long": "Pošli všetkým", |   "privacy.public.long": "Pošli všetkým", | ||||||
|   "privacy.public.short": "Verejne", |   "privacy.public.short": "Verejne", | ||||||
|   "privacy.unlisted.long": "Neposielať verejne", |   "privacy.unlisted.long": "Neposielať do verejných časových osí", | ||||||
|   "privacy.unlisted.short": "Nie je v zozname", |   "privacy.unlisted.short": "Verejne mimo osí", | ||||||
|   "relative_time.days": "{number}d", |   "relative_time.days": "{number}d", | ||||||
|   "relative_time.hours": "{number}h", |   "relative_time.hours": "{number}h", | ||||||
|   "relative_time.just_now": "now", |   "relative_time.just_now": "now", | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|   "account.media": "Mediji", |   "account.media": "Mediji", | ||||||
|   "account.mention": "Pomeni korisnika @{name}", |   "account.mention": "Pomeni korisnika @{name}", | ||||||
|   "account.moved_to": "{name} se pomerio na:", |   "account.moved_to": "{name} se pomerio na:", | ||||||
|   "account.mute": "Mutiraj @{name}", |   "account.mute": "Ućutkaj korisnika @{name}", | ||||||
|   "account.mute_notifications": "Isključi obaveštenja od korisnika @{name}", |   "account.mute_notifications": "Isključi obaveštenja od korisnika @{name}", | ||||||
|   "account.posts": "Statusa", |   "account.posts": "Statusa", | ||||||
|   "account.report": "Prijavi @{name}", |   "account.report": "Prijavi @{name}", | ||||||
| @@ -21,7 +21,7 @@ | |||||||
|   "account.unblock": "Odblokiraj korisnika @{name}", |   "account.unblock": "Odblokiraj korisnika @{name}", | ||||||
|   "account.unblock_domain": "Odblokiraj domen {domain}", |   "account.unblock_domain": "Odblokiraj domen {domain}", | ||||||
|   "account.unfollow": "Otprati", |   "account.unfollow": "Otprati", | ||||||
|   "account.unmute": "Odmutiraj @{name}", |   "account.unmute": "Ukloni ućutkavanje korisniku @{name}", | ||||||
|   "account.unmute_notifications": "Uključi nazad obaveštenja od korisnika @{name}", |   "account.unmute_notifications": "Uključi nazad obaveštenja od korisnika @{name}", | ||||||
|   "account.view_full_profile": "Vidi ceo profil", |   "account.view_full_profile": "Vidi ceo profil", | ||||||
|   "boost_modal.combo": "Možete pritisnuti {combo} da preskočite ovo sledeći put", |   "boost_modal.combo": "Možete pritisnuti {combo} da preskočite ovo sledeći put", | ||||||
| @@ -37,10 +37,10 @@ | |||||||
|   "column.follow_requests": "Zahtevi za praćenje", |   "column.follow_requests": "Zahtevi za praćenje", | ||||||
|   "column.home": "Početna", |   "column.home": "Početna", | ||||||
|   "column.lists": "Liste", |   "column.lists": "Liste", | ||||||
|   "column.mutes": "Mutirani korisnici", |   "column.mutes": "Ućutkani korisnici", | ||||||
|   "column.notifications": "Obaveštenja", |   "column.notifications": "Obaveštenja", | ||||||
|   "column.pins": "Prikačeni tutovi", |   "column.pins": "Prikačeni tutovi", | ||||||
|   "column.public": "Združena lajna", |   "column.public": "Federisana lajna", | ||||||
|   "column_back_button.label": "Nazad", |   "column_back_button.label": "Nazad", | ||||||
|   "column_header.hide_settings": "Sakrij postavke", |   "column_header.hide_settings": "Sakrij postavke", | ||||||
|   "column_header.moveLeft_settings": "Pomeri kolonu ulevo", |   "column_header.moveLeft_settings": "Pomeri kolonu ulevo", | ||||||
| @@ -50,6 +50,7 @@ | |||||||
|   "column_header.unpin": "Otkači", |   "column_header.unpin": "Otkači", | ||||||
|   "column_subheading.navigation": "Navigacija", |   "column_subheading.navigation": "Navigacija", | ||||||
|   "column_subheading.settings": "Postavke", |   "column_subheading.settings": "Postavke", | ||||||
|  |   "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", | ||||||
|   "compose_form.lock_disclaimer": "Vaš nalog nije {locked}. Svako može da Vas zaprati i da vidi objave namenjene samo Vašim pratiocima.", |   "compose_form.lock_disclaimer": "Vaš nalog nije {locked}. Svako može da Vas zaprati i da vidi objave namenjene samo Vašim pratiocima.", | ||||||
|   "compose_form.lock_disclaimer.lock": "zaključan", |   "compose_form.lock_disclaimer.lock": "zaključan", | ||||||
|   "compose_form.placeholder": "Šta Vam je na umu?", |   "compose_form.placeholder": "Šta Vam je na umu?", | ||||||
| @@ -66,9 +67,9 @@ | |||||||
|   "confirmations.delete_list.confirm": "Obriši", |   "confirmations.delete_list.confirm": "Obriši", | ||||||
|   "confirmations.delete_list.message": "Da li ste sigurni da želite da bespovratno obrišete ovu listu?", |   "confirmations.delete_list.message": "Da li ste sigurni da želite da bespovratno obrišete ovu listu?", | ||||||
|   "confirmations.domain_block.confirm": "Sakrij ceo domen", |   "confirmations.domain_block.confirm": "Sakrij ceo domen", | ||||||
|   "confirmations.domain_block.message": "Da li ste stvarno, stvarno sigurno da želite da blokirate ceo domen {domain}? U većini slučajeva, par dobrih blokiranja ili mutiranja su dovoljna i preporučljiva.", |   "confirmations.domain_block.message": "Da li ste stvarno, stvarno sigurno da želite da blokirate ceo domen {domain}? U većini slučajeva, par dobrih blokiranja ili ućutkavanja su dovoljna i preporučljiva.", | ||||||
|   "confirmations.mute.confirm": "Mutiraj", |   "confirmations.mute.confirm": "Ućutkaj", | ||||||
|   "confirmations.mute.message": "Da li stvarno želite da mutirate korisnika {name}?", |   "confirmations.mute.message": "Da li stvarno želite da ućutkate korisnika {name}?", | ||||||
|   "confirmations.unfollow.confirm": "Otprati", |   "confirmations.unfollow.confirm": "Otprati", | ||||||
|   "confirmations.unfollow.message": "Da li ste sigurni da želite da otpratite korisnika {name}?", |   "confirmations.unfollow.message": "Da li ste sigurni da želite da otpratite korisnika {name}?", | ||||||
|   "embed.instructions": "Ugradi ovaj status na Vaš veb sajt kopiranjem koda ispod.", |   "embed.instructions": "Ugradi ovaj status na Vaš veb sajt kopiranjem koda ispod.", | ||||||
| @@ -148,10 +149,10 @@ | |||||||
|   "navigation_bar.keyboard_shortcuts": "Prečice na tastaturi", |   "navigation_bar.keyboard_shortcuts": "Prečice na tastaturi", | ||||||
|   "navigation_bar.lists": "Liste", |   "navigation_bar.lists": "Liste", | ||||||
|   "navigation_bar.logout": "Odjava", |   "navigation_bar.logout": "Odjava", | ||||||
|   "navigation_bar.mutes": "Mutirani korisnici", |   "navigation_bar.mutes": "Ućutkani korisnici", | ||||||
|   "navigation_bar.pins": "Prikačeni tutovi", |   "navigation_bar.pins": "Prikačeni tutovi", | ||||||
|   "navigation_bar.preferences": "Podešavanja", |   "navigation_bar.preferences": "Podešavanja", | ||||||
|   "navigation_bar.public_timeline": "Združena lajna", |   "navigation_bar.public_timeline": "Federisana lajna", | ||||||
|   "notification.favourite": "{name} je stavio Vaš status kao omiljeni", |   "notification.favourite": "{name} je stavio Vaš status kao omiljeni", | ||||||
|   "notification.follow": "{name} Vas je zapratio", |   "notification.follow": "{name} Vas je zapratio", | ||||||
|   "notification.mention": "{name} Vas je pomenuo", |   "notification.mention": "{name} Vas je pomenuo", | ||||||
| @@ -169,7 +170,7 @@ | |||||||
|   "notifications.column_settings.sound": "Puštaj zvuk", |   "notifications.column_settings.sound": "Puštaj zvuk", | ||||||
|   "onboarding.done": "Gotovo", |   "onboarding.done": "Gotovo", | ||||||
|   "onboarding.next": "Sledeće", |   "onboarding.next": "Sledeće", | ||||||
|   "onboarding.page_five.public_timelines": "Lokalna lajna prikazuje sve javne statuse od svih na domenu {domain}. Združena lajna prikazuje javne statuse od svih ljudi koje prate korisnici sa domena {domain}. Ovo su javne lajne, sjajan način da otkrijete nove ljude.", |   "onboarding.page_five.public_timelines": "Lokalna lajna prikazuje sve javne statuse od svih na domenu {domain}. Federisana lajna prikazuje javne statuse od svih ljudi koje prate korisnici sa domena {domain}. Ovo su javne lajne, sjajan način da otkrijete nove ljude.", | ||||||
|   "onboarding.page_four.home": "Početna lajna prikazuje statuse ljudi koje Vi pratite.", |   "onboarding.page_four.home": "Početna lajna prikazuje statuse ljudi koje Vi pratite.", | ||||||
|   "onboarding.page_four.notifications": "Kolona sa obaveštenjima Vam prikazuje kada neko priča sa Vama.", |   "onboarding.page_four.notifications": "Kolona sa obaveštenjima Vam prikazuje kada neko priča sa Vama.", | ||||||
|   "onboarding.page_one.federation": "Mastodont je mreža nezavisnih servera koji se uvezuju da naprave jednu veću društvenu mrežu. Ove servere zovemo instancama.", |   "onboarding.page_one.federation": "Mastodont je mreža nezavisnih servera koji se uvezuju da naprave jednu veću društvenu mrežu. Ove servere zovemo instancama.", | ||||||
| @@ -213,6 +214,7 @@ | |||||||
|   "search_popout.tips.user": "korisnik", |   "search_popout.tips.user": "korisnik", | ||||||
|   "search_results.total": "{count, number} {count, plural, one {rezultat} few {rezultata} other {rezultata}}", |   "search_results.total": "{count, number} {count, plural, one {rezultat} few {rezultata} other {rezultata}}", | ||||||
|   "standalone.public_title": "Pogled iznutra...", |   "standalone.public_title": "Pogled iznutra...", | ||||||
|  |   "status.block": "Block @{name}", | ||||||
|   "status.cannot_reblog": "Ovaj status ne može da se podrži", |   "status.cannot_reblog": "Ovaj status ne može da se podrži", | ||||||
|   "status.delete": "Obriši", |   "status.delete": "Obriši", | ||||||
|   "status.embed": "Ugradi na sajt", |   "status.embed": "Ugradi na sajt", | ||||||
| @@ -221,7 +223,8 @@ | |||||||
|   "status.media_hidden": "Multimedija sakrivena", |   "status.media_hidden": "Multimedija sakrivena", | ||||||
|   "status.mention": "Pomeni korisnika @{name}", |   "status.mention": "Pomeni korisnika @{name}", | ||||||
|   "status.more": "Još", |   "status.more": "Još", | ||||||
|   "status.mute_conversation": "Mutiraj prepisku", |   "status.mute": "Mute @{name}", | ||||||
|  |   "status.mute_conversation": "Ućutkaj prepisku", | ||||||
|   "status.open": "Proširi ovaj status", |   "status.open": "Proširi ovaj status", | ||||||
|   "status.pin": "Prikači na profil", |   "status.pin": "Prikači na profil", | ||||||
|   "status.reblog": "Podrži", |   "status.reblog": "Podrži", | ||||||
| @@ -237,7 +240,7 @@ | |||||||
|   "status.unmute_conversation": "Uključi prepisku", |   "status.unmute_conversation": "Uključi prepisku", | ||||||
|   "status.unpin": "Otkači sa profila", |   "status.unpin": "Otkači sa profila", | ||||||
|   "tabs_bar.compose": "Napiši", |   "tabs_bar.compose": "Napiši", | ||||||
|   "tabs_bar.federated_timeline": "Združeno", |   "tabs_bar.federated_timeline": "Federisano", | ||||||
|   "tabs_bar.home": "Početna", |   "tabs_bar.home": "Početna", | ||||||
|   "tabs_bar.local_timeline": "Lokalno", |   "tabs_bar.local_timeline": "Lokalno", | ||||||
|   "tabs_bar.notifications": "Obaveštenja", |   "tabs_bar.notifications": "Obaveštenja", | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ | |||||||
|   "account.media": "Медији", |   "account.media": "Медији", | ||||||
|   "account.mention": "Помени корисника @{name}", |   "account.mention": "Помени корисника @{name}", | ||||||
|   "account.moved_to": "{name} се померио на:", |   "account.moved_to": "{name} се померио на:", | ||||||
|   "account.mute": "Мутирај @{name}", |   "account.mute": "Ућуткај корисника @{name}", | ||||||
|   "account.mute_notifications": "Искључи обавештења од корисника @{name}", |   "account.mute_notifications": "Искључи обавештења од корисника @{name}", | ||||||
|   "account.posts": "Статуса", |   "account.posts": "Статуса", | ||||||
|   "account.report": "Пријави @{name}", |   "account.report": "Пријави @{name}", | ||||||
| @@ -21,7 +21,7 @@ | |||||||
|   "account.unblock": "Одблокирај корисника @{name}", |   "account.unblock": "Одблокирај корисника @{name}", | ||||||
|   "account.unblock_domain": "Одблокирај домен {domain}", |   "account.unblock_domain": "Одблокирај домен {domain}", | ||||||
|   "account.unfollow": "Отпрати", |   "account.unfollow": "Отпрати", | ||||||
|   "account.unmute": "Одмутирај @{name}", |   "account.unmute": "Уклони ућуткавање кориснику @{name}", | ||||||
|   "account.unmute_notifications": "Укључи назад обавештења од корисника @{name}", |   "account.unmute_notifications": "Укључи назад обавештења од корисника @{name}", | ||||||
|   "account.view_full_profile": "Види цео профил", |   "account.view_full_profile": "Види цео профил", | ||||||
|   "boost_modal.combo": "Можете притиснути {combo} да прескочите ово следећи пут", |   "boost_modal.combo": "Можете притиснути {combo} да прескочите ово следећи пут", | ||||||
| @@ -37,10 +37,10 @@ | |||||||
|   "column.follow_requests": "Захтеви за праћење", |   "column.follow_requests": "Захтеви за праћење", | ||||||
|   "column.home": "Почетна", |   "column.home": "Почетна", | ||||||
|   "column.lists": "Листе", |   "column.lists": "Листе", | ||||||
|   "column.mutes": "Мутирани корисници", |   "column.mutes": "Ућуткани корисници", | ||||||
|   "column.notifications": "Обавештења", |   "column.notifications": "Обавештења", | ||||||
|   "column.pins": "Прикачени тутови", |   "column.pins": "Прикачени тутови", | ||||||
|   "column.public": "Здружена лајна", |   "column.public": "Федерисана лајна", | ||||||
|   "column_back_button.label": "Назад", |   "column_back_button.label": "Назад", | ||||||
|   "column_header.hide_settings": "Сакриј поставке", |   "column_header.hide_settings": "Сакриј поставке", | ||||||
|   "column_header.moveLeft_settings": "Помери колону улево", |   "column_header.moveLeft_settings": "Помери колону улево", | ||||||
| @@ -67,9 +67,9 @@ | |||||||
|   "confirmations.delete_list.confirm": "Обриши", |   "confirmations.delete_list.confirm": "Обриши", | ||||||
|   "confirmations.delete_list.message": "Да ли сте сигурни да желите да бесповратно обришете ову листу?", |   "confirmations.delete_list.message": "Да ли сте сигурни да желите да бесповратно обришете ову листу?", | ||||||
|   "confirmations.domain_block.confirm": "Сакриј цео домен", |   "confirmations.domain_block.confirm": "Сакриј цео домен", | ||||||
|   "confirmations.domain_block.message": "Да ли сте стварно, стварно сигурно да желите да блокирате цео домен {domain}? У већини случајева, пар добрих блокирања или мутирања су довољна и препоручљива.", |   "confirmations.domain_block.message": "Да ли сте стварно, стварно сигурно да желите да блокирате цео домен {domain}? У већини случајева, пар добрих блокирања или ућуткавања су довољна и препоручљива.", | ||||||
|   "confirmations.mute.confirm": "Мутирај", |   "confirmations.mute.confirm": "Ућуткај", | ||||||
|   "confirmations.mute.message": "Да ли стварно желите да мутирате корисника {name}?", |   "confirmations.mute.message": "Да ли стварно желите да ућуткате корисника {name}?", | ||||||
|   "confirmations.unfollow.confirm": "Отпрати", |   "confirmations.unfollow.confirm": "Отпрати", | ||||||
|   "confirmations.unfollow.message": "Да ли сте сигурни да желите да отпратите корисника {name}?", |   "confirmations.unfollow.message": "Да ли сте сигурни да желите да отпратите корисника {name}?", | ||||||
|   "embed.instructions": "Угради овај статус на Ваш веб сајт копирањем кода испод.", |   "embed.instructions": "Угради овај статус на Ваш веб сајт копирањем кода испод.", | ||||||
| @@ -149,10 +149,10 @@ | |||||||
|   "navigation_bar.keyboard_shortcuts": "Пречице на тастатури", |   "navigation_bar.keyboard_shortcuts": "Пречице на тастатури", | ||||||
|   "navigation_bar.lists": "Листе", |   "navigation_bar.lists": "Листе", | ||||||
|   "navigation_bar.logout": "Одјава", |   "navigation_bar.logout": "Одјава", | ||||||
|   "navigation_bar.mutes": "Мутирани корисници", |   "navigation_bar.mutes": "Ућуткани корисници", | ||||||
|   "navigation_bar.pins": "Прикачени тутови", |   "navigation_bar.pins": "Прикачени тутови", | ||||||
|   "navigation_bar.preferences": "Подешавања", |   "navigation_bar.preferences": "Подешавања", | ||||||
|   "navigation_bar.public_timeline": "Здружена лајна", |   "navigation_bar.public_timeline": "Федерисана лајна", | ||||||
|   "notification.favourite": "{name} је ставио Ваш статус као омиљени", |   "notification.favourite": "{name} је ставио Ваш статус као омиљени", | ||||||
|   "notification.follow": "{name} Вас је запратио", |   "notification.follow": "{name} Вас је запратио", | ||||||
|   "notification.mention": "{name} Вас је поменуо", |   "notification.mention": "{name} Вас је поменуо", | ||||||
| @@ -170,7 +170,7 @@ | |||||||
|   "notifications.column_settings.sound": "Пуштај звук", |   "notifications.column_settings.sound": "Пуштај звук", | ||||||
|   "onboarding.done": "Готово", |   "onboarding.done": "Готово", | ||||||
|   "onboarding.next": "Следеће", |   "onboarding.next": "Следеће", | ||||||
|   "onboarding.page_five.public_timelines": "Локална лајна приказује све јавне статусе од свих на домену {domain}. Здружена лајна приказује јавне статусе од свих људи које прате корисници са домена {domain}. Ово су јавне лајне, сјајан начин да откријете нове људе.", |   "onboarding.page_five.public_timelines": "Локална лајна приказује све јавне статусе од свих на домену {domain}. Федерисана лајна приказује јавне статусе од свих људи које прате корисници са домена {domain}. Ово су јавне лајне, сјајан начин да откријете нове људе.", | ||||||
|   "onboarding.page_four.home": "Почетна лајна приказује статусе људи које Ви пратите.", |   "onboarding.page_four.home": "Почетна лајна приказује статусе људи које Ви пратите.", | ||||||
|   "onboarding.page_four.notifications": "Колона са обавештењима Вам приказује када неко прича са Вама.", |   "onboarding.page_four.notifications": "Колона са обавештењима Вам приказује када неко прича са Вама.", | ||||||
|   "onboarding.page_one.federation": "Мастодонт је мрежа независних сервера који се увезују да направе једну већу друштвену мрежу. Ове сервере зовемо инстанцама.", |   "onboarding.page_one.federation": "Мастодонт је мрежа независних сервера који се увезују да направе једну већу друштвену мрежу. Ове сервере зовемо инстанцама.", | ||||||
| @@ -224,7 +224,7 @@ | |||||||
|   "status.mention": "Помени корисника @{name}", |   "status.mention": "Помени корисника @{name}", | ||||||
|   "status.more": "Још", |   "status.more": "Још", | ||||||
|   "status.mute": "Mute @{name}", |   "status.mute": "Mute @{name}", | ||||||
|   "status.mute_conversation": "Мутирај преписку", |   "status.mute_conversation": "Ућуткај преписку", | ||||||
|   "status.open": "Прошири овај статус", |   "status.open": "Прошири овај статус", | ||||||
|   "status.pin": "Прикачи на профил", |   "status.pin": "Прикачи на профил", | ||||||
|   "status.reblog": "Подржи", |   "status.reblog": "Подржи", | ||||||
| @@ -240,7 +240,7 @@ | |||||||
|   "status.unmute_conversation": "Укључи преписку", |   "status.unmute_conversation": "Укључи преписку", | ||||||
|   "status.unpin": "Откачи са профила", |   "status.unpin": "Откачи са профила", | ||||||
|   "tabs_bar.compose": "Напиши", |   "tabs_bar.compose": "Напиши", | ||||||
|   "tabs_bar.federated_timeline": "Здружено", |   "tabs_bar.federated_timeline": "Федерисано", | ||||||
|   "tabs_bar.home": "Почетна", |   "tabs_bar.home": "Почетна", | ||||||
|   "tabs_bar.local_timeline": "Локално", |   "tabs_bar.local_timeline": "Локално", | ||||||
|   "tabs_bar.notifications": "Обавештења", |   "tabs_bar.notifications": "Обавештења", | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								app/javascript/mastodon/locales/whitelist_sr-Latn.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,2 @@ | |||||||
|  | [ | ||||||
|  | ] | ||||||
| @@ -50,7 +50,7 @@ | |||||||
|   "column_header.unpin": "取消固定", |   "column_header.unpin": "取消固定", | ||||||
|   "column_subheading.navigation": "导航", |   "column_subheading.navigation": "导航", | ||||||
|   "column_subheading.settings": "设置", |   "column_subheading.settings": "设置", | ||||||
|   "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", |   "compose_form.hashtag_warning": "这条嘟文被设置为“不公开”,因此它不会出现在任何话题标签的列表下。只有公开的嘟文才能通过话题标签进行搜索。", | ||||||
|   "compose_form.lock_disclaimer": "你的帐户没有{locked}。任何人都可以在关注你后立即查看仅关注者可见的嘟文。", |   "compose_form.lock_disclaimer": "你的帐户没有{locked}。任何人都可以在关注你后立即查看仅关注者可见的嘟文。", | ||||||
|   "compose_form.lock_disclaimer.lock": "开启保护", |   "compose_form.lock_disclaimer.lock": "开启保护", | ||||||
|   "compose_form.placeholder": "在想啥?", |   "compose_form.placeholder": "在想啥?", | ||||||
| @@ -139,6 +139,7 @@ | |||||||
|   "loading_indicator.label": "加载中……", |   "loading_indicator.label": "加载中……", | ||||||
|   "media_gallery.toggle_visible": "切换显示/隐藏", |   "media_gallery.toggle_visible": "切换显示/隐藏", | ||||||
|   "missing_indicator.label": "找不到内容", |   "missing_indicator.label": "找不到内容", | ||||||
|  |   "missing_indicator.sublabel": "无法找到此资源", | ||||||
|   "mute_modal.hide_notifications": "同时隐藏来自这个用户的通知", |   "mute_modal.hide_notifications": "同时隐藏来自这个用户的通知", | ||||||
|   "navigation_bar.blocks": "被屏蔽的用户", |   "navigation_bar.blocks": "被屏蔽的用户", | ||||||
|   "navigation_bar.community_timeline": "本站时间轴", |   "navigation_bar.community_timeline": "本站时间轴", | ||||||
| @@ -170,11 +171,12 @@ | |||||||
|   "notifications.column_settings.sound": "播放音效", |   "notifications.column_settings.sound": "播放音效", | ||||||
|   "onboarding.done": "出发!", |   "onboarding.done": "出发!", | ||||||
|   "onboarding.next": "下一步", |   "onboarding.next": "下一步", | ||||||
|   "onboarding.page_five.public_timelines": "本站时间轴显示的是由本站({domain})用户发布的所有公开嘟文。跨站公共时间轴显示的的是由本站用户关注对象所发布的所有公开嘟文。这些就是寻人好去处的公共时间轴啦。", |   "onboarding.page_five.public_timelines": "“本站时间轴”显示的是由本站({domain})用户发布的所有公开嘟文。“跨站公共时间轴”显示的的是由本站用户关注对象所发布的所有公开嘟文。这些就是寻人好去处的公共时间轴啦。", | ||||||
|   "onboarding.page_four.home": "你的主页时间轴上显示的是你的关注对象所发布的嘟文。", |   "onboarding.page_four.home": "你的“主页”时间轴上显示的是你的关注对象所发布的嘟文。", | ||||||
|   "onboarding.page_four.notifications": "如果有人与你互动了,他们就会出现在通知栏中哦~", |   "onboarding.page_four.notifications": "如果有人与你互动了,他们就会出现在“通知”栏中哦~", | ||||||
|   "onboarding.page_one.federation": "Mastodon 是由一系列独立的服务器共同打造的强大的社交网络,我们将这些各自独立而又相互连接的服务器叫做实例。", |   "onboarding.page_one.federation": "Mastodon 是由一系列独立的服务器共同打造的强大的社交网络,我们将这些各自独立而又相互连接的服务器叫做“实例”。", | ||||||
|   "onboarding.page_one.handle": "你是在 {domain} 上注册的,所以你的完整用户地址是 {handle}。", |   "onboarding.page_one.full_handle": "你的完整用户地址", | ||||||
|  |   "onboarding.page_one.handle_hint": "你的朋友们需要这个才能通过搜索功能找到你。", | ||||||
|   "onboarding.page_one.welcome": "欢迎来到 Mastodon!", |   "onboarding.page_one.welcome": "欢迎来到 Mastodon!", | ||||||
|   "onboarding.page_six.admin": "{admin} 是你所在服务器实例的管理员。", |   "onboarding.page_six.admin": "{admin} 是你所在服务器实例的管理员。", | ||||||
|   "onboarding.page_six.almost_done": "差不多了……", |   "onboarding.page_six.almost_done": "差不多了……", | ||||||
| @@ -184,8 +186,8 @@ | |||||||
|   "onboarding.page_six.guidelines": "社区指南", |   "onboarding.page_six.guidelines": "社区指南", | ||||||
|   "onboarding.page_six.read_guidelines": "别忘了看看 {domain} 的{guidelines}!", |   "onboarding.page_six.read_guidelines": "别忘了看看 {domain} 的{guidelines}!", | ||||||
|   "onboarding.page_six.various_app": "移动设备应用", |   "onboarding.page_six.various_app": "移动设备应用", | ||||||
|   "onboarding.page_three.profile": "你可以修改你的个人资料,比如头像、简介和昵称等偏好设置。", |   "onboarding.page_three.profile": "你还可以修改你的个人资料,比如头像、简介和昵称等偏好设置。", | ||||||
|   "onboarding.page_three.search": "你可以通过搜索功能寻找用户和话题标签,比如{illustration}或者{introductions}。如果你想搜索其他实例上的用户,就需要输入完整用户地址(@用户名@域名)哦。", |   "onboarding.page_three.search": "你可以通过搜索功能寻找用户和话题标签,比如“{illustration}”,或是“{introductions}”。如果你想搜索其他实例上的用户,就需要输入完整用户地址(@用户名@域名)哦。", | ||||||
|   "onboarding.page_two.compose": "在撰写栏中开始嘟嘟吧!下方的按钮分别可以用来上传图片、修改嘟文可见范围,以及添加警告信息。", |   "onboarding.page_two.compose": "在撰写栏中开始嘟嘟吧!下方的按钮分别可以用来上传图片、修改嘟文可见范围,以及添加警告信息。", | ||||||
|   "onboarding.skip": "跳过", |   "onboarding.skip": "跳过", | ||||||
|   "privacy.change": "设置嘟文可见范围", |   "privacy.change": "设置嘟文可见范围", | ||||||
| @@ -197,6 +199,8 @@ | |||||||
|   "privacy.public.short": "公开", |   "privacy.public.short": "公开", | ||||||
|   "privacy.unlisted.long": "所有人可见,但不会出现在公共时间轴上", |   "privacy.unlisted.long": "所有人可见,但不会出现在公共时间轴上", | ||||||
|   "privacy.unlisted.short": "不公开", |   "privacy.unlisted.short": "不公开", | ||||||
|  |   "regeneration_indicator.label": "加载中……", | ||||||
|  |   "regeneration_indicator.sublabel": "你的主页时间轴正在准备中!", | ||||||
|   "relative_time.days": "{number}天", |   "relative_time.days": "{number}天", | ||||||
|   "relative_time.hours": "{number}时", |   "relative_time.hours": "{number}时", | ||||||
|   "relative_time.just_now": "刚刚", |   "relative_time.just_now": "刚刚", | ||||||
| @@ -214,7 +218,7 @@ | |||||||
|   "search_popout.tips.user": "用户", |   "search_popout.tips.user": "用户", | ||||||
|   "search_results.total": "共 {count, number} 个结果", |   "search_results.total": "共 {count, number} 个结果", | ||||||
|   "standalone.public_title": "大家都在干啥?", |   "standalone.public_title": "大家都在干啥?", | ||||||
|   "status.block": "Block @{name}", |   "status.block": "屏蔽 @{name}", | ||||||
|   "status.cannot_reblog": "无法转嘟这条嘟文", |   "status.cannot_reblog": "无法转嘟这条嘟文", | ||||||
|   "status.delete": "删除", |   "status.delete": "删除", | ||||||
|   "status.embed": "嵌入", |   "status.embed": "嵌入", | ||||||
| @@ -223,7 +227,7 @@ | |||||||
|   "status.media_hidden": "隐藏媒体内容", |   "status.media_hidden": "隐藏媒体内容", | ||||||
|   "status.mention": "提及 @{name}", |   "status.mention": "提及 @{name}", | ||||||
|   "status.more": "更多", |   "status.more": "更多", | ||||||
|   "status.mute": "Mute @{name}", |   "status.mute": "隐藏 @{name}", | ||||||
|   "status.mute_conversation": "隐藏此对话", |   "status.mute_conversation": "隐藏此对话", | ||||||
|   "status.open": "展开嘟文", |   "status.open": "展开嘟文", | ||||||
|   "status.pin": "在个人资料页面置顶", |   "status.pin": "在个人资料页面置顶", | ||||||
|   | |||||||
| @@ -30,7 +30,7 @@ const initialTimeline = ImmutableMap({ | |||||||
|   items: ImmutableList(), |   items: ImmutableList(), | ||||||
| }); | }); | ||||||
|  |  | ||||||
| const normalizeTimeline = (state, timeline, statuses, next) => { | const normalizeTimeline = (state, timeline, statuses, next, isPartial) => { | ||||||
|   const oldIds    = state.getIn([timeline, 'items'], ImmutableList()); |   const oldIds    = state.getIn([timeline, 'items'], ImmutableList()); | ||||||
|   const ids       = ImmutableList(statuses.map(status => status.get('id'))).filter(newId => !oldIds.includes(newId)); |   const ids       = ImmutableList(statuses.map(status => status.get('id'))).filter(newId => !oldIds.includes(newId)); | ||||||
|   const wasLoaded = state.getIn([timeline, 'loaded']); |   const wasLoaded = state.getIn([timeline, 'loaded']); | ||||||
| @@ -40,7 +40,8 @@ const normalizeTimeline = (state, timeline, statuses, next) => { | |||||||
|     mMap.set('loaded', true); |     mMap.set('loaded', true); | ||||||
|     mMap.set('isLoading', false); |     mMap.set('isLoading', false); | ||||||
|     if (!hadNext) mMap.set('next', next); |     if (!hadNext) mMap.set('next', next); | ||||||
|     mMap.set('items', wasLoaded ? ids.concat(oldIds) : ids); |     mMap.set('items', wasLoaded ? ids.concat(oldIds) : oldIds.concat(ids)); | ||||||
|  |     mMap.set('isPartial', isPartial); | ||||||
|   })); |   })); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -124,7 +125,7 @@ export default function timelines(state = initialState, action) { | |||||||
|   case TIMELINE_EXPAND_FAIL: |   case TIMELINE_EXPAND_FAIL: | ||||||
|     return state.update(action.timeline, initialTimeline, map => map.set('isLoading', false)); |     return state.update(action.timeline, initialTimeline, map => map.set('isLoading', false)); | ||||||
|   case TIMELINE_REFRESH_SUCCESS: |   case TIMELINE_REFRESH_SUCCESS: | ||||||
|     return normalizeTimeline(state, action.timeline, fromJS(action.statuses), action.next); |     return normalizeTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial); | ||||||
|   case TIMELINE_EXPAND_SUCCESS: |   case TIMELINE_EXPAND_SUCCESS: | ||||||
|     return appendNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next); |     return appendNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next); | ||||||
|   case TIMELINE_UPDATE: |   case TIMELINE_UPDATE: | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								app/javascript/packs/mailer.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1 @@ | |||||||
|  | require('../styles/mailer.scss'); | ||||||
| @@ -6,7 +6,6 @@ | |||||||
|  |  | ||||||
| @import 'mastodon/reset'; | @import 'mastodon/reset'; | ||||||
| @import 'mastodon/basics'; | @import 'mastodon/basics'; | ||||||
| @import 'mastodon/modal'; |  | ||||||
| @import 'mastodon/containers'; | @import 'mastodon/containers'; | ||||||
| @import 'mastodon/lists'; | @import 'mastodon/lists'; | ||||||
| @import 'mastodon/footer'; | @import 'mastodon/footer'; | ||||||
| @@ -15,7 +14,9 @@ | |||||||
| @import 'mastodon/forms'; | @import 'mastodon/forms'; | ||||||
| @import 'mastodon/accounts'; | @import 'mastodon/accounts'; | ||||||
| @import 'mastodon/stream_entries'; | @import 'mastodon/stream_entries'; | ||||||
|  | @import 'mastodon/boost'; | ||||||
| @import 'mastodon/components'; | @import 'mastodon/components'; | ||||||
|  | @import 'mastodon/modal'; | ||||||
| @import 'mastodon/emoji_picker'; | @import 'mastodon/emoji_picker'; | ||||||
| @import 'mastodon/about'; | @import 'mastodon/about'; | ||||||
| @import 'mastodon/tables'; | @import 'mastodon/tables'; | ||||||
|   | |||||||
							
								
								
									
										546
									
								
								app/javascript/styles/mailer.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,546 @@ | |||||||
|  | @import 'mastodon/variables'; | ||||||
|  | @import 'fonts/roboto'; | ||||||
|  |  | ||||||
|  | table, | ||||||
|  | td, | ||||||
|  | div { | ||||||
|  |   box-sizing: border-box; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | html, | ||||||
|  | body { | ||||||
|  |   width: 100% !important; | ||||||
|  |   min-width: 100%; | ||||||
|  |   margin: 0; | ||||||
|  |   padding: 0; | ||||||
|  |   -webkit-text-size-adjust: 100%; | ||||||
|  |   -ms-text-size-adjust: 100%; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .email-body { | ||||||
|  |   td, | ||||||
|  |   div, | ||||||
|  |   a, | ||||||
|  |   span { | ||||||
|  |     line-height: inherit; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | a { | ||||||
|  |   &, | ||||||
|  |   &:visited, | ||||||
|  |   span { | ||||||
|  |     text-decoration: none; | ||||||
|  |     color: $ui-highlight-color; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   #outlook & { | ||||||
|  |     padding: 0; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | img { | ||||||
|  |   outline: none; | ||||||
|  |   border: 0; | ||||||
|  |   text-decoration: none; | ||||||
|  |   -ms-interpolation-mode: bicubic; | ||||||
|  |   clear: both; | ||||||
|  |   line-height: 100%; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | table { | ||||||
|  |   border-spacing: 0; | ||||||
|  |   mso-table-lspace: 0; | ||||||
|  |   mso-table-rspace: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | td { | ||||||
|  |   vertical-align: top; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .email-table, | ||||||
|  | .content-section, | ||||||
|  | .column, | ||||||
|  | .column-cell { | ||||||
|  |   width: 100%; | ||||||
|  |   min-width: 100%; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .email-body { | ||||||
|  |   font-size: 0 !important; | ||||||
|  |   line-height: 100%; | ||||||
|  |   text-align: center; | ||||||
|  |   padding-left: 16px; | ||||||
|  |   padding-right: 16px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .email-start { | ||||||
|  |   padding-top: 32px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .email-end { | ||||||
|  |   padding-bottom: 32px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .email-body, | ||||||
|  | html, | ||||||
|  | body { | ||||||
|  |   background-color: lighten($ui-base-color, 4%); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .email-container, | ||||||
|  | .email-row, | ||||||
|  | .col-0, | ||||||
|  | .col-1, | ||||||
|  | .col-2, | ||||||
|  | .col-3, | ||||||
|  | .col-4, | ||||||
|  | .col-5, | ||||||
|  | .col-6, { | ||||||
|  |   font-size: 0; | ||||||
|  |   display: inline-block; | ||||||
|  |   width: 100%; | ||||||
|  |   min-width: 100%; | ||||||
|  |   min-width: 0 !important; | ||||||
|  |   vertical-align: top; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .content-cell { | ||||||
|  |   width: 100%; | ||||||
|  |   min-width: 100%; | ||||||
|  |   min-width: 0 !important; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .column-cell { | ||||||
|  |   padding-top: 16px; | ||||||
|  |   padding-bottom: 16px; | ||||||
|  |   vertical-align: top; | ||||||
|  |  | ||||||
|  |   &.button-cell { | ||||||
|  |     padding-top: 0; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .email-container { | ||||||
|  |   max-width: 632px; | ||||||
|  |   margin: 0 auto; | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .email-row { | ||||||
|  |   display: block; | ||||||
|  |   max-width: 600px !important; | ||||||
|  |   margin: 0 auto; | ||||||
|  |   text-align: center; | ||||||
|  |   clear: both; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .col-0 { | ||||||
|  |   max-width: 50px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .col-1 { | ||||||
|  |   max-width: 100px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .col-2 { | ||||||
|  |   max-width: 200px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .col-3 { | ||||||
|  |   max-width: 300px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .col-4 { | ||||||
|  |   max-width: 400px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .col-5 { | ||||||
|  |   max-width: 500px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .col-6 { | ||||||
|  |   max-width: 600px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .column-cell, | ||||||
|  | .column-cell td, | ||||||
|  | p { | ||||||
|  |   font-family: Helvetica, Arial, sans-serif; | ||||||
|  |  | ||||||
|  |   @media only screen { | ||||||
|  |     font-family: 'mastodon-font-sans-serif', sans-serif !important; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .email-body .column-cell, | ||||||
|  | .column-cell, | ||||||
|  | p { | ||||||
|  |   font-size: 15px; | ||||||
|  |   line-height: 23px; | ||||||
|  |   color: $ui-primary-color; | ||||||
|  |   mso-line-height-rule: exactly; | ||||||
|  |   text-rendering: optimizelegibility; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | p { | ||||||
|  |   display: block; | ||||||
|  |   margin-top: 0; | ||||||
|  |   margin-bottom: 16px; | ||||||
|  |  | ||||||
|  |   &.small { | ||||||
|  |     font-size: 13px; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &.lead { | ||||||
|  |     font-size: 19px; | ||||||
|  |     line-height: 27px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | h1, | ||||||
|  | h2, | ||||||
|  | h3, | ||||||
|  | h4, | ||||||
|  | h5, | ||||||
|  | h6 { | ||||||
|  |   color: $ui-secondary-color; | ||||||
|  |   margin-left: 0; | ||||||
|  |   margin-right: 0; | ||||||
|  |   margin-top: 20px; | ||||||
|  |   margin-bottom: 8px; | ||||||
|  |   padding: 0; | ||||||
|  |   font-weight: 500; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | h1 { | ||||||
|  |   font-size: 26px; | ||||||
|  |   line-height: 36px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | h2 { | ||||||
|  |   font-size: 23px; | ||||||
|  |   line-height: 30px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | h3 { | ||||||
|  |   font-size: 19px; | ||||||
|  |   line-height: 25px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | h5 { | ||||||
|  |   font-size: 16px; | ||||||
|  |   line-height: 21px; | ||||||
|  |   font-weight: 700; | ||||||
|  |   color: lighten($ui-base-color, 34%); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .input-cell { | ||||||
|  |   h5 { | ||||||
|  |     margin-top: 4px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .input { | ||||||
|  |   td { | ||||||
|  |     background: darken($ui-base-color, 8%); | ||||||
|  |     border-radius: 4px; | ||||||
|  |     padding: 16px; | ||||||
|  |     line-height: 20px; | ||||||
|  |     mso-line-height-rule: exactly; | ||||||
|  |     border-radius: 4px; | ||||||
|  |     text-align: center; | ||||||
|  |     font-weight: 500; | ||||||
|  |     font-size: 17px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .content-cell, | ||||||
|  | .blank-cell { | ||||||
|  |   width: 100%; | ||||||
|  |   font-size: 0; | ||||||
|  |   text-align: center; | ||||||
|  |   vertical-align: top; | ||||||
|  |   padding-left: 16px; | ||||||
|  |   padding-right: 16px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .content-cell { | ||||||
|  |   background-color: darken($ui-base-color, 4%); | ||||||
|  |  | ||||||
|  |   &.darker { | ||||||
|  |     background-color: darken($ui-base-color, 8%); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .hero { | ||||||
|  |   background-color: $ui-base-color; | ||||||
|  |   padding-top: 20px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .hero-with-button { | ||||||
|  |   h1 { | ||||||
|  |     margin-bottom: 4px; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   p.lead { | ||||||
|  |     margin-bottom: 32px; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   padding-bottom: 16px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .header { | ||||||
|  |   border-radius: 5px 5px 0 0; | ||||||
|  |   background-color: darken($ui-base-color, 8%); | ||||||
|  |  | ||||||
|  |   .column-cell { | ||||||
|  |     text-align: center; | ||||||
|  |     padding-top: 20px; | ||||||
|  |     padding-bottom: 8px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .content-start { | ||||||
|  |   padding-top: 32px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .content-end { | ||||||
|  |   border-radius: 0 0 5px 5px; | ||||||
|  |   padding-top: 16px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .footer { | ||||||
|  |   .column-cell, | ||||||
|  |   p { | ||||||
|  |     color: lighten($ui-base-color, 34%); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   p { | ||||||
|  |     margin-bottom: 0; | ||||||
|  |     font-size: 13px; | ||||||
|  |  | ||||||
|  |     &.small { | ||||||
|  |       margin-bottom: 0; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   a { | ||||||
|  |     color: lighten($ui-base-color, 34%); | ||||||
|  |     text-decoration: underline; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   img { | ||||||
|  |     opacity: 0.3; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .logo { | ||||||
|  |   position: relative; | ||||||
|  |   left: -4px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .button { | ||||||
|  |   display: table; | ||||||
|  |   margin-left: auto; | ||||||
|  |   margin-right: auto; | ||||||
|  |  | ||||||
|  |   td { | ||||||
|  |     line-height: 20px; | ||||||
|  |     mso-line-height-rule: exactly; | ||||||
|  |     border-radius: 4px; | ||||||
|  |     text-align: center; | ||||||
|  |     font-weight: 500; | ||||||
|  |     font-size: 17px; | ||||||
|  |     padding: 0 !important; | ||||||
|  |  | ||||||
|  |     a, | ||||||
|  |     a span { | ||||||
|  |       color: $primary-text-color; | ||||||
|  |       display: block !important; | ||||||
|  |       text-align: center !important; | ||||||
|  |       vertical-align: top !important; | ||||||
|  |       line-height: inherit !important; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     a { | ||||||
|  |       padding: 10px 22px !important; | ||||||
|  |       line-height: 26px !important; | ||||||
|  |       font-weight: 500 !important; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &.button-small { | ||||||
|  |     td { | ||||||
|  |       border-radius: 4px; | ||||||
|  |       font-size: 14px; | ||||||
|  |       padding: 8px 16px; | ||||||
|  |  | ||||||
|  |       a { | ||||||
|  |         padding: 5px 16px !important; | ||||||
|  |         line-height: 26px !important; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .button-default { | ||||||
|  |   background-color: darken($ui-base-color, 8%); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .button-primary { | ||||||
|  |   background-color: darken($ui-highlight-color, 3%); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .text-center { | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .text-right { | ||||||
|  |   text-align: right; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .padded { | ||||||
|  |   padding-left: 16px; | ||||||
|  |   padding-right: 16px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .padded-bottom { | ||||||
|  |   padding-bottom: 32px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .margin-bottom { | ||||||
|  |   margin-bottom: 20px; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .hero-icon { | ||||||
|  |   width: 64px; | ||||||
|  |  | ||||||
|  |   td { | ||||||
|  |     text-align: center; | ||||||
|  |     vertical-align: middle; | ||||||
|  |     line-height: 100%; | ||||||
|  |     mso-line-height-rule: exactly; | ||||||
|  |     padding: 16px; | ||||||
|  |     border-radius: 80px; | ||||||
|  |     background: $success-green; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   img { | ||||||
|  |     max-width: 32px; | ||||||
|  |     width: 32px; | ||||||
|  |     height: 32px; | ||||||
|  |     display: block; | ||||||
|  |     line-height: 100%; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .hr { | ||||||
|  |   width: 100%; | ||||||
|  |  | ||||||
|  |   td { | ||||||
|  |     font-size: 0; | ||||||
|  |     line-height: 1px; | ||||||
|  |     mso-line-height-rule: exactly; | ||||||
|  |     min-height: 1px; | ||||||
|  |     overflow: hidden; | ||||||
|  |     height: 2px; | ||||||
|  |     background-color: transparent !important; | ||||||
|  |     border-top: 1px solid lighten($ui-base-color, 8%); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .status { | ||||||
|  |   padding-bottom: 32px; | ||||||
|  |  | ||||||
|  |   .status-header { | ||||||
|  |     td { | ||||||
|  |       font-size: 14px; | ||||||
|  |       padding-bottom: 15px; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     bdi { | ||||||
|  |       color: $white; | ||||||
|  |       font-size: 16px; | ||||||
|  |       display: block; | ||||||
|  |       font-weight: 500; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     td:first-child { | ||||||
|  |       padding-right: 10px; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     img { | ||||||
|  |       width: 48px; | ||||||
|  |       height: 48px; | ||||||
|  |       border-radius: 4px; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   p { | ||||||
|  |     font-size: 19px; | ||||||
|  |     margin-bottom: 20px; | ||||||
|  |  | ||||||
|  |     &.status-footer { | ||||||
|  |       color: lighten($ui-base-color, 26%); | ||||||
|  |       font-size: 14px; | ||||||
|  |       margin-bottom: 0; | ||||||
|  |  | ||||||
|  |       a { | ||||||
|  |         color: lighten($ui-base-color, 26%); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .border-top { | ||||||
|  |   border-top: 1px solid lighten($ui-base-color, 8%); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | ul { | ||||||
|  |   padding-left: 15px; | ||||||
|  |   margin-top: 0; | ||||||
|  |   margin-bottom: 0; | ||||||
|  |   padding-top: 16px; | ||||||
|  |  | ||||||
|  |   li { | ||||||
|  |     margin-bottom: 16px; | ||||||
|  |     color: lighten($ui-base-color, 26%); | ||||||
|  |  | ||||||
|  |     span { | ||||||
|  |       color: $ui-primary-color; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (orientation: landscape) { | ||||||
|  |   body { | ||||||
|  |     min-height: 1024px !important; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @media (max-width: 697px) { | ||||||
|  |   .email-container, | ||||||
|  |   .col-1, | ||||||
|  |   .col-2, | ||||||
|  |   .col-3, | ||||||
|  |   .col-4, | ||||||
|  |   .col-5, | ||||||
|  |   .col-6 { | ||||||
|  |     width: 100% !important; | ||||||
|  |     max-width: none !important; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   .email-start { | ||||||
|  |     padding-top: 16px !important; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   .email-end { | ||||||
|  |     padding-bottom: 16px !important; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   .padded { | ||||||
|  |     padding-left: 0 !important; | ||||||
|  |     padding-right: 0 !important; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -398,10 +398,12 @@ | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   &__content { | ||||||
|  |     max-width: calc(100% - 90px); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   &__title { |   &__title { | ||||||
|     overflow: hidden; |     word-wrap: break-word; | ||||||
|     text-overflow: ellipsis; |  | ||||||
|     white-space: nowrap; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   &__timestamp { |   &__timestamp { | ||||||
| @@ -415,7 +417,7 @@ | |||||||
|     color: $ui-primary-color; |     color: $ui-primary-color; | ||||||
|     font-family: 'mastodon-font-monospace', monospace; |     font-family: 'mastodon-font-monospace', monospace; | ||||||
|     font-size: 12px; |     font-size: 12px; | ||||||
|     white-space: nowrap; |     word-wrap: break-word; | ||||||
|     min-height: 20px; |     min-height: 20px; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1419,6 +1419,10 @@ | |||||||
|     color: $primary-text-color; |     color: $primary-text-color; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   a { | ||||||
|  |     color: inherit; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   .permalink { |   .permalink { | ||||||
|     text-decoration: none; |     text-decoration: none; | ||||||
|   } |   } | ||||||
| @@ -1613,6 +1617,10 @@ | |||||||
|   justify-content: flex-start; |   justify-content: flex-start; | ||||||
|   overflow-x: auto; |   overflow-x: auto; | ||||||
|   position: relative; |   position: relative; | ||||||
|  |  | ||||||
|  |   &.unscrollable { | ||||||
|  |     overflow-x: hidden; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @media screen and (min-width: 360px) { | @media screen and (min-width: 360px) { | ||||||
| @@ -1758,7 +1766,7 @@ | |||||||
|   position: absolute; |   position: absolute; | ||||||
|   top: 0; |   top: 0; | ||||||
|   left: 0; |   left: 0; | ||||||
|   background: lighten($ui-base-color, 13%) url('../images/wave-drawer.png') no-repeat bottom / 100% auto; |   background: lighten($ui-base-color, 13%) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 234.80078 31.757813" width="234.80078" height="31.757812"><path d="M19.599609 0c-1.05 0-2.10039.375-2.90039 1.125L0 16.925781v14.832031h234.80078V17.025391l-16.5-15.900391c-1.6-1.5-4.20078-1.5-5.80078 0l-13.80078 13.099609c-1.6 1.5-4.19883 1.5-5.79883 0L179.09961 1.125c-1.6-1.5-4.19883-1.5-5.79883 0L159.5 14.224609c-1.6 1.5-4.20078 1.5-5.80078 0L139.90039 1.125c-1.6-1.5-4.20078-1.5-5.80078 0l-13.79883 13.099609c-1.6 1.5-4.20078 1.5-5.80078 0L100.69922 1.125c-1.600001-1.5-4.198829-1.5-5.798829 0l-13.59961 13.099609c-1.6 1.5-4.200781 1.5-5.800781 0L61.699219 1.125c-1.6-1.5-4.198828-1.5-5.798828 0L42.099609 14.224609c-1.6 1.5-4.198828 1.5-5.798828 0L22.5 1.125C21.7.375 20.649609 0 19.599609 0z" fill="#{hex-color($ui-base-color)}"/></svg>') no-repeat bottom / 100% auto; | ||||||
|   box-sizing: border-box; |   box-sizing: border-box; | ||||||
|   padding: 0; |   padding: 0; | ||||||
|   display: flex; |   display: flex; | ||||||
| @@ -1773,7 +1781,7 @@ | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   > .mastodon { |   > .mastodon { | ||||||
|     background: url('../images/mastodon-ui.png') no-repeat left bottom / contain; |     background: url('../images/elephant_ui_plane.svg') no-repeat left bottom / contain; | ||||||
|     flex: 1; |     flex: 1; | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -2150,10 +2158,7 @@ | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @import 'boost'; |  | ||||||
|  |  | ||||||
| .no-reduce-motion button.icon-button i.fa-retweet { | .no-reduce-motion button.icon-button i.fa-retweet { | ||||||
|  |  | ||||||
|   background-position: 0 0; |   background-position: 0 0; | ||||||
|   height: 19px; |   height: 19px; | ||||||
|   transition: background-position 0.9s steps(10); |   transition: background-position 0.9s steps(10); | ||||||
| @@ -2299,7 +2304,7 @@ | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| .missing-indicator { | .regeneration-indicator { | ||||||
|   text-align: center; |   text-align: center; | ||||||
|   font-size: 16px; |   font-size: 16px; | ||||||
|   font-weight: 500; |   font-weight: 500; | ||||||
| @@ -2310,11 +2315,46 @@ | |||||||
|   flex: 1 1 auto; |   flex: 1 1 auto; | ||||||
|   align-items: center; |   align-items: center; | ||||||
|   justify-content: center; |   justify-content: center; | ||||||
|  |   padding: 20px; | ||||||
|  |  | ||||||
|   & > div { |   & > div { | ||||||
|     background: url('../images/mastodon-not-found.png') no-repeat center -50px; |  | ||||||
|     padding-top: 210px; |  | ||||||
|     width: 100%; |     width: 100%; | ||||||
|  |     background: transparent; | ||||||
|  |     padding-top: 0; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &__figure { | ||||||
|  |     background: url('../images/elephant_ui_working.svg') no-repeat center 0; | ||||||
|  |     width: 100%; | ||||||
|  |     height: 160px; | ||||||
|  |     background-size: contain; | ||||||
|  |     position: absolute; | ||||||
|  |     top: 50%; | ||||||
|  |     left: 50%; | ||||||
|  |     transform: translate(-50%, -50%); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &.missing-indicator { | ||||||
|  |     padding-top: 20px + 48px; | ||||||
|  |  | ||||||
|  |     .regeneration-indicator__figure { | ||||||
|  |       background-image: url('../images/elephant_ui_disappointed.svg'); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &__label { | ||||||
|  |     margin-top: 200px; | ||||||
|  |  | ||||||
|  |     strong { | ||||||
|  |       display: block; | ||||||
|  |       margin-bottom: 10px; | ||||||
|  |       color: lighten($ui-base-color, 34%); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     span { | ||||||
|  |       font-size: 15px; | ||||||
|  |       font-weight: 400; | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -2350,6 +2390,19 @@ | |||||||
|   position: relative; |   position: relative; | ||||||
|   z-index: 2; |   z-index: 2; | ||||||
|   outline: 0; |   outline: 0; | ||||||
|  |   overflow: hidden; | ||||||
|  |  | ||||||
|  |   & > button { | ||||||
|  |     display: flex; | ||||||
|  |     flex: auto; | ||||||
|  |     margin: 0; | ||||||
|  |     border: none; | ||||||
|  |     padding: 0; | ||||||
|  |     color: inherit; | ||||||
|  |     background: transparent; | ||||||
|  |     font: inherit; | ||||||
|  |     text-align: left; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   &.active { |   &.active { | ||||||
|     box-shadow: 0 1px 0 rgba($ui-highlight-color, 0.3); |     box-shadow: 0 1px 0 rgba($ui-highlight-color, 0.3); | ||||||
| @@ -2711,6 +2764,7 @@ | |||||||
|   flex: 1 1 auto; |   flex: 1 1 auto; | ||||||
|   align-items: center; |   align-items: center; | ||||||
|   justify-content: center; |   justify-content: center; | ||||||
|  |  | ||||||
|   @supports(display: grid) { // hack to fix Chrome <57 |   @supports(display: grid) { // hack to fix Chrome <57 | ||||||
|     contain: strict; |     contain: strict; | ||||||
|   } |   } | ||||||
| @@ -2732,7 +2786,6 @@ | |||||||
| @keyframes heartbeat { | @keyframes heartbeat { | ||||||
|   from { |   from { | ||||||
|     transform: scale(1); |     transform: scale(1); | ||||||
|     transform-origin: center center; |  | ||||||
|     animation-timing-function: ease-out; |     animation-timing-function: ease-out; | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -2757,10 +2810,48 @@ | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| .pulse-loading { | .no-reduce-motion .pulse-loading { | ||||||
|  |   transform-origin: center center; | ||||||
|   animation: heartbeat 1.5s ease-in-out infinite both; |   animation: heartbeat 1.5s ease-in-out infinite both; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @keyframes shake-bottom { | ||||||
|  |   0%, | ||||||
|  |   100% { | ||||||
|  |     transform: rotate(0deg); | ||||||
|  |     transform-origin: 50% 100%; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   10% { | ||||||
|  |     transform: rotate(2deg); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   20%, | ||||||
|  |   40%, | ||||||
|  |   60% { | ||||||
|  |     transform: rotate(-4deg); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   30%, | ||||||
|  |   50%, | ||||||
|  |   70% { | ||||||
|  |     transform: rotate(4deg); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   80% { | ||||||
|  |     transform: rotate(-2deg); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   90% { | ||||||
|  |     transform: rotate(2deg); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .no-reduce-motion .shake-bottom { | ||||||
|  |   transform-origin: 50% 100%; | ||||||
|  |   animation: shake-bottom 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) 2s 2 both; | ||||||
|  | } | ||||||
|  |  | ||||||
| .emoji-picker-dropdown__menu { | .emoji-picker-dropdown__menu { | ||||||
|   background: $simple-background-color; |   background: $simple-background-color; | ||||||
|   position: absolute; |   position: absolute; | ||||||
| @@ -3266,13 +3357,12 @@ | |||||||
|   height: 80vh; |   height: 80vh; | ||||||
|   width: 80vw; |   width: 80vw; | ||||||
|   max-width: 520px; |   max-width: 520px; | ||||||
|   max-height: 420px; |   max-height: 470px; | ||||||
|  |  | ||||||
|   .react-swipeable-view-container > div { |   .react-swipeable-view-container > div { | ||||||
|     width: 100%; |     width: 100%; | ||||||
|     height: 100%; |     height: 100%; | ||||||
|     box-sizing: border-box; |     box-sizing: border-box; | ||||||
|     padding: 25px; |  | ||||||
|     display: none; |     display: none; | ||||||
|     flex-direction: column; |     flex-direction: column; | ||||||
|     align-items: center; |     align-items: center; | ||||||
| @@ -3345,23 +3435,32 @@ | |||||||
|   .onboarding-modal__nav, |   .onboarding-modal__nav, | ||||||
|   .error-modal__nav { |   .error-modal__nav { | ||||||
|     color: darken($ui-secondary-color, 34%); |     color: darken($ui-secondary-color, 34%); | ||||||
|     background-color: transparent; |  | ||||||
|     border: 0; |     border: 0; | ||||||
|     font-size: 14px; |     font-size: 14px; | ||||||
|     font-weight: 500; |     font-weight: 500; | ||||||
|     padding: 0; |     padding: 10px 25px; | ||||||
|     line-height: inherit; |     line-height: inherit; | ||||||
|     height: auto; |     height: auto; | ||||||
|  |     margin: -10px; | ||||||
|  |     border-radius: 4px; | ||||||
|  |     background-color: transparent; | ||||||
|  |  | ||||||
|     &:hover, |     &:hover, | ||||||
|     &:focus, |     &:focus, | ||||||
|     &:active { |     &:active { | ||||||
|       color: darken($ui-secondary-color, 38%); |       color: darken($ui-secondary-color, 38%); | ||||||
|  |       background-color: darken($ui-secondary-color, 16%); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     &.onboarding-modal__done, |     &.onboarding-modal__done, | ||||||
|     &.onboarding-modal__next { |     &.onboarding-modal__next { | ||||||
|       color: $ui-highlight-color; |       color: $ui-base-color; | ||||||
|  |  | ||||||
|  |       &:hover, | ||||||
|  |       &:focus, | ||||||
|  |       &:active { | ||||||
|  |         color: darken($ui-base-color, 4%); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -3397,6 +3496,8 @@ | |||||||
|  |  | ||||||
| .onboarding-modal__page__wrapper { | .onboarding-modal__page__wrapper { | ||||||
|   pointer-events: none; |   pointer-events: none; | ||||||
|  |   padding: 25px; | ||||||
|  |   padding-bottom: 0; | ||||||
|  |  | ||||||
|   &.onboarding-modal__page__wrapper--active { |   &.onboarding-modal__page__wrapper--active { | ||||||
|     pointer-events: auto; |     pointer-events: auto; | ||||||
| @@ -3424,6 +3525,10 @@ | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   .navigation-bar a { | ||||||
|  |     color: inherit; | ||||||
|  |   } | ||||||
|  |  | ||||||
|   p { |   p { | ||||||
|     font-size: 16px; |     font-size: 16px; | ||||||
|     color: lighten($ui-base-color, 8%); |     color: lighten($ui-base-color, 8%); | ||||||
| @@ -3451,29 +3556,56 @@ | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | .onboarding-modal__page__wrapper-0 { | ||||||
|  |   background: url('../images/elephant_ui_greeting.svg') no-repeat left bottom / auto 250px; | ||||||
|  |   height: 100%; | ||||||
|  |   padding: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
| .onboarding-modal__page-one { | .onboarding-modal__page-one { | ||||||
|   display: flex; |   &__lead { | ||||||
|   align-items: center; |     padding: 65px; | ||||||
|  |     padding-top: 45px; | ||||||
|  |     padding-bottom: 0; | ||||||
|  |     margin-bottom: 10px; | ||||||
|  |  | ||||||
|  |     h1 { | ||||||
|  |       font-size: 26px; | ||||||
|  |       line-height: 36px; | ||||||
|  |       margin-bottom: 8px; | ||||||
|     } |     } | ||||||
|  |  | ||||||
| .onboarding-modal__page-one__elephant-friend { |     p { | ||||||
|   background: url('../images/elephant-friend-1.png') no-repeat center center / contain; |       margin-bottom: 0; | ||||||
|   width: 155px; |     } | ||||||
|   height: 193px; |  | ||||||
|   margin-right: 15px; |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @media screen and (max-width: 400px) { |   &__extra { | ||||||
|   .onboarding-modal__page-one { |     padding-right: 65px; | ||||||
|     flex-direction: column; |     padding-left: 185px; | ||||||
|     align-items: normal; |     text-align: center; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|   .onboarding-modal__page-one__elephant-friend { | .display-case { | ||||||
|     width: 100%; |   text-align: center; | ||||||
|     height: 30vh; |   font-size: 15px; | ||||||
|     max-height: 160px; |   margin-bottom: 15px; | ||||||
|     margin-bottom: 5vh; |  | ||||||
|  |   &__label { | ||||||
|  |     font-weight: 500; | ||||||
|  |     color: $ui-base-color; | ||||||
|  |     margin-bottom: 5px; | ||||||
|  |     text-transform: uppercase; | ||||||
|  |     font-size: 12px; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   &__case { | ||||||
|  |     background: $ui-base-color; | ||||||
|  |     color: $ui-secondary-color; | ||||||
|  |     font-weight: 500; | ||||||
|  |     padding: 10px; | ||||||
|  |     border-radius: 4px; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -3563,16 +3695,6 @@ | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| .onboarding-modal__image { |  | ||||||
|   border-radius: 8px; |  | ||||||
|   width: 70vw; |  | ||||||
|   max-width: 450px; |  | ||||||
|   max-height: auto; |  | ||||||
|   display: block; |  | ||||||
|   margin: auto; |  | ||||||
|   margin-bottom: 20px; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| .onboard-sliders { | .onboard-sliders { | ||||||
|   display: inline-block; |   display: inline-block; | ||||||
|   max-width: 30px; |   max-width: 30px; | ||||||
|   | |||||||