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
|
||||
eslint:
|
||||
enabled: true
|
||||
channel: eslint-4
|
||||
rubocop:
|
||||
enabled: true
|
||||
scss-lint:
|
||||
|
@@ -17,11 +17,9 @@ plugins:
|
||||
parserOptions:
|
||||
sourceType: module
|
||||
ecmaFeatures:
|
||||
arrowFunctions: true
|
||||
experimentalObjectRestSpread: true
|
||||
jsx: true
|
||||
destructuring: true
|
||||
modules: true
|
||||
spread: true
|
||||
ecmaVersion: 2018
|
||||
|
||||
settings:
|
||||
import/extensions:
|
||||
@@ -109,6 +107,7 @@ rules:
|
||||
react/self-closing-comp: error
|
||||
|
||||
jsx-a11y/accessible-emoji: warn
|
||||
jsx-a11y/alt-text: warn
|
||||
jsx-a11y/anchor-has-content: warn
|
||||
jsx-a11y/aria-activedescendant-has-tabindex: warn
|
||||
jsx-a11y/aria-props: warn
|
||||
@@ -119,16 +118,22 @@ rules:
|
||||
jsx-a11y/href-no-hash: warn
|
||||
jsx-a11y/html-has-lang: warn
|
||||
jsx-a11y/iframe-has-title: warn
|
||||
jsx-a11y/img-has-alt: warn
|
||||
jsx-a11y/img-redundant-alt: warn
|
||||
jsx-a11y/interactive-supports-focus: warn
|
||||
jsx-a11y/label-has-for: off
|
||||
jsx-a11y/mouse-events-have-key-events: warn
|
||||
jsx-a11y/no-access-key: warn
|
||||
jsx-a11y/no-distracting-elements: warn
|
||||
jsx-a11y/no-noninteractive-element-interactions:
|
||||
- warn
|
||||
- handlers:
|
||||
- onClick
|
||||
jsx-a11y/no-onchange: warn
|
||||
jsx-a11y/no-redundant-roles: warn
|
||||
jsx-a11y/onclick-has-focus: warn
|
||||
jsx-a11y/onclick-has-role: warn
|
||||
jsx-a11y/no-static-element-interactions:
|
||||
- warn
|
||||
- handlers:
|
||||
- onClick
|
||||
jsx-a11y/role-has-required-aria-props: warn
|
||||
jsx-a11y/role-supports-aria-props: off
|
||||
jsx-a11y/scope: warn
|
||||
|
@@ -1 +1 @@
|
||||
2.4.2
|
||||
2.5.0
|
||||
|
@@ -9,6 +9,9 @@ cache:
|
||||
- tmp/cache/babel-loader
|
||||
dist: trusty
|
||||
sudo: required
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
@@ -37,20 +40,20 @@ addons:
|
||||
- yarn
|
||||
|
||||
rvm:
|
||||
- 2.3.4
|
||||
- 2.4.2
|
||||
- 2.5.0
|
||||
|
||||
services:
|
||||
- redis-server
|
||||
|
||||
install:
|
||||
- gem update --system
|
||||
- nvm install
|
||||
- bundle install --path=vendor/bundle --without development production --retry=3 --jobs=16
|
||||
- yarn install
|
||||
|
||||
before_script:
|
||||
- bundle exec rake parallel:create parallel:load_schema parallel:prepare
|
||||
- bundle exec rails assets:precompile
|
||||
- ./bin/rails parallel:create parallel:load_schema parallel:prepare assets:precompile
|
||||
- ln -s /usr/bin/x86_64-linux-gnu-g++-6 "$HOME/g++"
|
||||
|
||||
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" \
|
||||
description="A GNU Social-compatible microblogging server"
|
||||
@@ -40,6 +40,7 @@ RUN apk -U upgrade \
|
||||
protobuf \
|
||||
su-exec \
|
||||
tini \
|
||||
tzdata \
|
||||
&& update-ca-certificates \
|
||||
&& 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" \
|
||||
|
5
Gemfile
@@ -1,7 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
ruby '>= 2.3.0', '< 2.5.0'
|
||||
ruby '>= 2.3.0', '< 2.6.0'
|
||||
|
||||
gem 'pkg-config', '~> 1.2'
|
||||
|
||||
@@ -28,7 +28,7 @@ gem 'browser'
|
||||
gem 'charlock_holmes', '~> 0.7.5'
|
||||
gem 'iso-639'
|
||||
gem 'cld3', '~> 3.2.0'
|
||||
gem 'devise', '~> 4.3'
|
||||
gem 'devise', '~> 4.4'
|
||||
gem 'devise-two-factor', '~> 3.0'
|
||||
gem 'doorkeeper', '~> 4.2'
|
||||
gem 'fast_blank', '~> 1.0'
|
||||
@@ -49,6 +49,7 @@ gem 'oj', '~> 3.3'
|
||||
gem 'ostatus2', '~> 2.0'
|
||||
gem 'ox', '~> 2.8'
|
||||
gem 'pundit', '~> 1.1'
|
||||
gem 'premailer-rails'
|
||||
gem 'rack-attack', '~> 5.0'
|
||||
gem 'rack-cors', '~> 0.4', require: 'rack/cors'
|
||||
gem 'rack-timeout', '~> 0.4'
|
||||
|
18
Gemfile.lock
@@ -110,7 +110,7 @@ GEM
|
||||
activesupport
|
||||
charlock_holmes (0.7.5)
|
||||
chunky_png (1.3.8)
|
||||
cld3 (3.2.1)
|
||||
cld3 (3.2.2)
|
||||
ffi (>= 1.1.0, < 1.10.0)
|
||||
climate_control (0.2.0)
|
||||
cocaine (0.5.8)
|
||||
@@ -122,8 +122,10 @@ GEM
|
||||
crack (0.4.3)
|
||||
safe_yaml (~> 1.0.0)
|
||||
crass (1.0.3)
|
||||
css_parser (1.6.0)
|
||||
addressable
|
||||
debug_inspector (0.0.3)
|
||||
devise (4.3.0)
|
||||
devise (4.4.0)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 4.1.0, < 5.2)
|
||||
@@ -324,6 +326,13 @@ GEM
|
||||
activerecord
|
||||
pkg-config (1.2.8)
|
||||
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)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.9.0)
|
||||
@@ -559,7 +568,7 @@ DEPENDENCIES
|
||||
charlock_holmes (~> 0.7.5)
|
||||
cld3 (~> 3.2.0)
|
||||
climate_control (~> 0.2)
|
||||
devise (~> 4.3)
|
||||
devise (~> 4.4)
|
||||
devise-two-factor (~> 3.0)
|
||||
doorkeeper (~> 4.2)
|
||||
dotenv-rails (~> 2.2)
|
||||
@@ -600,6 +609,7 @@ DEPENDENCIES
|
||||
pg (~> 0.20)
|
||||
pghero (~> 1.7)
|
||||
pkg-config (~> 1.2)
|
||||
premailer-rails
|
||||
pry-rails (~> 0.3)
|
||||
puma (~> 3.10)
|
||||
pundit (~> 1.1)
|
||||
@@ -639,7 +649,7 @@ DEPENDENCIES
|
||||
webpush
|
||||
|
||||
RUBY VERSION
|
||||
ruby 2.4.2p198
|
||||
ruby 2.5.0p0
|
||||
|
||||
BUNDLED WITH
|
||||
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
|
||||
|
||||
skip_before_action :verify_authenticity_token
|
||||
skip_before_action :store_current_location
|
||||
protect_from_forgery with: :null_session
|
||||
|
||||
rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
|
||||
render json: { error: e.to_s }, status: 422
|
||||
|
@@ -21,9 +21,9 @@ class Api::V1::Instances::ActivityController < Api::BaseController
|
||||
|
||||
weeks << {
|
||||
week: week.to_time.to_i.to_s,
|
||||
statuses: Redis.current.get("activity:statuses:local:#{week_id}") || 0,
|
||||
logins: Redis.current.pfcount("activity:logins:#{week_id}"),
|
||||
registrations: Redis.current.get("activity:accounts:local:#{week_id}") || 0,
|
||||
statuses: Redis.current.get("activity:statuses:local:#{week_id}") || '0',
|
||||
logins: Redis.current.pfcount("activity:logins:#{week_id}").to_s,
|
||||
registrations: Redis.current.get("activity:accounts:local:#{week_id}") || '0',
|
||||
}
|
||||
end
|
||||
|
||||
|
@@ -9,7 +9,11 @@ class Api::V1::Timelines::HomeController < Api::BaseController
|
||||
|
||||
def show
|
||||
@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
|
||||
|
||||
private
|
||||
@@ -57,4 +61,8 @@ class Api::V1::Timelines::HomeController < Api::BaseController
|
||||
def pagination_since_id
|
||||
@statuses.first.id
|
||||
end
|
||||
|
||||
def regeneration_in_progress?
|
||||
Redis.current.exists("account:#{current_account.id}:regeneration")
|
||||
end
|
||||
end
|
||||
|
@@ -4,6 +4,7 @@ class Api::Web::PushSubscriptionsController < Api::BaseController
|
||||
respond_to :json
|
||||
|
||||
before_action :require_user!
|
||||
protect_from_forgery with: :exception
|
||||
|
||||
def create
|
||||
params.require(:subscription).require(:endpoint)
|
||||
|
@@ -30,7 +30,7 @@ class ApplicationController < ActionController::Base
|
||||
private
|
||||
|
||||
def https_enabled?
|
||||
Rails.env.production? && ENV['LOCAL_HTTPS'] == 'true'
|
||||
Rails.env.production?
|
||||
end
|
||||
|
||||
def store_current_location
|
||||
@@ -124,15 +124,15 @@ class ApplicationController < ActionController::Base
|
||||
|
||||
def render_cached_json(cache_key, **options)
|
||||
options[:expires_in] ||= 3.minutes
|
||||
options[:public] ||= true
|
||||
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'
|
||||
|
||||
data = Rails.cache.fetch(cache_key, { raw: true }.merge(options)) do
|
||||
yield.to_json
|
||||
end
|
||||
|
||||
expires_in options[:expires_in], public: options[:public]
|
||||
expires_in options[:expires_in], public: cache_public
|
||||
render json: data, content_type: content_type
|
||||
end
|
||||
|
||||
|
@@ -32,7 +32,7 @@ module UserTrackingConcern
|
||||
end
|
||||
|
||||
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)
|
||||
end
|
||||
end
|
||||
|
@@ -14,13 +14,14 @@ class SharesController < ApplicationController
|
||||
private
|
||||
|
||||
def initial_state_params
|
||||
text = [params[:title], params[:text], params[:url]].compact.join(' ')
|
||||
{
|
||||
settings: Web::Setting.find_by(user: current_user)&.data || {},
|
||||
push_subscription: current_account.user.web_push_subscription(current_session),
|
||||
current_account: current_account,
|
||||
token: current_session.token,
|
||||
admin: Account.find_local(Setting.site_contact_username),
|
||||
text: params[:text],
|
||||
text: text,
|
||||
}
|
||||
end
|
||||
|
||||
|
@@ -6,6 +6,6 @@ module InstanceHelper
|
||||
end
|
||||
|
||||
def site_hostname
|
||||
Rails.configuration.x.local_domain
|
||||
@site_hostname ||= Addressable::URI.parse("//#{Rails.configuration.x.local_domain}").display_uri.host
|
||||
end
|
||||
end
|
||||
|
@@ -39,6 +39,10 @@ module JsonLdHelper
|
||||
!json.nil? && equals_or_includes?(json['@context'], ActivityPub::TagManager::CONTEXT)
|
||||
end
|
||||
|
||||
def unsupported_uri_scheme?(uri)
|
||||
!uri.start_with?('http://', 'https://')
|
||||
end
|
||||
|
||||
def canonicalize(json)
|
||||
graph = RDF::Graph.new << JSON::LD::API.toRdf(json)
|
||||
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 { setBrowserSupport, setSubscription, clearSubscription } from './setter';
|
||||
|
||||
@@ -35,7 +35,7 @@ const subscribe = (registration) =>
|
||||
const unsubscribe = ({ registration, subscription }) =>
|
||||
subscription ? subscription.unsubscribe().then(() => registration) : registration;
|
||||
|
||||
const sendSubscriptionToBackend = (subscription, me) => {
|
||||
const sendSubscriptionToBackend = (getState, subscription, me) => {
|
||||
const params = { subscription };
|
||||
|
||||
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
|
||||
@@ -85,13 +85,13 @@ export function register () {
|
||||
} else {
|
||||
// Something went wrong, try to subscribe again
|
||||
return unsubscribe({ registration, subscription }).then(subscribe).then(
|
||||
subscription => sendSubscriptionToBackend(subscription, me));
|
||||
subscription => sendSubscriptionToBackend(getState, subscription, me));
|
||||
}
|
||||
}
|
||||
|
||||
// No subscription, try to subscribe
|
||||
return subscribe(registration).then(
|
||||
subscription => sendSubscriptionToBackend(subscription, me));
|
||||
subscription => sendSubscriptionToBackend(getState, subscription, me));
|
||||
})
|
||||
.then(subscription => {
|
||||
// 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 data = { alerts };
|
||||
|
||||
axios.put(`/api/web/push_subscriptions/${subscription.get('id')}`, {
|
||||
api(getState).put(`/api/web/push_subscriptions/${subscription.get('id')}`, {
|
||||
data,
|
||||
}).then(() => {
|
||||
const me = getState().getIn(['meta', 'me']);
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import axios from 'axios';
|
||||
import api from '../api';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
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();
|
||||
|
||||
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 });
|
||||
|
||||
export function saveSettings() {
|
||||
|
@@ -19,13 +19,14 @@ export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
|
||||
|
||||
export const TIMELINE_CONTEXT_UPDATE = 'CONTEXT_UPDATE';
|
||||
|
||||
export function refreshTimelineSuccess(timeline, statuses, skipLoading, next) {
|
||||
export function refreshTimelineSuccess(timeline, statuses, skipLoading, next, partial) {
|
||||
return {
|
||||
type: TIMELINE_REFRESH_SUCCESS,
|
||||
timeline,
|
||||
statuses,
|
||||
skipLoading,
|
||||
next,
|
||||
partial,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -88,7 +89,7 @@ export function refreshTimeline(timelineId, path, params = {}) {
|
||||
return function (dispatch, getState) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -104,8 +105,12 @@ export function refreshTimeline(timelineId, path, params = {}) {
|
||||
dispatch(refreshTimelineRequest(timelineId, skipLoading));
|
||||
|
||||
api(getState).get(path, { params }).then(response => {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(refreshTimelineSuccess(timelineId, response.data, skipLoading, next ? next.uri : null));
|
||||
if (response.status === 206) {
|
||||
dispatch(refreshTimelineSuccess(timelineId, [], skipLoading, null, true));
|
||||
} else {
|
||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||
dispatch(refreshTimelineSuccess(timelineId, response.data, skipLoading, next ? next.uri : null, false));
|
||||
}
|
||||
}).catch(error => {
|
||||
dispatch(refreshTimelineFail(timelineId, error, skipLoading));
|
||||
});
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import axios from 'axios';
|
||||
import ready from './ready';
|
||||
import LinkHeader from './link_header';
|
||||
|
||||
export const getLinks = response => {
|
||||
@@ -11,10 +12,17 @@ export const getLinks = response => {
|
||||
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({
|
||||
headers: {
|
||||
headers: Object.assign(csrfHeader, getState ? {
|
||||
'Authorization': `Bearer ${getState().getIn(['meta', 'access_token'], '')}`,
|
||||
},
|
||||
} : {}),
|
||||
|
||||
transformResponse: [function (data) {
|
||||
try {
|
||||
|
@@ -4,14 +4,16 @@ exports[`<DisplayName /> renders display name + account name 1`] = `
|
||||
<span
|
||||
className="display-name"
|
||||
>
|
||||
<strong
|
||||
className="display-name__html"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<p>Foo</p>",
|
||||
<bdi>
|
||||
<strong
|
||||
className="display-name__html"
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "<p>Foo</p>",
|
||||
}
|
||||
}
|
||||
}
|
||||
/>
|
||||
/>
|
||||
</bdi>
|
||||
|
||||
<span
|
||||
className="display-name__account"
|
||||
|
@@ -93,7 +93,7 @@ export default class Account extends ImmutablePureComponent {
|
||||
{hidingNotificationsButton}
|
||||
</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} />;
|
||||
}
|
||||
}
|
||||
|
@@ -20,11 +20,11 @@ export default class AttachmentList extends ImmutablePureComponent {
|
||||
</div>
|
||||
|
||||
<ul className='attachment-list__list'>
|
||||
{media.map(attachment =>
|
||||
{media.map(attachment => (
|
||||
<li key={attachment.get('id')}>
|
||||
<a href={attachment.get('remote_url')} target='_blank' rel='noopener'>{filename(attachment.get('remote_url'))}</a>
|
||||
</li>
|
||||
)}
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
|
@@ -5,11 +5,11 @@ import PropTypes from 'prop-types';
|
||||
|
||||
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) }}>
|
||||
{({ opacity, height }) =>
|
||||
{({ opacity, height }) => (
|
||||
<div style={{ height: `${height}px`, overflow: 'hidden', opacity: opacity / 100, display: Math.floor(opacity) === 0 ? 'none' : 'block' }}>
|
||||
{children}
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</Motion>
|
||||
);
|
||||
|
||||
|
@@ -23,7 +23,6 @@ export default class ColumnHeader extends React.PureComponent {
|
||||
icon: PropTypes.string.isRequired,
|
||||
active: PropTypes.bool,
|
||||
multiColumn: PropTypes.bool,
|
||||
focusable: PropTypes.bool,
|
||||
showBackButton: PropTypes.bool,
|
||||
children: PropTypes.node,
|
||||
pinned: PropTypes.bool,
|
||||
@@ -32,10 +31,6 @@ export default class ColumnHeader extends React.PureComponent {
|
||||
onClick: PropTypes.func,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
focusable: true,
|
||||
}
|
||||
|
||||
state = {
|
||||
collapsed: true,
|
||||
animating: false,
|
||||
@@ -68,7 +63,7 @@ export default class ColumnHeader extends React.PureComponent {
|
||||
}
|
||||
|
||||
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 wrapperClassName = classNames('column-header__wrapper', {
|
||||
@@ -135,11 +130,13 @@ export default class ColumnHeader extends React.PureComponent {
|
||||
|
||||
return (
|
||||
<div className={wrapperClassName}>
|
||||
<h1 tabIndex={focusable ? 0 : null} role='button' className={buttonClassName} aria-label={title} onClick={this.handleTitleClick}>
|
||||
<i className={`fa fa-fw fa-${icon} column-header__icon`} />
|
||||
<span className='column-header__title'>
|
||||
{title}
|
||||
</span>
|
||||
<h1 className={buttonClassName}>
|
||||
<button onClick={this.handleTitleClick}>
|
||||
<i className={`fa fa-fw fa-${icon} column-header__icon`} />
|
||||
<span className='column-header__title'>
|
||||
{title}
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<div className='column-header__buttons'>
|
||||
{backButton}
|
||||
|
@@ -12,7 +12,7 @@ export default class DisplayName extends React.PureComponent {
|
||||
|
||||
return (
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
@@ -93,7 +93,7 @@ export default class IconButton extends React.PureComponent {
|
||||
|
||||
return (
|
||||
<Motion defaultStyle={{ rotate: active ? -360 : 0 }} style={{ rotate: animate ? spring(active ? -360 : 0, { stiffness: 120, damping: 7 }) : 0 }}>
|
||||
{({ rotate }) =>
|
||||
{({ rotate }) => (
|
||||
<button
|
||||
aria-label={title}
|
||||
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' />
|
||||
</button>
|
||||
}
|
||||
)}
|
||||
</Motion>
|
||||
);
|
||||
}
|
||||
|
@@ -2,9 +2,14 @@ import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
const MissingIndicator = () => (
|
||||
<div className='missing-indicator'>
|
||||
<div className='regeneration-indicator missing-indicator'>
|
||||
<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>
|
||||
);
|
||||
|
@@ -162,7 +162,7 @@ export default class Status extends ImmutablePureComponent {
|
||||
prepend = (
|
||||
<div className='status__prepend'>
|
||||
<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>
|
||||
);
|
||||
|
||||
@@ -178,14 +178,16 @@ export default class Status extends ImmutablePureComponent {
|
||||
|
||||
media = (
|
||||
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} >
|
||||
{Component => <Component
|
||||
preview={video.get('preview_url')}
|
||||
src={video.get('url')}
|
||||
width={239}
|
||||
height={110}
|
||||
sensitive={status.get('sensitive')}
|
||||
onOpenVideo={this.handleOpenVideo}
|
||||
/>}
|
||||
{Component => (
|
||||
<Component
|
||||
preview={video.get('preview_url')}
|
||||
src={video.get('url')}
|
||||
width={239}
|
||||
height={110}
|
||||
sensitive={status.get('sensitive')}
|
||||
onOpenVideo={this.handleOpenVideo}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
);
|
||||
} else {
|
||||
|
@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
|
||||
import StatusContainer from '../containers/status_container';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import ScrollableList from './scrollable_list';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
export default class StatusList extends ImmutablePureComponent {
|
||||
|
||||
@@ -16,6 +17,7 @@ export default class StatusList extends ImmutablePureComponent {
|
||||
trackScroll: PropTypes.bool,
|
||||
shouldUpdateScroll: PropTypes.func,
|
||||
isLoading: PropTypes.bool,
|
||||
isPartial: PropTypes.bool,
|
||||
hasMore: PropTypes.bool,
|
||||
prepend: PropTypes.node,
|
||||
emptyMessage: PropTypes.node,
|
||||
@@ -48,8 +50,23 @@ export default class StatusList extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { statusIds, ...other } = this.props;
|
||||
const { isLoading } = other;
|
||||
const { statusIds, ...other } = this.props;
|
||||
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) ? (
|
||||
statusIds.map((statusId) => (
|
||||
|
@@ -41,7 +41,7 @@ class Avatar extends ImmutablePureComponent {
|
||||
|
||||
return (
|
||||
<Motion defaultStyle={{ radius: 90 }} style={{ radius: spring(isHovered ? 30 : 90, { stiffness: 180, damping: 12 }) }}>
|
||||
{({ radius }) =>
|
||||
{({ radius }) => (
|
||||
<a
|
||||
href={account.get('url')}
|
||||
className='account__header__avatar'
|
||||
@@ -56,7 +56,7 @@ class Avatar extends ImmutablePureComponent {
|
||||
>
|
||||
<span style={{ display: 'none' }}>{account.get('acct')}</span>
|
||||
</a>
|
||||
}
|
||||
)}
|
||||
</Motion>
|
||||
);
|
||||
}
|
||||
@@ -103,7 +103,7 @@ export default class Header extends ImmutablePureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
if (account.get('moved')) {
|
||||
if (account.get('moved') && !account.getIn(['relationship', 'following'])) {
|
||||
actionBtn = '';
|
||||
}
|
||||
|
||||
|
@@ -94,12 +94,12 @@ export default class AccountGallery extends ImmutablePureComponent {
|
||||
</div>
|
||||
|
||||
<div className='account-gallery__container'>
|
||||
{medias.map(media =>
|
||||
{medias.map(media => (
|
||||
<MediaItem
|
||||
key={media.get('id')}
|
||||
media={media}
|
||||
/>
|
||||
)}
|
||||
))}
|
||||
{loadMore}
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -34,7 +34,7 @@ export default class MovedNote extends ImmutablePureComponent {
|
||||
<div className='account__moved-note'>
|
||||
<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>
|
||||
<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>
|
||||
|
||||
<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 }) }}>
|
||||
{({ opacity, scaleX, scaleY }) => (
|
||||
<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 className='privacy-dropdown__option__icon'>
|
||||
<i className={`fa fa-fw fa-${item.icon}`} />
|
||||
@@ -83,7 +83,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
|
||||
{item.meta}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Motion>
|
||||
|
@@ -40,11 +40,11 @@ export default class SearchResults extends ImmutablePureComponent {
|
||||
count += results.get('hashtags').size;
|
||||
hashtags = (
|
||||
<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}`}>
|
||||
#{hashtag}
|
||||
</Link>
|
||||
)}
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -94,15 +94,15 @@ export default class Compose extends React.PureComponent {
|
||||
<div className='drawer__inner' onFocus={this.onFocus}>
|
||||
<NavigationContainer onClose={this.onBlur} />
|
||||
<ComposeFormContainer />
|
||||
<div className='mastodon' />
|
||||
{multiColumn && <div className='mastodon' />}
|
||||
</div>
|
||||
|
||||
<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' }}>
|
||||
<SearchResultsContainer />
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</Motion>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -40,98 +40,98 @@ export const urlRegex = (function() {
|
||||
regexen.validSubdomain = regexSupplant(/(?:(?:#{validDomainChars}(?:[_-]|#{validDomainChars})*)?#{validDomainChars}\.)/);
|
||||
regexen.validDomainName = regexSupplant(/(?:(?:#{validDomainChars}(?:-|#{validDomainChars})*)?#{validDomainChars}\.)/);
|
||||
regexen.validGTLD = regexSupplant(RegExp(
|
||||
'(?:(?:' +
|
||||
'삼성|닷컴|닷넷|香格里拉|餐厅|食品|飞利浦|電訊盈科|集团|通販|购物|谷歌|诺基亚|联通|网络|网站|网店|网址|组织机构|移动|珠宝|点看|游戏|淡马锡|机构|書籍|时尚|新闻|政府|' +
|
||||
'政务|手表|手机|我爱你|慈善|微博|广东|工行|家電|娱乐|天主教|大拿|大众汽车|在线|嘉里大酒店|嘉里|商标|商店|商城|公益|公司|八卦|健康|信息|佛山|企业|中文网|中信|世界|' +
|
||||
'ポイント|ファッション|セール|ストア|コム|グーグル|クラウド|みんな|คอม|संगठन|नेट|कॉम|همراه|موقع|موبايلي|كوم|كاثوليك|عرب|شبكة|' +
|
||||
'بيتك|بازار|العليان|ارامكو|اتصالات|ابوظبي|קום|сайт|рус|орг|онлайн|москва|ком|католик|дети|' +
|
||||
'zuerich|zone|zippo|zip|zero|zara|zappos|yun|youtube|you|yokohama|yoga|yodobashi|yandex|yamaxun|' +
|
||||
'yahoo|yachts|xyz|xxx|xperia|xin|xihuan|xfinity|xerox|xbox|wtf|wtc|wow|world|works|work|woodside|' +
|
||||
'wolterskluwer|wme|winners|wine|windows|win|williamhill|wiki|wien|whoswho|weir|weibo|wedding|wed|' +
|
||||
'website|weber|webcam|weatherchannel|weather|watches|watch|warman|wanggou|wang|walter|walmart|' +
|
||||
'wales|vuelos|voyage|voto|voting|vote|volvo|volkswagen|vodka|vlaanderen|vivo|viva|vistaprint|' +
|
||||
'vista|vision|visa|virgin|vip|vin|villas|viking|vig|video|viajes|vet|versicherung|' +
|
||||
'vermögensberatung|vermögensberater|verisign|ventures|vegas|vanguard|vana|vacations|ups|uol|uno|' +
|
||||
'university|unicom|uconnect|ubs|ubank|tvs|tushu|tunes|tui|tube|trv|trust|travelersinsurance|' +
|
||||
'travelers|travelchannel|travel|training|trading|trade|toys|toyota|town|tours|total|toshiba|' +
|
||||
'toray|top|tools|tokyo|today|tmall|tkmaxx|tjx|tjmaxx|tirol|tires|tips|tiffany|tienda|tickets|' +
|
||||
'tiaa|theatre|theater|thd|teva|tennis|temasek|telefonica|telecity|tel|technology|tech|team|tdk|' +
|
||||
'tci|taxi|tax|tattoo|tatar|tatamotors|target|taobao|talk|taipei|tab|systems|symantec|sydney|' +
|
||||
'swiss|swiftcover|swatch|suzuki|surgery|surf|support|supply|supplies|sucks|style|study|studio|' +
|
||||
'stream|store|storage|stockholm|stcgroup|stc|statoil|statefarm|statebank|starhub|star|staples|' +
|
||||
'stada|srt|srl|spreadbetting|spot|spiegel|space|soy|sony|song|solutions|solar|sohu|software|' +
|
||||
'softbank|social|soccer|sncf|smile|smart|sling|skype|sky|skin|ski|site|singles|sina|silk|shriram|' +
|
||||
'showtime|show|shouji|shopping|shop|shoes|shiksha|shia|shell|shaw|sharp|shangrila|sfr|sexy|sex|' +
|
||||
'sew|seven|ses|services|sener|select|seek|security|secure|seat|search|scot|scor|scjohnson|' +
|
||||
'science|schwarz|schule|school|scholarships|schmidt|schaeffler|scb|sca|sbs|sbi|saxo|save|sas|' +
|
||||
'sarl|sapo|sap|sanofi|sandvikcoromant|sandvik|samsung|samsclub|salon|sale|sakura|safety|safe|' +
|
||||
'saarland|ryukyu|rwe|run|ruhr|rugby|rsvp|room|rogers|rodeo|rocks|rocher|rmit|rip|rio|ril|' +
|
||||
'rightathome|ricoh|richardli|rich|rexroth|reviews|review|restaurant|rest|republican|report|' +
|
||||
'repair|rentals|rent|ren|reliance|reit|reisen|reise|rehab|redumbrella|redstone|red|recipes|' +
|
||||
'realty|realtor|realestate|read|raid|radio|racing|qvc|quest|quebec|qpon|pwc|pub|prudential|pru|' +
|
||||
'protection|property|properties|promo|progressive|prof|productions|prod|pro|prime|press|praxi|' +
|
||||
'pramerica|post|porn|politie|poker|pohl|pnc|plus|plumbing|playstation|play|place|pizza|pioneer|' +
|
||||
'pink|ping|pin|pid|pictures|pictet|pics|piaget|physio|photos|photography|photo|phone|philips|phd|' +
|
||||
'pharmacy|pfizer|pet|pccw|pay|passagens|party|parts|partners|pars|paris|panerai|panasonic|' +
|
||||
'pamperedchef|page|ovh|ott|otsuka|osaka|origins|orientexpress|organic|org|orange|oracle|open|ooo|' +
|
||||
'onyourside|online|onl|ong|one|omega|ollo|oldnavy|olayangroup|olayan|okinawa|office|off|observer|' +
|
||||
'obi|nyc|ntt|nrw|nra|nowtv|nowruz|now|norton|northwesternmutual|nokia|nissay|nissan|ninja|nikon|' +
|
||||
'nike|nico|nhk|ngo|nfl|nexus|nextdirect|next|news|newholland|new|neustar|network|netflix|netbank|' +
|
||||
'net|nec|nba|navy|natura|nationwide|name|nagoya|nadex|nab|mutuelle|mutual|museum|mtr|mtpc|mtn|' +
|
||||
'msd|movistar|movie|mov|motorcycles|moto|moscow|mortgage|mormon|mopar|montblanc|monster|money|' +
|
||||
'monash|mom|moi|moe|moda|mobily|mobile|mobi|mma|mls|mlb|mitsubishi|mit|mint|mini|mil|microsoft|' +
|
||||
'miami|metlife|merckmsd|meo|menu|men|memorial|meme|melbourne|meet|media|med|mckinsey|mcdonalds|' +
|
||||
'mcd|mba|mattel|maserati|marshalls|marriott|markets|marketing|market|map|mango|management|man|' +
|
||||
'makeup|maison|maif|madrid|macys|luxury|luxe|lupin|lundbeck|ltda|ltd|lplfinancial|lpl|love|lotto|' +
|
||||
'lotte|london|lol|loft|locus|locker|loans|loan|lixil|living|live|lipsy|link|linde|lincoln|limo|' +
|
||||
'limited|lilly|like|lighting|lifestyle|lifeinsurance|life|lidl|liaison|lgbt|lexus|lego|legal|' +
|
||||
'lefrak|leclerc|lease|lds|lawyer|law|latrobe|latino|lat|lasalle|lanxess|landrover|land|lancome|' +
|
||||
'lancia|lancaster|lamer|lamborghini|ladbrokes|lacaixa|kyoto|kuokgroup|kred|krd|kpn|kpmg|kosher|' +
|
||||
'komatsu|koeln|kiwi|kitchen|kindle|kinder|kim|kia|kfh|kerryproperties|kerrylogistics|kerryhotels|' +
|
||||
'kddi|kaufen|juniper|juegos|jprs|jpmorgan|joy|jot|joburg|jobs|jnj|jmp|jll|jlc|jio|jewelry|jetzt|' +
|
||||
'jeep|jcp|jcb|java|jaguar|iwc|iveco|itv|itau|istanbul|ist|ismaili|iselect|irish|ipiranga|' +
|
||||
'investments|intuit|international|intel|int|insure|insurance|institute|ink|ing|info|infiniti|' +
|
||||
'industries|immobilien|immo|imdb|imamat|ikano|iinet|ifm|ieee|icu|ice|icbc|ibm|hyundai|hyatt|' +
|
||||
'hughes|htc|hsbc|how|house|hotmail|hotels|hoteles|hot|hosting|host|hospital|horse|honeywell|' +
|
||||
'honda|homesense|homes|homegoods|homedepot|holiday|holdings|hockey|hkt|hiv|hitachi|hisamitsu|' +
|
||||
'hiphop|hgtv|hermes|here|helsinki|help|healthcare|health|hdfcbank|hdfc|hbo|haus|hangout|hamburg|' +
|
||||
'hair|guru|guitars|guide|guge|gucci|guardian|group|grocery|gripe|green|gratis|graphics|grainger|' +
|
||||
'gov|got|gop|google|goog|goodyear|goodhands|goo|golf|goldpoint|gold|godaddy|gmx|gmo|gmbh|gmail|' +
|
||||
'globo|global|gle|glass|glade|giving|gives|gifts|gift|ggee|george|genting|gent|gea|gdn|gbiz|' +
|
||||
'garden|gap|games|game|gallup|gallo|gallery|gal|fyi|futbol|furniture|fund|fun|fujixerox|fujitsu|' +
|
||||
'ftr|frontier|frontdoor|frogans|frl|fresenius|free|fox|foundation|forum|forsale|forex|ford|' +
|
||||
'football|foodnetwork|food|foo|fly|flsmidth|flowers|florist|flir|flights|flickr|fitness|fit|' +
|
||||
'fishing|fish|firmdale|firestone|fire|financial|finance|final|film|fido|fidelity|fiat|ferrero|' +
|
||||
'ferrari|feedback|fedex|fast|fashion|farmers|farm|fans|fan|family|faith|fairwinds|fail|fage|' +
|
||||
'extraspace|express|exposed|expert|exchange|everbank|events|eus|eurovision|etisalat|esurance|' +
|
||||
'estate|esq|erni|ericsson|equipment|epson|epost|enterprises|engineering|engineer|energy|emerck|' +
|
||||
'email|education|edu|edeka|eco|eat|earth|dvr|dvag|durban|dupont|duns|dunlop|duck|dubai|dtv|drive|' +
|
||||
'download|dot|doosan|domains|doha|dog|dodge|doctor|docs|dnp|diy|dish|discover|discount|directory|' +
|
||||
'direct|digital|diet|diamonds|dhl|dev|design|desi|dentist|dental|democrat|delta|deloitte|dell|' +
|
||||
'delivery|degree|deals|dealer|deal|dds|dclk|day|datsun|dating|date|data|dance|dad|dabur|cyou|' +
|
||||
'cymru|cuisinella|csc|cruises|cruise|crs|crown|cricket|creditunion|creditcard|credit|courses|' +
|
||||
'coupons|coupon|country|corsica|coop|cool|cookingchannel|cooking|contractors|contact|consulting|' +
|
||||
'construction|condos|comsec|computer|compare|company|community|commbank|comcast|com|cologne|' +
|
||||
'college|coffee|codes|coach|clubmed|club|cloud|clothing|clinique|clinic|click|cleaning|claims|' +
|
||||
'cityeats|city|citic|citi|citadel|cisco|circle|cipriani|church|chrysler|chrome|christmas|chloe|' +
|
||||
'chintai|cheap|chat|chase|channel|chanel|cfd|cfa|cern|ceo|center|ceb|cbs|cbre|cbn|cba|catholic|' +
|
||||
'catering|cat|casino|cash|caseih|case|casa|cartier|cars|careers|career|care|cards|caravan|car|' +
|
||||
'capitalone|capital|capetown|canon|cancerresearch|camp|camera|cam|calvinklein|call|cal|cafe|cab|' +
|
||||
'bzh|buzz|buy|business|builders|build|bugatti|budapest|brussels|brother|broker|broadway|' +
|
||||
'bridgestone|bradesco|box|boutique|bot|boston|bostik|bosch|boots|booking|book|boo|bond|bom|bofa|' +
|
||||
'boehringer|boats|bnpparibas|bnl|bmw|bms|blue|bloomberg|blog|blockbuster|blanco|blackfriday|' +
|
||||
'black|biz|bio|bingo|bing|bike|bid|bible|bharti|bet|bestbuy|best|berlin|bentley|beer|beauty|' +
|
||||
'beats|bcn|bcg|bbva|bbt|bbc|bayern|bauhaus|basketball|baseball|bargains|barefoot|barclays|' +
|
||||
'barclaycard|barcelona|bar|bank|band|bananarepublic|banamex|baidu|baby|azure|axa|aws|avianca|' +
|
||||
'autos|auto|author|auspost|audio|audible|audi|auction|attorney|athleta|associates|asia|asda|arte|' +
|
||||
'art|arpa|army|archi|aramco|arab|aquarelle|apple|app|apartments|aol|anz|anquan|android|analytics|' +
|
||||
'amsterdam|amica|amfam|amex|americanfamily|americanexpress|alstom|alsace|ally|allstate|allfinanz|' +
|
||||
'alipay|alibaba|alfaromeo|akdn|airtel|airforce|airbus|aigo|aig|agency|agakhan|africa|afl|' +
|
||||
'afamilycompany|aetna|aero|aeg|adult|ads|adac|actor|active|aco|accountants|accountant|accenture|' +
|
||||
'academy|abudhabi|abogado|able|abc|abbvie|abbott|abb|abarth|aarp|aaa|onion' +
|
||||
')(?=[^0-9a-zA-Z@]|$))'));
|
||||
'(?:(?:' +
|
||||
'삼성|닷컴|닷넷|香格里拉|餐厅|食品|飞利浦|電訊盈科|集团|通販|购物|谷歌|诺基亚|联通|网络|网站|网店|网址|组织机构|移动|珠宝|点看|游戏|淡马锡|机构|書籍|时尚|新闻|政府|' +
|
||||
'政务|手表|手机|我爱你|慈善|微博|广东|工行|家電|娱乐|天主教|大拿|大众汽车|在线|嘉里大酒店|嘉里|商标|商店|商城|公益|公司|八卦|健康|信息|佛山|企业|中文网|中信|世界|' +
|
||||
'ポイント|ファッション|セール|ストア|コム|グーグル|クラウド|みんな|คอม|संगठन|नेट|कॉम|همراه|موقع|موبايلي|كوم|كاثوليك|عرب|شبكة|' +
|
||||
'بيتك|بازار|العليان|ارامكو|اتصالات|ابوظبي|קום|сайт|рус|орг|онлайн|москва|ком|католик|дети|' +
|
||||
'zuerich|zone|zippo|zip|zero|zara|zappos|yun|youtube|you|yokohama|yoga|yodobashi|yandex|yamaxun|' +
|
||||
'yahoo|yachts|xyz|xxx|xperia|xin|xihuan|xfinity|xerox|xbox|wtf|wtc|wow|world|works|work|woodside|' +
|
||||
'wolterskluwer|wme|winners|wine|windows|win|williamhill|wiki|wien|whoswho|weir|weibo|wedding|wed|' +
|
||||
'website|weber|webcam|weatherchannel|weather|watches|watch|warman|wanggou|wang|walter|walmart|' +
|
||||
'wales|vuelos|voyage|voto|voting|vote|volvo|volkswagen|vodka|vlaanderen|vivo|viva|vistaprint|' +
|
||||
'vista|vision|visa|virgin|vip|vin|villas|viking|vig|video|viajes|vet|versicherung|' +
|
||||
'vermögensberatung|vermögensberater|verisign|ventures|vegas|vanguard|vana|vacations|ups|uol|uno|' +
|
||||
'university|unicom|uconnect|ubs|ubank|tvs|tushu|tunes|tui|tube|trv|trust|travelersinsurance|' +
|
||||
'travelers|travelchannel|travel|training|trading|trade|toys|toyota|town|tours|total|toshiba|' +
|
||||
'toray|top|tools|tokyo|today|tmall|tkmaxx|tjx|tjmaxx|tirol|tires|tips|tiffany|tienda|tickets|' +
|
||||
'tiaa|theatre|theater|thd|teva|tennis|temasek|telefonica|telecity|tel|technology|tech|team|tdk|' +
|
||||
'tci|taxi|tax|tattoo|tatar|tatamotors|target|taobao|talk|taipei|tab|systems|symantec|sydney|' +
|
||||
'swiss|swiftcover|swatch|suzuki|surgery|surf|support|supply|supplies|sucks|style|study|studio|' +
|
||||
'stream|store|storage|stockholm|stcgroup|stc|statoil|statefarm|statebank|starhub|star|staples|' +
|
||||
'stada|srt|srl|spreadbetting|spot|spiegel|space|soy|sony|song|solutions|solar|sohu|software|' +
|
||||
'softbank|social|soccer|sncf|smile|smart|sling|skype|sky|skin|ski|site|singles|sina|silk|shriram|' +
|
||||
'showtime|show|shouji|shopping|shop|shoes|shiksha|shia|shell|shaw|sharp|shangrila|sfr|sexy|sex|' +
|
||||
'sew|seven|ses|services|sener|select|seek|security|secure|seat|search|scot|scor|scjohnson|' +
|
||||
'science|schwarz|schule|school|scholarships|schmidt|schaeffler|scb|sca|sbs|sbi|saxo|save|sas|' +
|
||||
'sarl|sapo|sap|sanofi|sandvikcoromant|sandvik|samsung|samsclub|salon|sale|sakura|safety|safe|' +
|
||||
'saarland|ryukyu|rwe|run|ruhr|rugby|rsvp|room|rogers|rodeo|rocks|rocher|rmit|rip|rio|ril|' +
|
||||
'rightathome|ricoh|richardli|rich|rexroth|reviews|review|restaurant|rest|republican|report|' +
|
||||
'repair|rentals|rent|ren|reliance|reit|reisen|reise|rehab|redumbrella|redstone|red|recipes|' +
|
||||
'realty|realtor|realestate|read|raid|radio|racing|qvc|quest|quebec|qpon|pwc|pub|prudential|pru|' +
|
||||
'protection|property|properties|promo|progressive|prof|productions|prod|pro|prime|press|praxi|' +
|
||||
'pramerica|post|porn|politie|poker|pohl|pnc|plus|plumbing|playstation|play|place|pizza|pioneer|' +
|
||||
'pink|ping|pin|pid|pictures|pictet|pics|piaget|physio|photos|photography|photo|phone|philips|phd|' +
|
||||
'pharmacy|pfizer|pet|pccw|pay|passagens|party|parts|partners|pars|paris|panerai|panasonic|' +
|
||||
'pamperedchef|page|ovh|ott|otsuka|osaka|origins|orientexpress|organic|org|orange|oracle|open|ooo|' +
|
||||
'onyourside|online|onl|ong|one|omega|ollo|oldnavy|olayangroup|olayan|okinawa|office|off|observer|' +
|
||||
'obi|nyc|ntt|nrw|nra|nowtv|nowruz|now|norton|northwesternmutual|nokia|nissay|nissan|ninja|nikon|' +
|
||||
'nike|nico|nhk|ngo|nfl|nexus|nextdirect|next|news|newholland|new|neustar|network|netflix|netbank|' +
|
||||
'net|nec|nba|navy|natura|nationwide|name|nagoya|nadex|nab|mutuelle|mutual|museum|mtr|mtpc|mtn|' +
|
||||
'msd|movistar|movie|mov|motorcycles|moto|moscow|mortgage|mormon|mopar|montblanc|monster|money|' +
|
||||
'monash|mom|moi|moe|moda|mobily|mobile|mobi|mma|mls|mlb|mitsubishi|mit|mint|mini|mil|microsoft|' +
|
||||
'miami|metlife|merckmsd|meo|menu|men|memorial|meme|melbourne|meet|media|med|mckinsey|mcdonalds|' +
|
||||
'mcd|mba|mattel|maserati|marshalls|marriott|markets|marketing|market|map|mango|management|man|' +
|
||||
'makeup|maison|maif|madrid|macys|luxury|luxe|lupin|lundbeck|ltda|ltd|lplfinancial|lpl|love|lotto|' +
|
||||
'lotte|london|lol|loft|locus|locker|loans|loan|lixil|living|live|lipsy|link|linde|lincoln|limo|' +
|
||||
'limited|lilly|like|lighting|lifestyle|lifeinsurance|life|lidl|liaison|lgbt|lexus|lego|legal|' +
|
||||
'lefrak|leclerc|lease|lds|lawyer|law|latrobe|latino|lat|lasalle|lanxess|landrover|land|lancome|' +
|
||||
'lancia|lancaster|lamer|lamborghini|ladbrokes|lacaixa|kyoto|kuokgroup|kred|krd|kpn|kpmg|kosher|' +
|
||||
'komatsu|koeln|kiwi|kitchen|kindle|kinder|kim|kia|kfh|kerryproperties|kerrylogistics|kerryhotels|' +
|
||||
'kddi|kaufen|juniper|juegos|jprs|jpmorgan|joy|jot|joburg|jobs|jnj|jmp|jll|jlc|jio|jewelry|jetzt|' +
|
||||
'jeep|jcp|jcb|java|jaguar|iwc|iveco|itv|itau|istanbul|ist|ismaili|iselect|irish|ipiranga|' +
|
||||
'investments|intuit|international|intel|int|insure|insurance|institute|ink|ing|info|infiniti|' +
|
||||
'industries|immobilien|immo|imdb|imamat|ikano|iinet|ifm|ieee|icu|ice|icbc|ibm|hyundai|hyatt|' +
|
||||
'hughes|htc|hsbc|how|house|hotmail|hotels|hoteles|hot|hosting|host|hospital|horse|honeywell|' +
|
||||
'honda|homesense|homes|homegoods|homedepot|holiday|holdings|hockey|hkt|hiv|hitachi|hisamitsu|' +
|
||||
'hiphop|hgtv|hermes|here|helsinki|help|healthcare|health|hdfcbank|hdfc|hbo|haus|hangout|hamburg|' +
|
||||
'hair|guru|guitars|guide|guge|gucci|guardian|group|grocery|gripe|green|gratis|graphics|grainger|' +
|
||||
'gov|got|gop|google|goog|goodyear|goodhands|goo|golf|goldpoint|gold|godaddy|gmx|gmo|gmbh|gmail|' +
|
||||
'globo|global|gle|glass|glade|giving|gives|gifts|gift|ggee|george|genting|gent|gea|gdn|gbiz|' +
|
||||
'garden|gap|games|game|gallup|gallo|gallery|gal|fyi|futbol|furniture|fund|fun|fujixerox|fujitsu|' +
|
||||
'ftr|frontier|frontdoor|frogans|frl|fresenius|free|fox|foundation|forum|forsale|forex|ford|' +
|
||||
'football|foodnetwork|food|foo|fly|flsmidth|flowers|florist|flir|flights|flickr|fitness|fit|' +
|
||||
'fishing|fish|firmdale|firestone|fire|financial|finance|final|film|fido|fidelity|fiat|ferrero|' +
|
||||
'ferrari|feedback|fedex|fast|fashion|farmers|farm|fans|fan|family|faith|fairwinds|fail|fage|' +
|
||||
'extraspace|express|exposed|expert|exchange|everbank|events|eus|eurovision|etisalat|esurance|' +
|
||||
'estate|esq|erni|ericsson|equipment|epson|epost|enterprises|engineering|engineer|energy|emerck|' +
|
||||
'email|education|edu|edeka|eco|eat|earth|dvr|dvag|durban|dupont|duns|dunlop|duck|dubai|dtv|drive|' +
|
||||
'download|dot|doosan|domains|doha|dog|dodge|doctor|docs|dnp|diy|dish|discover|discount|directory|' +
|
||||
'direct|digital|diet|diamonds|dhl|dev|design|desi|dentist|dental|democrat|delta|deloitte|dell|' +
|
||||
'delivery|degree|deals|dealer|deal|dds|dclk|day|datsun|dating|date|data|dance|dad|dabur|cyou|' +
|
||||
'cymru|cuisinella|csc|cruises|cruise|crs|crown|cricket|creditunion|creditcard|credit|courses|' +
|
||||
'coupons|coupon|country|corsica|coop|cool|cookingchannel|cooking|contractors|contact|consulting|' +
|
||||
'construction|condos|comsec|computer|compare|company|community|commbank|comcast|com|cologne|' +
|
||||
'college|coffee|codes|coach|clubmed|club|cloud|clothing|clinique|clinic|click|cleaning|claims|' +
|
||||
'cityeats|city|citic|citi|citadel|cisco|circle|cipriani|church|chrysler|chrome|christmas|chloe|' +
|
||||
'chintai|cheap|chat|chase|channel|chanel|cfd|cfa|cern|ceo|center|ceb|cbs|cbre|cbn|cba|catholic|' +
|
||||
'catering|cat|casino|cash|caseih|case|casa|cartier|cars|careers|career|care|cards|caravan|car|' +
|
||||
'capitalone|capital|capetown|canon|cancerresearch|camp|camera|cam|calvinklein|call|cal|cafe|cab|' +
|
||||
'bzh|buzz|buy|business|builders|build|bugatti|budapest|brussels|brother|broker|broadway|' +
|
||||
'bridgestone|bradesco|box|boutique|bot|boston|bostik|bosch|boots|booking|book|boo|bond|bom|bofa|' +
|
||||
'boehringer|boats|bnpparibas|bnl|bmw|bms|blue|bloomberg|blog|blockbuster|blanco|blackfriday|' +
|
||||
'black|biz|bio|bingo|bing|bike|bid|bible|bharti|bet|bestbuy|best|berlin|bentley|beer|beauty|' +
|
||||
'beats|bcn|bcg|bbva|bbt|bbc|bayern|bauhaus|basketball|baseball|bargains|barefoot|barclays|' +
|
||||
'barclaycard|barcelona|bar|bank|band|bananarepublic|banamex|baidu|baby|azure|axa|aws|avianca|' +
|
||||
'autos|auto|author|auspost|audio|audible|audi|auction|attorney|athleta|associates|asia|asda|arte|' +
|
||||
'art|arpa|army|archi|aramco|arab|aquarelle|apple|app|apartments|aol|anz|anquan|android|analytics|' +
|
||||
'amsterdam|amica|amfam|amex|americanfamily|americanexpress|alstom|alsace|ally|allstate|allfinanz|' +
|
||||
'alipay|alibaba|alfaromeo|akdn|airtel|airforce|airbus|aigo|aig|agency|agakhan|africa|afl|' +
|
||||
'afamilycompany|aetna|aero|aeg|adult|ads|adac|actor|active|aco|accountants|accountant|accenture|' +
|
||||
'academy|abudhabi|abogado|able|abc|abbvie|abbott|abb|abarth|aarp|aaa|onion' +
|
||||
')(?=[^0-9a-zA-Z@]|$))'));
|
||||
regexen.validCCTLD = regexSupplant(RegExp(
|
||||
'(?:(?:' +
|
||||
'(?:(?:' +
|
||||
'한국|香港|澳門|新加坡|台灣|台湾|中國|中国|გე|ไทย|ලංකා|ഭാരതം|ಭಾರತ|భారత్|சிங்கப்பூர்|இலங்கை|இந்தியா|ଭାରତ|ભારત|ਭਾਰਤ|' +
|
||||
'ভাৰত|ভারত|বাংলা|भारोत|भारतम्|भारत|ڀارت|پاکستان|مليسيا|مصر|قطر|فلسطين|عمان|عراق|سورية|سودان|تونس|' +
|
||||
'بھارت|بارت|ایران|امارات|المغرب|السعودية|الجزائر|الاردن|հայ|қаз|укр|срб|рф|мон|мкд|ею|бел|бг|ελ|' +
|
||||
@@ -143,7 +143,7 @@ export const urlRegex = (function() {
|
||||
'gu|gt|gs|gr|gq|gp|gn|gm|gl|gi|gh|gg|gf|ge|gd|gb|ga|fr|fo|fm|fk|fj|fi|eu|et|es|er|eh|eg|ee|ec|dz|' +
|
||||
'do|dm|dk|dj|de|cz|cy|cx|cw|cv|cu|cr|co|cn|cm|cl|ck|ci|ch|cg|cf|cd|cc|ca|bz|by|bw|bv|bt|bs|br|bq|' +
|
||||
'bo|bn|bm|bl|bj|bi|bh|bg|bf|be|bd|bb|ba|az|ax|aw|au|at|as|ar|aq|ao|an|am|al|ai|ag|af|ae|ad|ac' +
|
||||
')(?=[^0-9a-zA-Z@]|$))'));
|
||||
')(?=[^0-9a-zA-Z@]|$))'));
|
||||
regexen.validPunycode = /(?:xn--[0-9a-z]+)/;
|
||||
regexen.validSpecialCCTLD = /(?:(?:co|tv)(?=[^0-9a-zA-Z@]|$))/;
|
||||
regexen.validDomain = regexSupplant(/(?:#{validSubdomain}*#{validDomainName}(?:#{validGTLD}|#{validCCTLD}|#{validPunycode}))/);
|
||||
@@ -168,8 +168,8 @@ export const urlRegex = (function() {
|
||||
'#{validGeneralUrlPathChars}*' +
|
||||
')' +
|
||||
')' +
|
||||
'\\)'
|
||||
, 'i');
|
||||
'\\)',
|
||||
'i');
|
||||
// Valid end-of-path chracters (so /foo. does not gobble the period).
|
||||
// 1. Allow =&# for empty URL parameters and other URL-join artifacts
|
||||
regexen.validUrlPathEndingChars = regexSupplant(/[^#{spaces_group}\(\)\?!\*';:=\,\.\$%\[\]#{pd}~&\|@]|(?:#{validUrlBalancedParens})/i);
|
||||
@@ -190,7 +190,7 @@ export const urlRegex = (function() {
|
||||
'(?::(#{validPortNumber}))?' + // $4 Port number (optional)
|
||||
'(\\/#{validUrlPath}*)?' + // $5 URL Path
|
||||
'(\\?#{validUrlQueryChars}*#{validUrlQueryEndingChars})?' + // $6 Query String
|
||||
')'
|
||||
, 'gi');
|
||||
')',
|
||||
'gi');
|
||||
return regexen.validUrl;
|
||||
}());
|
||||
|
@@ -24,10 +24,10 @@ describe('emoji', () => {
|
||||
expect(emojify('\uD83D\uDC69\u200D\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66')).toEqual(
|
||||
'<img draggable="false" class="emojione" alt="👩👩👦👦" title=":woman-woman-boy-boy:" src="/emoji/1f469-200d-1f469-200d-1f466-200d-1f466.svg" />');
|
||||
expect(emojify('👨👩👧👧')).toEqual(
|
||||
'<img draggable="false" class="emojione" alt="👨👩👧👧" title=":man-woman-girl-girl:" src="/emoji/1f468-200d-1f469-200d-1f467-200d-1f467.svg" />');
|
||||
'<img draggable="false" class="emojione" alt="👨👩👧👧" title=":man-woman-girl-girl:" src="/emoji/1f468-200d-1f469-200d-1f467-200d-1f467.svg" />');
|
||||
expect(emojify('👩👩👦')).toEqual('<img draggable="false" class="emojione" alt="👩👩👦" title=":woman-woman-boy:" src="/emoji/1f469-200d-1f469-200d-1f466.svg" />');
|
||||
expect(emojify('\u2757')).toEqual(
|
||||
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" />');
|
||||
'<img draggable="false" class="emojione" alt="❗" title=":exclamation:" src="/emoji/2757.svg" />');
|
||||
});
|
||||
|
||||
it('does multiple unicode', () => {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { expandHomeTimeline } from '../../actions/timelines';
|
||||
import { expandHomeTimeline, refreshHomeTimeline } from '../../actions/timelines';
|
||||
import PropTypes from 'prop-types';
|
||||
import StatusListContainer from '../ui/containers/status_list_container';
|
||||
import Column from '../../components/column';
|
||||
@@ -16,6 +16,7 @@ const messages = defineMessages({
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0,
|
||||
isPartial: state.getIn(['timelines', 'home', 'isPartial'], false),
|
||||
});
|
||||
|
||||
@connect(mapStateToProps)
|
||||
@@ -26,6 +27,7 @@ export default class HomeTimeline extends React.PureComponent {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired,
|
||||
hasUnread: PropTypes.bool,
|
||||
isPartial: PropTypes.bool,
|
||||
columnId: PropTypes.string,
|
||||
multiColumn: PropTypes.bool,
|
||||
};
|
||||
@@ -57,6 +59,39 @@ export default class HomeTimeline extends React.PureComponent {
|
||||
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 () {
|
||||
const { intl, hasUnread, columnId, multiColumn } = this.props;
|
||||
const pinned = !!columnId;
|
||||
|
@@ -66,11 +66,11 @@ export default class ListEditor extends ImmutablePureComponent {
|
||||
{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 }) }}>
|
||||
{({ x }) =>
|
||||
{({ x }) => (
|
||||
<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} />)}
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</Motion>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -120,13 +120,17 @@ export default class ListTimeline extends React.PureComponent {
|
||||
if (typeof list === 'undefined') {
|
||||
return (
|
||||
<Column>
|
||||
<LoadingIndicator />
|
||||
<div className='scrollable'>
|
||||
<LoadingIndicator />
|
||||
</div>
|
||||
</Column>
|
||||
);
|
||||
} else if (list === false) {
|
||||
return (
|
||||
<Column>
|
||||
<MissingIndicator />
|
||||
<div className='scrollable'>
|
||||
<MissingIndicator />
|
||||
</div>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
@@ -133,7 +133,7 @@ export default class Notification extends ImmutablePureComponent {
|
||||
const { notification } = this.props;
|
||||
const account = notification.get('account');
|
||||
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')) {
|
||||
case 'follow':
|
||||
|
@@ -25,7 +25,7 @@ export default class ColumnHeader extends React.PureComponent {
|
||||
}
|
||||
|
||||
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}
|
||||
{type}
|
||||
</div>
|
||||
|
@@ -37,6 +37,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
columns: ImmutablePropTypes.list.isRequired,
|
||||
isModalOpen: PropTypes.bool.isRequired,
|
||||
singleColumn: PropTypes.bool,
|
||||
children: PropTypes.node,
|
||||
};
|
||||
@@ -144,7 +145,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { columns, children, singleColumn } = this.props;
|
||||
const { columns, children, singleColumn, isModalOpen } = this.props;
|
||||
const { shouldAnimate } = this.state;
|
||||
|
||||
const columnIndex = getIndex(this.context.router.history.location.pathname);
|
||||
@@ -159,7 +160,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='columns-area' ref={this.setRef}>
|
||||
<div className={`columns-area ${ isModalOpen ? 'unscrollable' : '' }`} ref={this.setRef}>
|
||||
{columns.map(column => {
|
||||
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 ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { FormattedMessage, injectIntl } from 'react-intl';
|
||||
import axios from 'axios';
|
||||
import api from '../../../api';
|
||||
|
||||
@injectIntl
|
||||
export default class EmbedModal extends ImmutablePureComponent {
|
||||
@@ -23,7 +23,7 @@ export default class EmbedModal extends ImmutablePureComponent {
|
||||
|
||||
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 });
|
||||
|
||||
const iframeDocument = this.iframe.contentWindow.document;
|
||||
|
@@ -113,13 +113,11 @@ export default class ModalRoot extends React.PureComponent {
|
||||
<div style={{ pointerEvents: visible ? 'auto' : 'none' }}>
|
||||
<div role='presentation' className='modal-root__overlay' onClick={onClose} />
|
||||
<div role='dialog' className='modal-root__container'>
|
||||
{
|
||||
visible ?
|
||||
(<BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}>
|
||||
{(SpecificComponent) => <SpecificComponent {...props} onClose={onClose} />}
|
||||
</BundleContainer>) :
|
||||
null
|
||||
}
|
||||
{visible && (
|
||||
<BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}>
|
||||
{(SpecificComponent) => <SpecificComponent {...props} onClose={onClose} />}
|
||||
</BundleContainer>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -24,14 +24,23 @@ const messages = defineMessages({
|
||||
|
||||
const PageOne = ({ acct, domain }) => (
|
||||
<div className='onboarding-modal__page onboarding-modal__page-one'>
|
||||
<div style={{ flex: '0 0 auto' }}>
|
||||
<div className='onboarding-modal__page-one__elephant-friend' />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className='onboarding-modal__page-one__lead'>
|
||||
<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.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>
|
||||
);
|
||||
@@ -46,22 +55,23 @@ const PageTwo = ({ myAccount }) => (
|
||||
<div className='figure non-interactive'>
|
||||
<div className='pseudo-drawer'>
|
||||
<NavigationBar account={myAccount} />
|
||||
|
||||
<ComposeForm
|
||||
text='Awoo! #introductions'
|
||||
suggestions={ImmutableList()}
|
||||
mentionedDomains={[]}
|
||||
spoiler={false}
|
||||
onChange={noop}
|
||||
onSubmit={noop}
|
||||
onPaste={noop}
|
||||
onPickEmoji={noop}
|
||||
onChangeSpoilerText={noop}
|
||||
onClearSuggestions={noop}
|
||||
onFetchSuggestions={noop}
|
||||
onSuggestionSelected={noop}
|
||||
showSearch
|
||||
/>
|
||||
</div>
|
||||
<ComposeForm
|
||||
text='Awoo! #introductions'
|
||||
suggestions={ImmutableList()}
|
||||
mentionedDomains={[]}
|
||||
spoiler={false}
|
||||
onChange={noop}
|
||||
onSubmit={noop}
|
||||
onPaste={noop}
|
||||
onPickEmoji={noop}
|
||||
onChangeSpoilerText={noop}
|
||||
onClearSuggestions={noop}
|
||||
onFetchSuggestions={noop}
|
||||
onSuggestionSelected={noop}
|
||||
showSearch
|
||||
/>
|
||||
</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>
|
||||
@@ -251,18 +261,12 @@ export default class OnboardingModal extends React.PureComponent {
|
||||
const hasMore = currentIndex < pages.length - 1;
|
||||
|
||||
const nextOrDoneBtn = hasMore ? (
|
||||
<button
|
||||
onClick={this.handleNext}
|
||||
className='onboarding-modal__nav onboarding-modal__next'
|
||||
>
|
||||
<FormattedMessage id='onboarding.next' defaultMessage='Next' />
|
||||
<button onClick={this.handleNext} className='onboarding-modal__nav onboarding-modal__next shake-bottom'>
|
||||
<FormattedMessage id='onboarding.next' defaultMessage='Next' /> <i className='fa fa-fw fa-chevron-right' />
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={this.handleClose}
|
||||
className='onboarding-modal__nav onboarding-modal__done'
|
||||
>
|
||||
<FormattedMessage id='onboarding.done' defaultMessage='Done' />
|
||||
<button onClick={this.handleClose} className='onboarding-modal__nav onboarding-modal__done shake-bottom'>
|
||||
<FormattedMessage id='onboarding.done' defaultMessage='Done' /> <i className='fa fa-fw fa-check' />
|
||||
</button>
|
||||
);
|
||||
|
||||
@@ -270,9 +274,10 @@ export default class OnboardingModal extends React.PureComponent {
|
||||
<div className='modal-root__modal onboarding-modal'>
|
||||
<ReactSwipeableViews index={currentIndex} onChangeIndex={this.handleSwipe} className='onboarding-modal__pager'>
|
||||
{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,
|
||||
});
|
||||
|
||||
return (
|
||||
<div key={i} className={className}>{page}</div>
|
||||
);
|
||||
@@ -294,6 +299,7 @@ export default class OnboardingModal extends React.PureComponent {
|
||||
const className = classNames('onboarding-modal__dot', {
|
||||
active: i === currentIndex,
|
||||
});
|
||||
|
||||
return (
|
||||
<div
|
||||
key={`dot-${i}`}
|
||||
|
@@ -37,14 +37,14 @@ export default class UploadArea extends React.PureComponent {
|
||||
|
||||
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 }) }}>
|
||||
{({ backgroundOpacity, backgroundScale }) =>
|
||||
{({ backgroundOpacity, backgroundScale }) => (
|
||||
<div className='upload-area' style={{ visibility: active ? 'visible' : 'hidden', opacity: backgroundOpacity }}>
|
||||
<div className='upload-area__drop'>
|
||||
<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>
|
||||
</div>
|
||||
}
|
||||
)}
|
||||
</Motion>
|
||||
);
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ import ColumnsArea from '../components/columns_area';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
columns: state.getIn(['settings', 'columns']),
|
||||
isModalOpen: !!state.get('modal').modalType,
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, null, null, { withRef: true })(ColumnsArea);
|
||||
|
@@ -47,6 +47,7 @@ const makeMapStateToProps = () => {
|
||||
const mapStateToProps = (state, { timelineId }) => ({
|
||||
statusIds: getStatusIds(state, { type: timelineId }),
|
||||
isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true),
|
||||
isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false),
|
||||
hasMore: !!state.getIn(['timelines', timelineId, 'next']),
|
||||
});
|
||||
|
||||
|
@@ -67,7 +67,7 @@
|
||||
"confirmations.delete_list.confirm": "Delete",
|
||||
"confirmations.delete_list.message": "هل تود حقا حذف هذه القائمة ؟",
|
||||
"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.message": "هل أنت متأكد أنك تريد كتم {name} ؟",
|
||||
"confirmations.unfollow.confirm": "إلغاء المتابعة",
|
||||
@@ -92,7 +92,7 @@
|
||||
"empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.",
|
||||
"empty_column.home": "إنك لا تتبع بعد أي شخص إلى حد الآن. زر {public} أو استخدام حقل البحث لكي تبدأ على التعرف على مستخدمين آخرين.",
|
||||
"empty_column.home.public_timeline": "الخيط العام",
|
||||
"empty_column.list": "هذه القائمة فارغة.",
|
||||
"empty_column.list": "هذه القائمة فارغة مؤقتا و لكن سوف تمتلئ تدريجيا عندما يبدأ الأعضاء المُنتَمين إليها بنشر تبويقات.",
|
||||
"empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.",
|
||||
"empty_column.public": "لا يوجد أي شيء هنا ! قم بنشر شيء ما للعامة، أو إتبع مستخدمين آخرين في الخوادم المثيلة الأخرى لملء خيط المحادثات العام",
|
||||
"follow_request.authorize": "ترخيص",
|
||||
@@ -123,7 +123,7 @@
|
||||
"keyboard_shortcuts.reply": "للردّ",
|
||||
"keyboard_shortcuts.search": "للتركيز على البحث",
|
||||
"keyboard_shortcuts.toot": "لتحرير تبويق جديد",
|
||||
"keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
|
||||
"keyboard_shortcuts.unfocus": "لإلغاء التركيز على حقل النص أو نافذة البحث",
|
||||
"keyboard_shortcuts.up": "للإنتقال إلى أعلى القائمة",
|
||||
"lightbox.close": "إغلاق",
|
||||
"lightbox.next": "التالي",
|
||||
|
@@ -92,7 +92,7 @@
|
||||
"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.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.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",
|
||||
|
@@ -174,7 +174,8 @@
|
||||
"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_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_six.admin": "Your instance's admin is {admin}.",
|
||||
"onboarding.page_six.almost_done": "Almost done...",
|
||||
|
@@ -7,22 +7,22 @@
|
||||
"account.followers": "پیگیران",
|
||||
"account.follows": "پی میگیرد",
|
||||
"account.follows_you": "پیگیر شماست",
|
||||
"account.hide_reblogs": "Hide boosts from @{name}",
|
||||
"account.hide_reblogs": "پنهان کردن بازبوقهای @{name}",
|
||||
"account.media": "رسانه",
|
||||
"account.mention": "نامبردن از @{name}",
|
||||
"account.moved_to": "{name} has moved to:",
|
||||
"account.moved_to": "{name} منتقل شده است به:",
|
||||
"account.mute": "بیصدا کردن @{name}",
|
||||
"account.mute_notifications": "Mute notifications from @{name}",
|
||||
"account.mute_notifications": "بیصداکردن اعلانها از طرف @{name}",
|
||||
"account.posts": "نوشتهها",
|
||||
"account.report": "گزارش @{name}",
|
||||
"account.requested": "در انتظار پذیرش",
|
||||
"account.share": "همرسانی نمایهٔ @{name}",
|
||||
"account.show_reblogs": "Show boosts from @{name}",
|
||||
"account.show_reblogs": "نشاندادن بازبوقهای @{name}",
|
||||
"account.unblock": "رفع انسداد @{name}",
|
||||
"account.unblock_domain": "رفع پنهانسازی از {domain}",
|
||||
"account.unfollow": "پایان پیگیری",
|
||||
"account.unmute": "باصدا کردن @{name}",
|
||||
"account.unmute_notifications": "Unmute notifications from @{name}",
|
||||
"account.unmute_notifications": "باصداکردن اعلانها از طرف @{name}",
|
||||
"account.view_full_profile": "نمایش نمایهٔ کامل",
|
||||
"boost_modal.combo": "دکمهٔ {combo} را بزنید تا دیگر این را نبینید",
|
||||
"bundle_column_error.body": "هنگام بازکردن این بخش خطایی رخ داد.",
|
||||
@@ -36,7 +36,7 @@
|
||||
"column.favourites": "پسندیدهها",
|
||||
"column.follow_requests": "درخواستهای پیگیری",
|
||||
"column.home": "خانه",
|
||||
"column.lists": "Lists",
|
||||
"column.lists": "فهرستها",
|
||||
"column.mutes": "کاربران بیصداشده",
|
||||
"column.notifications": "اعلانها",
|
||||
"column.pins": "نوشتههای ثابت",
|
||||
@@ -65,7 +65,7 @@
|
||||
"confirmations.delete.confirm": "پاک کن",
|
||||
"confirmations.delete.message": "آیا واقعاً میخواهید این نوشته را پاک کنید؟",
|
||||
"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.message": "آیا جدی جدی میخواهید کل دامین {domain} را مسدود کنید؟ بیشتر وقتها مسدودکردن یا بیصداکردن چند حساب کاربری خاص کافی است و توصیه میشود.",
|
||||
"confirmations.mute.confirm": "بیصدا کن",
|
||||
@@ -92,7 +92,7 @@
|
||||
"empty_column.hashtag": "هنوز هیچ چیزی با این هشتگ نیست.",
|
||||
"empty_column.home": "شما هنوز پیگیر کسی نیستید. {public} را ببینید یا چیزی را جستجو کنید تا کاربران دیگر را ببینید.",
|
||||
"empty_column.home.public_timeline": "فهرست نوشتههای همهجا",
|
||||
"empty_column.list": "There is nothing in this list yet.",
|
||||
"empty_column.list": "در این فهرست هنوز چیزی نیست. وقتی اعضای این فهرست چیزی بنویسند، اینجا ظاهر خواهد شد.",
|
||||
"empty_column.notifications": "هنوز هیچ اعلانی ندارید. به نوشتههای دیگران واکنش نشان دهید تا گفتگو آغاز شود.",
|
||||
"empty_column.public": "اینجا هنوز چیزی نیست! خودتان چیزی بنویسید یا کاربران دیگر را پی بگیرید تا اینجا پر شود",
|
||||
"follow_request.authorize": "اجازه دهید",
|
||||
@@ -108,46 +108,46 @@
|
||||
"home.column_settings.show_reblogs": "نمایش بازبوقها",
|
||||
"home.column_settings.show_replies": "نمایش پاسخها",
|
||||
"home.settings": "تنظیمات ستون",
|
||||
"keyboard_shortcuts.back": "to navigate back",
|
||||
"keyboard_shortcuts.boost": "to boost",
|
||||
"keyboard_shortcuts.column": "to focus a status in one of the columns",
|
||||
"keyboard_shortcuts.compose": "to focus the compose textarea",
|
||||
"keyboard_shortcuts.back": "برای بازگشت",
|
||||
"keyboard_shortcuts.boost": "برای بازبوقیدن",
|
||||
"keyboard_shortcuts.column": "برای برجستهکردن یک نوشته در یکی از ستونها",
|
||||
"keyboard_shortcuts.compose": "برای فعالکردن کادر نوشتهٔ تازه",
|
||||
"keyboard_shortcuts.description": "Description",
|
||||
"keyboard_shortcuts.down": "to move down in the list",
|
||||
"keyboard_shortcuts.down": "برای پایینرفتن در فهرست",
|
||||
"keyboard_shortcuts.enter": "to open status",
|
||||
"keyboard_shortcuts.favourite": "to favourite",
|
||||
"keyboard_shortcuts.favourite": "برای پسندیدن",
|
||||
"keyboard_shortcuts.heading": "Keyboard Shortcuts",
|
||||
"keyboard_shortcuts.hotkey": "Hotkey",
|
||||
"keyboard_shortcuts.legend": "to display this legend",
|
||||
"keyboard_shortcuts.mention": "to mention author",
|
||||
"keyboard_shortcuts.reply": "to reply",
|
||||
"keyboard_shortcuts.search": "to focus search",
|
||||
"keyboard_shortcuts.toot": "to start a brand new toot",
|
||||
"keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
|
||||
"keyboard_shortcuts.up": "to move up in the list",
|
||||
"keyboard_shortcuts.hotkey": "میانبر",
|
||||
"keyboard_shortcuts.legend": "برای نمایش این راهنما",
|
||||
"keyboard_shortcuts.mention": "برای نامبردن از نویسنده",
|
||||
"keyboard_shortcuts.reply": "برای پاسخدادن",
|
||||
"keyboard_shortcuts.search": "برای فعالکردن جستجو",
|
||||
"keyboard_shortcuts.toot": "برای آغاز یک بوق تازه",
|
||||
"keyboard_shortcuts.unfocus": "برای برداشتن توجه از نوشتن/جستجو",
|
||||
"keyboard_shortcuts.up": "برای بالا رفتن در فهرست",
|
||||
"lightbox.close": "بستن",
|
||||
"lightbox.next": "بعدی",
|
||||
"lightbox.previous": "قبلی",
|
||||
"lists.account.add": "Add to list",
|
||||
"lists.account.remove": "Remove from list",
|
||||
"lists.delete": "Delete list",
|
||||
"lists.edit": "Edit list",
|
||||
"lists.new.create": "Add list",
|
||||
"lists.new.title_placeholder": "New list title",
|
||||
"lists.search": "Search among people you follow",
|
||||
"lists.subheading": "Your lists",
|
||||
"lists.account.add": "افزودن به فهرست",
|
||||
"lists.account.remove": "پاککردن از فهرست",
|
||||
"lists.delete": "حذف فهرست",
|
||||
"lists.edit": "ویرایش فهرست",
|
||||
"lists.new.create": "افزودن فهرست",
|
||||
"lists.new.title_placeholder": "نام فهرست تازه",
|
||||
"lists.search": "بین کسانی که پی میگیرید بگردید",
|
||||
"lists.subheading": "فهرستهای شما",
|
||||
"loading_indicator.label": "بارگیری...",
|
||||
"media_gallery.toggle_visible": "تغییر پیدایی",
|
||||
"missing_indicator.label": "پیدا نشد",
|
||||
"mute_modal.hide_notifications": "Hide notifications from this user?",
|
||||
"mute_modal.hide_notifications": "اعلانهای این کاربر پنهان شود؟",
|
||||
"navigation_bar.blocks": "کاربران مسدودشده",
|
||||
"navigation_bar.community_timeline": "نوشتههای محلی",
|
||||
"navigation_bar.edit_profile": "ویرایش نمایه",
|
||||
"navigation_bar.favourites": "پسندیدهها",
|
||||
"navigation_bar.follow_requests": "درخواستهای پیگیری",
|
||||
"navigation_bar.info": "اطلاعات تکمیلی",
|
||||
"navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
|
||||
"navigation_bar.lists": "Lists",
|
||||
"navigation_bar.keyboard_shortcuts": "میانبرهای صفحهکلید",
|
||||
"navigation_bar.lists": "فهرستها",
|
||||
"navigation_bar.logout": "خروج",
|
||||
"navigation_bar.mutes": "کاربران بیصداشده",
|
||||
"navigation_bar.pins": "نوشتههای ثابت",
|
||||
@@ -174,7 +174,7 @@
|
||||
"onboarding.page_four.home": "ستون «خانه» نوشتههای کسانی را نشان میدهد که شما پی میگیرید.",
|
||||
"onboarding.page_four.notifications": "ستون «اعلانها» ارتباطهای شما با دیگران را نشان میدهد.",
|
||||
"onboarding.page_one.federation": "ماستدون شبکهای از سرورهای مستقل است که با پیوستن به یکدیگر یک شبکهٔ اجتماعی بزرگ را تشکیل میدهند.",
|
||||
"onboarding.page_one.handle": "شما روی سرور {domain} هستید، بنابراین شناسهٔ کامل شما {handle} است.",
|
||||
"onboarding.page_one.handle": "شما روی سرور {domain} هستید، بنابراین شناسهٔ کامل شما {handle} است",
|
||||
"onboarding.page_one.welcome": "به ماستدون خوش آمدید!",
|
||||
"onboarding.page_six.admin": "نشانی مسئول سرور شما {admin} است.",
|
||||
"onboarding.page_six.almost_done": "الان تقریباً آمادهاید...",
|
||||
@@ -199,7 +199,7 @@
|
||||
"privacy.unlisted.short": "فهرستنشده",
|
||||
"relative_time.days": "{number}d",
|
||||
"relative_time.hours": "{number}h",
|
||||
"relative_time.just_now": "now",
|
||||
"relative_time.just_now": "الان",
|
||||
"relative_time.minutes": "{number}m",
|
||||
"relative_time.seconds": "{number}s",
|
||||
"reply_indicator.cancel": "لغو",
|
||||
@@ -222,7 +222,7 @@
|
||||
"status.load_more": "بیشتر نشان بده",
|
||||
"status.media_hidden": "تصویر پنهان شده",
|
||||
"status.mention": "نامبردن از @{name}",
|
||||
"status.more": "More",
|
||||
"status.more": "بیشتر",
|
||||
"status.mute": "Mute @{name}",
|
||||
"status.mute_conversation": "بیصداکردن گفتگو",
|
||||
"status.open": "این نوشته را باز کن",
|
||||
@@ -244,7 +244,7 @@
|
||||
"tabs_bar.home": "خانه",
|
||||
"tabs_bar.local_timeline": "محلی",
|
||||
"tabs_bar.notifications": "اعلانها",
|
||||
"ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
|
||||
"ui.beforeunload": "اگر از ماستدون خارج شوید پیشنویس شما پاک خواهد شد.",
|
||||
"upload_area.title": "برای بارگذاری به اینجا بکشید",
|
||||
"upload_button.label": "افزودن تصویر",
|
||||
"upload_form.description": "نوشتهٔ توضیحی برای کمبینایان و نابینایان",
|
||||
|
@@ -92,7 +92,7 @@
|
||||
"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.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.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",
|
||||
@@ -109,7 +109,7 @@
|
||||
"home.column_settings.show_replies": "Mostrar respostas",
|
||||
"home.settings": "Axustes da columna",
|
||||
"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.compose": "Foco no área de escritura",
|
||||
"keyboard_shortcuts.description": "Descrición",
|
||||
@@ -227,8 +227,8 @@
|
||||
"status.mute_conversation": "Acalar conversa",
|
||||
"status.open": "Expandir este estado",
|
||||
"status.pin": "Fixar no perfil",
|
||||
"status.reblog": "Promocionar",
|
||||
"status.reblogged_by": "{name} promocionado",
|
||||
"status.reblog": "Promover",
|
||||
"status.reblogged_by": "{name} promoveu",
|
||||
"status.reply": "Resposta",
|
||||
"status.replyAll": "Resposta a conversa",
|
||||
"status.report": "Informar @{name}",
|
||||
|
@@ -25,11 +25,11 @@
|
||||
"account.unmute_notifications": "@{name}의 알림 뮤트 해제",
|
||||
"account.view_full_profile": "전체 프로필 보기",
|
||||
"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.title": "네트워크 에러",
|
||||
"bundle_modal_error.close": "닫기",
|
||||
"bundle_modal_error.message": "Something went wrong while loading this component.",
|
||||
"bundle_modal_error.message": "컴포넌트를 불러오는 과정에서 문제가 발생했습니다.",
|
||||
"bundle_modal_error.retry": "다시 시도",
|
||||
"column.blocks": "차단 중인 사용자",
|
||||
"column.community": "로컬 타임라인",
|
||||
@@ -50,7 +50,7 @@
|
||||
"column_header.unpin": "고정 해제",
|
||||
"column_subheading.navigation": "내비게이션",
|
||||
"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.lock": "비공개",
|
||||
"compose_form.placeholder": "지금 무엇을 하고 있나요?",
|
||||
@@ -135,7 +135,7 @@
|
||||
"lists.new.create": "리스트 추가",
|
||||
"lists.new.title_placeholder": "새 리스트의 이름",
|
||||
"lists.search": "팔로우 중인 사람들 중에서 찾기",
|
||||
"lists.subheading": "Your lists",
|
||||
"lists.subheading": "당신의 리스트",
|
||||
"loading_indicator.label": "불러오는 중...",
|
||||
"media_gallery.toggle_visible": "표시 전환",
|
||||
"missing_indicator.label": "찾을 수 없습니다",
|
||||
@@ -178,7 +178,7 @@
|
||||
"onboarding.page_one.welcome": "Mastodon에 어서 오세요!",
|
||||
"onboarding.page_six.admin": "이 인스턴스의 관리자는 {admin}입니다.",
|
||||
"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.github": "Mastodon는 오픈 소스 소프트웨어입니다. 버그 보고나 기능 추가 요청, 기여는 {github}에서 할 수 있습니다.",
|
||||
"onboarding.page_six.guidelines": "커뮤니티 가이드라인",
|
||||
@@ -213,7 +213,7 @@
|
||||
"search_popout.tips.text": "단순한 텍스트 검색은 관계된 프로필 이름, 유저 이름 그리고 해시태그를 표시합니다",
|
||||
"search_popout.tips.user": "유저",
|
||||
"search_results.total": "{count, number}건의 결과",
|
||||
"standalone.public_title": "A look inside...",
|
||||
"standalone.public_title": "지금 이런 이야기를 하고 있습니다…",
|
||||
"status.block": "@{name} 차단",
|
||||
"status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다",
|
||||
"status.delete": "삭제",
|
||||
@@ -247,7 +247,7 @@
|
||||
"ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.",
|
||||
"upload_area.title": "드래그 & 드롭으로 업로드",
|
||||
"upload_button.label": "미디어 추가",
|
||||
"upload_form.description": "Describe for the visually impaired",
|
||||
"upload_form.description": "시각장애인을 위한 설명",
|
||||
"upload_form.undo": "재시도",
|
||||
"upload_progress.label": "업로드 중...",
|
||||
"video.close": "동영상 닫기",
|
||||
|
@@ -100,10 +100,10 @@
|
||||
"getting_started.appsshort": "Apps",
|
||||
"getting_started.faq": "FAQ",
|
||||
"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",
|
||||
"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.show_reblogs": "Boosts tonen",
|
||||
"home.column_settings.show_replies": "Reacties tonen",
|
||||
@@ -146,7 +146,7 @@
|
||||
"navigation_bar.favourites": "Favorieten",
|
||||
"navigation_bar.follow_requests": "Volgverzoeken",
|
||||
"navigation_bar.info": "Uitgebreide informatie",
|
||||
"navigation_bar.keyboard_shortcuts": "Toetsenbord sneltoetsen",
|
||||
"navigation_bar.keyboard_shortcuts": "Sneltoetsen",
|
||||
"navigation_bar.lists": "Lijsten",
|
||||
"navigation_bar.logout": "Afmelden",
|
||||
"navigation_bar.mutes": "Genegeerde gebruikers",
|
||||
@@ -180,7 +180,7 @@
|
||||
"onboarding.page_six.almost_done": "Bijna klaar...",
|
||||
"onboarding.page_six.appetoot": "Veel succes!",
|
||||
"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.read_guidelines": "Vergeet niet de {guidelines} van {domain} te lezen!",
|
||||
"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.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.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.public": "Tu nic nie ma! Napisz coś publicznie, lub dodaj ludzi z innych instancji, aby to wyświetlić.",
|
||||
"follow_request.authorize": "Autoryzuj",
|
||||
|
@@ -50,7 +50,7 @@
|
||||
"column_header.unpin": "Desafixar",
|
||||
"column_subheading.navigation": "Navegação",
|
||||
"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.lock": "trancada",
|
||||
"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.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.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.public": "Não há nada aqui! Escreva algo publicamente ou siga manualmente usuários de outras instâncias",
|
||||
"follow_request.authorize": "Autorizar",
|
||||
@@ -223,7 +223,7 @@
|
||||
"status.media_hidden": "Mídia escondida",
|
||||
"status.mention": "Mencionar @{name}",
|
||||
"status.more": "Mais",
|
||||
"status.mute": "Mute @{name}",
|
||||
"status.mute": "Silenciar @{name}",
|
||||
"status.mute_conversation": "Silenciar conversa",
|
||||
"status.open": "Expandir",
|
||||
"status.pin": "Fixar no perfil",
|
||||
|
@@ -35,11 +35,11 @@
|
||||
"column.community": "Local",
|
||||
"column.favourites": "Favoritos",
|
||||
"column.follow_requests": "Seguidores Pendentes",
|
||||
"column.home": "Home",
|
||||
"column.home": "Início",
|
||||
"column.lists": "Listas",
|
||||
"column.mutes": "Utilizadores silenciados",
|
||||
"column.notifications": "Notificações",
|
||||
"column.pins": "Pinned toot",
|
||||
"column.pins": "Posts fixos",
|
||||
"column.public": "Global",
|
||||
"column_back_button.label": "Voltar",
|
||||
"column_header.hide_settings": "Esconder preferências",
|
||||
@@ -47,7 +47,7 @@
|
||||
"column_header.moveRight_settings": "Mover coluna para a direita",
|
||||
"column_header.pin": "Fixar",
|
||||
"column_header.show_settings": "Mostrar preferências",
|
||||
"column_header.unpin": "Remover fixar",
|
||||
"column_header.unpin": "Desafixar",
|
||||
"column_subheading.navigation": "Navegação",
|
||||
"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.",
|
||||
@@ -92,7 +92,7 @@
|
||||
"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.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.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",
|
||||
@@ -226,7 +226,7 @@
|
||||
"status.mute": "Mute @{name}",
|
||||
"status.mute_conversation": "Silenciar conversa",
|
||||
"status.open": "Expandir",
|
||||
"status.pin": "Pin on profile",
|
||||
"status.pin": "Fixar no perfil",
|
||||
"status.reblog": "Partilhar",
|
||||
"status.reblogged_by": "{name} partilhou",
|
||||
"status.reply": "Responder",
|
||||
@@ -234,7 +234,7 @@
|
||||
"status.report": "Denunciar @{name}",
|
||||
"status.sensitive_toggle": "Clique para ver",
|
||||
"status.sensitive_warning": "Conteúdo sensível",
|
||||
"status.share": "Share",
|
||||
"status.share": "Compartilhar",
|
||||
"status.show_less": "Mostrar menos",
|
||||
"status.show_more": "Mostrar mais",
|
||||
"status.unmute_conversation": "Deixar de silenciar esta conversa",
|
||||
|
@@ -195,8 +195,8 @@
|
||||
"privacy.private.short": "Iba sledujúci",
|
||||
"privacy.public.long": "Pošli všetkým",
|
||||
"privacy.public.short": "Verejne",
|
||||
"privacy.unlisted.long": "Neposielať verejne",
|
||||
"privacy.unlisted.short": "Nie je v zozname",
|
||||
"privacy.unlisted.long": "Neposielať do verejných časových osí",
|
||||
"privacy.unlisted.short": "Verejne mimo osí",
|
||||
"relative_time.days": "{number}d",
|
||||
"relative_time.hours": "{number}h",
|
||||
"relative_time.just_now": "now",
|
||||
|
@@ -11,7 +11,7 @@
|
||||
"account.media": "Mediji",
|
||||
"account.mention": "Pomeni korisnika @{name}",
|
||||
"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.posts": "Statusa",
|
||||
"account.report": "Prijavi @{name}",
|
||||
@@ -21,7 +21,7 @@
|
||||
"account.unblock": "Odblokiraj korisnika @{name}",
|
||||
"account.unblock_domain": "Odblokiraj domen {domain}",
|
||||
"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.view_full_profile": "Vidi ceo profil",
|
||||
"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.home": "Početna",
|
||||
"column.lists": "Liste",
|
||||
"column.mutes": "Mutirani korisnici",
|
||||
"column.mutes": "Ućutkani korisnici",
|
||||
"column.notifications": "Obaveštenja",
|
||||
"column.pins": "Prikačeni tutovi",
|
||||
"column.public": "Združena lajna",
|
||||
"column.public": "Federisana lajna",
|
||||
"column_back_button.label": "Nazad",
|
||||
"column_header.hide_settings": "Sakrij postavke",
|
||||
"column_header.moveLeft_settings": "Pomeri kolonu ulevo",
|
||||
@@ -50,6 +50,7 @@
|
||||
"column_header.unpin": "Otkači",
|
||||
"column_subheading.navigation": "Navigacija",
|
||||
"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.lock": "zaključan",
|
||||
"compose_form.placeholder": "Šta Vam je na umu?",
|
||||
@@ -66,9 +67,9 @@
|
||||
"confirmations.delete_list.confirm": "Obriši",
|
||||
"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.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.mute.confirm": "Mutiraj",
|
||||
"confirmations.mute.message": "Da li stvarno želite da mutirate korisnika {name}?",
|
||||
"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": "Ućutkaj",
|
||||
"confirmations.mute.message": "Da li stvarno želite da ućutkate korisnika {name}?",
|
||||
"confirmations.unfollow.confirm": "Otprati",
|
||||
"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.",
|
||||
@@ -148,10 +149,10 @@
|
||||
"navigation_bar.keyboard_shortcuts": "Prečice na tastaturi",
|
||||
"navigation_bar.lists": "Liste",
|
||||
"navigation_bar.logout": "Odjava",
|
||||
"navigation_bar.mutes": "Mutirani korisnici",
|
||||
"navigation_bar.mutes": "Ućutkani korisnici",
|
||||
"navigation_bar.pins": "Prikačeni tutovi",
|
||||
"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.follow": "{name} Vas je zapratio",
|
||||
"notification.mention": "{name} Vas je pomenuo",
|
||||
@@ -169,7 +170,7 @@
|
||||
"notifications.column_settings.sound": "Puštaj zvuk",
|
||||
"onboarding.done": "Gotovo",
|
||||
"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.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.",
|
||||
@@ -213,6 +214,7 @@
|
||||
"search_popout.tips.user": "korisnik",
|
||||
"search_results.total": "{count, number} {count, plural, one {rezultat} few {rezultata} other {rezultata}}",
|
||||
"standalone.public_title": "Pogled iznutra...",
|
||||
"status.block": "Block @{name}",
|
||||
"status.cannot_reblog": "Ovaj status ne može da se podrži",
|
||||
"status.delete": "Obriši",
|
||||
"status.embed": "Ugradi na sajt",
|
||||
@@ -221,7 +223,8 @@
|
||||
"status.media_hidden": "Multimedija sakrivena",
|
||||
"status.mention": "Pomeni korisnika @{name}",
|
||||
"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.pin": "Prikači na profil",
|
||||
"status.reblog": "Podrži",
|
||||
@@ -237,7 +240,7 @@
|
||||
"status.unmute_conversation": "Uključi prepisku",
|
||||
"status.unpin": "Otkači sa profila",
|
||||
"tabs_bar.compose": "Napiši",
|
||||
"tabs_bar.federated_timeline": "Združeno",
|
||||
"tabs_bar.federated_timeline": "Federisano",
|
||||
"tabs_bar.home": "Početna",
|
||||
"tabs_bar.local_timeline": "Lokalno",
|
||||
"tabs_bar.notifications": "Obaveštenja",
|
||||
|
@@ -11,7 +11,7 @@
|
||||
"account.media": "Медији",
|
||||
"account.mention": "Помени корисника @{name}",
|
||||
"account.moved_to": "{name} се померио на:",
|
||||
"account.mute": "Мутирај @{name}",
|
||||
"account.mute": "Ућуткај корисника @{name}",
|
||||
"account.mute_notifications": "Искључи обавештења од корисника @{name}",
|
||||
"account.posts": "Статуса",
|
||||
"account.report": "Пријави @{name}",
|
||||
@@ -21,7 +21,7 @@
|
||||
"account.unblock": "Одблокирај корисника @{name}",
|
||||
"account.unblock_domain": "Одблокирај домен {domain}",
|
||||
"account.unfollow": "Отпрати",
|
||||
"account.unmute": "Одмутирај @{name}",
|
||||
"account.unmute": "Уклони ућуткавање кориснику @{name}",
|
||||
"account.unmute_notifications": "Укључи назад обавештења од корисника @{name}",
|
||||
"account.view_full_profile": "Види цео профил",
|
||||
"boost_modal.combo": "Можете притиснути {combo} да прескочите ово следећи пут",
|
||||
@@ -37,10 +37,10 @@
|
||||
"column.follow_requests": "Захтеви за праћење",
|
||||
"column.home": "Почетна",
|
||||
"column.lists": "Листе",
|
||||
"column.mutes": "Мутирани корисници",
|
||||
"column.mutes": "Ућуткани корисници",
|
||||
"column.notifications": "Обавештења",
|
||||
"column.pins": "Прикачени тутови",
|
||||
"column.public": "Здружена лајна",
|
||||
"column.public": "Федерисана лајна",
|
||||
"column_back_button.label": "Назад",
|
||||
"column_header.hide_settings": "Сакриј поставке",
|
||||
"column_header.moveLeft_settings": "Помери колону улево",
|
||||
@@ -67,9 +67,9 @@
|
||||
"confirmations.delete_list.confirm": "Обриши",
|
||||
"confirmations.delete_list.message": "Да ли сте сигурни да желите да бесповратно обришете ову листу?",
|
||||
"confirmations.domain_block.confirm": "Сакриј цео домен",
|
||||
"confirmations.domain_block.message": "Да ли сте стварно, стварно сигурно да желите да блокирате цео домен {domain}? У већини случајева, пар добрих блокирања или мутирања су довољна и препоручљива.",
|
||||
"confirmations.mute.confirm": "Мутирај",
|
||||
"confirmations.mute.message": "Да ли стварно желите да мутирате корисника {name}?",
|
||||
"confirmations.domain_block.message": "Да ли сте стварно, стварно сигурно да желите да блокирате цео домен {domain}? У већини случајева, пар добрих блокирања или ућуткавања су довољна и препоручљива.",
|
||||
"confirmations.mute.confirm": "Ућуткај",
|
||||
"confirmations.mute.message": "Да ли стварно желите да ућуткате корисника {name}?",
|
||||
"confirmations.unfollow.confirm": "Отпрати",
|
||||
"confirmations.unfollow.message": "Да ли сте сигурни да желите да отпратите корисника {name}?",
|
||||
"embed.instructions": "Угради овај статус на Ваш веб сајт копирањем кода испод.",
|
||||
@@ -149,10 +149,10 @@
|
||||
"navigation_bar.keyboard_shortcuts": "Пречице на тастатури",
|
||||
"navigation_bar.lists": "Листе",
|
||||
"navigation_bar.logout": "Одјава",
|
||||
"navigation_bar.mutes": "Мутирани корисници",
|
||||
"navigation_bar.mutes": "Ућуткани корисници",
|
||||
"navigation_bar.pins": "Прикачени тутови",
|
||||
"navigation_bar.preferences": "Подешавања",
|
||||
"navigation_bar.public_timeline": "Здружена лајна",
|
||||
"navigation_bar.public_timeline": "Федерисана лајна",
|
||||
"notification.favourite": "{name} је ставио Ваш статус као омиљени",
|
||||
"notification.follow": "{name} Вас је запратио",
|
||||
"notification.mention": "{name} Вас је поменуо",
|
||||
@@ -170,7 +170,7 @@
|
||||
"notifications.column_settings.sound": "Пуштај звук",
|
||||
"onboarding.done": "Готово",
|
||||
"onboarding.next": "Следеће",
|
||||
"onboarding.page_five.public_timelines": "Локална лајна приказује све јавне статусе од свих на домену {domain}. Здружена лајна приказује јавне статусе од свих људи које прате корисници са домена {domain}. Ово су јавне лајне, сјајан начин да откријете нове људе.",
|
||||
"onboarding.page_five.public_timelines": "Локална лајна приказује све јавне статусе од свих на домену {domain}. Федерисана лајна приказује јавне статусе од свих људи које прате корисници са домена {domain}. Ово су јавне лајне, сјајан начин да откријете нове људе.",
|
||||
"onboarding.page_four.home": "Почетна лајна приказује статусе људи које Ви пратите.",
|
||||
"onboarding.page_four.notifications": "Колона са обавештењима Вам приказује када неко прича са Вама.",
|
||||
"onboarding.page_one.federation": "Мастодонт је мрежа независних сервера који се увезују да направе једну већу друштвену мрежу. Ове сервере зовемо инстанцама.",
|
||||
@@ -224,7 +224,7 @@
|
||||
"status.mention": "Помени корисника @{name}",
|
||||
"status.more": "Још",
|
||||
"status.mute": "Mute @{name}",
|
||||
"status.mute_conversation": "Мутирај преписку",
|
||||
"status.mute_conversation": "Ућуткај преписку",
|
||||
"status.open": "Прошири овај статус",
|
||||
"status.pin": "Прикачи на профил",
|
||||
"status.reblog": "Подржи",
|
||||
@@ -240,7 +240,7 @@
|
||||
"status.unmute_conversation": "Укључи преписку",
|
||||
"status.unpin": "Откачи са профила",
|
||||
"tabs_bar.compose": "Напиши",
|
||||
"tabs_bar.federated_timeline": "Здружено",
|
||||
"tabs_bar.federated_timeline": "Федерисано",
|
||||
"tabs_bar.home": "Почетна",
|
||||
"tabs_bar.local_timeline": "Локално",
|
||||
"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_subheading.navigation": "导航",
|
||||
"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.lock": "开启保护",
|
||||
"compose_form.placeholder": "在想啥?",
|
||||
@@ -139,6 +139,7 @@
|
||||
"loading_indicator.label": "加载中……",
|
||||
"media_gallery.toggle_visible": "切换显示/隐藏",
|
||||
"missing_indicator.label": "找不到内容",
|
||||
"missing_indicator.sublabel": "无法找到此资源",
|
||||
"mute_modal.hide_notifications": "同时隐藏来自这个用户的通知",
|
||||
"navigation_bar.blocks": "被屏蔽的用户",
|
||||
"navigation_bar.community_timeline": "本站时间轴",
|
||||
@@ -170,11 +171,12 @@
|
||||
"notifications.column_settings.sound": "播放音效",
|
||||
"onboarding.done": "出发!",
|
||||
"onboarding.next": "下一步",
|
||||
"onboarding.page_five.public_timelines": "本站时间轴显示的是由本站({domain})用户发布的所有公开嘟文。跨站公共时间轴显示的的是由本站用户关注对象所发布的所有公开嘟文。这些就是寻人好去处的公共时间轴啦。",
|
||||
"onboarding.page_four.home": "你的主页时间轴上显示的是你的关注对象所发布的嘟文。",
|
||||
"onboarding.page_four.notifications": "如果有人与你互动了,他们就会出现在通知栏中哦~",
|
||||
"onboarding.page_one.federation": "Mastodon 是由一系列独立的服务器共同打造的强大的社交网络,我们将这些各自独立而又相互连接的服务器叫做实例。",
|
||||
"onboarding.page_one.handle": "你是在 {domain} 上注册的,所以你的完整用户地址是 {handle}。",
|
||||
"onboarding.page_five.public_timelines": "“本站时间轴”显示的是由本站({domain})用户发布的所有公开嘟文。“跨站公共时间轴”显示的的是由本站用户关注对象所发布的所有公开嘟文。这些就是寻人好去处的公共时间轴啦。",
|
||||
"onboarding.page_four.home": "你的“主页”时间轴上显示的是你的关注对象所发布的嘟文。",
|
||||
"onboarding.page_four.notifications": "如果有人与你互动了,他们就会出现在“通知”栏中哦~",
|
||||
"onboarding.page_one.federation": "Mastodon 是由一系列独立的服务器共同打造的强大的社交网络,我们将这些各自独立而又相互连接的服务器叫做“实例”。",
|
||||
"onboarding.page_one.full_handle": "你的完整用户地址",
|
||||
"onboarding.page_one.handle_hint": "你的朋友们需要这个才能通过搜索功能找到你。",
|
||||
"onboarding.page_one.welcome": "欢迎来到 Mastodon!",
|
||||
"onboarding.page_six.admin": "{admin} 是你所在服务器实例的管理员。",
|
||||
"onboarding.page_six.almost_done": "差不多了……",
|
||||
@@ -184,8 +186,8 @@
|
||||
"onboarding.page_six.guidelines": "社区指南",
|
||||
"onboarding.page_six.read_guidelines": "别忘了看看 {domain} 的{guidelines}!",
|
||||
"onboarding.page_six.various_app": "移动设备应用",
|
||||
"onboarding.page_three.profile": "你可以修改你的个人资料,比如头像、简介和昵称等偏好设置。",
|
||||
"onboarding.page_three.search": "你可以通过搜索功能寻找用户和话题标签,比如{illustration}或者{introductions}。如果你想搜索其他实例上的用户,就需要输入完整用户地址(@用户名@域名)哦。",
|
||||
"onboarding.page_three.profile": "你还可以修改你的个人资料,比如头像、简介和昵称等偏好设置。",
|
||||
"onboarding.page_three.search": "你可以通过搜索功能寻找用户和话题标签,比如“{illustration}”,或是“{introductions}”。如果你想搜索其他实例上的用户,就需要输入完整用户地址(@用户名@域名)哦。",
|
||||
"onboarding.page_two.compose": "在撰写栏中开始嘟嘟吧!下方的按钮分别可以用来上传图片、修改嘟文可见范围,以及添加警告信息。",
|
||||
"onboarding.skip": "跳过",
|
||||
"privacy.change": "设置嘟文可见范围",
|
||||
@@ -197,6 +199,8 @@
|
||||
"privacy.public.short": "公开",
|
||||
"privacy.unlisted.long": "所有人可见,但不会出现在公共时间轴上",
|
||||
"privacy.unlisted.short": "不公开",
|
||||
"regeneration_indicator.label": "加载中……",
|
||||
"regeneration_indicator.sublabel": "你的主页时间轴正在准备中!",
|
||||
"relative_time.days": "{number}天",
|
||||
"relative_time.hours": "{number}时",
|
||||
"relative_time.just_now": "刚刚",
|
||||
@@ -214,7 +218,7 @@
|
||||
"search_popout.tips.user": "用户",
|
||||
"search_results.total": "共 {count, number} 个结果",
|
||||
"standalone.public_title": "大家都在干啥?",
|
||||
"status.block": "Block @{name}",
|
||||
"status.block": "屏蔽 @{name}",
|
||||
"status.cannot_reblog": "无法转嘟这条嘟文",
|
||||
"status.delete": "删除",
|
||||
"status.embed": "嵌入",
|
||||
@@ -223,7 +227,7 @@
|
||||
"status.media_hidden": "隐藏媒体内容",
|
||||
"status.mention": "提及 @{name}",
|
||||
"status.more": "更多",
|
||||
"status.mute": "Mute @{name}",
|
||||
"status.mute": "隐藏 @{name}",
|
||||
"status.mute_conversation": "隐藏此对话",
|
||||
"status.open": "展开嘟文",
|
||||
"status.pin": "在个人资料页面置顶",
|
||||
|
@@ -30,7 +30,7 @@ const initialTimeline = ImmutableMap({
|
||||
items: ImmutableList(),
|
||||
});
|
||||
|
||||
const normalizeTimeline = (state, timeline, statuses, next) => {
|
||||
const normalizeTimeline = (state, timeline, statuses, next, isPartial) => {
|
||||
const oldIds = state.getIn([timeline, 'items'], ImmutableList());
|
||||
const ids = ImmutableList(statuses.map(status => status.get('id'))).filter(newId => !oldIds.includes(newId));
|
||||
const wasLoaded = state.getIn([timeline, 'loaded']);
|
||||
@@ -40,7 +40,8 @@ const normalizeTimeline = (state, timeline, statuses, next) => {
|
||||
mMap.set('loaded', true);
|
||||
mMap.set('isLoading', false);
|
||||
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:
|
||||
return state.update(action.timeline, initialTimeline, map => map.set('isLoading', false));
|
||||
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:
|
||||
return appendNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next);
|
||||
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/basics';
|
||||
@import 'mastodon/modal';
|
||||
@import 'mastodon/containers';
|
||||
@import 'mastodon/lists';
|
||||
@import 'mastodon/footer';
|
||||
@@ -15,7 +14,9 @@
|
||||
@import 'mastodon/forms';
|
||||
@import 'mastodon/accounts';
|
||||
@import 'mastodon/stream_entries';
|
||||
@import 'mastodon/boost';
|
||||
@import 'mastodon/components';
|
||||
@import 'mastodon/modal';
|
||||
@import 'mastodon/emoji_picker';
|
||||
@import 'mastodon/about';
|
||||
@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 {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
&__timestamp {
|
||||
@@ -415,7 +417,7 @@
|
||||
color: $ui-primary-color;
|
||||
font-family: 'mastodon-font-monospace', monospace;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
word-wrap: break-word;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
|