Merge branch 'features/3.5.0' into v3.5.0
This commit is contained in:
commit
356c02d873
32
.github/CODEOWNERS
vendored
Normal file
32
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# CODEOWNERS for mastodon/mastodon
|
||||||
|
|
||||||
|
# Translators
|
||||||
|
# To add translator, copy these lines, replace `fr` with appropriate language code and replace `@żelipapą` with user's GitHub nickname preceded by `@` sign or e-mail address.
|
||||||
|
# /app/javascript/mastodon/locales/fr.json @żelipapą
|
||||||
|
# /app/views/user_mailer/*.fr.html.erb @żelipapą
|
||||||
|
# /app/views/user_mailer/*.fr.text.erb @żelipapą
|
||||||
|
# /config/locales/*.fr.yml @żelipapą
|
||||||
|
# /config/locales/fr.yml @żelipapą
|
||||||
|
|
||||||
|
# Polish
|
||||||
|
/app/javascript/mastodon/locales/pl.json @m4sk1n
|
||||||
|
/app/views/user_mailer/*.pl.html.erb @m4sk1n
|
||||||
|
/app/views/user_mailer/*.pl.text.erb @m4sk1n
|
||||||
|
/config/locales/*.pl.yml @m4sk1n
|
||||||
|
/config/locales/pl.yml @m4sk1n
|
||||||
|
|
||||||
|
# French
|
||||||
|
/app/javascript/mastodon/locales/fr.json @aldarone
|
||||||
|
/app/javascript/mastodon/locales/whitelist_fr.json @aldarone
|
||||||
|
/app/views/user_mailer/*.fr.html.erb @aldarone
|
||||||
|
/app/views/user_mailer/*.fr.text.erb @aldarone
|
||||||
|
/config/locales/*.fr.yml @aldarone
|
||||||
|
/config/locales/fr.yml @aldarone
|
||||||
|
|
||||||
|
# Dutch
|
||||||
|
/app/javascript/mastodon/locales/nl.json @jeroenpraat
|
||||||
|
/app/javascript/mastodon/locales/whitelist_nl.json @jeroenpraat
|
||||||
|
/app/views/user_mailer/*.nl.html.erb @jeroenpraat
|
||||||
|
/app/views/user_mailer/*.nl.text.erb @jeroenpraat
|
||||||
|
/config/locales/*.nl.yml @jeroenpraat
|
||||||
|
/config/locales/nl.yml @jeroenpraat
|
22
.github/dependabot.yml
vendored
22
.github/dependabot.yml
vendored
@ -1,22 +0,0 @@
|
|||||||
# To get started with Dependabot version updates, you'll need to specify which
|
|
||||||
# package ecosystems to update and where the package manifests are located.
|
|
||||||
# Please see the documentation for all configuration options:
|
|
||||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
|
||||||
|
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: npm
|
|
||||||
directory: '/'
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
open-pull-requests-limit: 99
|
|
||||||
allow:
|
|
||||||
- dependency-type: direct
|
|
||||||
|
|
||||||
- package-ecosystem: bundler
|
|
||||||
directory: '/'
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
open-pull-requests-limit: 99
|
|
||||||
allow:
|
|
||||||
- dependency-type: direct
|
|
6
.github/workflows/build-image.yml
vendored
6
.github/workflows/build-image.yml
vendored
@ -5,11 +5,7 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- 'main'
|
- 'main'
|
||||||
tags:
|
tags:
|
||||||
- '*'
|
- "*"
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- .github/workflows/build-image.yml
|
|
||||||
- Dockerfile
|
|
||||||
jobs:
|
jobs:
|
||||||
build-image:
|
build-image:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
@ -1695,7 +1695,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
- Change Docker image to use Ubuntu with jemalloc ([Sir-Boops](https://github.com/mastodon/mastodon/pull/10100), [BenLubar](https://github.com/mastodon/mastodon/pull/10212))
|
- Change Docker image to use Ubuntu with jemalloc ([Sir-Boops](https://github.com/mastodon/mastodon/pull/10100), [BenLubar](https://github.com/mastodon/mastodon/pull/10212))
|
||||||
- Change public pages to be cacheable by proxies ([BenLubar](https://github.com/mastodon/mastodon/pull/9059))
|
- Change public pages to be cacheable by proxies ([BenLubar](https://github.com/mastodon/mastodon/pull/9059))
|
||||||
- Change the 410 gone response for suspended accounts to be cacheable by proxies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10339))
|
- Change the 410 gone response for suspended accounts to be cacheable by proxies ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10339))
|
||||||
- Change web UI to not empty timeline of blocked users on block ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10359))
|
- Change web UI to not not empty timeline of blocked users on block ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/10359))
|
||||||
- Change JSON serializer to remove unused `@context` values ([Gargron](https://github.com/mastodon/mastodon/pull/10378))
|
- Change JSON serializer to remove unused `@context` values ([Gargron](https://github.com/mastodon/mastodon/pull/10378))
|
||||||
- Change GIFV file size limit to be the same as for other videos ([rinsuki](https://github.com/mastodon/mastodon/pull/9924))
|
- Change GIFV file size limit to be the same as for other videos ([rinsuki](https://github.com/mastodon/mastodon/pull/9924))
|
||||||
- Change Webpack to not use @babel/preset-env to compile node_modules ([ykzts](https://github.com/mastodon/mastodon/pull/10289))
|
- Change Webpack to not use @babel/preset-env to compile node_modules ([ykzts](https://github.com/mastodon/mastodon/pull/10289))
|
||||||
|
2
Gemfile
2
Gemfile
@ -98,6 +98,8 @@ gem 'json-ld'
|
|||||||
gem 'json-ld-preloaded', '~> 3.2'
|
gem 'json-ld-preloaded', '~> 3.2'
|
||||||
gem 'rdf-normalize', '~> 0.5'
|
gem 'rdf-normalize', '~> 0.5'
|
||||||
|
|
||||||
|
gem 'redcarpet', "~> 3.4.0"
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'fabrication', '~> 2.27'
|
gem 'fabrication', '~> 2.27'
|
||||||
gem 'fuubar', '~> 2.5'
|
gem 'fuubar', '~> 2.5'
|
||||||
|
@ -522,6 +522,14 @@ GEM
|
|||||||
rdf-normalize (0.5.0)
|
rdf-normalize (0.5.0)
|
||||||
rdf (~> 3.2)
|
rdf (~> 3.2)
|
||||||
redis (4.5.1)
|
redis (4.5.1)
|
||||||
|
redcarpet (3.4.0)
|
||||||
|
redis-actionpack (5.2.0)
|
||||||
|
actionpack (>= 5, < 7)
|
||||||
|
redis-rack (>= 2.1.0, < 3)
|
||||||
|
redis-store (>= 1.1.0, < 2)
|
||||||
|
redis-activesupport (5.2.0)
|
||||||
|
activesupport (>= 3, < 7)
|
||||||
|
redis-store (>= 1.3, < 2)
|
||||||
redis-namespace (1.8.2)
|
redis-namespace (1.8.2)
|
||||||
redis (>= 3.0.4)
|
redis (>= 3.0.4)
|
||||||
regexp_parser (2.2.1)
|
regexp_parser (2.2.1)
|
||||||
@ -807,6 +815,7 @@ DEPENDENCIES
|
|||||||
rails-i18n (~> 6.0)
|
rails-i18n (~> 6.0)
|
||||||
rails-settings-cached (~> 0.6)
|
rails-settings-cached (~> 0.6)
|
||||||
rdf-normalize (~> 0.5)
|
rdf-normalize (~> 0.5)
|
||||||
|
redcarpet (~> 3.4.0)
|
||||||
redis (~> 4.5)
|
redis (~> 4.5)
|
||||||
redis-namespace (~> 1.8)
|
redis-namespace (~> 1.8)
|
||||||
rexml (~> 3.2)
|
rexml (~> 3.2)
|
||||||
|
@ -7,8 +7,9 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
|
|||||||
after_action :insert_pagination_headers, unless: -> { truthy_param?(:pinned) }
|
after_action :insert_pagination_headers, unless: -> { truthy_param?(:pinned) }
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@statuses = load_statuses
|
@statuses = load_statuses
|
||||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
accountIds = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
|
||||||
|
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id), account_relationships: AccountRelationshipsPresenter.new(accountIds, current_user&.account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -6,8 +6,9 @@ class Api::V1::BookmarksController < Api::BaseController
|
|||||||
after_action :insert_pagination_headers
|
after_action :insert_pagination_headers
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@statuses = load_statuses
|
@statuses = load_statuses
|
||||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
accountIds = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
|
||||||
|
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id), account_relationships: AccountRelationshipsPresenter.new(accountIds, current_user&.account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -6,8 +6,9 @@ class Api::V1::FavouritesController < Api::BaseController
|
|||||||
after_action :insert_pagination_headers
|
after_action :insert_pagination_headers
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@statuses = load_statuses
|
@statuses = load_statuses
|
||||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
accountIds = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
|
||||||
|
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id), account_relationships: AccountRelationshipsPresenter.new(accountIds, current_user&.account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -31,8 +31,9 @@ class Api::V1::StatusesController < Api::BaseController
|
|||||||
|
|
||||||
@context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
|
@context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
|
||||||
statuses = [@status] + @context.ancestors + @context.descendants
|
statuses = [@status] + @context.ancestors + @context.descendants
|
||||||
|
accountIds = statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
|
||||||
|
|
||||||
render json: @context, serializer: REST::ContextSerializer, relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id)
|
render json: @context, serializer: REST::ContextSerializer, relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id), account_relationships: AccountRelationshipsPresenter.new(accountIds, current_user&.account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
@ -49,7 +50,8 @@ class Api::V1::StatusesController < Api::BaseController
|
|||||||
application: doorkeeper_token.application,
|
application: doorkeeper_token.application,
|
||||||
poll: status_params[:poll],
|
poll: status_params[:poll],
|
||||||
idempotency: request.headers['Idempotency-Key'],
|
idempotency: request.headers['Idempotency-Key'],
|
||||||
with_rate_limit: true
|
with_rate_limit: true,
|
||||||
|
quote_id: status_params[:quote_id].presence,
|
||||||
)
|
)
|
||||||
|
|
||||||
render json: @status, serializer: @status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer
|
render json: @status, serializer: @status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer
|
||||||
@ -108,6 +110,7 @@ class Api::V1::StatusesController < Api::BaseController
|
|||||||
:visibility,
|
:visibility,
|
||||||
:language,
|
:language,
|
||||||
:scheduled_at,
|
:scheduled_at,
|
||||||
|
:quote_id,
|
||||||
media_ids: [],
|
media_ids: [],
|
||||||
poll: [
|
poll: [
|
||||||
:multiple,
|
:multiple,
|
||||||
|
@ -6,11 +6,13 @@ class Api::V1::Timelines::HomeController < Api::BaseController
|
|||||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@statuses = load_statuses
|
@statuses = load_statuses
|
||||||
|
accountIds = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
|
||||||
|
|
||||||
render json: @statuses,
|
render json: @statuses,
|
||||||
each_serializer: REST::StatusSerializer,
|
each_serializer: REST::StatusSerializer,
|
||||||
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
|
relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id),
|
||||||
|
account_relationships: AccountRelationshipsPresenter.new(accountIds, current_user&.account_id),
|
||||||
status: account_home_feed.regenerating? ? 206 : 200
|
status: account_home_feed.regenerating? ? 206 : 200
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -9,9 +9,12 @@ class Api::V1::Timelines::ListController < Api::BaseController
|
|||||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
accountIds = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
|
||||||
|
|
||||||
render json: @statuses,
|
render json: @statuses,
|
||||||
each_serializer: REST::StatusSerializer,
|
each_serializer: REST::StatusSerializer,
|
||||||
relationships: StatusRelationshipsPresenter.new(@statuses, current_user.account_id)
|
relationships: StatusRelationshipsPresenter.new(@statuses, current_user.account_id),
|
||||||
|
account_relationships: AccountRelationshipsPresenter.new(accountIds, current_user&.account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -5,8 +5,10 @@ class Api::V1::Timelines::PublicController < Api::BaseController
|
|||||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@statuses = load_statuses
|
@statuses = load_statuses
|
||||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
accountIds = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
|
||||||
|
|
||||||
|
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id), account_relationships: AccountRelationshipsPresenter.new(accountIds, current_user&.account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -5,8 +5,9 @@ class Api::V1::Timelines::TagController < Api::BaseController
|
|||||||
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@statuses = load_statuses
|
@statuses = load_statuses
|
||||||
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
|
accountIds = @statuses.filter(&:quote?).map { |status| status.quote.account_id }.uniq
|
||||||
|
render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id), account_relationships: AccountRelationshipsPresenter.new(accountIds, current_user&.account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V2::MediaController < Api::V1::MediaController
|
||||||
|
end
|
||||||
|
__END__
|
||||||
class Api::V2::MediaController < Api::V1::MediaController
|
class Api::V2::MediaController < Api::V1::MediaController
|
||||||
def create
|
def create
|
||||||
@media_attachment = current_account.media_attachments.create!({ delay_processing: true }.merge(media_attachment_params))
|
@media_attachment = current_account.media_attachments.create!({ delay_processing: true }.merge(media_attachment_params))
|
||||||
|
@ -54,6 +54,8 @@ class Settings::PreferencesController < Settings::BaseController
|
|||||||
:setting_use_pending_items,
|
:setting_use_pending_items,
|
||||||
:setting_trends,
|
:setting_trends,
|
||||||
:setting_crop_images,
|
:setting_crop_images,
|
||||||
|
:setting_place_tab_bar_at_bottom,
|
||||||
|
:setting_show_tab_bar_label,
|
||||||
notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account trending_tag),
|
notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account trending_tag),
|
||||||
interactions: %i(must_be_follower must_be_following must_be_following_dm)
|
interactions: %i(must_be_follower must_be_following must_be_following_dm)
|
||||||
)
|
)
|
||||||
|
@ -23,6 +23,7 @@ module ContextHelper
|
|||||||
voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' },
|
voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' },
|
||||||
olm: { 'toot' => 'http://joinmastodon.org/ns#', 'Device' => 'toot:Device', 'Ed25519Signature' => 'toot:Ed25519Signature', 'Ed25519Key' => 'toot:Ed25519Key', 'Curve25519Key' => 'toot:Curve25519Key', 'EncryptedMessage' => 'toot:EncryptedMessage', 'publicKeyBase64' => 'toot:publicKeyBase64', 'deviceId' => 'toot:deviceId', 'claim' => { '@type' => '@id', '@id' => 'toot:claim' }, 'fingerprintKey' => { '@type' => '@id', '@id' => 'toot:fingerprintKey' }, 'identityKey' => { '@type' => '@id', '@id' => 'toot:identityKey' }, 'devices' => { '@type' => '@id', '@id' => 'toot:devices' }, 'messageFranking' => 'toot:messageFranking', 'messageType' => 'toot:messageType', 'cipherText' => 'toot:cipherText' },
|
olm: { 'toot' => 'http://joinmastodon.org/ns#', 'Device' => 'toot:Device', 'Ed25519Signature' => 'toot:Ed25519Signature', 'Ed25519Key' => 'toot:Ed25519Key', 'Curve25519Key' => 'toot:Curve25519Key', 'EncryptedMessage' => 'toot:EncryptedMessage', 'publicKeyBase64' => 'toot:publicKeyBase64', 'deviceId' => 'toot:deviceId', 'claim' => { '@type' => '@id', '@id' => 'toot:claim' }, 'fingerprintKey' => { '@type' => '@id', '@id' => 'toot:fingerprintKey' }, 'identityKey' => { '@type' => '@id', '@id' => 'toot:identityKey' }, 'devices' => { '@type' => '@id', '@id' => 'toot:devices' }, 'messageFranking' => 'toot:messageFranking', 'messageType' => 'toot:messageType', 'cipherText' => 'toot:cipherText' },
|
||||||
suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' },
|
suspended: { 'toot' => 'http://joinmastodon.org/ns#', 'suspended' => 'toot:suspended' },
|
||||||
|
quoteUrl: { 'quoteUrl' => 'as:quoteUrl' },
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
def full_context
|
def full_context
|
||||||
|
@ -5,6 +5,10 @@ module FormattingHelper
|
|||||||
HtmlAwareFormatter.new(text, local, options).to_s
|
HtmlAwareFormatter.new(text, local, options).to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def quotify(html, status, text, local, options = {})
|
||||||
|
QuoteFormatter.new(html, status, text, local, options).to_s
|
||||||
|
end
|
||||||
|
|
||||||
def linkify(text, options = {})
|
def linkify(text, options = {})
|
||||||
TextFormatter.new(text, options).to_s
|
TextFormatter.new(text, options).to_s
|
||||||
end
|
end
|
||||||
|
BIN
app/javascript/fonts/MaterialIcons-Regular.ttf
Normal file
BIN
app/javascript/fonts/MaterialIcons-Regular.ttf
Normal file
Binary file not shown.
@ -1 +1,17 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 216.4144 232.00976"><path d="M211.80734 139.0875c-3.18125 16.36625-28.4925 34.2775-57.5625 37.74875-15.15875 1.80875-30.08375 3.47125-45.99875 2.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125 0 2.53375.15625 4.94625.46875 7.2025 3.38375 25.68625 25.47 27.225 46.39125 27.9425 21.11625.7225 39.91875-5.20625 39.91875-5.20625l.8675 19.09s-14.77 7.93125-41.08125 9.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23234 213.82 1.40609 165.31125.20859 116.09125c-.365-14.61375-.14-28.39375-.14-39.91875 0-50.33 32.97625-65.0825 32.97625-65.0825C49.67234 3.45375 78.20359.2425 107.86484 0h.72875c29.66125.2425 58.21125 3.45375 74.8375 11.09 0 0 32.975 14.7525 32.975 65.0825 0 0 .41375 37.13375-4.59875 62.915" fill="#3088d4"/><path d="M177.50984 80.077v60.94125h-24.14375v-59.15c0-12.46875-5.24625-18.7975-15.74-18.7975-11.6025 0-17.4175 7.5075-17.4175 22.3525v32.37625H96.20734V85.42325c0-14.845-5.81625-22.3525-17.41875-22.3525-10.49375 0-15.74 6.32875-15.74 18.7975v59.15H38.90484V80.077c0-12.455 3.17125-22.3525 9.54125-29.675 6.56875-7.3225 15.17125-11.07625 25.85-11.07625 12.355 0 21.71125 4.74875 27.8975 14.2475l6.01375 10.08125 6.015-10.08125c6.185-9.49875 15.54125-14.2475 27.8975-14.2475 10.6775 0 19.28 3.75375 25.85 11.07625 6.36875 7.3225 9.54 17.22 9.54 29.675" fill="#fff"/></svg>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 25.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||||
|
y="0px" viewBox="0 0 216.4 232" style="enable-background:new 0 0 216.4 232;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#92787D;}
|
||||||
|
.st1{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<path class="st0" d="M211.8,139.1c-3.2,16.4-28.5,34.3-57.6,37.7c-15.2,1.8-30.1,3.5-46,2.7c-26-1.2-46.6-6.2-46.6-6.2
|
||||||
|
c0,2.5,0.2,4.9,0.5,7.2c3.4,25.7,25.5,27.2,46.4,27.9c21.1,0.7,39.9-5.2,39.9-5.2l0.9,19.1c0,0-14.8,7.9-41.1,9.4
|
||||||
|
c-14.5,0.8-32.5-0.4-53.5-5.9c-45.5-12-53.3-60.6-54.5-109.8c-0.4-14.6-0.1-28.4-0.1-39.9c0-50.3,33-65.1,33-65.1
|
||||||
|
C49.7,3.5,78.2,0.2,107.9,0h0.7c29.7,0.2,58.2,3.5,74.8,11.1c0,0,33,14.8,33,65.1C216.4,76.2,216.8,113.3,211.8,139.1"/>
|
||||||
|
<path class="st1" d="M177.5,80.1V141h-24.1V81.9c0-12.5-5.2-18.8-15.7-18.8c-11.6,0-17.4,7.5-17.4,22.4v32.4h-24V85.4
|
||||||
|
c0-14.8-5.8-22.4-17.4-22.4C68.3,63.1,63,69.4,63,81.9V141H38.9V80.1c0-12.5,3.2-22.4,9.5-29.7c6.6-7.3,15.2-11.1,25.8-11.1
|
||||||
|
c12.4,0,21.7,4.7,27.9,14.2l6,10.1l6-10.1c6.2-9.5,15.5-14.2,27.9-14.2c10.7,0,19.3,3.8,25.9,11.1C174.3,57.7,177.5,67.6,177.5,80.1
|
||||||
|
"/>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
@ -1 +1,16 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 216.41507 232.00976"><path d="M211.80683 139.0875c-3.1825 16.36625-28.4925 34.2775-57.5625 37.74875-15.16 1.80875-30.0825 3.47125-45.99875 2.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125 0 2.53375.15625 4.94625.46875 7.2025 3.38375 25.68625 25.47 27.225 46.3925 27.9425 21.115.7225 39.91625-5.20625 39.91625-5.20625l.86875 19.09s-14.77 7.93125-41.08125 9.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23183 213.82 1.40558 165.31125.20808 116.09125c-.36375-14.61375-.14-28.39375-.14-39.91875 0-50.33 32.97625-65.0825 32.97625-65.0825C49.67058 3.45375 78.20308.2425 107.86433 0h.72875c29.66125.2425 58.21125 3.45375 74.8375 11.09 0 0 32.97625 14.7525 32.97625 65.0825 0 0 .4125 37.13375-4.6 62.915" fill="#3088d4"/><path d="M65.68743 96.45938c0 9.01375-7.3075 16.32125-16.3225 16.32125-9.01375 0-16.32-7.3075-16.32-16.32125 0-9.01375 7.30625-16.3225 16.32-16.3225 9.015 0 16.3225 7.30875 16.3225 16.3225M124.52893 96.45938c0 9.01375-7.30875 16.32125-16.3225 16.32125-9.01375 0-16.32125-7.3075-16.32125-16.32125 0-9.01375 7.3075-16.3225 16.32125-16.3225 9.01375 0 16.3225 7.30875 16.3225 16.3225M183.36933 96.45938c0 9.01375-7.3075 16.32125-16.32125 16.32125-9.01375 0-16.32125-7.3075-16.32125-16.32125 0-9.01375 7.3075-16.3225 16.32125-16.3225 9.01375 0 16.32125 7.30875 16.32125 16.3225" fill="#fff"/></svg>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 25.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||||
|
y="0px" viewBox="0 0 216.4 232" style="enable-background:new 0 0 216.4 232;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:#92787D;}
|
||||||
|
.st1{fill:#FFFFFF;}
|
||||||
|
</style>
|
||||||
|
<path class="st0" d="M211.8,139.1c-3.2,16.4-28.5,34.3-57.6,37.7c-15.2,1.8-30.1,3.5-46,2.7c-26-1.2-46.6-6.2-46.6-6.2
|
||||||
|
c0,2.5,0.2,4.9,0.5,7.2c3.4,25.7,25.5,27.2,46.4,27.9c21.1,0.7,39.9-5.2,39.9-5.2l0.9,19.1c0,0-14.8,7.9-41.1,9.4
|
||||||
|
c-14.5,0.8-32.5-0.4-53.5-5.9c-45.5-12-53.3-60.6-54.5-109.8c-0.4-14.6-0.1-28.4-0.1-39.9c0-50.3,33-65.1,33-65.1
|
||||||
|
C49.7,3.5,78.2,0.2,107.9,0h0.7c29.7,0.2,58.2,3.5,74.8,11.1c0,0,33,14.8,33,65.1C216.4,76.2,216.8,113.3,211.8,139.1"/>
|
||||||
|
<path class="st1" d="M65.7,96.5c0,9-7.3,16.3-16.3,16.3c-9,0-16.3-7.3-16.3-16.3s7.3-16.3,16.3-16.3C58.4,80.1,65.7,87.4,65.7,96.5
|
||||||
|
M124.5,96.5c0,9-7.3,16.3-16.3,16.3s-16.3-7.3-16.3-16.3s7.3-16.3,16.3-16.3S124.5,87.4,124.5,96.5 M183.4,96.5
|
||||||
|
c0,9-7.3,16.3-16.3,16.3c-9,0-16.3-7.3-16.3-16.3S158,80.1,167,80.1C176.1,80.1,183.4,87.4,183.4,96.5"/>
|
||||||
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
71
app/javascript/mastodon/actions/UtilBtns.js
Normal file
71
app/javascript/mastodon/actions/UtilBtns.js
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { changeCompose } from '../actions/compose';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const UTILBTNS_GOJI = 'UTILBTNS_GOJI';
|
||||||
|
export const UTILBTNS_HARUKIN = 'UTILBTNS_HARUKIN';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function submitGoji (textarea) {
|
||||||
|
return function (dispatch, getState) {
|
||||||
|
if (!textarea.value) {
|
||||||
|
let text = [
|
||||||
|
"#ゴジモリィィィィイイ",
|
||||||
|
":goji:"
|
||||||
|
].join("\r\n");
|
||||||
|
|
||||||
|
dispatch(submitGojiRequest());
|
||||||
|
dispatch(changeCompose(text));
|
||||||
|
|
||||||
|
textarea.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function submitGojiRequest () {
|
||||||
|
return {
|
||||||
|
type: UTILBTNS_GOJI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function submitHarukin (textarea) {
|
||||||
|
return function (dispatch, getState) {
|
||||||
|
const HARUKINS = [":harukin: ", ":harukin_old: ", ":harukin_ika: ", ":harukin_tako: "];
|
||||||
|
const MAX = 6;
|
||||||
|
|
||||||
|
if (!textarea.value) {
|
||||||
|
let text = "";
|
||||||
|
|
||||||
|
let quantity = Math.round(Math.random() * MAX + 1);
|
||||||
|
let type = Math.round(Math.random() * (HARUKINS.length - 1));
|
||||||
|
|
||||||
|
let harukin = HARUKINS[type];
|
||||||
|
|
||||||
|
switch (quantity) {
|
||||||
|
default:
|
||||||
|
text = [
|
||||||
|
harukin.repeat(quantity),
|
||||||
|
"🔥 ".repeat(quantity)
|
||||||
|
].join("\r\n");
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MAX + 1:
|
||||||
|
text = `${harukin}💕\r\n`.repeat(6);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(submitHarukinRequest());
|
||||||
|
dispatch(changeCompose(text));
|
||||||
|
|
||||||
|
textarea.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function submitHarukinRequest () {
|
||||||
|
return {
|
||||||
|
type: UTILBTNS_HARUKIN
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
|
import { fetchRelationships } from './accounts';
|
||||||
import api, { getLinks } from '../api';
|
import api, { getLinks } from '../api';
|
||||||
import { importFetchedStatuses } from './importer';
|
import { importFetchedStatuses } from './importer';
|
||||||
|
import { uniq } from '../utils/uniq';
|
||||||
|
|
||||||
export const BOOKMARKED_STATUSES_FETCH_REQUEST = 'BOOKMARKED_STATUSES_FETCH_REQUEST';
|
export const BOOKMARKED_STATUSES_FETCH_REQUEST = 'BOOKMARKED_STATUSES_FETCH_REQUEST';
|
||||||
export const BOOKMARKED_STATUSES_FETCH_SUCCESS = 'BOOKMARKED_STATUSES_FETCH_SUCCESS';
|
export const BOOKMARKED_STATUSES_FETCH_SUCCESS = 'BOOKMARKED_STATUSES_FETCH_SUCCESS';
|
||||||
@ -20,6 +22,7 @@ export function fetchBookmarkedStatuses() {
|
|||||||
api(getState).get('/api/v1/bookmarks').then(response => {
|
api(getState).get('/api/v1/bookmarks').then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedStatuses(response.data));
|
dispatch(importFetchedStatuses(response.data));
|
||||||
|
dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id))));
|
||||||
dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
|
dispatch(fetchBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(fetchBookmarkedStatusesFail(error));
|
dispatch(fetchBookmarkedStatusesFail(error));
|
||||||
@ -61,6 +64,7 @@ export function expandBookmarkedStatuses() {
|
|||||||
api(getState).get(url).then(response => {
|
api(getState).get(url).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedStatuses(response.data));
|
dispatch(importFetchedStatuses(response.data));
|
||||||
|
dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id))));
|
||||||
dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
|
dispatch(expandBookmarkedStatusesSuccess(response.data, next ? next.uri : null));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(expandBookmarkedStatusesFail(error));
|
dispatch(expandBookmarkedStatusesFail(error));
|
||||||
|
@ -21,6 +21,8 @@ export const COMPOSE_SUBMIT_FAIL = 'COMPOSE_SUBMIT_FAIL';
|
|||||||
export const COMPOSE_REPLY = 'COMPOSE_REPLY';
|
export const COMPOSE_REPLY = 'COMPOSE_REPLY';
|
||||||
export const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL';
|
export const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL';
|
||||||
export const COMPOSE_DIRECT = 'COMPOSE_DIRECT';
|
export const COMPOSE_DIRECT = 'COMPOSE_DIRECT';
|
||||||
|
export const COMPOSE_QUOTE = 'COMPOSE_QUOTE';
|
||||||
|
export const COMPOSE_QUOTE_CANCEL = 'COMPOSE_QUOTE_CANCEL';
|
||||||
export const COMPOSE_MENTION = 'COMPOSE_MENTION';
|
export const COMPOSE_MENTION = 'COMPOSE_MENTION';
|
||||||
export const COMPOSE_RESET = 'COMPOSE_RESET';
|
export const COMPOSE_RESET = 'COMPOSE_RESET';
|
||||||
export const COMPOSE_UPLOAD_REQUEST = 'COMPOSE_UPLOAD_REQUEST';
|
export const COMPOSE_UPLOAD_REQUEST = 'COMPOSE_UPLOAD_REQUEST';
|
||||||
@ -118,6 +120,23 @@ export function cancelReplyCompose() {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function quoteCompose(status, routerHistory) {
|
||||||
|
return (dispatch, getState) => {
|
||||||
|
dispatch({
|
||||||
|
type: COMPOSE_QUOTE,
|
||||||
|
status: status,
|
||||||
|
});
|
||||||
|
|
||||||
|
ensureComposeIsVisible(getState, routerHistory);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function cancelQuoteCompose() {
|
||||||
|
return {
|
||||||
|
type: COMPOSE_QUOTE_CANCEL,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export function resetCompose() {
|
export function resetCompose() {
|
||||||
return {
|
return {
|
||||||
type: COMPOSE_RESET,
|
type: COMPOSE_RESET,
|
||||||
@ -169,6 +188,7 @@ export function submitCompose(routerHistory) {
|
|||||||
spoiler_text: getState().getIn(['compose', 'spoiler']) ? getState().getIn(['compose', 'spoiler_text'], '') : '',
|
spoiler_text: getState().getIn(['compose', 'spoiler']) ? getState().getIn(['compose', 'spoiler_text'], '') : '',
|
||||||
visibility: getState().getIn(['compose', 'privacy']),
|
visibility: getState().getIn(['compose', 'privacy']),
|
||||||
poll: getState().getIn(['compose', 'poll'], null),
|
poll: getState().getIn(['compose', 'poll'], null),
|
||||||
|
quote_id: getState().getIn(['compose', 'quote_from'], null),
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
|
'Idempotency-Key': getState().getIn(['compose', 'idempotencyKey']),
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
|
import { fetchRelationships } from './accounts';
|
||||||
import api, { getLinks } from '../api';
|
import api, { getLinks } from '../api';
|
||||||
import { importFetchedStatuses } from './importer';
|
import { importFetchedStatuses } from './importer';
|
||||||
|
import { uniq } from '../utils/uniq';
|
||||||
|
|
||||||
export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST';
|
export const FAVOURITED_STATUSES_FETCH_REQUEST = 'FAVOURITED_STATUSES_FETCH_REQUEST';
|
||||||
export const FAVOURITED_STATUSES_FETCH_SUCCESS = 'FAVOURITED_STATUSES_FETCH_SUCCESS';
|
export const FAVOURITED_STATUSES_FETCH_SUCCESS = 'FAVOURITED_STATUSES_FETCH_SUCCESS';
|
||||||
@ -20,6 +22,7 @@ export function fetchFavouritedStatuses() {
|
|||||||
api(getState).get('/api/v1/favourites').then(response => {
|
api(getState).get('/api/v1/favourites').then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedStatuses(response.data));
|
dispatch(importFetchedStatuses(response.data));
|
||||||
|
dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id))));
|
||||||
dispatch(fetchFavouritedStatusesSuccess(response.data, next ? next.uri : null));
|
dispatch(fetchFavouritedStatusesSuccess(response.data, next ? next.uri : null));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(fetchFavouritedStatusesFail(error));
|
dispatch(fetchFavouritedStatusesFail(error));
|
||||||
@ -64,6 +67,7 @@ export function expandFavouritedStatuses() {
|
|||||||
api(getState).get(url).then(response => {
|
api(getState).get(url).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedStatuses(response.data));
|
dispatch(importFetchedStatuses(response.data));
|
||||||
|
dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id))));
|
||||||
dispatch(expandFavouritedStatusesSuccess(response.data, next ? next.uri : null));
|
dispatch(expandFavouritedStatusesSuccess(response.data, next ? next.uri : null));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
dispatch(expandFavouritedStatusesFail(error));
|
dispatch(expandFavouritedStatusesFail(error));
|
||||||
|
@ -70,6 +70,10 @@ export function importFetchedStatuses(statuses) {
|
|||||||
processStatus(status.reblog);
|
processStatus(status.reblog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (status.quote && status.quote.id) {
|
||||||
|
processStatus(status.quote);
|
||||||
|
}
|
||||||
|
|
||||||
if (status.poll && status.poll.id) {
|
if (status.poll && status.poll.id) {
|
||||||
pushUnique(polls, normalizePoll(status.poll));
|
pushUnique(polls, normalizePoll(status.poll));
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,8 @@ export function normalizeStatus(status, normalOldStatus) {
|
|||||||
normalStatus.spoilerHtml = normalOldStatus.get('spoilerHtml');
|
normalStatus.spoilerHtml = normalOldStatus.get('spoilerHtml');
|
||||||
normalStatus.spoiler_text = normalOldStatus.get('spoiler_text');
|
normalStatus.spoiler_text = normalOldStatus.get('spoiler_text');
|
||||||
normalStatus.hidden = normalOldStatus.get('hidden');
|
normalStatus.hidden = normalOldStatus.get('hidden');
|
||||||
|
normalStatus.quote = normalOldStatus.get('quote');
|
||||||
|
normalStatus.quote_hidden = normalOldStatus.get('quote_hidden');
|
||||||
} else {
|
} else {
|
||||||
// If the status has a CW but no contents, treat the CW as if it were the
|
// If the status has a CW but no contents, treat the CW as if it were the
|
||||||
// status' contents, to avoid having a CW toggle with seemingly no effect.
|
// status' contents, to avoid having a CW toggle with seemingly no effect.
|
||||||
@ -79,6 +81,29 @@ export function normalizeStatus(status, normalOldStatus) {
|
|||||||
normalStatus.contentHtml = emojify(normalStatus.content, emojiMap);
|
normalStatus.contentHtml = emojify(normalStatus.content, emojiMap);
|
||||||
normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap);
|
normalStatus.spoilerHtml = emojify(escapeTextContentForBrowser(spoilerText), emojiMap);
|
||||||
normalStatus.hidden = expandSpoilers ? false : spoilerText.length > 0 || normalStatus.sensitive;
|
normalStatus.hidden = expandSpoilers ? false : spoilerText.length > 0 || normalStatus.sensitive;
|
||||||
|
|
||||||
|
if (status.quote && status.quote.id) {
|
||||||
|
const quote_spoilerText = status.quote.spoiler_text || '';
|
||||||
|
const quote_searchContent = [quote_spoilerText, status.quote.content].join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n');
|
||||||
|
|
||||||
|
const quote_emojiMap = makeEmojiMap(normalStatus.quote);
|
||||||
|
|
||||||
|
const quote_account_emojiMap = makeEmojiMap(status.quote.account);
|
||||||
|
const displayName = normalStatus.quote.account.display_name.length === 0 ? normalStatus.quote.account.username : normalStatus.quote.account.display_name;
|
||||||
|
normalStatus.quote.account.display_name_html = emojify(escapeTextContentForBrowser(displayName), quote_account_emojiMap);
|
||||||
|
normalStatus.quote.search_index = domParser.parseFromString(quote_searchContent, 'text/html').documentElement.textContent;
|
||||||
|
let docElem = domParser.parseFromString(normalStatus.quote.content, 'text/html').documentElement;
|
||||||
|
Array.from(docElem.querySelectorAll('p,br'), line => {
|
||||||
|
let parentNode = line.parentNode;
|
||||||
|
if (line.nextSibling) {
|
||||||
|
parentNode.insertBefore(document.createTextNode(' '), line.nextSibling);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let _contentHtml = docElem.textContent;
|
||||||
|
normalStatus.quote.contentHtml = '<p>'+emojify(_contentHtml.substr(0, 150), quote_emojiMap) + (_contentHtml.substr(150) ? '...' : '')+'</p>';
|
||||||
|
normalStatus.quote.spoilerHtml = emojify(escapeTextContentForBrowser(quote_spoilerText), quote_emojiMap);
|
||||||
|
normalStatus.quote_hidden = expandSpoilers ? false : quote_spoilerText.length > 0 || normalStatus.quote.sensitive;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return normalStatus;
|
return normalStatus;
|
||||||
|
@ -30,6 +30,9 @@ export const STATUS_COLLAPSE = 'STATUS_COLLAPSE';
|
|||||||
|
|
||||||
export const REDRAFT = 'REDRAFT';
|
export const REDRAFT = 'REDRAFT';
|
||||||
|
|
||||||
|
export const QUOTE_REVEAL = 'QUOTE_REVEAL';
|
||||||
|
export const QUOTE_HIDE = 'QUOTE_HIDE';
|
||||||
|
|
||||||
export const STATUS_FETCH_SOURCE_REQUEST = 'STATUS_FETCH_SOURCE_REQUEST';
|
export const STATUS_FETCH_SOURCE_REQUEST = 'STATUS_FETCH_SOURCE_REQUEST';
|
||||||
export const STATUS_FETCH_SOURCE_SUCCESS = 'STATUS_FETCH_SOURCE_SUCCESS';
|
export const STATUS_FETCH_SOURCE_SUCCESS = 'STATUS_FETCH_SOURCE_SUCCESS';
|
||||||
export const STATUS_FETCH_SOURCE_FAIL = 'STATUS_FETCH_SOURCE_FAIL';
|
export const STATUS_FETCH_SOURCE_FAIL = 'STATUS_FETCH_SOURCE_FAIL';
|
||||||
@ -310,3 +313,25 @@ export function toggleStatusCollapse(id, isCollapsed) {
|
|||||||
isCollapsed,
|
isCollapsed,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function hideQuote(ids) {
|
||||||
|
if (!Array.isArray(ids)) {
|
||||||
|
ids = [ids];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: QUOTE_HIDE,
|
||||||
|
ids,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export function revealQuote(ids) {
|
||||||
|
if (!Array.isArray(ids)) {
|
||||||
|
ids = [ids];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: QUOTE_REVEAL,
|
||||||
|
ids,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
|
import { fetchRelationships } from './accounts';
|
||||||
import { importFetchedStatus, importFetchedStatuses } from './importer';
|
import { importFetchedStatus, importFetchedStatuses } from './importer';
|
||||||
import { submitMarkers } from './markers';
|
import { submitMarkers } from './markers';
|
||||||
import api, { getLinks } from 'mastodon/api';
|
import api, { getLinks } from 'mastodon/api';
|
||||||
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
|
||||||
import compareId from 'mastodon/compare_id';
|
import compareId from 'mastodon/compare_id';
|
||||||
import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
|
import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
|
||||||
|
import { uniq } from '../utils/uniq';
|
||||||
|
|
||||||
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
|
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
|
||||||
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
|
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
|
||||||
@ -39,6 +41,7 @@ export function updateTimeline(timeline, status, accept) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dispatch(importFetchedStatus(status));
|
dispatch(importFetchedStatus(status));
|
||||||
|
dispatch(fetchRelationships([status.reblog ? status.reblog.account.id : status.account.id, status.quote ? status.quote.account.id : null].filter(function(e){return e})));
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: TIMELINE_UPDATE,
|
type: TIMELINE_UPDATE,
|
||||||
@ -111,6 +114,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
|
|||||||
api(getState).get(path, { params }).then(response => {
|
api(getState).get(path, { params }).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
dispatch(importFetchedStatuses(response.data));
|
dispatch(importFetchedStatuses(response.data));
|
||||||
|
dispatch(fetchRelationships(uniq(response.data.map(item => item.reblog ? item.reblog.account.id : item.account.id).concat(response.data.map(item => item.quote ? item.quote.account.id : null)).filter(function(e){return e}))));
|
||||||
dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.status === 206, isLoadingRecent, isLoadingMore, isLoadingRecent && preferPendingItems));
|
dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.status === 206, isLoadingRecent, isLoadingMore, isLoadingRecent && preferPendingItems));
|
||||||
|
|
||||||
if (timelineId === 'home') {
|
if (timelineId === 'home') {
|
||||||
|
@ -0,0 +1,90 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Motion from 'react-motion/lib/Motion';
|
||||||
|
import spring from 'react-motion/lib/spring';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
class IconButton extends React.PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
className: PropTypes.string,
|
||||||
|
title: PropTypes.string.isRequired,
|
||||||
|
icon: PropTypes.string.isRequired,
|
||||||
|
onClick: PropTypes.func,
|
||||||
|
size: PropTypes.number,
|
||||||
|
active: PropTypes.bool,
|
||||||
|
style: PropTypes.object,
|
||||||
|
activeStyle: PropTypes.object,
|
||||||
|
disabled: PropTypes.bool,
|
||||||
|
inverted: PropTypes.bool,
|
||||||
|
animate: PropTypes.bool,
|
||||||
|
overlay: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
size: 18,
|
||||||
|
active: false,
|
||||||
|
disabled: false,
|
||||||
|
animate: false,
|
||||||
|
overlay: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
handleClick = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (!this.props.disabled) {
|
||||||
|
this.props.onClick(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const style = {
|
||||||
|
fontSize: `${this.props.size}px`,
|
||||||
|
width: `${this.props.size * 1.28571429}px`,
|
||||||
|
height: `${this.props.size * 1.28571429}px`,
|
||||||
|
lineHeight: `${this.props.size}px`,
|
||||||
|
...this.props.style,
|
||||||
|
...(this.props.active ? this.props.activeStyle : {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const classes = ['icon-button'];
|
||||||
|
|
||||||
|
if (this.props.active) {
|
||||||
|
classes.push('active');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.disabled) {
|
||||||
|
classes.push('disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.inverted) {
|
||||||
|
classes.push('inverted');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.overlay) {
|
||||||
|
classes.push('overlayed');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.className) {
|
||||||
|
classes.push(this.props.className);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Motion defaultStyle={{ rotate: this.props.active ? 180 : 0 }} style={{ rotate: this.props.animate ? spring(this.props.active ? 0 : 180) : 0 }}>
|
||||||
|
{({ rotate }) =>
|
||||||
|
<button
|
||||||
|
aria-label={this.props.title}
|
||||||
|
title={this.props.title}
|
||||||
|
className={classes.join(' ')}
|
||||||
|
onClick={this.handleClick}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
|
<i style={{ transform: `rotate(${rotate}deg)` }} className={`fa fa-fw fa-${this.props.icon}`} aria-hidden='true' />
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
</Motion>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default IconButton;
|
@ -236,10 +236,12 @@ class MediaGallery extends React.PureComponent {
|
|||||||
visible: PropTypes.bool,
|
visible: PropTypes.bool,
|
||||||
autoplay: PropTypes.bool,
|
autoplay: PropTypes.bool,
|
||||||
onToggleVisibility: PropTypes.func,
|
onToggleVisibility: PropTypes.func,
|
||||||
|
quote: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
standalone: false,
|
standalone: false,
|
||||||
|
quote: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -310,7 +312,7 @@ class MediaGallery extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { media, intl, sensitive, height, defaultWidth, standalone, autoplay } = this.props;
|
const { media, intl, sensitive, height, defaultWidth, standalone, autoplay, quote } = this.props;
|
||||||
const { visible } = this.state;
|
const { visible } = this.state;
|
||||||
|
|
||||||
const width = this.state.width || defaultWidth;
|
const width = this.state.width || defaultWidth;
|
||||||
@ -332,6 +334,10 @@ class MediaGallery extends React.PureComponent {
|
|||||||
const size = media.take(4).size;
|
const size = media.take(4).size;
|
||||||
const uncached = media.every(attachment => attachment.get('type') === 'unknown');
|
const uncached = media.every(attachment => attachment.get('type') === 'unknown');
|
||||||
|
|
||||||
|
if (quote && style.height) {
|
||||||
|
style.height /= 2;
|
||||||
|
}
|
||||||
|
|
||||||
if (standalone && this.isFullSizeEligible()) {
|
if (standalone && this.isFullSizeEligible()) {
|
||||||
children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} displayWidth={width} visible={visible} />;
|
children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} displayWidth={width} visible={visible} />;
|
||||||
} else {
|
} else {
|
||||||
|
@ -49,6 +49,9 @@ class Poll extends ImmutablePureComponent {
|
|||||||
|
|
||||||
static getDerivedStateFromProps (props, state) {
|
static getDerivedStateFromProps (props, state) {
|
||||||
const { poll, intl } = props;
|
const { poll, intl } = props;
|
||||||
|
if (!poll) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const expires_at = poll.get('expires_at');
|
const expires_at = poll.get('expires_at');
|
||||||
const expired = poll.get('expired') || expires_at !== null && (new Date(expires_at)).getTime() < intl.now();
|
const expired = poll.get('expired') || expires_at !== null && (new Date(expires_at)).getTime() < intl.now();
|
||||||
return (expired === state.expired) ? null : { expired };
|
return (expired === state.expired) ? null : { expired };
|
||||||
@ -69,7 +72,7 @@ class Poll extends ImmutablePureComponent {
|
|||||||
_setupTimer () {
|
_setupTimer () {
|
||||||
const { poll, intl } = this.props;
|
const { poll, intl } = this.props;
|
||||||
clearTimeout(this._timer);
|
clearTimeout(this._timer);
|
||||||
if (!this.state.expired) {
|
if (!this.state.expired && !!poll) {
|
||||||
const delay = (new Date(poll.get('expires_at'))).getTime() - intl.now();
|
const delay = (new Date(poll.get('expires_at'))).getTime() - intl.now();
|
||||||
this._timer = setTimeout(() => {
|
this._timer = setTimeout(() => {
|
||||||
this.setState({ expired: true });
|
this.setState({ expired: true });
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Avatar from './avatar';
|
import Avatar from './avatar';
|
||||||
@ -23,6 +24,29 @@ import PictureInPicturePlaceholder from 'mastodon/components/picture_in_picture_
|
|||||||
// to use the progress bar to show download progress
|
// to use the progress bar to show download progress
|
||||||
import Bundle from '../features/ui/components/bundle';
|
import Bundle from '../features/ui/components/bundle';
|
||||||
|
|
||||||
|
const mapStateToProps = (state, props) => {
|
||||||
|
let status = props.status;
|
||||||
|
|
||||||
|
if (status === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
||||||
|
status = status.get('reblog');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.get('quote', null) === null) {
|
||||||
|
return {
|
||||||
|
quote_muted: status.get('quote_id', null) ? true : false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const id = status.getIn(['quote', 'account', 'id'], null);
|
||||||
|
|
||||||
|
return {
|
||||||
|
quote_muted: id !== null && (state.getIn(['relationships', id, 'muting']) || state.getIn(['relationships', id, 'blocking']) || state.getIn(['relationships', id, 'blocked_by']) || state.getIn(['relationships', id, 'domain_blocking'])) || status.getIn(['quote', 'quote_muted']),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export const textForScreenReader = (intl, status, rebloggedByText = false) => {
|
export const textForScreenReader = (intl, status, rebloggedByText = false) => {
|
||||||
const displayName = status.getIn(['account', 'display_name']);
|
const displayName = status.getIn(['account', 'display_name']);
|
||||||
|
|
||||||
@ -71,6 +95,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
status: ImmutablePropTypes.map,
|
status: ImmutablePropTypes.map,
|
||||||
account: ImmutablePropTypes.map,
|
account: ImmutablePropTypes.map,
|
||||||
otherAccounts: ImmutablePropTypes.list,
|
otherAccounts: ImmutablePropTypes.list,
|
||||||
|
quote_muted: PropTypes.bool,
|
||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
onReply: PropTypes.func,
|
onReply: PropTypes.func,
|
||||||
onFavourite: PropTypes.func,
|
onFavourite: PropTypes.func,
|
||||||
@ -86,6 +111,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
onHeightChange: PropTypes.func,
|
onHeightChange: PropTypes.func,
|
||||||
onToggleHidden: PropTypes.func,
|
onToggleHidden: PropTypes.func,
|
||||||
onToggleCollapsed: PropTypes.func,
|
onToggleCollapsed: PropTypes.func,
|
||||||
|
onQuoteToggleHidden: PropTypes.func,
|
||||||
muted: PropTypes.bool,
|
muted: PropTypes.bool,
|
||||||
hidden: PropTypes.bool,
|
hidden: PropTypes.bool,
|
||||||
unread: PropTypes.bool,
|
unread: PropTypes.bool,
|
||||||
@ -102,6 +128,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
inUse: PropTypes.bool,
|
inUse: PropTypes.bool,
|
||||||
available: PropTypes.bool,
|
available: PropTypes.bool,
|
||||||
}),
|
}),
|
||||||
|
contextType: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Avoid checking props that are functions (and whose equality will always
|
// Avoid checking props that are functions (and whose equality will always
|
||||||
@ -113,10 +140,12 @@ class Status extends ImmutablePureComponent {
|
|||||||
'hidden',
|
'hidden',
|
||||||
'unread',
|
'unread',
|
||||||
'pictureInPicture',
|
'pictureInPicture',
|
||||||
|
'quote_muted',
|
||||||
];
|
];
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
showMedia: defaultMediaVisibility(this.props.status),
|
showMedia: defaultMediaVisibility(this.props.status),
|
||||||
|
showQuoteMedia: defaultMediaVisibility(this.props.status ? this.props.status.get('quote', null) : null),
|
||||||
statusId: undefined,
|
statusId: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -124,6 +153,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) {
|
if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) {
|
||||||
return {
|
return {
|
||||||
showMedia: defaultMediaVisibility(nextProps.status),
|
showMedia: defaultMediaVisibility(nextProps.status),
|
||||||
|
showQuoteMedia: defaultMediaVisibility(nextProps.status.get('quote', null)),
|
||||||
statusId: nextProps.status.get('id'),
|
statusId: nextProps.status.get('id'),
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
@ -135,6 +165,10 @@ class Status extends ImmutablePureComponent {
|
|||||||
this.setState({ showMedia: !this.state.showMedia });
|
this.setState({ showMedia: !this.state.showMedia });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleToggleQuoteMediaVisibility = () => {
|
||||||
|
this.setState({ showQuoteMedia: !this.state.showQuoteMedia });
|
||||||
|
}
|
||||||
|
|
||||||
handleClick = e => {
|
handleClick = e => {
|
||||||
if (e && (e.button !== 0 || e.ctrlKey || e.metaKey)) {
|
if (e && (e.button !== 0 || e.ctrlKey || e.metaKey)) {
|
||||||
return;
|
return;
|
||||||
@ -152,10 +186,20 @@ class Status extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleAccountClick = (e, proper = true) => {
|
handleAccountClick = (e, proper = true) => {
|
||||||
if (e && (e.button !== 0 || e.ctrlKey || e.metaKey)) {
|
if (e && (e.button !== 0 || e.ctrlKey || e.metaKey)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleQuoteClick = () => {
|
||||||
|
if (!this.context.router) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { status } = this.props;
|
||||||
|
this.context.router.history.push(`/statuses/${status.getIn(['reblog', 'quote', 'id'], status.getIn(['quote', 'id']))}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAccountClick = (e) => {
|
||||||
if (e) {
|
if (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
@ -171,15 +215,19 @@ class Status extends ImmutablePureComponent {
|
|||||||
this.props.onToggleCollapsed(this._properStatus(), isCollapsed);
|
this.props.onToggleCollapsed(this._properStatus(), isCollapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLoadingMediaGallery () {
|
handleExpandedQuoteToggle = () => {
|
||||||
|
this.props.onQuoteToggleHidden(this._properStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLoadingMediaGallery = () => {
|
||||||
return <div className='media-gallery' style={{ height: '110px' }} />;
|
return <div className='media-gallery' style={{ height: '110px' }} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLoadingVideoPlayer () {
|
renderLoadingVideoPlayer = () => {
|
||||||
return <div className='video-player' style={{ height: '110px' }} />;
|
return <div className='video-player' style={{ height: '110px' }} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLoadingAudioPlayer () {
|
renderLoadingAudioPlayer = () => {
|
||||||
return <div className='audio-player' style={{ height: '110px' }} />;
|
return <div className='audio-player' style={{ height: '110px' }} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,10 +236,19 @@ class Status extends ImmutablePureComponent {
|
|||||||
this.props.onOpenVideo(status.get('id'), status.getIn(['media_attachments', 0]), options);
|
this.props.onOpenVideo(status.get('id'), status.getIn(['media_attachments', 0]), options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleOpenVideoQuote = (options) => {
|
||||||
|
const status = this._properQuoteStatus();
|
||||||
|
this.props.onOpenVideo(status.get('id'), status.getIn(['media_attachments', 0]), options);
|
||||||
|
}
|
||||||
|
|
||||||
handleOpenMedia = (media, index) => {
|
handleOpenMedia = (media, index) => {
|
||||||
this.props.onOpenMedia(this._properStatus().get('id'), media, index);
|
this.props.onOpenMedia(this._properStatus().get('id'), media, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleOpenMediaQuote = (media, index) => {
|
||||||
|
this.props.onOpenMedia(this._properQuoteStatus().get('id'), media, index);
|
||||||
|
}
|
||||||
|
|
||||||
handleHotkeyOpenMedia = e => {
|
handleHotkeyOpenMedia = e => {
|
||||||
const { onOpenMedia, onOpenVideo } = this.props;
|
const { onOpenMedia, onOpenVideo } = this.props;
|
||||||
const status = this._properStatus();
|
const status = this._properStatus();
|
||||||
@ -279,7 +336,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
this.handleToggleMediaVisibility();
|
this.handleToggleMediaVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
_properStatus () {
|
_properStatus = () => {
|
||||||
const { status } = this.props;
|
const { status } = this.props;
|
||||||
|
|
||||||
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
||||||
@ -289,15 +346,25 @@ class Status extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_properQuoteStatus = () => {
|
||||||
|
const status = this._properStatus();
|
||||||
|
|
||||||
|
if (status.get('quote', null) !== null && typeof status.get('quote') === 'object') {
|
||||||
|
return status.get('quote');
|
||||||
|
} else {
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleRef = c => {
|
handleRef = c => {
|
||||||
this.node = c;
|
this.node = c;
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render = () => {
|
||||||
let media = null;
|
let media = null;
|
||||||
let statusAvatar, prepend, rebloggedByText;
|
let statusAvatar, prepend, rebloggedByText;
|
||||||
|
|
||||||
const { intl, hidden, featured, otherAccounts, unread, showThread, scrollKey, pictureInPicture } = this.props;
|
const { intl, hidden, featured, otherAccounts, unread, showThread, scrollKey, pictureInPicture, contextType, quote_muted } = this.props;
|
||||||
|
|
||||||
let { status, account, ...other } = this.props;
|
let { status, account, ...other } = this.props;
|
||||||
|
|
||||||
@ -365,13 +432,13 @@ class Status extends ImmutablePureComponent {
|
|||||||
rebloggedByText = intl.formatMessage({ id: 'status.reblogged_by', defaultMessage: '{name} boosted' }, { name: status.getIn(['account', 'acct']) });
|
rebloggedByText = intl.formatMessage({ id: 'status.reblogged_by', defaultMessage: '{name} boosted' }, { name: status.getIn(['account', 'acct']) });
|
||||||
|
|
||||||
account = status.get('account');
|
account = status.get('account');
|
||||||
status = status.get('reblog');
|
status = status.get('reblog');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pictureInPicture.get('inUse')) {
|
if (status.get('media_attachments').size > 0) {
|
||||||
media = <PictureInPicturePlaceholder width={this.props.cachedMediaWidth} />;
|
if (pictureInPicture.get('inUse')) {
|
||||||
} else if (status.get('media_attachments').size > 0) {
|
media = <PictureInPicturePlaceholder width={this.props.cachedMediaWidth} />;
|
||||||
if (this.props.muted) {
|
} else if (this.props.muted) {
|
||||||
media = (
|
media = (
|
||||||
<AttachmentList
|
<AttachmentList
|
||||||
compact
|
compact
|
||||||
@ -473,6 +540,132 @@ class Status extends ImmutablePureComponent {
|
|||||||
|
|
||||||
const visibilityIcon = visibilityIconInfo[status.get('visibility')];
|
const visibilityIcon = visibilityIconInfo[status.get('visibility')];
|
||||||
|
|
||||||
|
let quote = null;
|
||||||
|
if (status.get('quote', null) !== null && typeof status.get('quote') === 'object') {
|
||||||
|
let quote_status = status.get('quote');
|
||||||
|
|
||||||
|
let quote_media = null;
|
||||||
|
if (quote_status.get('media_attachments').size > 0) {
|
||||||
|
if (pictureInPicture.get('inUse')) {
|
||||||
|
quote_media = <PictureInPicturePlaceholder width={this.props.cachedMediaWidth} />;
|
||||||
|
} else if (this.props.muted) {
|
||||||
|
quote_media = (
|
||||||
|
<AttachmentList
|
||||||
|
compact
|
||||||
|
media={quote_status.get('media_attachments')}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else if (quote_status.getIn(['media_attachments', 0, 'type']) === 'audio') {
|
||||||
|
const attachment = quote_status.getIn(['media_attachments', 0]);
|
||||||
|
|
||||||
|
quote_media = (
|
||||||
|
<Bundle fetchComponent={Audio} loading={this.renderLoadingAudioPlayer} >
|
||||||
|
{Component => (
|
||||||
|
<Component
|
||||||
|
src={attachment.get('url')}
|
||||||
|
alt={attachment.get('description')}
|
||||||
|
poster={attachment.get('preview_url') || quote_status.getIn(['account', 'avatar_static'])}
|
||||||
|
backgroundColor={attachment.getIn(['meta', 'colors', 'background'])}
|
||||||
|
foregroundColor={attachment.getIn(['meta', 'colors', 'foreground'])}
|
||||||
|
accentColor={attachment.getIn(['meta', 'colors', 'accent'])}
|
||||||
|
duration={attachment.getIn(['meta', 'original', 'duration'], 0)}
|
||||||
|
width={this.props.cachedMediaWidth}
|
||||||
|
height={70}
|
||||||
|
cacheWidth={this.props.cacheMediaWidth}
|
||||||
|
deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Bundle>
|
||||||
|
);
|
||||||
|
} else if (quote_status.getIn(['media_attachments', 0, 'type']) === 'video') {
|
||||||
|
const attachment = quote_status.getIn(['media_attachments', 0]);
|
||||||
|
|
||||||
|
quote_media = (
|
||||||
|
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} >
|
||||||
|
{Component => (
|
||||||
|
<Component
|
||||||
|
preview={attachment.get('preview_url')}
|
||||||
|
frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])}
|
||||||
|
blurhash={attachment.get('blurhash')}
|
||||||
|
src={attachment.get('url')}
|
||||||
|
alt={attachment.get('description')}
|
||||||
|
width={this.props.cachedMediaWidth}
|
||||||
|
height={110}
|
||||||
|
inline
|
||||||
|
sensitive={quote_status.get('sensitive')}
|
||||||
|
onOpenVideo={this.handleOpenVideoQuote}
|
||||||
|
cacheWidth={this.props.cacheMediaWidth}
|
||||||
|
deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined}
|
||||||
|
visible={this.state.showQuoteMedia}
|
||||||
|
onToggleVisibility={this.handleToggleQuoteMediaVisibility}
|
||||||
|
quote
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Bundle>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
quote_media = (
|
||||||
|
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
|
||||||
|
{Component => (
|
||||||
|
<Component
|
||||||
|
media={quote_status.get('media_attachments')}
|
||||||
|
sensitive={quote_status.get('sensitive')}
|
||||||
|
height={110}
|
||||||
|
onOpenMedia={this.handleOpenMediaQuote}
|
||||||
|
cacheWidth={this.props.cacheMediaWidth}
|
||||||
|
defaultWidth={this.props.cachedMediaWidth}
|
||||||
|
visible={this.state.showQuoteMedia}
|
||||||
|
onToggleVisibility={this.handleToggleQuoteMediaVisibility}
|
||||||
|
quote
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Bundle>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quote_muted) {
|
||||||
|
quote = (
|
||||||
|
<div className={classNames('quote-status', `status-${quote_status.get('visibility')}`, { muted: this.props.muted })} data-id={quote_status.get('id')}>
|
||||||
|
<div className={classNames('status__content muted-quote', { 'status__content--with-action': this.context.router })}>
|
||||||
|
<FormattedMessage id='status.muted_quote' defaultMessage='Muted quote' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (quote_status.get('visibility') === 'unlisted' && !!contextType && ['public', 'community', 'hashtag'].includes(contextType.split(':', 2)[0])) {
|
||||||
|
quote = (
|
||||||
|
<div className={classNames('quote-status', `status-${quote_status.get('visibility')}`, { muted: this.props.muted })} data-id={quote_status.get('id')}>
|
||||||
|
<div className={classNames('status__content unlisted-quote', { 'status__content--with-action': this.context.router })}>
|
||||||
|
<button onClick={this.handleQuoteClick}>
|
||||||
|
<FormattedMessage id='status.unlisted_quote' defaultMessage='Unlisted quote' />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
quote = (
|
||||||
|
<div className={classNames('quote-status', `status-${quote_status.get('visibility')}`, { muted: this.props.muted })} data-id={quote_status.get('id')}>
|
||||||
|
<div className='status__info'>
|
||||||
|
<a onClick={this.handleAccountClick} target='_blank' data-id={quote_status.getIn(['account', 'id'])} href={quote_status.getIn(['account', 'url'])} title={quote_status.getIn(['account', 'acct'])} className='status__display-name'>
|
||||||
|
<div className='status__avatar'><Avatar account={quote_status.get('account')} size={18} /></div>
|
||||||
|
<DisplayName account={quote_status.get('account')} />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<StatusContent status={quote_status} onClick={this.handleQuoteClick} expanded={!status.get('quote_hidden')} onExpandedToggle={this.handleExpandedQuoteToggle} quote />
|
||||||
|
{quote_media}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (quote_muted) {
|
||||||
|
quote = (
|
||||||
|
<div className={classNames('quote-status', { muted: this.props.muted })}>
|
||||||
|
<div className={classNames('status__content muted-quote', { 'status__content--with-action': this.context.router })}>
|
||||||
|
<FormattedMessage id='status.muted_quote' defaultMessage='Muted quote' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HotKeys handlers={handlers}>
|
<HotKeys handlers={handlers}>
|
||||||
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), unread, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText)} ref={this.handleRef}>
|
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), unread, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText)} ref={this.handleRef}>
|
||||||
@ -498,6 +691,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
|
|
||||||
<StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} showThread={showThread} onExpandedToggle={this.handleExpandedToggle} collapsable onCollapsedToggle={this.handleCollapsedToggle} />
|
<StatusContent status={status} onClick={this.handleClick} expanded={!status.get('hidden')} showThread={showThread} onExpandedToggle={this.handleExpandedToggle} collapsable onCollapsedToggle={this.handleCollapsedToggle} />
|
||||||
|
|
||||||
|
{quote}
|
||||||
{media}
|
{media}
|
||||||
|
|
||||||
<StatusActionBar scrollKey={scrollKey} status={status} account={account} {...other} />
|
<StatusActionBar scrollKey={scrollKey} status={status} account={account} {...other} />
|
||||||
|
@ -24,7 +24,9 @@ const messages = defineMessages({
|
|||||||
reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
|
reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
|
||||||
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
|
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
|
||||||
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
||||||
|
cannot_quote: { id: 'status.cannot_quote', defaultMessage: 'This post cannot be quoted' },
|
||||||
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
||||||
|
quote: { id: 'status.quote', defaultMessage: 'Quote' },
|
||||||
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
|
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
|
||||||
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
||||||
removeBookmark: { id: 'status.remove_bookmark', defaultMessage: 'Remove bookmark' },
|
removeBookmark: { id: 'status.remove_bookmark', defaultMessage: 'Remove bookmark' },
|
||||||
@ -62,6 +64,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
onReply: PropTypes.func,
|
onReply: PropTypes.func,
|
||||||
onFavourite: PropTypes.func,
|
onFavourite: PropTypes.func,
|
||||||
onReblog: PropTypes.func,
|
onReblog: PropTypes.func,
|
||||||
|
onQuote: PropTypes.func,
|
||||||
onDelete: PropTypes.func,
|
onDelete: PropTypes.func,
|
||||||
onDirect: PropTypes.func,
|
onDirect: PropTypes.func,
|
||||||
onMention: PropTypes.func,
|
onMention: PropTypes.func,
|
||||||
@ -123,6 +126,14 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleBookmarkClick = () => {
|
||||||
|
this.props.onBookmark(this.props.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleReblogClick = (e) => {
|
||||||
|
this.props.onReblog(this.props.status, e);
|
||||||
|
}
|
||||||
|
|
||||||
_openInteractionDialog = type => {
|
_openInteractionDialog = type => {
|
||||||
window.open(`/interact/${this.props.status.get('id')}?type=${type}`, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
|
window.open(`/interact/${this.props.status.get('id')}?type=${type}`, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
|
||||||
}
|
}
|
||||||
@ -131,6 +142,10 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
this.props.onBookmark(this.props.status);
|
this.props.onBookmark(this.props.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleQuoteClick = () => {
|
||||||
|
this.props.onQuote(this.props.status, this.context.router.history);
|
||||||
|
}
|
||||||
|
|
||||||
handleDeleteClick = () => {
|
handleDeleteClick = () => {
|
||||||
this.props.onDelete(this.props.status, this.context.router.history);
|
this.props.onDelete(this.props.status, this.context.router.history);
|
||||||
}
|
}
|
||||||
@ -334,6 +349,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||||||
<IconButton className='status__action-bar-button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} counter={status.get('replies_count')} obfuscateCount />
|
<IconButton className='status__action-bar-button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} counter={status.get('replies_count')} obfuscateCount />
|
||||||
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} pressed={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} />
|
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} pressed={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} />
|
||||||
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
|
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
|
||||||
|
<IconButton className='status__action-bar-button' disabled={anonymousAccess || !publicStatus} title={!publicStatus ? intl.formatMessage(messages.cannot_quote) : intl.formatMessage(messages.quote)} icon='quote-right' onClick={this.handleQuoteClick} />
|
||||||
|
|
||||||
{shareButton}
|
{shareButton}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ export default class StatusContent extends React.PureComponent {
|
|||||||
onClick: PropTypes.func,
|
onClick: PropTypes.func,
|
||||||
collapsable: PropTypes.bool,
|
collapsable: PropTypes.bool,
|
||||||
onCollapsedToggle: PropTypes.func,
|
onCollapsedToggle: PropTypes.func,
|
||||||
|
quote: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -125,6 +126,15 @@ export default class StatusContent extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onQuoteClick = (statusId, e) => {
|
||||||
|
let statusUrl = `/statuses/${statusId}`;
|
||||||
|
|
||||||
|
if (this.context.router && e.button === 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.context.router.history.push(statusUrl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleMouseDown = (e) => {
|
handleMouseDown = (e) => {
|
||||||
this.startXY = [e.clientX, e.clientY];
|
this.startXY = [e.clientX, e.clientY];
|
||||||
}
|
}
|
||||||
@ -168,11 +178,12 @@ export default class StatusContent extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { status } = this.props;
|
const { status, quote } = this.props;
|
||||||
|
|
||||||
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
|
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
|
||||||
const renderReadMore = this.props.onClick && status.get('collapsed');
|
const renderReadMore = this.props.onClick && status.get('collapsed');
|
||||||
const renderViewThread = this.props.showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id']);
|
const renderViewThread = this.props.showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id']);
|
||||||
|
const renderShowPoll = !!status.get('poll');
|
||||||
|
|
||||||
const content = { __html: status.get('contentHtml') };
|
const content = { __html: status.get('contentHtml') };
|
||||||
const spoilerContent = { __html: status.get('spoilerHtml') };
|
const spoilerContent = { __html: status.get('spoilerHtml') };
|
||||||
@ -194,6 +205,16 @@ export default class StatusContent extends React.PureComponent {
|
|||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const showPollButton = (
|
||||||
|
<button className='status__content__read-more-button' onClick={this.props.onClick} key='show-poll'>
|
||||||
|
<FormattedMessage id='status.show_poll' defaultMessage='Show poll' /><Icon id='angle-right' fixedWidth />
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
|
||||||
|
const pollContainer = (
|
||||||
|
<PollContainer pollId={status.get('poll')} />
|
||||||
|
);
|
||||||
|
|
||||||
if (status.get('spoiler_text').length > 0) {
|
if (status.get('spoiler_text').length > 0) {
|
||||||
let mentionsPlaceholder = '';
|
let mentionsPlaceholder = '';
|
||||||
|
|
||||||
@ -221,7 +242,7 @@ export default class StatusContent extends React.PureComponent {
|
|||||||
|
|
||||||
<div tabIndex={!hidden ? 0 : null} className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''} translate`} dangerouslySetInnerHTML={content} />
|
<div tabIndex={!hidden ? 0 : null} className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''} translate`} dangerouslySetInnerHTML={content} />
|
||||||
|
|
||||||
{!hidden && !!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
|
{!hidden && renderShowPoll && quote ? showPollButton : pollContainer}
|
||||||
|
|
||||||
{renderViewThread && showThreadButton}
|
{renderViewThread && showThreadButton}
|
||||||
</div>
|
</div>
|
||||||
@ -231,7 +252,7 @@ export default class StatusContent extends React.PureComponent {
|
|||||||
<div className={classNames} ref={this.setRef} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} key='status-content' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
<div className={classNames} ref={this.setRef} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} key='status-content' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
||||||
<div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} />
|
<div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} />
|
||||||
|
|
||||||
{!!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
|
{renderShowPoll && quote ? showPollButton : pollContainer}
|
||||||
|
|
||||||
{renderViewThread && showThreadButton}
|
{renderViewThread && showThreadButton}
|
||||||
</div>,
|
</div>,
|
||||||
@ -247,7 +268,7 @@ export default class StatusContent extends React.PureComponent {
|
|||||||
<div className={classNames} ref={this.setRef} tabIndex='0' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
<div className={classNames} ref={this.setRef} tabIndex='0' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
||||||
<div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} />
|
<div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} />
|
||||||
|
|
||||||
{!!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
|
{renderShowPoll && quote ? showPollButton : pollContainer}
|
||||||
|
|
||||||
{renderViewThread && showThreadButton}
|
{renderViewThread && showThreadButton}
|
||||||
</div>
|
</div>
|
||||||
|
@ -4,6 +4,7 @@ import Status from '../components/status';
|
|||||||
import { makeGetStatus, makeGetPictureInPicture } from '../selectors';
|
import { makeGetStatus, makeGetPictureInPicture } from '../selectors';
|
||||||
import {
|
import {
|
||||||
replyCompose,
|
replyCompose,
|
||||||
|
quoteCompose,
|
||||||
mentionCompose,
|
mentionCompose,
|
||||||
directCompose,
|
directCompose,
|
||||||
} from '../actions/compose';
|
} from '../actions/compose';
|
||||||
@ -24,6 +25,8 @@ import {
|
|||||||
hideStatus,
|
hideStatus,
|
||||||
revealStatus,
|
revealStatus,
|
||||||
toggleStatusCollapse,
|
toggleStatusCollapse,
|
||||||
|
hideQuote,
|
||||||
|
revealQuote,
|
||||||
editStatus,
|
editStatus,
|
||||||
} from '../actions/statuses';
|
} from '../actions/statuses';
|
||||||
import {
|
import {
|
||||||
@ -51,6 +54,8 @@ const messages = defineMessages({
|
|||||||
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
|
redraftMessage: { id: 'confirmations.redraft.message', defaultMessage: 'Are you sure you want to delete this status and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.' },
|
||||||
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
||||||
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
||||||
|
quoteConfirm: { id: 'confirmations.quote.confirm', defaultMessage: 'Quote' },
|
||||||
|
quoteMessage: { id: 'confirmations.quote.message', defaultMessage: 'Quoting now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
||||||
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
|
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -100,6 +105,22 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onQuote (status, router) {
|
||||||
|
dispatch((_, getState) => {
|
||||||
|
let state = getState();
|
||||||
|
|
||||||
|
if (state.getIn(['compose', 'text']).trim().length !== 0) {
|
||||||
|
dispatch(openModal('CONFIRM', {
|
||||||
|
message: intl.formatMessage(messages.quoteMessage),
|
||||||
|
confirm: intl.formatMessage(messages.quoteConfirm),
|
||||||
|
onConfirm: () => dispatch(quoteCompose(status, router)),
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
dispatch(quoteCompose(status, router));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
onFavourite (status) {
|
onFavourite (status) {
|
||||||
if (status.get('favourited')) {
|
if (status.get('favourited')) {
|
||||||
dispatch(unfavourite(status));
|
dispatch(unfavourite(status));
|
||||||
@ -220,6 +241,14 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||||||
dispatch(deployPictureInPicture(status.get('id'), status.getIn(['account', 'id']), type, mediaProps));
|
dispatch(deployPictureInPicture(status.get('id'), status.getIn(['account', 'id']), type, mediaProps));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onQuoteToggleHidden (status) {
|
||||||
|
if (status.get('quote_hidden')) {
|
||||||
|
dispatch(revealQuote(status.get('id')));
|
||||||
|
} else {
|
||||||
|
dispatch(hideQuote(status.get('id')));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Status));
|
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Status));
|
||||||
|
@ -47,7 +47,9 @@ export default class TimelineContainer extends React.PureComponent {
|
|||||||
<IntlProvider locale={locale} messages={messages}>
|
<IntlProvider locale={locale} messages={messages}>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{timeline}
|
<div className='standalone-timeline'>
|
||||||
|
{timeline}
|
||||||
|
</div>
|
||||||
|
|
||||||
{ReactDOM.createPortal(
|
{ReactDOM.createPortal(
|
||||||
<ModalContainer />,
|
<ModalContainer />,
|
||||||
|
@ -0,0 +1,127 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Immutable from 'immutable';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Link from 'react-router-dom/Link';
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
import IconButton from '../../../components/announcement_icon_button';
|
||||||
|
import Motion from 'react-motion/lib/Motion';
|
||||||
|
import spring from 'react-motion/lib/spring';
|
||||||
|
|
||||||
|
const Collapsable = ({ fullHeight, minHeight, isVisible, children }) => (
|
||||||
|
<Motion defaultStyle={{ height: isVisible ? fullHeight : minHeight }} style={{ height: spring(!isVisible ? minHeight : fullHeight) }}>
|
||||||
|
{({ height }) =>
|
||||||
|
<div style={{ height: `${height}px`, overflow: 'hidden' }}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</Motion>
|
||||||
|
);
|
||||||
|
|
||||||
|
Collapsable.propTypes = {
|
||||||
|
fullHeight: PropTypes.number.isRequired,
|
||||||
|
minHeight: PropTypes.number.isRequired,
|
||||||
|
isVisible: PropTypes.bool.isRequired,
|
||||||
|
children: PropTypes.node.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' },
|
||||||
|
welcome: { id: 'welcome.message', defaultMessage: '{domain}へようこそ!' },
|
||||||
|
markdown: { id: 'markdown.list', defaultMessage: 'markdown一覧' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const hashtags = Immutable.fromJS([
|
||||||
|
'神崎ドン自己紹介',
|
||||||
|
]);
|
||||||
|
|
||||||
|
class Announcements extends React.PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
homeSize: PropTypes.number,
|
||||||
|
isLoading: PropTypes.bool,
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
|
showId: null,
|
||||||
|
isLoaded: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
onClick = (announcementId, currentState) => {
|
||||||
|
this.setState({ showId: currentState.showId === announcementId ? null : announcementId });
|
||||||
|
}
|
||||||
|
nl2br (text) {
|
||||||
|
return text.split(/(\n)/g).map((line, i) => {
|
||||||
|
if (line.match(/(\n)/g)) {
|
||||||
|
return React.createElement('br', { key: i });
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { intl } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ul className='announcements'>
|
||||||
|
<li>
|
||||||
|
<Collapsable isVisible={this.state.showId === 'markdown'} fullHeight={1240} minHeight={20} >
|
||||||
|
<div className='announcements__body'>
|
||||||
|
<p>{ this.nl2br(intl.formatMessage(messages.markdown, { domain: document.title }))}<br />
|
||||||
|
<br />
|
||||||
|
(半角)は半角スペースを入力する必要がある場所です。(半角)だけの列は半角スペースのみが入力された列が必要であるを指します。<br /><br />
|
||||||
|
〜〜〜〜〜〜見出し〜〜〜〜〜〜<br /><br />
|
||||||
|
#(半角)見出しテキスト<br /><br />
|
||||||
|
#は1〜6個重ねることができます。<br /><br />
|
||||||
|
〜〜〜〜コードブロック〜〜〜〜<br /><br />
|
||||||
|
`コード`<br /><br />
|
||||||
|
〜〜〜〜〜〜引用〜〜〜〜〜〜<br /><br />
|
||||||
|
>引用文<br />
|
||||||
|
(半角)<br />
|
||||||
|
ここから先は引用が切れます<br />
|
||||||
|
引用は複数回重ねることが可能です。<br /><br />
|
||||||
|
〜〜〜〜〜〜リスト〜〜〜〜〜〜<br /><br />
|
||||||
|
(半角)<br />
|
||||||
|
+(半角)内容1<br />
|
||||||
|
+(半角)内容2<br />
|
||||||
|
(半角)<br /><br />
|
||||||
|
内容の数に制限はありません。<br />
|
||||||
|
投稿トップにリストを持ってくる場合に限り1行目の(半角)は必要ありません。<br />
|
||||||
|
+(半角)を1.(半角)に置き換えることで数字付きリストになります。<br /><br />
|
||||||
|
〜〜〜〜〜上付き文字〜〜〜〜〜<br /><br />
|
||||||
|
_上付き文字_<br /><br />
|
||||||
|
〜〜〜〜〜下付き文字〜〜〜〜〜<br /><br />
|
||||||
|
__下付き文字__<br /><br />
|
||||||
|
〜〜〜〜〜小さい文字〜〜〜〜〜<br /><br />
|
||||||
|
___小さい文字___<br /><br />
|
||||||
|
〜〜〜〜〜取り消し線〜〜〜〜〜<br /><br />
|
||||||
|
~~取り消したい文字列~~<br /><br />
|
||||||
|
〜〜〜〜〜〜横罫線〜〜〜〜〜〜<br /><br />
|
||||||
|
___<br /><br />
|
||||||
|
〜〜〜〜〜〜リンク〜〜〜〜〜〜<br /><br />
|
||||||
|
[リンク文章](https://・・・)<br /><br />
|
||||||
|
〜〜〜〜〜〜画像〜〜〜〜〜〜<br /><br />
|
||||||
|
<br /><br />
|
||||||
|
リンク、画像ともにURLにはhttps://から始まる物のみご利用可能です。
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Collapsable>
|
||||||
|
<div className='announcements__icon'>
|
||||||
|
<IconButton title={intl.formatMessage(messages.toggle_visible)} icon='caret-up' onClick={() => this.onClick('markdown', this.state)} size={20} animate active={this.state.showId === 'markdown'} />
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps (nextProps) {
|
||||||
|
if (!this.state.isLoaded) {
|
||||||
|
if (!nextProps.isLoading && (nextProps.homeSize === 0 || this.props.homeSize !== nextProps.homeSize)) {
|
||||||
|
this.setState({ isLoaded: true });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default injectIntl(Announcements);
|
@ -4,6 +4,7 @@ import Button from '../../../components/button';
|
|||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ReplyIndicatorContainer from '../containers/reply_indicator_container';
|
import ReplyIndicatorContainer from '../containers/reply_indicator_container';
|
||||||
|
import QuoteIndicatorContainer from '../containers/quote_indicator_container';
|
||||||
import AutosuggestTextarea from '../../../components/autosuggest_textarea';
|
import AutosuggestTextarea from '../../../components/autosuggest_textarea';
|
||||||
import AutosuggestInput from '../../../components/autosuggest_input';
|
import AutosuggestInput from '../../../components/autosuggest_input';
|
||||||
import PollButtonContainer from '../containers/poll_button_container';
|
import PollButtonContainer from '../containers/poll_button_container';
|
||||||
@ -20,6 +21,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||||||
import { length } from 'stringz';
|
import { length } from 'stringz';
|
||||||
import { countableText } from '../util/counter';
|
import { countableText } from '../util/counter';
|
||||||
import Icon from 'mastodon/components/icon';
|
import Icon from 'mastodon/components/icon';
|
||||||
|
import { UserCounter } from './user_counter';
|
||||||
|
|
||||||
const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
|
const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d';
|
||||||
|
|
||||||
@ -28,6 +30,8 @@ const messages = defineMessages({
|
|||||||
spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' },
|
spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' },
|
||||||
publish: { id: 'compose_form.publish', defaultMessage: 'Toot' },
|
publish: { id: 'compose_form.publish', defaultMessage: 'Toot' },
|
||||||
publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' },
|
publishLoud: { id: 'compose_form.publish_loud', defaultMessage: '{publish}!' },
|
||||||
|
utilBtns_goji: { id: 'compose_form.utilBtns_goji', defaultMessage: 'Typo!!!' },
|
||||||
|
utilBtns_harukin: { id: 'compose_form.utilBtns_harukin', defaultMessage: 'Burn Harukin' },
|
||||||
saveChanges: { id: 'compose_form.save_changes', defaultMessage: 'Save changes' },
|
saveChanges: { id: 'compose_form.save_changes', defaultMessage: 'Save changes' },
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -62,6 +66,9 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
onPickEmoji: PropTypes.func.isRequired,
|
onPickEmoji: PropTypes.func.isRequired,
|
||||||
showSearch: PropTypes.bool,
|
showSearch: PropTypes.bool,
|
||||||
anyMedia: PropTypes.bool,
|
anyMedia: PropTypes.bool,
|
||||||
|
singleColumn: PropTypes.bool,
|
||||||
|
onGojiSubmit: PropTypes.func.isRequired,
|
||||||
|
onHarukinSubmit: PropTypes.func.isRequired,
|
||||||
isInReply: PropTypes.bool,
|
isInReply: PropTypes.bool,
|
||||||
singleColumn: PropTypes.bool,
|
singleColumn: PropTypes.bool,
|
||||||
};
|
};
|
||||||
@ -89,7 +96,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
const fulltext = this.getFulltextForCharacterCounting();
|
const fulltext = this.getFulltextForCharacterCounting();
|
||||||
const isOnlyWhitespace = fulltext.length !== 0 && fulltext.trim().length === 0;
|
const isOnlyWhitespace = fulltext.length !== 0 && fulltext.trim().length === 0;
|
||||||
|
|
||||||
return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > 500 || (isOnlyWhitespace && !anyMedia));
|
return !(isSubmitting || isUploading || isChangingUpload || length(fulltext) > 2048 || (isOnlyWhitespace && !anyMedia));
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit = () => {
|
handleSubmit = () => {
|
||||||
@ -201,6 +208,10 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
this.props.onPickEmoji(position, data, needsSpace);
|
this.props.onPickEmoji(position, data, needsSpace);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
handleOnGojiSubmit = () => this.props.onGojiSubmit(this.autosuggestTextarea.textarea);
|
||||||
|
handleOnHarukinSubmit = () => this.props.onHarukinSubmit(this.autosuggestTextarea.textarea);
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, onPaste, showSearch } = this.props;
|
const { intl, onPaste, showSearch } = this.props;
|
||||||
const disabled = this.props.isSubmitting;
|
const disabled = this.props.isSubmitting;
|
||||||
@ -219,6 +230,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
<WarningContainer />
|
<WarningContainer />
|
||||||
|
|
||||||
<ReplyIndicatorContainer />
|
<ReplyIndicatorContainer />
|
||||||
|
<QuoteIndicatorContainer />
|
||||||
|
|
||||||
<div className={`spoiler-input ${this.props.spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef}>
|
<div className={`spoiler-input ${this.props.spoiler ? 'spoiler-input--visible' : ''}`} ref={this.setRef}>
|
||||||
<AutosuggestInput
|
<AutosuggestInput
|
||||||
@ -267,11 +279,22 @@ class ComposeForm extends ImmutablePureComponent {
|
|||||||
<PrivacyDropdownContainer disabled={this.props.isEditing} />
|
<PrivacyDropdownContainer disabled={this.props.isEditing} />
|
||||||
<SpoilerButtonContainer />
|
<SpoilerButtonContainer />
|
||||||
</div>
|
</div>
|
||||||
<div className='character-counter__wrapper'><CharacterCounter max={500} text={this.getFulltextForCharacterCounting()} /></div>
|
<div className='character-counter__wrapper'><CharacterCounter max={2048} text={this.getFulltextForCharacterCounting()} /></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='compose-form__publish'>
|
<div className='compose-form__publish'>
|
||||||
<div className='compose-form__publish-button-wrapper'><Button text={publishText} onClick={this.handleSubmit} disabled={!this.canSubmit()} block /></div>
|
<div className='compose-form__publish-button-wrapper'>
|
||||||
|
|
||||||
|
<Button text={publishText} onClick={this.handleSubmit} disabled={!this.canSubmit()} block>
|
||||||
|
<span className="fa fa-send">{publishText}</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="compose-form__utilBtns">
|
||||||
|
|
||||||
|
<Button className="compose-form__utilBtns-goji" text={intl.formatMessage(messages.utilBtns_goji)} onClick={this.handleOnGojiSubmit} block />
|
||||||
|
<Button className="compose-form__utilBtns-harukin" text={intl.formatMessage(messages.utilBtns_harukin)} onClick={this.handleOnHarukinSubmit} block />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -0,0 +1,71 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import Avatar from '../../../components/avatar';
|
||||||
|
import IconButton from '../../../components/icon_button';
|
||||||
|
import DisplayName from '../../../components/display_name';
|
||||||
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
import AttachmentList from 'mastodon/components/attachment_list';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
cancel: { id: 'quote_indicator.cancel', defaultMessage: 'Cancel' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default @injectIntl
|
||||||
|
class QuoteIndicator extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
static contextTypes = {
|
||||||
|
router: PropTypes.object,
|
||||||
|
};
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
status: ImmutablePropTypes.map,
|
||||||
|
onCancel: PropTypes.func.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
handleClick = () => {
|
||||||
|
this.props.onCancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAccountClick = (e) => {
|
||||||
|
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { status, intl } = this.props;
|
||||||
|
|
||||||
|
if (!status) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const content = { __html: status.get('contentHtml') };
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='quote-indicator'>
|
||||||
|
<div className='quote-indicator__header'>
|
||||||
|
<div className='quote-indicator__cancel'><IconButton title={intl.formatMessage(messages.cancel)} icon='times' onClick={this.handleClick} /></div>
|
||||||
|
|
||||||
|
<a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='quote-indicator__display-name'>
|
||||||
|
<div className='quote-indicator__display-avatar'><Avatar account={status.get('account')} size={24} /></div>
|
||||||
|
<DisplayName account={status.get('account')} />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='quote-indicator__content' dir='auto' dangerouslySetInnerHTML={content} />
|
||||||
|
|
||||||
|
{status.get('media_attachments').size > 0 && (
|
||||||
|
<AttachmentList
|
||||||
|
compact
|
||||||
|
media={status.get('media_attachments')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
|
export default class UserCounter extends React.PureComponent {
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<span>10人</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import Announcements from '../components/announcements';
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
homeSize: state.getIn(['timelines', 'home', 'items']).size,
|
||||||
|
isLoading: state.getIn(['timelines', 'home', 'isLoading']),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps)(Announcements);
|
@ -11,6 +11,11 @@ import {
|
|||||||
uploadCompose,
|
uploadCompose,
|
||||||
} from '../../../actions/compose';
|
} from '../../../actions/compose';
|
||||||
|
|
||||||
|
import {
|
||||||
|
submitGoji,
|
||||||
|
submitHarukin
|
||||||
|
} from '../../../actions/UtilBtns';
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
text: state.getIn(['compose', 'text']),
|
text: state.getIn(['compose', 'text']),
|
||||||
suggestions: state.getIn(['compose', 'suggestions']),
|
suggestions: state.getIn(['compose', 'suggestions']),
|
||||||
@ -62,6 +67,14 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
onPickEmoji (position, data, needsSpace) {
|
onPickEmoji (position, data, needsSpace) {
|
||||||
dispatch(insertEmojiCompose(position, data, needsSpace));
|
dispatch(insertEmojiCompose(position, data, needsSpace));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onGojiSubmit (textarea) {
|
||||||
|
dispatch(submitGoji(textarea));
|
||||||
|
},
|
||||||
|
|
||||||
|
onHarukinSubmit (textarea) {
|
||||||
|
dispatch(submitHarukin(textarea));
|
||||||
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { cancelQuoteCompose } from '../../../actions/compose';
|
||||||
|
import { makeGetStatus } from '../../../selectors';
|
||||||
|
import QuoteIndicator from '../components/quote_indicator';
|
||||||
|
|
||||||
|
const makeMapStateToProps = () => {
|
||||||
|
const getStatus = makeGetStatus();
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({
|
||||||
|
status: getStatus(state, { id: state.getIn(['compose', 'quote_from']) }),
|
||||||
|
});
|
||||||
|
|
||||||
|
return mapStateToProps;
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
|
||||||
|
onCancel () {
|
||||||
|
dispatch(cancelQuoteCompose());
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(makeMapStateToProps, mapDispatchToProps)(QuoteIndicator);
|
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { Fragment } from 'react';
|
||||||
import ComposeFormContainer from './containers/compose_form_container';
|
import ComposeFormContainer from './containers/compose_form_container';
|
||||||
import NavigationContainer from './containers/navigation_container';
|
import NavigationContainer from './containers/navigation_container';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
@ -14,16 +14,28 @@ import SearchResultsContainer from './containers/search_results_container';
|
|||||||
import { changeComposing } from '../../actions/compose';
|
import { changeComposing } from '../../actions/compose';
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
import elephantUIPlane from '../../../images/elephant_ui_plane.svg';
|
import elephantUIPlane from '../../../images/elephant_ui_plane.svg';
|
||||||
import { mascot } from '../../initial_state';
|
import { mascot, show_tab_bar_label } from '../../initial_state';
|
||||||
import Icon from 'mastodon/components/icon';
|
import Icon from 'mastodon/components/icon';
|
||||||
import { logOut } from 'mastodon/utils/log_out';
|
import { logOut } from 'mastodon/utils/log_out';
|
||||||
|
import AnnouncementsContainer from './containers/announcements_container';
|
||||||
|
import NotificationsCounterIcon from '../ui/components/notifications_counter_icon';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
short_start: { id: 'navigation_bar.short.getting_started', defaultMessage: 'Started' },
|
||||||
|
short_home_timeline: { id: 'navigation_bar.short.home', defaultMessage: 'Home' },
|
||||||
|
short_notifications: { id: 'navigation_bar.short.notifications', defaultMessage: 'Notif.' },
|
||||||
|
short_public: { id: 'navigation_bar.short.public_timeline', defaultMessage: 'FTL' },
|
||||||
|
short_community: { id: 'navigation_bar.short.community_timeline', defaultMessage: 'LTL' },
|
||||||
|
short_lists: { id: 'navigation_bar.short.lists', defaultMessage: 'Lists' },
|
||||||
|
short_preferences: { id: 'navigation_bar.short.preferences', defaultMessage: 'Pref.' },
|
||||||
|
short_logout: { id: 'navigation_bar.short.logout', defaultMessage: 'Logout' },
|
||||||
start: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
|
start: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
|
||||||
home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
|
home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
|
||||||
notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
|
notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
|
||||||
public: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
|
public: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
|
||||||
community: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
|
community: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
|
||||||
|
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
|
||||||
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
||||||
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
|
logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
|
||||||
compose: { id: 'navigation_bar.compose', defaultMessage: 'Compose new toot' },
|
compose: { id: 'navigation_bar.compose', defaultMessage: 'Compose new toot' },
|
||||||
@ -89,30 +101,54 @@ class Compose extends React.PureComponent {
|
|||||||
this.props.dispatch(changeComposing(false));
|
this.props.dispatch(changeComposing(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tab (id) {
|
||||||
|
const { columns, intl: { formatMessage } } = this.props;
|
||||||
|
|
||||||
|
if (!columns.some(column => column.get('id') === id)) {
|
||||||
|
const tabParams = {
|
||||||
|
'START': { to: '/getting-started', title: formatMessage(messages.start), label: formatMessage(messages.short_start), icon_id: 'bars' },
|
||||||
|
'HOME': { to: '/timelines/home', title: formatMessage(messages.home_timeline), label: formatMessage(messages.short_home_timeline), icon_id: 'home' },
|
||||||
|
'NOTIFICATIONS': { to: '/notifications', title: formatMessage(messages.notifications), label: formatMessage(messages.short_notifications), icon_id: 'bell' },
|
||||||
|
'COMMUNITY': { to: '/timelines/public/local', title: formatMessage(messages.community), label: formatMessage(messages.short_community), icon_id: 'users' },
|
||||||
|
'PUBLIC': { to: '/timelines/public', title: formatMessage(messages.public), label: formatMessage(messages.short_public), icon_id: 'globe' },
|
||||||
|
'LIST': { to: '/lists', title: formatMessage(messages.lists), label: formatMessage(messages.short_lists), icon_id: 'list-ul' },
|
||||||
|
'PREFERENCES': { href: '/settings/preferences', title: formatMessage(messages.preferences), label: formatMessage(messages.short_preferences), icon_id: 'cog' },
|
||||||
|
'SIGN_OUT': { href: '/auth/sign_out', title: formatMessage(messages.logout), label: formatMessage(messages.short_logout), icon_id: 'sign-out', method: 'delete' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const { href, to, title, label, icon_id, method } = tabParams[id];
|
||||||
|
|
||||||
|
const icon = (id === 'NOTIFICATIONS') ? <NotificationsCounterIcon /> : <Icon id={icon_id} fixedWidth />;
|
||||||
|
|
||||||
|
if (href) {
|
||||||
|
return (
|
||||||
|
<a href={href} className={classNames('drawer__tab', { 'short-label': show_tab_bar_label })} title={title} aria-label={title} data-method={method}>{icon}<span className='drawer__tab__short-label'>{label}</span></a>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<Link to={to} className={classNames('drawer__tab', { 'short-label': show_tab_bar_label })} title={title} aria-label={title}>{icon}<span className='drawer__tab__short-label'>{label}</span></Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { multiColumn, showSearch, isSearchPage, intl } = this.props;
|
const { multiColumn, showSearch, isSearchPage, intl } = this.props;
|
||||||
|
|
||||||
let header = '';
|
let header = '';
|
||||||
|
|
||||||
if (multiColumn) {
|
if (multiColumn) {
|
||||||
const { columns } = this.props;
|
const defaultTabIds = ['START', 'HOME', 'NOTIFICATIONS', 'COMMUNITY', 'PUBLIC', 'LIST', 'PREFERENCES', 'SIGN_OUT'];
|
||||||
|
// const defaultTabIds = ['START', 'HOME', 'NOTIFICATIONS', 'PUBLIC', 'LIST', 'PREFERENCES', 'SIGN_OUT'];
|
||||||
|
|
||||||
|
let tabs = defaultTabIds;
|
||||||
|
|
||||||
header = (
|
header = (
|
||||||
<nav className='drawer__header'>
|
<nav className='drawer__header'>
|
||||||
<Link to='/getting-started' className='drawer__tab' title={intl.formatMessage(messages.start)} aria-label={intl.formatMessage(messages.start)}><Icon id='bars' fixedWidth /></Link>
|
{tabs.map(tabId => (
|
||||||
{!columns.some(column => column.get('id') === 'HOME') && (
|
<Fragment key={tabId}>{this.tab(tabId)}</Fragment>
|
||||||
<Link to='/home' className='drawer__tab' title={intl.formatMessage(messages.home_timeline)} aria-label={intl.formatMessage(messages.home_timeline)}><Icon id='home' fixedWidth /></Link>
|
))}
|
||||||
)}
|
|
||||||
{!columns.some(column => column.get('id') === 'NOTIFICATIONS') && (
|
|
||||||
<Link to='/notifications' className='drawer__tab' title={intl.formatMessage(messages.notifications)} aria-label={intl.formatMessage(messages.notifications)}><Icon id='bell' fixedWidth /></Link>
|
|
||||||
)}
|
|
||||||
{!columns.some(column => column.get('id') === 'COMMUNITY') && (
|
|
||||||
<Link to='/public/local' className='drawer__tab' title={intl.formatMessage(messages.community)} aria-label={intl.formatMessage(messages.community)}><Icon id='users' fixedWidth /></Link>
|
|
||||||
)}
|
|
||||||
{!columns.some(column => column.get('id') === 'PUBLIC') && (
|
|
||||||
<Link to='/public' className='drawer__tab' title={intl.formatMessage(messages.public)} aria-label={intl.formatMessage(messages.public)}><Icon id='globe' fixedWidth /></Link>
|
|
||||||
)}
|
|
||||||
<a href='/settings/preferences' className='drawer__tab' title={intl.formatMessage(messages.preferences)} aria-label={intl.formatMessage(messages.preferences)}><Icon id='cog' fixedWidth /></a>
|
|
||||||
<a href='/auth/sign_out' className='drawer__tab' title={intl.formatMessage(messages.logout)} aria-label={intl.formatMessage(messages.logout)} onClick={this.handleLogoutClick}><Icon id='sign-out' fixedWidth /></a>
|
|
||||||
</nav>
|
</nav>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -128,6 +164,7 @@ class Compose extends React.PureComponent {
|
|||||||
<NavigationContainer onClose={this.onBlur} />
|
<NavigationContainer onClose={this.onBlur} />
|
||||||
|
|
||||||
<ComposeFormContainer />
|
<ComposeFormContainer />
|
||||||
|
<AnnouncementsContainer />
|
||||||
|
|
||||||
<div className='drawer__inner__mastodon'>
|
<div className='drawer__inner__mastodon'>
|
||||||
<img alt='' draggable='false' src={mascot || elephantUIPlane} />
|
<img alt='' draggable='false' src={mascot || elephantUIPlane} />
|
||||||
|
@ -18,6 +18,7 @@ import TrendsContainer from './containers/trends_container';
|
|||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
|
home_timeline: { id: 'tabs_bar.home', defaultMessage: 'Home' },
|
||||||
notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
|
notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
|
||||||
|
admin_notifications: { id: 'tabs_bar.admin_notifications', defaultMessage: 'Admin Notifications' },
|
||||||
public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
|
public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
|
||||||
settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings' },
|
settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings' },
|
||||||
community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
|
community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
|
||||||
|
@ -7,7 +7,7 @@ import IconButton from 'mastodon/components/icon_button';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { me, boostModal } from 'mastodon/initial_state';
|
import { me, boostModal } from 'mastodon/initial_state';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import { replyCompose } from 'mastodon/actions/compose';
|
import { replyCompose, quoteCompose } from 'mastodon/actions/compose';
|
||||||
import { reblog, favourite, unreblog, unfavourite } from 'mastodon/actions/interactions';
|
import { reblog, favourite, unreblog, unfavourite } from 'mastodon/actions/interactions';
|
||||||
import { makeGetStatus } from 'mastodon/selectors';
|
import { makeGetStatus } from 'mastodon/selectors';
|
||||||
import { initBoostModal } from 'mastodon/actions/boosts';
|
import { initBoostModal } from 'mastodon/actions/boosts';
|
||||||
@ -20,9 +20,13 @@ const messages = defineMessages({
|
|||||||
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
|
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
|
||||||
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
||||||
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
||||||
|
cannot_quote: { id: 'status.cannot_quote', defaultMessage: 'This post cannot be quoted' },
|
||||||
|
quote: { id: 'status.quote', defaultMessage: 'Quote' },
|
||||||
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
|
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
|
||||||
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
||||||
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
||||||
|
quoteConfirm: { id: 'confirmations.quote.confirm', defaultMessage: 'Quote' },
|
||||||
|
quoteMessage: { id: 'confirmations.quote.message', defaultMessage: 'Quoting now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
||||||
open: { id: 'status.open', defaultMessage: 'Expand this status' },
|
open: { id: 'status.open', defaultMessage: 'Expand this status' },
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -123,6 +127,31 @@ class Footer extends ImmutablePureComponent {
|
|||||||
router.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
|
router.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_performQuote = () => {
|
||||||
|
const { dispatch, status, onClose } = this.props;
|
||||||
|
const { router } = this.context;
|
||||||
|
|
||||||
|
if (onClose) {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(quoteCompose(status, router.history));
|
||||||
|
};
|
||||||
|
|
||||||
|
handleQuoteClick = () => {
|
||||||
|
const { dispatch, askReplyConfirmation, intl } = this.props;
|
||||||
|
|
||||||
|
if (askReplyConfirmation) {
|
||||||
|
dispatch(openModal('CONFIRM', {
|
||||||
|
message: intl.formatMessage(messages.quoteMessage),
|
||||||
|
confirm: intl.formatMessage(messages.quoteConfirm),
|
||||||
|
onConfirm: this._performQuote,
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
this._performQuote();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { status, intl, withOpenButton } = this.props;
|
const { status, intl, withOpenButton } = this.props;
|
||||||
|
|
||||||
@ -156,6 +185,7 @@ class Footer extends ImmutablePureComponent {
|
|||||||
<IconButton className='status__action-bar-button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} counter={status.get('replies_count')} obfuscateCount />
|
<IconButton className='status__action-bar-button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} counter={status.get('replies_count')} obfuscateCount />
|
||||||
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} pressed={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} counter={status.get('reblogs_count')} />
|
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} pressed={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} counter={status.get('reblogs_count')} />
|
||||||
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={status.get('favourites_count')} />
|
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={status.get('favourites_count')} />
|
||||||
|
<IconButton className='status__action-bar-button' disabled={!publicStatus} title={!publicStatus ? intl.formatMessage(messages.cannot_quote) : intl.formatMessage(messages.quote)} icon='quote-right' onClick={this.handleQuoteClick} />
|
||||||
{withOpenButton && <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.open)} icon='external-link' onClick={this.handleOpenClick} href={status.get('url')} />}
|
{withOpenButton && <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.open)} icon='external-link' onClick={this.handleOpenClick} href={status.get('url')} />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -18,7 +18,9 @@ const messages = defineMessages({
|
|||||||
reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
|
reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
|
||||||
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
|
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
|
||||||
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
||||||
|
cannot_quote: { id: 'status.cannot_quote', defaultMessage: 'This post cannot be quoted' },
|
||||||
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
||||||
|
quote: { id: 'status.quote', defaultMessage: 'Quote' },
|
||||||
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
|
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
|
||||||
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
||||||
more: { id: 'status.more', defaultMessage: 'More' },
|
more: { id: 'status.more', defaultMessage: 'More' },
|
||||||
@ -57,6 +59,7 @@ class ActionBar extends React.PureComponent {
|
|||||||
relationship: ImmutablePropTypes.map,
|
relationship: ImmutablePropTypes.map,
|
||||||
onReply: PropTypes.func.isRequired,
|
onReply: PropTypes.func.isRequired,
|
||||||
onReblog: PropTypes.func.isRequired,
|
onReblog: PropTypes.func.isRequired,
|
||||||
|
onQuote: PropTypes.func.isRequired,
|
||||||
onFavourite: PropTypes.func.isRequired,
|
onFavourite: PropTypes.func.isRequired,
|
||||||
onBookmark: PropTypes.func.isRequired,
|
onBookmark: PropTypes.func.isRequired,
|
||||||
onDelete: PropTypes.func.isRequired,
|
onDelete: PropTypes.func.isRequired,
|
||||||
@ -84,6 +87,10 @@ class ActionBar extends React.PureComponent {
|
|||||||
this.props.onReblog(this.props.status, e);
|
this.props.onReblog(this.props.status, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleQuoteClick = () => {
|
||||||
|
this.props.onQuote(this.props.status, this.context.router.history);
|
||||||
|
}
|
||||||
|
|
||||||
handleFavouriteClick = () => {
|
handleFavouriteClick = () => {
|
||||||
this.props.onFavourite(this.props.status);
|
this.props.onFavourite(this.props.status);
|
||||||
}
|
}
|
||||||
@ -285,6 +292,7 @@ class ActionBar extends React.PureComponent {
|
|||||||
<div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.reply)} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} /></div>
|
<div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.reply)} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} /></div>
|
||||||
<div className='detailed-status__button' ><IconButton className={classNames({ reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} /></div>
|
<div className='detailed-status__button' ><IconButton className={classNames({ reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} /></div>
|
||||||
<div className='detailed-status__button'><IconButton className='star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} /></div>
|
<div className='detailed-status__button'><IconButton className='star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} /></div>
|
||||||
|
<div className='detailed-status__button'><IconButton disabled={!publicStatus} title={!publicStatus ? intl.formatMessage(messages.cannot_quote) : intl.formatMessage(messages.quote)} icon='quote-right' onClick={this.handleQuoteClick} /></div>
|
||||||
{shareButton}
|
{shareButton}
|
||||||
<div className='detailed-status__button'><IconButton className='bookmark-icon' active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} /></div>
|
<div className='detailed-status__button'><IconButton className='bookmark-icon' active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} /></div>
|
||||||
|
|
||||||
|
@ -60,6 +60,10 @@ const addAutoPlay = html => {
|
|||||||
|
|
||||||
export default class Card extends React.PureComponent {
|
export default class Card extends React.PureComponent {
|
||||||
|
|
||||||
|
static contextTypes = {
|
||||||
|
router: PropTypes.object,
|
||||||
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
card: ImmutablePropTypes.map,
|
card: ImmutablePropTypes.map,
|
||||||
maxDescription: PropTypes.number,
|
maxDescription: PropTypes.number,
|
||||||
@ -68,6 +72,7 @@ export default class Card extends React.PureComponent {
|
|||||||
defaultWidth: PropTypes.number,
|
defaultWidth: PropTypes.number,
|
||||||
cacheWidth: PropTypes.func,
|
cacheWidth: PropTypes.func,
|
||||||
sensitive: PropTypes.bool,
|
sensitive: PropTypes.bool,
|
||||||
|
quote: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
@ -184,7 +189,7 @@ export default class Card extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { card, maxDescription, compact } = this.props;
|
const { card, maxDescription, compact, quote } = this.props;
|
||||||
const { width, embedded, revealed } = this.state;
|
const { width, embedded, revealed } = this.state;
|
||||||
|
|
||||||
if (card === null) {
|
if (card === null) {
|
||||||
@ -197,7 +202,11 @@ export default class Card extends React.PureComponent {
|
|||||||
const className = classnames('status-card', { horizontal, compact, interactive });
|
const className = classnames('status-card', { horizontal, compact, interactive });
|
||||||
const title = interactive ? <a className='status-card__title' href={card.get('url')} title={card.get('title')} rel='noopener noreferrer' target='_blank'><strong>{card.get('title')}</strong></a> : <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>;
|
const title = interactive ? <a className='status-card__title' href={card.get('url')} title={card.get('title')} rel='noopener noreferrer' target='_blank'><strong>{card.get('title')}</strong></a> : <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>;
|
||||||
const ratio = card.get('width') / card.get('height');
|
const ratio = card.get('width') / card.get('height');
|
||||||
const height = (compact && !embedded) ? (width / (16 / 9)) : (width / ratio);
|
let height = (compact && !embedded) ? (width / (16 / 9)) : (width / ratio);
|
||||||
|
|
||||||
|
if (quote && height) {
|
||||||
|
height /= 2;
|
||||||
|
}
|
||||||
|
|
||||||
const description = (
|
const description = (
|
||||||
<div className='status-card__content'>
|
<div className='status-card__content'>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import Avatar from '../../../components/avatar';
|
import Avatar from '../../../components/avatar';
|
||||||
@ -6,7 +7,7 @@ import DisplayName from '../../../components/display_name';
|
|||||||
import StatusContent from '../../../components/status_content';
|
import StatusContent from '../../../components/status_content';
|
||||||
import MediaGallery from '../../../components/media_gallery';
|
import MediaGallery from '../../../components/media_gallery';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { injectIntl, defineMessages, FormattedDate } from 'react-intl';
|
import { injectIntl, defineMessages, FormattedDate, FormattedMessage } from 'react-intl';
|
||||||
import Card from './card';
|
import Card from './card';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import Video from '../../video';
|
import Video from '../../video';
|
||||||
@ -25,7 +26,31 @@ const messages = defineMessages({
|
|||||||
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
|
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
|
||||||
});
|
});
|
||||||
|
|
||||||
export default @injectIntl
|
const mapStateToProps = (state, props) => {
|
||||||
|
let status = props.status;
|
||||||
|
|
||||||
|
if (status === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
||||||
|
status = status.get('reblog');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status.get('quote', null) === null) {
|
||||||
|
return {
|
||||||
|
quote_muted: status.get('quote_id', null) ? true : false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const id = status.getIn(['quote', 'account', 'id'], null);
|
||||||
|
|
||||||
|
return {
|
||||||
|
quote_muted: id !== null && (state.getIn(['relationships', id, 'muting']) || state.getIn(['relationships', id, 'blocking']) || state.getIn(['relationships', id, 'blocked_by']) || state.getIn(['relationships', id, 'domain_blocking'])) || status.getIn(['quote', 'quote_muted']),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default @connect(mapStateToProps)
|
||||||
|
@injectIntl
|
||||||
class DetailedStatus extends ImmutablePureComponent {
|
class DetailedStatus extends ImmutablePureComponent {
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
@ -34,8 +59,11 @@ class DetailedStatus extends ImmutablePureComponent {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
status: ImmutablePropTypes.map,
|
status: ImmutablePropTypes.map,
|
||||||
|
quote_muted: PropTypes.bool,
|
||||||
onOpenMedia: PropTypes.func.isRequired,
|
onOpenMedia: PropTypes.func.isRequired,
|
||||||
onOpenVideo: PropTypes.func.isRequired,
|
onOpenVideo: PropTypes.func.isRequired,
|
||||||
|
onOpenMediaQuote: PropTypes.func.isRequired,
|
||||||
|
onOpenVideoQuote: PropTypes.func.isRequired,
|
||||||
onToggleHidden: PropTypes.func.isRequired,
|
onToggleHidden: PropTypes.func.isRequired,
|
||||||
measureHeight: PropTypes.bool,
|
measureHeight: PropTypes.bool,
|
||||||
onHeightChange: PropTypes.func,
|
onHeightChange: PropTypes.func,
|
||||||
@ -47,6 +75,9 @@ class DetailedStatus extends ImmutablePureComponent {
|
|||||||
available: PropTypes.bool,
|
available: PropTypes.bool,
|
||||||
}),
|
}),
|
||||||
onToggleMediaVisibility: PropTypes.func,
|
onToggleMediaVisibility: PropTypes.func,
|
||||||
|
onQuoteToggleHidden: PropTypes.func.isRequired,
|
||||||
|
showQuoteMedia: PropTypes.bool,
|
||||||
|
onToggleQuoteMediaVisibility: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
@ -55,6 +86,7 @@ class DetailedStatus extends ImmutablePureComponent {
|
|||||||
|
|
||||||
handleAccountClick = (e) => {
|
handleAccountClick = (e) => {
|
||||||
if (e.button === 0 && !(e.ctrlKey || e.metaKey) && this.context.router) {
|
if (e.button === 0 && !(e.ctrlKey || e.metaKey) && this.context.router) {
|
||||||
|
const id = e.currentTarget.getAttribute('data-id');
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`);
|
this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`);
|
||||||
}
|
}
|
||||||
@ -66,6 +98,10 @@ class DetailedStatus extends ImmutablePureComponent {
|
|||||||
this.props.onOpenVideo(this.props.status.getIn(['media_attachments', 0]), options);
|
this.props.onOpenVideo(this.props.status.getIn(['media_attachments', 0]), options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleOpenVideoQuote = (options) => {
|
||||||
|
this.props.onOpenVideoQuote(this.props.status.getIn(['quote', 'media_attachments', 0]), options);
|
||||||
|
}
|
||||||
|
|
||||||
handleExpandedToggle = () => {
|
handleExpandedToggle = () => {
|
||||||
this.props.onToggleHidden(this.props.status);
|
this.props.onToggleHidden(this.props.status);
|
||||||
}
|
}
|
||||||
@ -103,8 +139,22 @@ class DetailedStatus extends ImmutablePureComponent {
|
|||||||
window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
|
window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleExpandedQuoteToggle = () => {
|
||||||
|
this.props.onQuoteToggleHidden(this.props.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleQuoteClick = () => {
|
||||||
|
if (!this.context.router) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { status } = this.props;
|
||||||
|
this.context.router.history.push(`/statuses/${status.getIn(['quote', 'id'])}`);
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
|
const status = (this.props.status && this.props.status.get('reblog')) ? this.props.status.get('reblog') : this.props.status;
|
||||||
|
const quote_muted = this.props.quote_muted
|
||||||
const outerStyle = { boxSizing: 'border-box' };
|
const outerStyle = { boxSizing: 'border-box' };
|
||||||
const { intl, compact, pictureInPicture } = this.props;
|
const { intl, compact, pictureInPicture } = this.props;
|
||||||
|
|
||||||
@ -123,6 +173,95 @@ class DetailedStatus extends ImmutablePureComponent {
|
|||||||
outerStyle.height = `${this.state.height}px`;
|
outerStyle.height = `${this.state.height}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let quote = null;
|
||||||
|
if (status.get('quote', null) !== null) {
|
||||||
|
let quote_status = status.get('quote');
|
||||||
|
|
||||||
|
let quote_media = null;
|
||||||
|
if (quote_status.get('media_attachments').size > 0) {
|
||||||
|
|
||||||
|
if (quote_status.getIn(['media_attachments', 0, 'type']) === 'audio') {
|
||||||
|
const attachment = quote_status.getIn(['media_attachments', 0]);
|
||||||
|
|
||||||
|
quote_media = (
|
||||||
|
<Audio
|
||||||
|
src={attachment.get('url')}
|
||||||
|
alt={attachment.get('description')}
|
||||||
|
duration={attachment.getIn(['meta', 'original', 'duration'], 0)}
|
||||||
|
poster={attachment.get('preview_url') || quote_status.getIn(['account', 'avatar_static'])}
|
||||||
|
backgroundColor={attachment.getIn(['meta', 'colors', 'background'])}
|
||||||
|
foregroundColor={attachment.getIn(['meta', 'colors', 'foreground'])}
|
||||||
|
accentColor={attachment.getIn(['meta', 'colors', 'accent'])}
|
||||||
|
height={60}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else if (quote_status.getIn(['media_attachments', 0, 'type']) === 'video') {
|
||||||
|
const attachment = quote_status.getIn(['media_attachments', 0]);
|
||||||
|
|
||||||
|
quote_media = (
|
||||||
|
<Video
|
||||||
|
preview={attachment.get('preview_url')}
|
||||||
|
frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])}
|
||||||
|
blurhash={attachment.get('blurhash')}
|
||||||
|
src={attachment.get('url')}
|
||||||
|
alt={attachment.get('description')}
|
||||||
|
width={300}
|
||||||
|
height={150}
|
||||||
|
inline
|
||||||
|
onOpenVideo={this.handleOpenVideoQuote}
|
||||||
|
sensitive={quote_status.get('sensitive')}
|
||||||
|
visible={this.props.showQuoteMedia}
|
||||||
|
onToggleVisibility={this.props.onToggleQuoteMediaVisibility}
|
||||||
|
quote
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
quote_media = (
|
||||||
|
<MediaGallery
|
||||||
|
standalone
|
||||||
|
sensitive={quote_status.get('sensitive')}
|
||||||
|
media={quote_status.get('media_attachments')}
|
||||||
|
height={300}
|
||||||
|
onOpenMedia={this.props.onOpenMediaQuote}
|
||||||
|
visible={this.props.showQuoteMedia}
|
||||||
|
onToggleVisibility={this.props.onToggleQuoteMediaVisibility}
|
||||||
|
quote
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (quote_muted) {
|
||||||
|
quote = (
|
||||||
|
<div className='quote-status' data-id={quote_status.get('id')} dataurl={quote_status.get('url')}>
|
||||||
|
<div className='status__content muted-quote'>
|
||||||
|
<FormattedMessage id='status.muted_quote' defaultMessage='Muted quote' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
quote = (
|
||||||
|
<div className='quote-status' data-id={quote_status.get('id')} dataurl={quote_status.get('url')}>
|
||||||
|
<a href={quote_status.getIn(['account', 'url'])} onClick={this.handleAccountClick} data-id={quote_status.getIn(['account', 'id'])} className='detailed-status__display-name'>
|
||||||
|
<div className='detailed-status__display-avatar'><Avatar account={quote_status.get('account')} size={18} /></div>
|
||||||
|
<DisplayName account={quote_status.get('account')} localDomain={this.props.domain} />
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<StatusContent status={quote_status} onClick={this.handleQuoteClick} expanded={!status.get('quote_hidden')} onExpandedToggle={this.handleExpandedQuoteToggle} quote />
|
||||||
|
{quote_media}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (quote_muted) {
|
||||||
|
quote = (
|
||||||
|
<div className={classNames('quote-status', { muted: this.props.muted })} data-id={quote_status.get('id')} dataurl={quote_status.get('url')}>
|
||||||
|
<div className={classNames('status__content muted-quote', { 'status__content--with-action': this.context.router })}>
|
||||||
|
<FormattedMessage id='status.muted_quote' defaultMessage='Muted quote' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (pictureInPicture.get('inUse')) {
|
if (pictureInPicture.get('inUse')) {
|
||||||
media = <PictureInPicturePlaceholder />;
|
media = <PictureInPicturePlaceholder />;
|
||||||
} else if (status.get('media_attachments').size > 0) {
|
} else if (status.get('media_attachments').size > 0) {
|
||||||
@ -258,6 +397,7 @@ class DetailedStatus extends ImmutablePureComponent {
|
|||||||
|
|
||||||
<StatusContent status={status} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} />
|
<StatusContent status={status} expanded={!status.get('hidden')} onExpandedToggle={this.handleExpandedToggle} />
|
||||||
|
|
||||||
|
{quote}
|
||||||
{media}
|
{media}
|
||||||
|
|
||||||
<div className='detailed-status__meta'>
|
<div className='detailed-status__meta'>
|
||||||
|
@ -20,6 +20,8 @@ import {
|
|||||||
deleteStatus,
|
deleteStatus,
|
||||||
hideStatus,
|
hideStatus,
|
||||||
revealStatus,
|
revealStatus,
|
||||||
|
hideQuote,
|
||||||
|
revealQuote,
|
||||||
} from '../../../actions/statuses';
|
} from '../../../actions/statuses';
|
||||||
import { initMuteModal } from '../../../actions/mutes';
|
import { initMuteModal } from '../../../actions/mutes';
|
||||||
import { initBlockModal } from '../../../actions/blocks';
|
import { initBlockModal } from '../../../actions/blocks';
|
||||||
@ -136,6 +138,14 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||||||
dispatch(openModal('VIDEO', { media, options }));
|
dispatch(openModal('VIDEO', { media, options }));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onOpenMediaQuote (media, index) {
|
||||||
|
dispatch(openModal('MEDIA', { media, index }));
|
||||||
|
},
|
||||||
|
|
||||||
|
onOpenVideoQuote (media, options) {
|
||||||
|
dispatch(openModal('VIDEO', { media, options }));
|
||||||
|
},
|
||||||
|
|
||||||
onBlock (status) {
|
onBlock (status) {
|
||||||
const account = status.get('account');
|
const account = status.get('account');
|
||||||
dispatch(initBlockModal(account));
|
dispatch(initBlockModal(account));
|
||||||
@ -165,6 +175,13 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onQuoteToggleHidden (status) {
|
||||||
|
if (status.get('quote_hidden')) {
|
||||||
|
dispatch(revealQuote(status.get('id')));
|
||||||
|
} else {
|
||||||
|
dispatch(hideQuote(status.get('id')));
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(DetailedStatus));
|
export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(DetailedStatus));
|
||||||
|
@ -22,6 +22,7 @@ import {
|
|||||||
} from '../../actions/interactions';
|
} from '../../actions/interactions';
|
||||||
import {
|
import {
|
||||||
replyCompose,
|
replyCompose,
|
||||||
|
quoteCompose,
|
||||||
mentionCompose,
|
mentionCompose,
|
||||||
directCompose,
|
directCompose,
|
||||||
} from '../../actions/compose';
|
} from '../../actions/compose';
|
||||||
@ -32,6 +33,8 @@ import {
|
|||||||
editStatus,
|
editStatus,
|
||||||
hideStatus,
|
hideStatus,
|
||||||
revealStatus,
|
revealStatus,
|
||||||
|
hideQuote,
|
||||||
|
revealQuote,
|
||||||
} from '../../actions/statuses';
|
} from '../../actions/statuses';
|
||||||
import {
|
import {
|
||||||
unblockAccount,
|
unblockAccount,
|
||||||
@ -69,6 +72,8 @@ const messages = defineMessages({
|
|||||||
detailedStatus: { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' },
|
detailedStatus: { id: 'status.detailed_status', defaultMessage: 'Detailed conversation view' },
|
||||||
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
replyConfirm: { id: 'confirmations.reply.confirm', defaultMessage: 'Reply' },
|
||||||
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
replyMessage: { id: 'confirmations.reply.message', defaultMessage: 'Replying now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
||||||
|
quoteConfirm: { id: 'confirmations.quote.confirm', defaultMessage: 'Quote' },
|
||||||
|
quoteMessage: { id: 'confirmations.quote.message', defaultMessage: 'Quoting now will overwrite the message you are currently composing. Are you sure you want to proceed?' },
|
||||||
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
|
blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' },
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -181,6 +186,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
state = {
|
state = {
|
||||||
fullscreen: false,
|
fullscreen: false,
|
||||||
showMedia: defaultMediaVisibility(this.props.status),
|
showMedia: defaultMediaVisibility(this.props.status),
|
||||||
|
showQuoteMedia: defaultMediaVisibility(this.props.status ? this.props.status.get('quote', null) : null),
|
||||||
loadedStatusId: undefined,
|
loadedStatusId: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -199,7 +205,8 @@ class Status extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (nextProps.status && nextProps.status.get('id') !== this.state.loadedStatusId) {
|
if (nextProps.status && nextProps.status.get('id') !== this.state.loadedStatusId) {
|
||||||
this.setState({ showMedia: defaultMediaVisibility(nextProps.status), loadedStatusId: nextProps.status.get('id') });
|
this.setState({ showMedia: defaultMediaVisibility(nextProps.status), loadedStatusId: nextProps.status.get('id'),
|
||||||
|
showQuoteMedia: defaultMediaVisibility(nextProps.status.get('quote', null)) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,6 +214,10 @@ class Status extends ImmutablePureComponent {
|
|||||||
this.setState({ showMedia: !this.state.showMedia });
|
this.setState({ showMedia: !this.state.showMedia });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleToggleQuoteMediaVisibility = () => {
|
||||||
|
this.setState({ showQuoteMedia: !this.state.showQuoteMedia });
|
||||||
|
}
|
||||||
|
|
||||||
handleFavouriteClick = (status) => {
|
handleFavouriteClick = (status) => {
|
||||||
if (status.get('favourited')) {
|
if (status.get('favourited')) {
|
||||||
this.props.dispatch(unfavourite(status));
|
this.props.dispatch(unfavourite(status));
|
||||||
@ -260,6 +271,19 @@ class Status extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleQuoteClick = (status) => {
|
||||||
|
let { askReplyConfirmation, dispatch, intl } = this.props;
|
||||||
|
if (askReplyConfirmation) {
|
||||||
|
dispatch(openModal('CONFIRM', {
|
||||||
|
message: intl.formatMessage(messages.quoteMessage),
|
||||||
|
confirm: intl.formatMessage(messages.quoteConfirm),
|
||||||
|
onConfirm: () => dispatch(quoteCompose(status, this.context.router.history)),
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
dispatch(quoteCompose(status, this.context.router.history));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleDeleteClick = (status, history, withRedraft = false) => {
|
handleDeleteClick = (status, history, withRedraft = false) => {
|
||||||
const { dispatch, intl } = this.props;
|
const { dispatch, intl } = this.props;
|
||||||
|
|
||||||
@ -294,6 +318,14 @@ class Status extends ImmutablePureComponent {
|
|||||||
this.props.dispatch(openModal('VIDEO', { statusId: this.props.status.get('id'), media, options }));
|
this.props.dispatch(openModal('VIDEO', { statusId: this.props.status.get('id'), media, options }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleOpenMediaQuote = (media, index) => {
|
||||||
|
this.props.dispatch(openModal('MEDIA', { statusId: this.props.status.getIn(['quote', 'id']), media, index }));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleOpenVideoQuote = (media, options) => {
|
||||||
|
this.props.dispatch(openModal('VIDEO', { statusId: this.props.status.getIn(['quote', 'id']), media, options }));
|
||||||
|
}
|
||||||
|
|
||||||
handleHotkeyOpenMedia = e => {
|
handleHotkeyOpenMedia = e => {
|
||||||
const { status } = this.props;
|
const { status } = this.props;
|
||||||
|
|
||||||
@ -328,6 +360,14 @@ class Status extends ImmutablePureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleQuoteToggleHidden = (status) => {
|
||||||
|
if (status.get('quote_hidden')) {
|
||||||
|
this.props.dispatch(revealQuote(status.get('id')));
|
||||||
|
} else {
|
||||||
|
this.props.dispatch(hideQuote(status.get('id')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleToggleAll = () => {
|
handleToggleAll = () => {
|
||||||
const { status, ancestorsIds, descendantsIds } = this.props;
|
const { status, ancestorsIds, descendantsIds } = this.props;
|
||||||
const statusIds = [status.get('id')].concat(ancestorsIds.toJS(), descendantsIds.toJS());
|
const statusIds = [status.get('id')].concat(ancestorsIds.toJS(), descendantsIds.toJS());
|
||||||
@ -557,11 +597,16 @@ class Status extends ImmutablePureComponent {
|
|||||||
status={status}
|
status={status}
|
||||||
onOpenVideo={this.handleOpenVideo}
|
onOpenVideo={this.handleOpenVideo}
|
||||||
onOpenMedia={this.handleOpenMedia}
|
onOpenMedia={this.handleOpenMedia}
|
||||||
|
onOpenVideoQuote={this.handleOpenVideoQuote}
|
||||||
|
onOpenMediaQuote={this.handleOpenMediaQuote}
|
||||||
onToggleHidden={this.handleToggleHidden}
|
onToggleHidden={this.handleToggleHidden}
|
||||||
domain={domain}
|
domain={domain}
|
||||||
showMedia={this.state.showMedia}
|
showMedia={this.state.showMedia}
|
||||||
onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
||||||
pictureInPicture={pictureInPicture}
|
pictureInPicture={pictureInPicture}
|
||||||
|
onQuoteToggleHidden={this.handleQuoteToggleHidden}
|
||||||
|
showQuoteMedia={this.state.showQuoteMedia}
|
||||||
|
onToggleQuoteMediaVisibility={this.handleToggleQuoteMediaVisibility}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ActionBar
|
<ActionBar
|
||||||
@ -571,6 +616,7 @@ class Status extends ImmutablePureComponent {
|
|||||||
onFavourite={this.handleFavouriteClick}
|
onFavourite={this.handleFavouriteClick}
|
||||||
onReblog={this.handleReblogClick}
|
onReblog={this.handleReblogClick}
|
||||||
onBookmark={this.handleBookmarkClick}
|
onBookmark={this.handleBookmarkClick}
|
||||||
|
onQuote={this.handleQuoteClick}
|
||||||
onDelete={this.handleDeleteClick}
|
onDelete={this.handleDeleteClick}
|
||||||
onEdit={this.handleEditClick}
|
onEdit={this.handleEditClick}
|
||||||
onDirect={this.handleDirectClick}
|
onDirect={this.handleDirectClick}
|
||||||
|
@ -8,7 +8,7 @@ import ReactSwipeableViews from 'react-swipeable-views';
|
|||||||
import TabsBar, { links, getIndex, getLink } from './tabs_bar';
|
import TabsBar, { links, getIndex, getLink } from './tabs_bar';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { disableSwiping } from 'mastodon/initial_state';
|
import { disableSwiping, place_tab_bar_at_bottom } from 'mastodon/initial_state';
|
||||||
|
|
||||||
import BundleContainer from '../containers/bundle_container';
|
import BundleContainer from '../containers/bundle_container';
|
||||||
import ColumnLoading from './column_loading';
|
import ColumnLoading from './column_loading';
|
||||||
@ -34,6 +34,8 @@ import NavigationPanel from './navigation_panel';
|
|||||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||||
import { scrollRight } from '../../../scroll';
|
import { scrollRight } from '../../../scroll';
|
||||||
|
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
const componentMap = {
|
const componentMap = {
|
||||||
'COMPOSE': Compose,
|
'COMPOSE': Compose,
|
||||||
'HOME': HomeTimeline,
|
'HOME': HomeTimeline,
|
||||||
@ -216,14 +218,13 @@ class ColumnsArea extends ImmutablePureComponent {
|
|||||||
const columnIndex = getIndex(this.context.router.history.location.pathname);
|
const columnIndex = getIndex(this.context.router.history.location.pathname);
|
||||||
|
|
||||||
if (singleColumn) {
|
if (singleColumn) {
|
||||||
const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/publish' className='floating-action-button' aria-label={intl.formatMessage(messages.publish)}><Icon id='pencil' /></Link>;
|
const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/publish' className={classNames('floating-action-button', { 'bottom-bar': place_tab_bar_at_bottom })} aria-label={intl.formatMessage(messages.publish)}><Icon id='pencil' /></Link>;
|
||||||
|
|
||||||
const content = columnIndex !== -1 ? (
|
const content = columnIndex !== -1 ? (
|
||||||
<ReactSwipeableViews key='content' hysteresis={0.2} threshold={15} index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }} disabled={disableSwiping}>
|
<ReactSwipeableViews key='content' className={classNames('swipeable-view__wrapper', { 'bottom-bar': place_tab_bar_at_bottom })} hysteresis={0.2} threshold={15} index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }} disabled={disableSwiping}>
|
||||||
{links.map(this.renderView)}
|
{links.map(this.renderView)}
|
||||||
</ReactSwipeableViews>
|
</ReactSwipeableViews>
|
||||||
) : (
|
) : (
|
||||||
<div key='content' className='columns-area columns-area--mobile'>{children}</div>
|
<div key='content' className={classNames('columns-area columns-area--mobile', { 'bottom-bar': place_tab_bar_at_bottom })}>{children}</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -55,6 +55,7 @@ class LinkFooter extends React.PureComponent {
|
|||||||
{profileDirectory && <li><Link to='/directory'><FormattedMessage id='getting_started.directory' defaultMessage='Profile directory' /></Link> · </li>}
|
{profileDirectory && <li><Link to='/directory'><FormattedMessage id='getting_started.directory' defaultMessage='Profile directory' /></Link> · </li>}
|
||||||
<li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li>
|
<li><a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='navigation_bar.apps' defaultMessage='Mobile apps' /></a> · </li>
|
||||||
<li><a href='/terms' target='_blank'><FormattedMessage id='getting_started.terms' defaultMessage='Terms of service' /></a> · </li>
|
<li><a href='/terms' target='_blank'><FormattedMessage id='getting_started.terms' defaultMessage='Terms of service' /></a> · </li>
|
||||||
|
<li><a href='https://mattermost.pcgf.io/signup_user_complete/?id=i8xgw5ytb78b7ef5qz7zf7ieae' target='_blank'><FormattedMessage id='getting_started.chatroom' defaultMessage='Chatroom' /></a> · </li>
|
||||||
<li><a href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a> · </li>
|
<li><a href='/settings/applications' target='_blank'><FormattedMessage id='getting_started.developers' defaultMessage='Developers' /></a> · </li>
|
||||||
<li><a href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a> · </li>
|
<li><a href='https://docs.joinmastodon.org' target='_blank'><FormattedMessage id='getting_started.documentation' defaultMessage='Documentation' /></a> · </li>
|
||||||
<li><a href='/auth/sign_out' onClick={this.handleLogoutClick}><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a></li>
|
<li><a href='/auth/sign_out' onClick={this.handleLogoutClick}><FormattedMessage id='navigation_bar.logout' defaultMessage='Logout' /></a></li>
|
||||||
|
@ -6,14 +6,16 @@ import { debounce } from 'lodash';
|
|||||||
import { isUserTouching } from '../../../is_mobile';
|
import { isUserTouching } from '../../../is_mobile';
|
||||||
import Icon from 'mastodon/components/icon';
|
import Icon from 'mastodon/components/icon';
|
||||||
import NotificationsCounterIcon from './notifications_counter_icon';
|
import NotificationsCounterIcon from './notifications_counter_icon';
|
||||||
|
import { place_tab_bar_at_bottom, show_tab_bar_label } from 'mastodon/initial_state';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
export const links = [
|
export const links = [
|
||||||
<NavLink className='tabs-bar__link' to='/home' data-preview-title-id='column.home' data-preview-icon='home' ><Icon id='home' fixedWidth /><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></NavLink>,
|
<NavLink className='tabs-bar__link' to='/home' data-preview-title-id='column.home' data-preview-icon='home' ><Icon id='home' fixedWidth /><span className='tabs-bar__link__full-label'><FormattedMessage id='tabs_bar.home' defaultMessage='Home' /></span><span className='tabs-bar__link__short-label'><FormattedMessage id='navigation_bar.short.home' defaultMessage='Home' /></span></NavLink>,
|
||||||
<NavLink className='tabs-bar__link' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsCounterIcon /><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></NavLink>,
|
<NavLink className='tabs-bar__link' to='/notifications' data-preview-title-id='column.notifications' data-preview-icon='bell' ><NotificationsCounterIcon /><span className='tabs-bar__link__full-label'><FormattedMessage id='tabs_bar.notifications' defaultMessage='Notifications' /></span><span className='tabs-bar__link__short-label'><FormattedMessage id='navigation_bar.short.notifications' defaultMessage='Notif.' /></span></NavLink>,
|
||||||
<NavLink className='tabs-bar__link' to='/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><Icon id='users' fixedWidth /><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></NavLink>,
|
<NavLink className='tabs-bar__link' to='/public/local' data-preview-title-id='column.community' data-preview-icon='users' ><Icon id='users' fixedWidth /><span className='tabs-bar__link__full-label'><FormattedMessage id='tabs_bar.local_timeline' defaultMessage='Local' /></span><span className='tabs-bar__link__short-label'><FormattedMessage id='navigation_bar.short.local_timeline' defaultMessage='LTL' /></span></NavLink>,
|
||||||
<NavLink className='tabs-bar__link' exact to='/public' data-preview-title-id='column.public' data-preview-icon='globe' ><Icon id='globe' fixedWidth /><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></NavLink>,
|
<NavLink className='tabs-bar__link' exact to='/public' data-preview-title-id='column.public' data-preview-icon='globe' ><Icon id='globe' fixedWidth /><span className='tabs-bar__link__full-label'><FormattedMessage id='tabs_bar.federated_timeline' defaultMessage='Federated' /></span><span className='tabs-bar__link__short-label'><FormattedMessage id='navigation_bar.short.public_timeline' defaultMessage='FTL' /></span></NavLink>,
|
||||||
<NavLink className='tabs-bar__link optional' to='/explore' data-preview-title-id='tabs_bar.search' data-preview-icon='search' ><Icon id='search' fixedWidth /><FormattedMessage id='tabs_bar.search' defaultMessage='Search' /></NavLink>,
|
<NavLink className='tabs-bar__link optional' to='/explore' data-preview-title-id='tabs_bar.search' data-preview-icon='search' ><Icon id='search' fixedWidth /><span className='tabs-bar__link__full-label'><FormattedMessage id='tabs_bar.search' defaultMessage='Search' /></span><span className='tabs-bar__link__short-label'><FormattedMessage id='navigation_bar.short.search' defaultMessage='Search' /></span></NavLink>,
|
||||||
<NavLink className='tabs-bar__link' style={{ flexGrow: '0', flexBasis: '30px' }} to='/getting-started' data-preview-title-id='getting_started.heading' data-preview-icon='bars' ><Icon id='bars' fixedWidth /></NavLink>,
|
<NavLink className='tabs-bar__link hamburger' to='/getting-started' data-preview-title-id='getting_started.heading' data-preview-icon='bars' ><Icon id='bars' fixedWidth /></NavLink>,
|
||||||
];
|
];
|
||||||
|
|
||||||
export function getIndex (path) {
|
export function getIndex (path) {
|
||||||
@ -31,7 +33,7 @@ class TabsBar extends React.PureComponent {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
history: PropTypes.object.isRequired,
|
history: PropTypes.object.isRequired,
|
||||||
}
|
};
|
||||||
|
|
||||||
setRef = ref => {
|
setRef = ref => {
|
||||||
this.node = ref;
|
this.node = ref;
|
||||||
@ -74,8 +76,8 @@ class TabsBar extends React.PureComponent {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='tabs-bar__wrapper'>
|
<div className='tabs-bar__wrapper'>
|
||||||
<nav className='tabs-bar' ref={this.setRef}>
|
<nav className={classNames('tabs-bar', { 'bottom-bar': place_tab_bar_at_bottom })} ref={this.setRef}>
|
||||||
{links.map(link => React.cloneElement(link, { key: link.props.to, onClick: this.handleClick, 'aria-label': formatMessage({ id: link.props['data-preview-title-id'] }) }))}
|
{links.map(link => React.cloneElement(link, { key: link.props.to, className: classNames(link.props.className, { 'short-label': show_tab_bar_label }), onClick: this.handleClick, 'aria-label': formatMessage({ id: link.props['data-preview-title-id'] }) }))}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div id='tabs-bar__portal' />
|
<div id='tabs-bar__portal' />
|
||||||
|
@ -121,7 +121,8 @@ class Video extends React.PureComponent {
|
|||||||
autoPlay: PropTypes.bool,
|
autoPlay: PropTypes.bool,
|
||||||
volume: PropTypes.number,
|
volume: PropTypes.number,
|
||||||
muted: PropTypes.bool,
|
muted: PropTypes.bool,
|
||||||
componentIndex: PropTypes.number,
|
componetIndex: PropTypes.number,
|
||||||
|
quote: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
@ -523,7 +524,7 @@ class Video extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, detailed, sensitive, editable, blurhash } = this.props;
|
const { preview, src, inline, onOpenVideo, onCloseVideo, intl, alt, detailed, sensitive, editable, blurhash, quote } = this.props;
|
||||||
const { containerWidth, currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
|
const { containerWidth, currentTime, duration, volume, buffer, dragging, paused, fullscreen, hovered, muted, revealed } = this.state;
|
||||||
const progress = Math.min((currentTime / duration) * 100, 100);
|
const progress = Math.min((currentTime / duration) * 100, 100);
|
||||||
const playerStyle = {};
|
const playerStyle = {};
|
||||||
@ -537,6 +538,11 @@ class Video extends React.PureComponent {
|
|||||||
playerStyle.height = height;
|
playerStyle.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (quote && height) {
|
||||||
|
height /= 2;
|
||||||
|
playerStyle.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
let preload;
|
let preload;
|
||||||
|
|
||||||
if (this.props.currentTime || fullscreen || dragging) {
|
if (this.props.currentTime || fullscreen || dragging) {
|
||||||
|
@ -27,5 +27,7 @@ export const showTrends = getMeta('trends');
|
|||||||
export const title = getMeta('title');
|
export const title = getMeta('title');
|
||||||
export const cropImages = getMeta('crop_images');
|
export const cropImages = getMeta('crop_images');
|
||||||
export const disableSwiping = getMeta('disable_swiping');
|
export const disableSwiping = getMeta('disable_swiping');
|
||||||
|
export const place_tab_bar_at_bottom = getMeta('place_tab_bar_at_bottom');
|
||||||
|
export const show_tab_bar_label = getMeta('show_tab_bar_label');
|
||||||
|
|
||||||
export default initialState;
|
export default initialState;
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
"account.mute": "Skrýt @{name}",
|
"account.mute": "Skrýt @{name}",
|
||||||
"account.mute_notifications": "Skrýt oznámení od @{name}",
|
"account.mute_notifications": "Skrýt oznámení od @{name}",
|
||||||
"account.muted": "Skryt",
|
"account.muted": "Skryt",
|
||||||
|
"account.never_active": "Nikdy",
|
||||||
"account.posts": "Příspěvky",
|
"account.posts": "Příspěvky",
|
||||||
"account.posts_with_replies": "Příspěvky a odpovědi",
|
"account.posts_with_replies": "Příspěvky a odpovědi",
|
||||||
"account.report": "Nahlásit @{name}",
|
"account.report": "Nahlásit @{name}",
|
||||||
|
@ -552,6 +552,18 @@
|
|||||||
"defaultMessage": "This post cannot be boosted",
|
"defaultMessage": "This post cannot be boosted",
|
||||||
"id": "status.cannot_reblog"
|
"id": "status.cannot_reblog"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"defaultMessage": "Quote",
|
||||||
|
"id": "status.quote"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"defaultMessage": "Unlisted quote",
|
||||||
|
"id": "status.unlisted_quote"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"defaultMessage": "Muted quote",
|
||||||
|
"id": "status.muted_quote"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"defaultMessage": "Favourite",
|
"defaultMessage": "Favourite",
|
||||||
"id": "status.favourite"
|
"id": "status.favourite"
|
||||||
@ -1340,6 +1352,15 @@
|
|||||||
],
|
],
|
||||||
"path": "app/javascript/mastodon/features/compose/components/privacy_dropdown.json"
|
"path": "app/javascript/mastodon/features/compose/components/privacy_dropdown.json"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"descriptors": [
|
||||||
|
{
|
||||||
|
"defaultMessage": "Cancel",
|
||||||
|
"id": "quote_indicator.cancel"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"path": "app/javascript/mastodon/features/compose/components/quote_indicator.json"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"descriptors": [
|
"descriptors": [
|
||||||
{
|
{
|
||||||
|
@ -130,6 +130,8 @@
|
|||||||
"confirmations.mute.confirm": "Mute",
|
"confirmations.mute.confirm": "Mute",
|
||||||
"confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.",
|
"confirmations.mute.explanation": "This will hide posts from them and posts mentioning them, but it will still allow them to see your posts and follow you.",
|
||||||
"confirmations.mute.message": "Are you sure you want to mute {name}?",
|
"confirmations.mute.message": "Are you sure you want to mute {name}?",
|
||||||
|
"confirmations.quote.confirm": "Quote",
|
||||||
|
"confirmations.quote.message": "Quoting now will overwrite the message you are currently composing. Are you sure you want to proceed?",
|
||||||
"confirmations.redraft.confirm": "Delete & redraft",
|
"confirmations.redraft.confirm": "Delete & redraft",
|
||||||
"confirmations.redraft.message": "Are you sure you want to delete this post and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.",
|
"confirmations.redraft.message": "Are you sure you want to delete this post and re-draft it? Favourites and boosts will be lost, and replies to the original post will be orphaned.",
|
||||||
"confirmations.reply.confirm": "Reply",
|
"confirmations.reply.confirm": "Reply",
|
||||||
@ -309,6 +311,15 @@
|
|||||||
"navigation_bar.preferences": "Preferences",
|
"navigation_bar.preferences": "Preferences",
|
||||||
"navigation_bar.public_timeline": "Federated timeline",
|
"navigation_bar.public_timeline": "Federated timeline",
|
||||||
"navigation_bar.security": "Security",
|
"navigation_bar.security": "Security",
|
||||||
|
"navigation_bar.short.community_timeline": "LTL",
|
||||||
|
"navigation_bar.short.getting_started": "Started",
|
||||||
|
"navigation_bar.short.home": "Home",
|
||||||
|
"navigation_bar.short.lists": "Lists",
|
||||||
|
"navigation_bar.short.logout": "Logout",
|
||||||
|
"navigation_bar.short.notifications": "Notif.",
|
||||||
|
"navigation_bar.short.preferences": "Pref.",
|
||||||
|
"navigation_bar.short.public_timeline": "FTL",
|
||||||
|
"navigation_bar.short.search": "Search",
|
||||||
"notification.admin.sign_up": "{name} signed up",
|
"notification.admin.sign_up": "{name} signed up",
|
||||||
"notification.favourite": "{name} favourited your post",
|
"notification.favourite": "{name} favourited your post",
|
||||||
"notification.follow": "{name} followed you",
|
"notification.follow": "{name} followed you",
|
||||||
@ -374,6 +385,7 @@
|
|||||||
"privacy.public.short": "Public",
|
"privacy.public.short": "Public",
|
||||||
"privacy.unlisted.long": "Visible for all, but not in public timelines",
|
"privacy.unlisted.long": "Visible for all, but not in public timelines",
|
||||||
"privacy.unlisted.short": "Unlisted",
|
"privacy.unlisted.short": "Unlisted",
|
||||||
|
"quote_indicator.cancel": "Cancel",
|
||||||
"refresh": "Refresh",
|
"refresh": "Refresh",
|
||||||
"regeneration_indicator.label": "Loading…",
|
"regeneration_indicator.label": "Loading…",
|
||||||
"regeneration_indicator.sublabel": "Your home feed is being prepared!",
|
"regeneration_indicator.sublabel": "Your home feed is being prepared!",
|
||||||
@ -445,6 +457,7 @@
|
|||||||
"status.block": "Block @{name}",
|
"status.block": "Block @{name}",
|
||||||
"status.bookmark": "Bookmark",
|
"status.bookmark": "Bookmark",
|
||||||
"status.cancel_reblog_private": "Unboost",
|
"status.cancel_reblog_private": "Unboost",
|
||||||
|
"status.cannot_quote": "This post cannot be quoted",
|
||||||
"status.cannot_reblog": "This post cannot be boosted",
|
"status.cannot_reblog": "This post cannot be boosted",
|
||||||
"status.copy": "Copy link to post",
|
"status.copy": "Copy link to post",
|
||||||
"status.delete": "Delete",
|
"status.delete": "Delete",
|
||||||
@ -464,9 +477,11 @@
|
|||||||
"status.more": "More",
|
"status.more": "More",
|
||||||
"status.mute": "Mute @{name}",
|
"status.mute": "Mute @{name}",
|
||||||
"status.mute_conversation": "Mute conversation",
|
"status.mute_conversation": "Mute conversation",
|
||||||
|
"status.muted_quote": "Muted quote",
|
||||||
"status.open": "Expand this post",
|
"status.open": "Expand this post",
|
||||||
"status.pin": "Pin on profile",
|
"status.pin": "Pin on profile",
|
||||||
"status.pinned": "Pinned post",
|
"status.pinned": "Pinned post",
|
||||||
|
"status.quote": "Quote",
|
||||||
"status.read_more": "Read more",
|
"status.read_more": "Read more",
|
||||||
"status.reblog": "Boost",
|
"status.reblog": "Boost",
|
||||||
"status.reblog_private": "Boost with original visibility",
|
"status.reblog_private": "Boost with original visibility",
|
||||||
@ -483,8 +498,10 @@
|
|||||||
"status.show_less_all": "Show less for all",
|
"status.show_less_all": "Show less for all",
|
||||||
"status.show_more": "Show more",
|
"status.show_more": "Show more",
|
||||||
"status.show_more_all": "Show more for all",
|
"status.show_more_all": "Show more for all",
|
||||||
|
"status.show_poll": "Show poll",
|
||||||
"status.show_thread": "Show thread",
|
"status.show_thread": "Show thread",
|
||||||
"status.uncached_media_warning": "Not available",
|
"status.uncached_media_warning": "Not available",
|
||||||
|
"status.unlisted_quote": "Unlisted quote",
|
||||||
"status.unmute_conversation": "Unmute conversation",
|
"status.unmute_conversation": "Unmute conversation",
|
||||||
"status.unpin": "Unpin from profile",
|
"status.unpin": "Unpin from profile",
|
||||||
"suggestions.dismiss": "Dismiss suggestion",
|
"suggestions.dismiss": "Dismiss suggestion",
|
||||||
|
@ -97,7 +97,7 @@
|
|||||||
"compose_form.hashtag_warning": "この投稿は公開設定ではないのでハッシュタグの一覧に表示されません。公開投稿だけがハッシュタグで検索できます。",
|
"compose_form.hashtag_warning": "この投稿は公開設定ではないのでハッシュタグの一覧に表示されません。公開投稿だけがハッシュタグで検索できます。",
|
||||||
"compose_form.lock_disclaimer": "あなたのアカウントは{locked}になっていません。誰でもあなたをフォローすることができ、フォロワー限定の投稿を見ることができます。",
|
"compose_form.lock_disclaimer": "あなたのアカウントは{locked}になっていません。誰でもあなたをフォローすることができ、フォロワー限定の投稿を見ることができます。",
|
||||||
"compose_form.lock_disclaimer.lock": "承認制",
|
"compose_form.lock_disclaimer.lock": "承認制",
|
||||||
"compose_form.placeholder": "今なにしてる?",
|
"compose_form.placeholder": "ちょうどL.A.に到着しました。",
|
||||||
"compose_form.poll.add_option": "追加",
|
"compose_form.poll.add_option": "追加",
|
||||||
"compose_form.poll.duration": "アンケート期間",
|
"compose_form.poll.duration": "アンケート期間",
|
||||||
"compose_form.poll.option_placeholder": "項目 {number}",
|
"compose_form.poll.option_placeholder": "項目 {number}",
|
||||||
@ -113,6 +113,8 @@
|
|||||||
"compose_form.spoiler.marked": "本文は警告の後ろに隠されます",
|
"compose_form.spoiler.marked": "本文は警告の後ろに隠されます",
|
||||||
"compose_form.spoiler.unmarked": "本文は隠されていません",
|
"compose_form.spoiler.unmarked": "本文は隠されていません",
|
||||||
"compose_form.spoiler_placeholder": "ここに警告を書いてください",
|
"compose_form.spoiler_placeholder": "ここに警告を書いてください",
|
||||||
|
"compose_form.utilBtns_goji": "誤字盛!",
|
||||||
|
"compose_form.utilBtns_harukin": "はるきん焼却",
|
||||||
"confirmation_modal.cancel": "キャンセル",
|
"confirmation_modal.cancel": "キャンセル",
|
||||||
"confirmations.block.block_and_report": "ブロックし通報",
|
"confirmations.block.block_and_report": "ブロックし通報",
|
||||||
"confirmations.block.confirm": "ブロック",
|
"confirmations.block.confirm": "ブロック",
|
||||||
@ -130,6 +132,8 @@
|
|||||||
"confirmations.mute.confirm": "ミュート",
|
"confirmations.mute.confirm": "ミュート",
|
||||||
"confirmations.mute.explanation": "これにより相手の投稿と返信は見えなくなりますが、相手はあなたをフォローし続け投稿を見ることができます。",
|
"confirmations.mute.explanation": "これにより相手の投稿と返信は見えなくなりますが、相手はあなたをフォローし続け投稿を見ることができます。",
|
||||||
"confirmations.mute.message": "本当に{name}さんをミュートしますか?",
|
"confirmations.mute.message": "本当に{name}さんをミュートしますか?",
|
||||||
|
"confirmations.quote.confirm": "引用",
|
||||||
|
"confirmations.quote.message": "今引用すると現在作成中のメッセージが上書きされます。本当に実行しますか?",
|
||||||
"confirmations.redraft.confirm": "削除して下書きに戻す",
|
"confirmations.redraft.confirm": "削除して下書きに戻す",
|
||||||
"confirmations.redraft.message": "本当にこの投稿を削除して下書きに戻しますか? この投稿へのお気に入り登録やブーストは失われ、返信は孤立することになります。",
|
"confirmations.redraft.message": "本当にこの投稿を削除して下書きに戻しますか? この投稿へのお気に入り登録やブーストは失われ、返信は孤立することになります。",
|
||||||
"confirmations.reply.confirm": "返信",
|
"confirmations.reply.confirm": "返信",
|
||||||
@ -200,6 +204,7 @@
|
|||||||
"follow_request.reject": "拒否",
|
"follow_request.reject": "拒否",
|
||||||
"follow_requests.unlocked_explanation": "あなたのアカウントは承認制ではありませんが、{domain} のスタッフはこれらのアカウントからのフォローリクエストの確認が必要であると判断しました。",
|
"follow_requests.unlocked_explanation": "あなたのアカウントは承認制ではありませんが、{domain} のスタッフはこれらのアカウントからのフォローリクエストの確認が必要であると判断しました。",
|
||||||
"generic.saved": "保存しました",
|
"generic.saved": "保存しました",
|
||||||
|
"getting_started.chatroom": "チャットルーム",
|
||||||
"getting_started.developers": "開発",
|
"getting_started.developers": "開発",
|
||||||
"getting_started.directory": "ディレクトリ",
|
"getting_started.directory": "ディレクトリ",
|
||||||
"getting_started.documentation": "ドキュメント",
|
"getting_started.documentation": "ドキュメント",
|
||||||
@ -207,7 +212,8 @@
|
|||||||
"getting_started.invite": "招待",
|
"getting_started.invite": "招待",
|
||||||
"getting_started.open_source_notice": "Mastodonはオープンソースソフトウェアです。誰でもGitHub ( {github} ) から開発に参加したり、問題を報告したりできます。",
|
"getting_started.open_source_notice": "Mastodonはオープンソースソフトウェアです。誰でもGitHub ( {github} ) から開発に参加したり、問題を報告したりできます。",
|
||||||
"getting_started.security": "アカウント設定",
|
"getting_started.security": "アカウント設定",
|
||||||
"getting_started.terms": "プライバシーポリシー",
|
"getting_started.security": "セキュリティ",
|
||||||
|
"getting_started.terms": "利用規約とプライバシーポリシー",
|
||||||
"hashtag.column_header.tag_mode.all": "と {additional}",
|
"hashtag.column_header.tag_mode.all": "と {additional}",
|
||||||
"hashtag.column_header.tag_mode.any": "か {additional}",
|
"hashtag.column_header.tag_mode.any": "か {additional}",
|
||||||
"hashtag.column_header.tag_mode.none": "({additional} を除く)",
|
"hashtag.column_header.tag_mode.none": "({additional} を除く)",
|
||||||
@ -217,6 +223,7 @@
|
|||||||
"hashtag.column_settings.tag_mode.any": "いずれかを含む",
|
"hashtag.column_settings.tag_mode.any": "いずれかを含む",
|
||||||
"hashtag.column_settings.tag_mode.none": "これらを除く",
|
"hashtag.column_settings.tag_mode.none": "これらを除く",
|
||||||
"hashtag.column_settings.tag_toggle": "このカラムに追加のタグを含める",
|
"hashtag.column_settings.tag_toggle": "このカラムに追加のタグを含める",
|
||||||
|
"home.column_settings.advanced": "高度な設定",
|
||||||
"home.column_settings.basic": "基本設定",
|
"home.column_settings.basic": "基本設定",
|
||||||
"home.column_settings.show_reblogs": "ブースト表示",
|
"home.column_settings.show_reblogs": "ブースト表示",
|
||||||
"home.column_settings.show_replies": "返信表示",
|
"home.column_settings.show_replies": "返信表示",
|
||||||
@ -299,7 +306,8 @@
|
|||||||
"navigation_bar.filters": "フィルター設定",
|
"navigation_bar.filters": "フィルター設定",
|
||||||
"navigation_bar.follow_requests": "フォローリクエスト",
|
"navigation_bar.follow_requests": "フォローリクエスト",
|
||||||
"navigation_bar.follows_and_followers": "フォロー・フォロワー",
|
"navigation_bar.follows_and_followers": "フォロー・フォロワー",
|
||||||
"navigation_bar.info": "このサーバーについて",
|
"navigation_bar.generate_qrcode": "プロフィールのQRコードを生成",
|
||||||
|
"navigation_bar.info": "Yづドンについて",
|
||||||
"navigation_bar.keyboard_shortcuts": "ホットキー",
|
"navigation_bar.keyboard_shortcuts": "ホットキー",
|
||||||
"navigation_bar.lists": "リスト",
|
"navigation_bar.lists": "リスト",
|
||||||
"navigation_bar.logout": "ログアウト",
|
"navigation_bar.logout": "ログアウト",
|
||||||
@ -309,8 +317,19 @@
|
|||||||
"navigation_bar.preferences": "ユーザー設定",
|
"navigation_bar.preferences": "ユーザー設定",
|
||||||
"navigation_bar.public_timeline": "連合タイムライン",
|
"navigation_bar.public_timeline": "連合タイムライン",
|
||||||
"navigation_bar.security": "セキュリティ",
|
"navigation_bar.security": "セキュリティ",
|
||||||
|
"navigation_bar.announcements": "運営からのお知らせ",
|
||||||
|
"navigation_bar.trends": "トレンド",
|
||||||
|
"navigation_bar.short.community_timeline": "ローカル",
|
||||||
|
"navigation_bar.short.getting_started": "スタート",
|
||||||
|
"navigation_bar.short.home": "ホーム",
|
||||||
|
"navigation_bar.short.lists": "リスト",
|
||||||
|
"navigation_bar.short.logout": "ログアウト",
|
||||||
|
"navigation_bar.short.notifications": "通知",
|
||||||
|
"navigation_bar.short.preferences": "設定",
|
||||||
|
"navigation_bar.short.public_timeline": "連合",
|
||||||
|
"navigation_bar.short.search": "検索",
|
||||||
"notification.admin.sign_up": "{name} がサインアップしました",
|
"notification.admin.sign_up": "{name} がサインアップしました",
|
||||||
"notification.favourite": "{name}さんがあなたの投稿をお気に入りに登録しました",
|
"notification.favourite": "{name}さんがあなたの投稿に╰( ^o^)╮-=ニ=一=三★しました",
|
||||||
"notification.follow": "{name}さんにフォローされました",
|
"notification.follow": "{name}さんにフォローされました",
|
||||||
"notification.follow_request": "{name} さんがあなたにフォローリクエストしました",
|
"notification.follow_request": "{name} さんがあなたにフォローリクエストしました",
|
||||||
"notification.mention": "{name}さんがあなたに返信しました",
|
"notification.mention": "{name}さんがあなたに返信しました",
|
||||||
@ -374,7 +393,9 @@
|
|||||||
"privacy.public.short": "公開",
|
"privacy.public.short": "公開",
|
||||||
"privacy.unlisted.long": "誰でも閲覧可、公開TLに非表示",
|
"privacy.unlisted.long": "誰でも閲覧可、公開TLに非表示",
|
||||||
"privacy.unlisted.short": "未収載",
|
"privacy.unlisted.short": "未収載",
|
||||||
|
"quote_indicator.cancel": "キャンセル",
|
||||||
"refresh": "更新",
|
"refresh": "更新",
|
||||||
|
"qr_modal.description": "QRコードを読み取って簡単にプロフィールにアクセスしましょう。",
|
||||||
"regeneration_indicator.label": "読み込み中…",
|
"regeneration_indicator.label": "読み込み中…",
|
||||||
"regeneration_indicator.sublabel": "ホームタイムラインは準備中です!",
|
"regeneration_indicator.sublabel": "ホームタイムラインは準備中です!",
|
||||||
"relative_time.days": "{number}日前",
|
"relative_time.days": "{number}日前",
|
||||||
@ -445,6 +466,7 @@
|
|||||||
"status.block": "@{name}さんをブロック",
|
"status.block": "@{name}さんをブロック",
|
||||||
"status.bookmark": "ブックマーク",
|
"status.bookmark": "ブックマーク",
|
||||||
"status.cancel_reblog_private": "ブースト解除",
|
"status.cancel_reblog_private": "ブースト解除",
|
||||||
|
"status.cannot_quote": "この投稿は引用できません",
|
||||||
"status.cannot_reblog": "この投稿はブーストできません",
|
"status.cannot_reblog": "この投稿はブーストできません",
|
||||||
"status.copy": "投稿へのリンクをコピー",
|
"status.copy": "投稿へのリンクをコピー",
|
||||||
"status.delete": "削除",
|
"status.delete": "削除",
|
||||||
@ -464,9 +486,11 @@
|
|||||||
"status.more": "もっと見る",
|
"status.more": "もっと見る",
|
||||||
"status.mute": "@{name}さんをミュート",
|
"status.mute": "@{name}さんをミュート",
|
||||||
"status.mute_conversation": "会話をミュート",
|
"status.mute_conversation": "会話をミュート",
|
||||||
|
"status.muted_quote": "ミュートされた引用",
|
||||||
"status.open": "詳細を表示",
|
"status.open": "詳細を表示",
|
||||||
"status.pin": "プロフィールに固定表示",
|
"status.pin": "プロフィールに固定表示",
|
||||||
"status.pinned": "固定された投稿",
|
"status.pinned": "固定された投稿",
|
||||||
|
"status.quote": "引用",
|
||||||
"status.read_more": "もっと見る",
|
"status.read_more": "もっと見る",
|
||||||
"status.reblog": "ブースト",
|
"status.reblog": "ブースト",
|
||||||
"status.reblog_private": "ブースト",
|
"status.reblog_private": "ブースト",
|
||||||
@ -483,12 +507,15 @@
|
|||||||
"status.show_less_all": "全て隠す",
|
"status.show_less_all": "全て隠す",
|
||||||
"status.show_more": "もっと見る",
|
"status.show_more": "もっと見る",
|
||||||
"status.show_more_all": "全て見る",
|
"status.show_more_all": "全て見る",
|
||||||
|
"status.show_poll": "アンケートを表示",
|
||||||
"status.show_thread": "スレッドを表示",
|
"status.show_thread": "スレッドを表示",
|
||||||
"status.uncached_media_warning": "利用できません",
|
"status.uncached_media_warning": "利用できません",
|
||||||
|
"status.unlisted_quote": "未収載の引用",
|
||||||
"status.unmute_conversation": "会話のミュートを解除",
|
"status.unmute_conversation": "会話のミュートを解除",
|
||||||
"status.unpin": "プロフィールへの固定を解除",
|
"status.unpin": "プロフィールへの固定を解除",
|
||||||
"suggestions.dismiss": "隠す",
|
"suggestions.dismiss": "隠す",
|
||||||
"suggestions.header": "興味あるかもしれません…",
|
"suggestions.header": "興味あるかもしれません…",
|
||||||
|
"tabs_bar.admin_notifications": "Adminからのお知らせ",
|
||||||
"tabs_bar.federated_timeline": "連合",
|
"tabs_bar.federated_timeline": "連合",
|
||||||
"tabs_bar.home": "ホーム",
|
"tabs_bar.home": "ホーム",
|
||||||
"tabs_bar.local_timeline": "ローカル",
|
"tabs_bar.local_timeline": "ローカル",
|
||||||
|
@ -498,7 +498,7 @@
|
|||||||
"time_remaining.hours": "{number, plural, other {# giờ}}",
|
"time_remaining.hours": "{number, plural, other {# giờ}}",
|
||||||
"time_remaining.minutes": "{number, plural, other {# phút}}",
|
"time_remaining.minutes": "{number, plural, other {# phút}}",
|
||||||
"time_remaining.moments": "Còn lại",
|
"time_remaining.moments": "Còn lại",
|
||||||
"time_remaining.seconds": "{number, plural, other {# giây}}",
|
"time_remaining.seconds": "còn {number, plural, other {# giây}}",
|
||||||
"timeline_hint.remote_resource_not_displayed": "{resource} từ máy chủ khác sẽ không hiển thị.",
|
"timeline_hint.remote_resource_not_displayed": "{resource} từ máy chủ khác sẽ không hiển thị.",
|
||||||
"timeline_hint.resources.followers": "Người theo dõi",
|
"timeline_hint.resources.followers": "Người theo dõi",
|
||||||
"timeline_hint.resources.follows": "Đang theo dõi",
|
"timeline_hint.resources.follows": "Đang theo dõi",
|
||||||
|
@ -5,6 +5,8 @@ import {
|
|||||||
COMPOSE_REPLY,
|
COMPOSE_REPLY,
|
||||||
COMPOSE_REPLY_CANCEL,
|
COMPOSE_REPLY_CANCEL,
|
||||||
COMPOSE_DIRECT,
|
COMPOSE_DIRECT,
|
||||||
|
COMPOSE_QUOTE,
|
||||||
|
COMPOSE_QUOTE_CANCEL,
|
||||||
COMPOSE_MENTION,
|
COMPOSE_MENTION,
|
||||||
COMPOSE_SUBMIT_REQUEST,
|
COMPOSE_SUBMIT_REQUEST,
|
||||||
COMPOSE_SUBMIT_SUCCESS,
|
COMPOSE_SUBMIT_SUCCESS,
|
||||||
@ -65,6 +67,8 @@ const initialState = ImmutableMap({
|
|||||||
caretPosition: null,
|
caretPosition: null,
|
||||||
preselectDate: null,
|
preselectDate: null,
|
||||||
in_reply_to: null,
|
in_reply_to: null,
|
||||||
|
quote_from: null,
|
||||||
|
quote_from_url: null,
|
||||||
is_composing: false,
|
is_composing: false,
|
||||||
is_submitting: false,
|
is_submitting: false,
|
||||||
is_changing_upload: false,
|
is_changing_upload: false,
|
||||||
@ -116,6 +120,7 @@ function clearAll(state) {
|
|||||||
map.set('is_submitting', false);
|
map.set('is_submitting', false);
|
||||||
map.set('is_changing_upload', false);
|
map.set('is_changing_upload', false);
|
||||||
map.set('in_reply_to', null);
|
map.set('in_reply_to', null);
|
||||||
|
map.set('quote_from', null);
|
||||||
map.set('privacy', state.get('default_privacy'));
|
map.set('privacy', state.get('default_privacy'));
|
||||||
map.set('sensitive', false);
|
map.set('sensitive', false);
|
||||||
map.update('media_attachments', list => list.clear());
|
map.update('media_attachments', list => list.clear());
|
||||||
@ -272,6 +277,17 @@ const updateSuggestionTags = (state, token) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const rejectQuoteAltText = html => {
|
||||||
|
const fragment = domParser.parseFromString(html, 'text/html').documentElement;
|
||||||
|
|
||||||
|
const quote_inline = fragment.querySelector('span.quote-inline');
|
||||||
|
if (quote_inline) {
|
||||||
|
quote_inline.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
return fragment.innerHTML;
|
||||||
|
};
|
||||||
|
|
||||||
export default function compose(state = initialState, action) {
|
export default function compose(state = initialState, action) {
|
||||||
switch(action.type) {
|
switch(action.type) {
|
||||||
case STORE_HYDRATE:
|
case STORE_HYDRATE:
|
||||||
@ -318,6 +334,8 @@ export default function compose(state = initialState, action) {
|
|||||||
return state.withMutations(map => {
|
return state.withMutations(map => {
|
||||||
map.set('id', null);
|
map.set('id', null);
|
||||||
map.set('in_reply_to', action.status.get('id'));
|
map.set('in_reply_to', action.status.get('id'));
|
||||||
|
map.set('quote_from', null);
|
||||||
|
map.set('quote_from_url', null);
|
||||||
map.set('text', statusToTextMentions(state, action.status));
|
map.set('text', statusToTextMentions(state, action.status));
|
||||||
map.set('privacy', privacyPreference(action.status.get('visibility'), state.get('default_privacy')));
|
map.set('privacy', privacyPreference(action.status.get('visibility'), state.get('default_privacy')));
|
||||||
map.set('focusDate', new Date());
|
map.set('focusDate', new Date());
|
||||||
@ -333,6 +351,39 @@ export default function compose(state = initialState, action) {
|
|||||||
map.set('spoiler_text', '');
|
map.set('spoiler_text', '');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
case COMPOSE_QUOTE:
|
||||||
|
return state.withMutations(map => {
|
||||||
|
map.set('in_reply_to', null);
|
||||||
|
map.set('quote_from', action.status.get('id'));
|
||||||
|
map.set('quote_from_url', action.status.get('url'));
|
||||||
|
map.set('text', '');
|
||||||
|
map.set('privacy', privacyPreference(action.status.get('visibility'), state.get('default_privacy')));
|
||||||
|
map.set('focusDate', new Date());
|
||||||
|
map.set('preselectDate', new Date());
|
||||||
|
map.set('idempotencyKey', uuid());
|
||||||
|
|
||||||
|
if (action.status.get('spoiler_text').length > 0) {
|
||||||
|
map.set('spoiler', true);
|
||||||
|
map.set('spoiler_text', action.status.get('spoiler_text'));
|
||||||
|
} else {
|
||||||
|
map.set('spoiler', false);
|
||||||
|
map.set('spoiler_text', '');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
case COMPOSE_REPLY_CANCEL:
|
||||||
|
case COMPOSE_QUOTE_CANCEL:
|
||||||
|
case COMPOSE_RESET:
|
||||||
|
return state.withMutations(map => {
|
||||||
|
map.set('in_reply_to', null);
|
||||||
|
map.set('quote_from', null);
|
||||||
|
map.set('quote_from_url', null);
|
||||||
|
map.set('text', '');
|
||||||
|
map.set('spoiler', false);
|
||||||
|
map.set('spoiler_text', '');
|
||||||
|
map.set('privacy', state.get('default_privacy'));
|
||||||
|
map.set('poll', null);
|
||||||
|
map.set('idempotencyKey', uuid());
|
||||||
|
});
|
||||||
case COMPOSE_SUBMIT_REQUEST:
|
case COMPOSE_SUBMIT_REQUEST:
|
||||||
return state.set('is_submitting', true);
|
return state.set('is_submitting', true);
|
||||||
case COMPOSE_UPLOAD_CHANGE_REQUEST:
|
case COMPOSE_UPLOAD_CHANGE_REQUEST:
|
||||||
@ -432,8 +483,10 @@ export default function compose(state = initialState, action) {
|
|||||||
}));
|
}));
|
||||||
case REDRAFT:
|
case REDRAFT:
|
||||||
return state.withMutations(map => {
|
return state.withMutations(map => {
|
||||||
map.set('text', action.raw_text || unescapeHTML(expandMentions(action.status)));
|
map.set('text', action.raw_text || unescapeHTML(rejectQuoteAltText(expandMentions(action.status))));
|
||||||
map.set('in_reply_to', action.status.get('in_reply_to_id'));
|
map.set('in_reply_to', action.status.get('in_reply_to_id'));
|
||||||
|
map.set('quote_from', action.status.getIn(['quote', 'id']));
|
||||||
|
map.set('quote_from_url', action.status.getIn(['quote', 'url']));
|
||||||
map.set('privacy', action.status.get('visibility'));
|
map.set('privacy', action.status.get('visibility'));
|
||||||
map.set('media_attachments', action.status.get('media_attachments'));
|
map.set('media_attachments', action.status.get('media_attachments'));
|
||||||
map.set('focusDate', new Date());
|
map.set('focusDate', new Date());
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
COMPOSE_MENTION,
|
COMPOSE_MENTION,
|
||||||
COMPOSE_REPLY,
|
COMPOSE_REPLY,
|
||||||
COMPOSE_DIRECT,
|
COMPOSE_DIRECT,
|
||||||
|
COMPOSE_QUOTE,
|
||||||
} from '../actions/compose';
|
} from '../actions/compose';
|
||||||
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable';
|
||||||
|
|
||||||
@ -39,6 +40,7 @@ export default function search(state = initialState, action) {
|
|||||||
case COMPOSE_REPLY:
|
case COMPOSE_REPLY:
|
||||||
case COMPOSE_MENTION:
|
case COMPOSE_MENTION:
|
||||||
case COMPOSE_DIRECT:
|
case COMPOSE_DIRECT:
|
||||||
|
case COMPOSE_QUOTE:
|
||||||
return state.set('hidden', true);
|
return state.set('hidden', true);
|
||||||
case SEARCH_FETCH_REQUEST:
|
case SEARCH_FETCH_REQUEST:
|
||||||
return state.withMutations(map => {
|
return state.withMutations(map => {
|
||||||
|
@ -13,6 +13,8 @@ import {
|
|||||||
STATUS_REVEAL,
|
STATUS_REVEAL,
|
||||||
STATUS_HIDE,
|
STATUS_HIDE,
|
||||||
STATUS_COLLAPSE,
|
STATUS_COLLAPSE,
|
||||||
|
QUOTE_REVEAL,
|
||||||
|
QUOTE_HIDE,
|
||||||
} from '../actions/statuses';
|
} from '../actions/statuses';
|
||||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||||
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
|
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
|
||||||
@ -75,6 +77,14 @@ export default function statuses(state = initialState, action) {
|
|||||||
});
|
});
|
||||||
case STATUS_COLLAPSE:
|
case STATUS_COLLAPSE:
|
||||||
return state.setIn([action.id, 'collapsed'], action.isCollapsed);
|
return state.setIn([action.id, 'collapsed'], action.isCollapsed);
|
||||||
|
case QUOTE_REVEAL:
|
||||||
|
return state.withMutations(map => {
|
||||||
|
action.ids.forEach(id => map.setIn([id, 'quote_hidden'], false));
|
||||||
|
});
|
||||||
|
case QUOTE_HIDE:
|
||||||
|
return state.withMutations(map => {
|
||||||
|
action.ids.forEach(id => map.setIn([id, 'quote_hidden'], true));
|
||||||
|
});
|
||||||
case TIMELINE_DELETE:
|
case TIMELINE_DELETE:
|
||||||
return deleteStatus(state, action.id, action.references);
|
return deleteStatus(state, action.id, action.references);
|
||||||
default:
|
default:
|
||||||
|
@ -88,22 +88,60 @@ export const makeGetStatus = () => {
|
|||||||
[
|
[
|
||||||
(state, { id }) => state.getIn(['statuses', id]),
|
(state, { id }) => state.getIn(['statuses', id]),
|
||||||
(state, { id }) => state.getIn(['statuses', state.getIn(['statuses', id, 'reblog'])]),
|
(state, { id }) => state.getIn(['statuses', state.getIn(['statuses', id, 'reblog'])]),
|
||||||
|
(state, { id }) => state.getIn(['statuses', state.getIn(['statuses', id, 'quote_id'])]),
|
||||||
(state, { id }) => state.getIn(['accounts', state.getIn(['statuses', id, 'account'])]),
|
(state, { id }) => state.getIn(['accounts', state.getIn(['statuses', id, 'account'])]),
|
||||||
(state, { id }) => state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'account'])]),
|
(state, { id }) => state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'account'])]),
|
||||||
|
(state, { id }) => state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'quote_id']), 'account'])]),
|
||||||
|
(state, { id }) => state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'quote', 'account'])]),
|
||||||
|
(state, { id }) => state.getIn(['relationships', state.getIn(['statuses', id, 'account'])]),
|
||||||
|
(state, { id }) => state.getIn(['relationships', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'account'])]),
|
||||||
|
(state, { id }) => state.getIn(['relationships', state.getIn(['statuses', state.getIn(['statuses', id, 'quote_id']), 'account'])]),
|
||||||
|
(state, { id }) => state.getIn(['relationships', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'quote', 'account'])]),
|
||||||
|
(state, { id }) => state.getIn(['accounts', state.getIn(['accounts', state.getIn(['statuses', id, 'account']), 'moved'])]),
|
||||||
|
(state, { id }) => state.getIn(['accounts', state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'account']), 'moved'])]),
|
||||||
|
(state, { id }) => state.getIn(['accounts', state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'quote_id']), 'account']), 'moved'])]),
|
||||||
|
(state, { id }) => state.getIn(['accounts', state.getIn(['accounts', state.getIn(['statuses', state.getIn(['statuses', id, 'reblog']), 'quote', 'account']), 'moved'])]),
|
||||||
getFiltersRegex,
|
getFiltersRegex,
|
||||||
],
|
],
|
||||||
|
|
||||||
(statusBase, statusReblog, accountBase, accountReblog, filtersRegex) => {
|
(statusBase, statusReblog, statusQuote, accountBase, accountReblog, accountQuote, accountReblogQuote, relationship, reblogRelationship, quoteRelationship, reblogQuoteRelationship, moved, reblogMoved, quoteMoved, reblogQuoteMoved, filtersRegex) => {
|
||||||
if (!statusBase) {
|
if (!statusBase) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
accountBase = accountBase.withMutations(map => {
|
||||||
|
map.set('relationship', relationship);
|
||||||
|
map.set('moved', moved);
|
||||||
|
});
|
||||||
|
|
||||||
if (statusReblog) {
|
if (statusReblog) {
|
||||||
|
accountReblog = accountReblog.withMutations(map => {
|
||||||
|
map.set('relationship', reblogRelationship);
|
||||||
|
map.set('moved', reblogMoved);
|
||||||
|
});
|
||||||
statusReblog = statusReblog.set('account', accountReblog);
|
statusReblog = statusReblog.set('account', accountReblog);
|
||||||
} else {
|
} else {
|
||||||
statusReblog = null;
|
statusReblog = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (statusQuote) {
|
||||||
|
accountQuote = accountQuote.withMutations(map => {
|
||||||
|
map.set('relationship', quoteRelationship);
|
||||||
|
map.set('moved', quoteMoved);
|
||||||
|
});
|
||||||
|
statusQuote = statusQuote.set('account', accountQuote);
|
||||||
|
} else {
|
||||||
|
statusQuote = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusReblog && accountReblogQuote) {
|
||||||
|
accountReblogQuote = accountReblog.withMutations(map => {
|
||||||
|
map.set('relationship', reblogQuoteRelationship);
|
||||||
|
map.set('moved', reblogQuoteMoved);
|
||||||
|
});
|
||||||
|
statusReblog = statusReblog.setIn(['quote', 'account'], accountReblogQuote);
|
||||||
|
}
|
||||||
|
|
||||||
const dropRegex = (accountReblog || accountBase).get('id') !== me && filtersRegex[0];
|
const dropRegex = (accountReblog || accountBase).get('id') !== me && filtersRegex[0];
|
||||||
if (dropRegex && dropRegex.test(statusBase.get('reblog') ? statusReblog.get('search_index') : statusBase.get('search_index'))) {
|
if (dropRegex && dropRegex.test(statusBase.get('reblog') ? statusReblog.get('search_index') : statusBase.get('search_index'))) {
|
||||||
return null;
|
return null;
|
||||||
@ -114,6 +152,7 @@ export const makeGetStatus = () => {
|
|||||||
|
|
||||||
return statusBase.withMutations(map => {
|
return statusBase.withMutations(map => {
|
||||||
map.set('reblog', statusReblog);
|
map.set('reblog', statusReblog);
|
||||||
|
map.set('quote', statusQuote);
|
||||||
map.set('account', accountBase);
|
map.set('account', accountBase);
|
||||||
map.set('filtered', filtered);
|
map.set('filtered', filtered);
|
||||||
});
|
});
|
||||||
|
3
app/javascript/mastodon/utils/uniq.js
Normal file
3
app/javascript/mastodon/utils/uniq.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const uniq = array => {
|
||||||
|
return array.filter((x, i, self) => self.indexOf(x) === i)
|
||||||
|
};
|
@ -289,6 +289,31 @@ function main() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
delegate(document, '.quote-status', 'click', ({ target }) => {
|
||||||
|
if (target.closest('.status__content__spoiler-link') ||
|
||||||
|
target.closest('.media-gallery') ||
|
||||||
|
target.closest('.video-player') ||
|
||||||
|
target.closest('.audio-player')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let url = target.closest('.quote-status').getAttribute('dataurl');
|
||||||
|
if (target.closest('.status__display-name')) {
|
||||||
|
url = target.closest('.status__display-name').getAttribute('href');
|
||||||
|
} else if (target.closest('.detailed-status__display-name')) {
|
||||||
|
url = target.closest('.detailed-status__display-name').getAttribute('href');
|
||||||
|
} else if (target.closest('.status-card')) {
|
||||||
|
url = target.closest('.status-card').getAttribute('href');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.location.hostname === url.split('/')[2].split(':')[0]) {
|
||||||
|
window.location.href = url;
|
||||||
|
} else {
|
||||||
|
window.open(url, 'blank');
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loadPolyfills()
|
loadPolyfills()
|
||||||
|
@ -26,3 +26,6 @@
|
|||||||
@import 'mastodon/dashboard';
|
@import 'mastodon/dashboard';
|
||||||
@import 'mastodon/rtl';
|
@import 'mastodon/rtl';
|
||||||
@import 'mastodon/accessibility';
|
@import 'mastodon/accessibility';
|
||||||
|
|
||||||
|
@import 'markdown';
|
||||||
|
@import 'astarte';
|
||||||
|
59
app/javascript/styles/astarte.scss
Normal file
59
app/javascript/styles/astarte.scss
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
|
||||||
|
.announcements {
|
||||||
|
padding: 0 10px;
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
padding: 10px;
|
||||||
|
color: #282c37;
|
||||||
|
background: darken($white, 10%);
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
& + li {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcements__admin {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
p {
|
||||||
|
padding: 0 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcements__icon {
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
margin: -5px 5px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcements__body {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
p {
|
||||||
|
padding: 0 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
display: inline-block;
|
||||||
|
float: right;
|
||||||
|
clear: both;
|
||||||
|
color: #282c37;
|
||||||
|
background: darken($white, 5%);
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 1px 10px 0;
|
||||||
|
border: solid 1px #282c37;
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,5 @@
|
|||||||
@import 'contrast/variables';
|
@import 'contrast/variables';
|
||||||
@import 'application';
|
@import 'application';
|
||||||
@import 'contrast/diff';
|
@import 'contrast/diff';
|
||||||
|
|
||||||
|
@import 'markdown';
|
||||||
|
266
app/javascript/styles/markdown.scss
Normal file
266
app/javascript/styles/markdown.scss
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
.status__content {
|
||||||
|
|
||||||
|
font-family: inherit;
|
||||||
|
|
||||||
|
h1{
|
||||||
|
color: #ec840d;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.6em;
|
||||||
|
padding: 0.5em;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1.3;
|
||||||
|
background: #dbebf8;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-radius: 25px 25px 25px 25px;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: solid 3px #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2{
|
||||||
|
color: #ec840d;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.5em;
|
||||||
|
padding: 0.5em;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1.3;
|
||||||
|
background: #dbebf8;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-radius: 25px 25px 25px 25px;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: solid 3px #fffb00;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3{
|
||||||
|
color: #ec840d;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.4em;
|
||||||
|
padding: 0.5em;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1.3;
|
||||||
|
background: #dbebf8;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-radius: 25px 25px 25px 25px;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: solid 3px #2bff00;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4{
|
||||||
|
color: #ec840d;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.3em;
|
||||||
|
padding: 0.5em;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1.3;
|
||||||
|
background: #dbebf8;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-radius: 25px 25px 25px 25px;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: solid 3px #00ffea;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5{
|
||||||
|
color: #ec840d;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2em;
|
||||||
|
padding: 0.5em;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1.3;
|
||||||
|
background: #dbebf8;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-radius: 25px 25px 25px 25px;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: solid 3px #0004ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6{
|
||||||
|
color: #ec840d;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.1em;
|
||||||
|
padding: 0.5em;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 1.3;
|
||||||
|
background: #dbebf8;
|
||||||
|
vertical-align: middle;
|
||||||
|
border-radius: 25px 25px 25px 25px;
|
||||||
|
text-align: center;
|
||||||
|
border-bottom: solid 3px #7700ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
em{
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong{
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
code{
|
||||||
|
display: block;
|
||||||
|
border-left: 2px solid;
|
||||||
|
border-color: #079903;
|
||||||
|
color: $white;
|
||||||
|
padding-left: 10px;
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
margin-left: 5px;
|
||||||
|
background-color: #000000;
|
||||||
|
|
||||||
|
.positive{
|
||||||
|
color: #5bda57;
|
||||||
|
}
|
||||||
|
|
||||||
|
.negative{
|
||||||
|
color: #ff4949;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rust-fanc{
|
||||||
|
color: #ba7eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ruby-func{
|
||||||
|
color: #24a8e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rust-macro{
|
||||||
|
color: #d2ff6a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contents{
|
||||||
|
color: #ff9925;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pre{
|
||||||
|
display: inline-block;
|
||||||
|
font-family: 'Noto Sans Japanese', sans-serif;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
p ~ blockquote {
|
||||||
|
margin-top: -8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote{
|
||||||
|
padding-left: 8px;
|
||||||
|
margin-top: 0.5em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
color: $primary-text-color;
|
||||||
|
background-color: $ui-base-color;
|
||||||
|
display: block;
|
||||||
|
border-left: 4px solid $classic-highlight-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.md-contents {
|
||||||
|
border: double 4px #21b384;
|
||||||
|
padding: 0.5em 1em 0.5em 2.3em;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
ul li.md-contents {
|
||||||
|
line-height: 1.5;
|
||||||
|
padding: 0.2em 0;
|
||||||
|
list-style-type: none!important;
|
||||||
|
}
|
||||||
|
ul li.md-contents:before {
|
||||||
|
font-family: FontAwesome;
|
||||||
|
content: "\f0a4";
|
||||||
|
position: absolute;
|
||||||
|
left : 1em;
|
||||||
|
color: #21b384;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol.md-contents {
|
||||||
|
border: double 4px #ff954f;
|
||||||
|
padding: 0.5em 1em 0.5em 1em;
|
||||||
|
position: relative;
|
||||||
|
list-style: inside decimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol li.md-contents {
|
||||||
|
line-height: 1.5;
|
||||||
|
padding: 0.2em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border-width: 2px 0px 0px 0px;
|
||||||
|
border-style: dashed;
|
||||||
|
border-color: #ff7676;
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p>a>img{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
a>img{
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
}
|
||||||
|
|
||||||
|
p>a{
|
||||||
|
color: #1FBFF9;
|
||||||
|
}
|
||||||
|
|
||||||
|
a{
|
||||||
|
color: #1FBFF9;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup{
|
||||||
|
font-size: 75.5%;
|
||||||
|
vertical-align: top;
|
||||||
|
position: relative;
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub{
|
||||||
|
font-size: 75.5%;
|
||||||
|
vertical-align: bottom;
|
||||||
|
position: relative;
|
||||||
|
top: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
small{
|
||||||
|
font-size: 50.0%;
|
||||||
|
vertical-align: bottom;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
margin-bottom: 0.5em;
|
||||||
|
color: $classic-highlight-color;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
border-spacing: 0;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr{
|
||||||
|
background-color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th, table td{
|
||||||
|
padding: 6px 13px;
|
||||||
|
border: 1px solid $classic-highlight-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th{
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
table thead tr{
|
||||||
|
background-color: $black;
|
||||||
|
}
|
||||||
|
|
||||||
|
td, th{
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.img_FTL {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
2
app/javascript/styles/mastodon-auto.scss
Normal file
2
app/javascript/styles/mastodon-auto.scss
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
@use 'application';
|
||||||
|
@media (prefers-color-scheme: light) { @import 'mastodon-light'; }
|
@ -1,3 +1,5 @@
|
|||||||
@import 'mastodon-light/variables';
|
@import 'mastodon-light/variables';
|
||||||
@import 'application';
|
@import 'application';
|
||||||
@import 'mastodon-light/diff';
|
@import 'mastodon-light/diff';
|
||||||
|
|
||||||
|
@import 'markdown';
|
||||||
|
2
app/javascript/styles/mastodon-material-auto.scss
Normal file
2
app/javascript/styles/mastodon-material-auto.scss
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
@use 'mastodon-material-dark';
|
||||||
|
@media (prefers-color-scheme: light) { @import 'mastodon-material-light'; }
|
2
app/javascript/styles/mastodon-material-dark.scss
Normal file
2
app/javascript/styles/mastodon-material-dark.scss
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
@import 'application';
|
||||||
|
@import 'mastodon-material/profiles/mastodon-material-dark/loader';
|
2
app/javascript/styles/mastodon-material-light.scss
Normal file
2
app/javascript/styles/mastodon-material-light.scss
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
@import 'application';
|
||||||
|
@import 'mastodon-material/profiles/mastodon-material-light/loader';
|
149
app/javascript/styles/mastodon-material/color/black.scss
Normal file
149
app/javascript/styles/mastodon-material/color/black.scss
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
|
||||||
|
$color-scheme: dark;
|
||||||
|
|
||||||
|
// Base color
|
||||||
|
$primary-color: #4285f4;
|
||||||
|
$secondary-color: #db4437;
|
||||||
|
$error-color: #B00020;
|
||||||
|
$verified-color: #4caf50;
|
||||||
|
|
||||||
|
// Text color
|
||||||
|
$primary-text-color: #ffffff;
|
||||||
|
$secondary-text-color: #9aa0a6;
|
||||||
|
$ui-text-color: #e8eaed;
|
||||||
|
$inverted-text-color: #000000;
|
||||||
|
$section-text-color: $primary-color;
|
||||||
|
$info-text-color: #9aa0a6;
|
||||||
|
$tips-text-color: #c0c0c0;
|
||||||
|
$disabled-text-color: rgba(0,0,0,.54);
|
||||||
|
$link-text-color: #4285f4;
|
||||||
|
$menu-text-color: $ui-text-color;
|
||||||
|
$top-bar-text-color: $ui-text-color;
|
||||||
|
$search-bar-text-color: $inverted-text-color;
|
||||||
|
$contained-button-text-color: #ffffff;
|
||||||
|
$primary-text-on-shadow-color: #ffffff;
|
||||||
|
$text-field-color: $ui-text-color;
|
||||||
|
|
||||||
|
// Background-color
|
||||||
|
$bg-color: #000000;
|
||||||
|
$menu-bg-color: #121212;
|
||||||
|
$menu-bg-hover-color: lighten($menu-bg-color, 6%);
|
||||||
|
$menu-bg-active-color: lighten($menu-bg-color, 10%);
|
||||||
|
$menu-bg-active-hover-color: lighten($menu-bg-color, 16%);
|
||||||
|
$card-bg-color: #121212;
|
||||||
|
$card-bg-hover-color: lighten($card-bg-color, 6%);
|
||||||
|
$succeeded-card-bg-color: lighten($verified-color, 36%);
|
||||||
|
$list-bg-color: #000000;
|
||||||
|
$list-bg-hover-color: lighten($list-bg-color, 6%);
|
||||||
|
$list-bg-active-color: lighten($list-bg-color, 10%);
|
||||||
|
$list-bg-active-hover-color: lighten($list-bg-color, 16%);
|
||||||
|
$list-bg-inactive-color: lighten($list-bg-color, 10%);
|
||||||
|
$text-field-bg-color: $card-bg-color;
|
||||||
|
$dropdown-field-bg-color: lighten($bg-color, 6%);
|
||||||
|
$dropdown-field-bg-hover-color: lighten($dropdown-field-bg-color, 6%);
|
||||||
|
$dropdown-field-bg-active-color: lighten($dropdown-field-bg-color, 10%);
|
||||||
|
$verified-bg-color: darken($verified-color, 20%);
|
||||||
|
$unread-bg-color: transparentize($primary-color, 0.8);
|
||||||
|
|
||||||
|
// Chip color
|
||||||
|
$contained-chip-color: #1e1e1e;
|
||||||
|
$chip-selected-text-color: $primary-color;
|
||||||
|
$contained-chip-hover-color: lighten($contained-chip-color, 6%);
|
||||||
|
$contained-chip-selected-color: transparentize($chip-selected-text-color, 0.7);
|
||||||
|
$outlined-chip-color: #121212;
|
||||||
|
$outlined-chip-hover-color: lighten($outlined-chip-color, 6%);
|
||||||
|
$outlined-chip-selected-color: transparentize($chip-selected-text-color, 0.8);
|
||||||
|
$outlined-chip-selected-border-color: transparentize($chip-selected-text-color, 0.7);
|
||||||
|
|
||||||
|
// Relationship tag color
|
||||||
|
$relationship-tag-color: transparentize(#2e2e2e, 0.2);
|
||||||
|
|
||||||
|
// Badge color
|
||||||
|
$badge-color: $primary-color;
|
||||||
|
//$badge-color: $secondary-color;
|
||||||
|
|
||||||
|
// Icon color
|
||||||
|
$icon-color: #e2e2e3;
|
||||||
|
$icon-hover-color: #ffffff;
|
||||||
|
$icon-bg-hover-color: transparentize(#ffffff, 0.8);
|
||||||
|
$icon-bg-active-color: transparentize(#ffffff, 0.7);
|
||||||
|
$disabled-icon-color: darken($icon-color, 16%);
|
||||||
|
$top-bar-icon-color: $icon-color;
|
||||||
|
$top-bar-icon-hover-color: $icon-hover-color;
|
||||||
|
$top-bar-icon-active-color: $primary-color;
|
||||||
|
$top-bar-unread-icon-color: $secondary-color;
|
||||||
|
$media-icon-color: transparentize(#ffffff, 0.4);
|
||||||
|
$media-icon-hover-color: transparentize(#ffffff, 0.2);
|
||||||
|
$media-icon-bg-color: transparentize(#000000, 0.5);
|
||||||
|
$media-icon-bg-hover-color: transparentize(#000000, 0.8);
|
||||||
|
|
||||||
|
// Control color
|
||||||
|
$control-border-color: $icon-color;
|
||||||
|
$control-border-active-color: $primary-color;
|
||||||
|
$control-border-hover-color: transparentize($control-border-active-color, 0.8);
|
||||||
|
|
||||||
|
// Button color
|
||||||
|
$icon-button-color: $icon-color;
|
||||||
|
$icon-button-hover-color: $icon-hover-color;
|
||||||
|
$icon-button-active-color: $primary-color;
|
||||||
|
$icon-button-active-hover-color: lighten($icon-button-active-color, 10%);
|
||||||
|
$contained-button-color: $primary-color;
|
||||||
|
$contained-button-hover-color: lighten($contained-button-color, 6%);
|
||||||
|
$contained-button-active-color: lighten($contained-button-color, 10%);
|
||||||
|
$outlined-button-color: $primary-color;
|
||||||
|
$outlined-button-hover-color: transparentize($outlined-button-color, 0.8);
|
||||||
|
$outlined-button-active-color: transparentize($outlined-button-color, 0.7);
|
||||||
|
$text-button-color: $primary-color;
|
||||||
|
$text-button-hover-color: transparentize($text-button-color, 0.8);
|
||||||
|
$text-button-focus-color: transparentize($text-button-color, 0.7);
|
||||||
|
$disabled-button-color: #cccccc;
|
||||||
|
$floating-action-button-color: $primary-color;
|
||||||
|
$floating-action-button-hover-color: lighten($floating-action-button-color, 6%);
|
||||||
|
$floating-action-button-active-color: lighten($floating-action-button-color, 10%);
|
||||||
|
$floating-action-button-icon-color: #ffffff;
|
||||||
|
|
||||||
|
// Toggle color
|
||||||
|
$toggle-thumb-color: #ffffff;
|
||||||
|
$toggle-track-color: darken($toggle-thumb-color, 18%);
|
||||||
|
$toggle-thumb-active-color: #1a73e8;
|
||||||
|
$toggle-track-active-color: lighten($toggle-thumb-active-color, 18%);
|
||||||
|
|
||||||
|
// Border color
|
||||||
|
$border-color: #1e1e1e;
|
||||||
|
$border-hover-color: lighten($border-color, 30%);
|
||||||
|
$border-active-color: $primary-color;
|
||||||
|
|
||||||
|
// Scroll bar color
|
||||||
|
$scroll-bar-thumb-color: lighten($bg-color, 20%);
|
||||||
|
$scroll-bar-thumb-hover-color: lighten($scroll-bar-thumb-color, 10%);
|
||||||
|
$scroll-bar-thumb-active-color: lighten($scroll-bar-thumb-color, 18%);
|
||||||
|
|
||||||
|
// App bar color
|
||||||
|
$top-bar-color: #1e1e1e;
|
||||||
|
$search-bar-color: lighten($top-bar-color, 6%);
|
||||||
|
$search-bar-focus-color: #ffffff;
|
||||||
|
|
||||||
|
// Tab color
|
||||||
|
$tab-item-color: transparentize(#ffffff, 0.5);
|
||||||
|
$tab-item-active-color: $primary-color;
|
||||||
|
$tab-bg-color: $top-bar-color;
|
||||||
|
$tab-bg-hover-color: transparentize($tab-item-active-color, 0.9);
|
||||||
|
$tab-bg-focus-color: transparentize($tab-item-active-color, 0.8);
|
||||||
|
|
||||||
|
// Media indicator color
|
||||||
|
$media-page-indicator-color: #9e9e9e;
|
||||||
|
$media-page-indicator-active-color: #e6e6e6;
|
||||||
|
|
||||||
|
// Progress indicator color
|
||||||
|
$progress-indicator-color: $primary-color;
|
||||||
|
$progress-indicator-track-color: lighten($progress-indicator-color, 30%);
|
||||||
|
$loading-indicator-color: $primary-color;
|
||||||
|
|
||||||
|
// Contents color in read status
|
||||||
|
$read-primary-text-color: transparentize($primary-text-color, 0.3);
|
||||||
|
$read-secondary-text-color: transparentize($secondary-text-color, 0.3);
|
||||||
|
$read-ui-text-color: transparentize($ui-text-color, 0.3);
|
||||||
|
$read-poll-bar-leading-color: transparentize($progress-indicator-color, 0.3);
|
||||||
|
$read-poll-bar-color: transparentize($progress-indicator-track-color, 0.3);
|
149
app/javascript/styles/mastodon-material/color/mastodon-dark.scss
Normal file
149
app/javascript/styles/mastodon-material/color/mastodon-dark.scss
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
|
||||||
|
$color-scheme: dark;
|
||||||
|
|
||||||
|
// Base color
|
||||||
|
$primary-color: #2b90d9;
|
||||||
|
$secondary-color: #2b90d9;
|
||||||
|
$error-color: #B00020;
|
||||||
|
$verified-color: #4caf50;
|
||||||
|
|
||||||
|
// Text color
|
||||||
|
$primary-text-color: #ffffff;
|
||||||
|
$secondary-text-color: #9baec8;
|
||||||
|
$ui-text-color: #fff;
|
||||||
|
$inverted-text-color: #000000;
|
||||||
|
$section-text-color: $primary-color;
|
||||||
|
$info-text-color: #606984;
|
||||||
|
$tips-text-color: #6d7889;
|
||||||
|
$disabled-text-color: rgba(0,0,0,.54);
|
||||||
|
$link-text-color: #4ea2df;
|
||||||
|
$menu-text-color: #282c37;
|
||||||
|
$top-bar-text-color: $ui-text-color;
|
||||||
|
$search-bar-text-color: $inverted-text-color;
|
||||||
|
$contained-button-text-color: #ffffff;
|
||||||
|
$primary-text-on-shadow-color: #ffffff;
|
||||||
|
$text-field-color: $ui-text-color;
|
||||||
|
|
||||||
|
// Background-color
|
||||||
|
$bg-color: #191b22;
|
||||||
|
$menu-bg-color: #d9e1e8;
|
||||||
|
$menu-bg-hover-color: lighten($menu-bg-color, 6%);
|
||||||
|
$menu-bg-active-color: lighten($menu-bg-color, 10%);
|
||||||
|
$menu-bg-active-hover-color: lighten($menu-bg-color, 16%);
|
||||||
|
$card-bg-color: #313543;
|
||||||
|
$card-bg-hover-color: lighten($card-bg-color, 6%);
|
||||||
|
$succeeded-card-bg-color: lighten($verified-color, 36%);
|
||||||
|
$list-bg-color: #282c37;
|
||||||
|
$list-bg-hover-color: lighten($list-bg-color, 6%);
|
||||||
|
$list-bg-active-color: lighten($list-bg-color, 10%);
|
||||||
|
$list-bg-active-hover-color: lighten($list-bg-color, 16%);
|
||||||
|
$list-bg-inactive-color: lighten($list-bg-color, 10%);
|
||||||
|
$text-field-bg-color: $card-bg-color;
|
||||||
|
$dropdown-field-bg-color: lighten($bg-color, 6%);
|
||||||
|
$dropdown-field-bg-hover-color: lighten($dropdown-field-bg-color, 6%);
|
||||||
|
$dropdown-field-bg-active-color: lighten($dropdown-field-bg-color, 10%);
|
||||||
|
$verified-bg-color: darken($verified-color, 20%);
|
||||||
|
$unread-bg-color: transparentize($primary-color, 0.8);
|
||||||
|
|
||||||
|
// Chip color
|
||||||
|
$contained-chip-color: #42485a;
|
||||||
|
$chip-selected-text-color: $primary-color;
|
||||||
|
$contained-chip-hover-color: lighten($contained-chip-color, 6%);
|
||||||
|
$contained-chip-selected-color: transparentize($chip-selected-text-color, 0.7);
|
||||||
|
$outlined-chip-color: #393f4f;
|
||||||
|
$outlined-chip-hover-color: lighten($outlined-chip-color, 6%);
|
||||||
|
$outlined-chip-selected-color: transparentize($chip-selected-text-color, 0.8);
|
||||||
|
$outlined-chip-selected-border-color: transparentize($chip-selected-text-color, 0.7);
|
||||||
|
|
||||||
|
// Relationship tag color
|
||||||
|
$relationship-tag-color: transparentize(#42485a, 0.2);
|
||||||
|
|
||||||
|
// Badge color
|
||||||
|
$badge-color: $primary-color;
|
||||||
|
//$badge-color: $secondary-color;
|
||||||
|
|
||||||
|
// Icon color
|
||||||
|
$icon-color: #9baec8;
|
||||||
|
$icon-hover-color: lighten($icon-color, 30%);
|
||||||
|
$icon-bg-hover-color: lighten($bg-color, 14%);
|
||||||
|
$icon-bg-active-color: lighten($bg-color, 18%);
|
||||||
|
$disabled-icon-color: lighten($icon-color, 16%);
|
||||||
|
$top-bar-icon-color: #fff;
|
||||||
|
$top-bar-icon-hover-color: $icon-hover-color;
|
||||||
|
$top-bar-icon-active-color: $icon-hover-color;
|
||||||
|
$top-bar-unread-icon-color: $secondary-color;
|
||||||
|
$media-icon-color: transparentize(#ffffff, 0.4);
|
||||||
|
$media-icon-hover-color: transparentize(#ffffff, 0.2);
|
||||||
|
$media-icon-bg-color: transparentize(#000000, 0.5);
|
||||||
|
$media-icon-bg-hover-color: transparentize(#000000, 0.8);
|
||||||
|
|
||||||
|
// Control color
|
||||||
|
$control-border-color: $icon-color;
|
||||||
|
$control-border-active-color: $primary-color;
|
||||||
|
$control-border-hover-color: transparentize($control-border-active-color, 0.8);
|
||||||
|
|
||||||
|
// Button color
|
||||||
|
$icon-button-color: $icon-color;
|
||||||
|
$icon-button-hover-color: $icon-hover-color;
|
||||||
|
$icon-button-active-color: $primary-color;
|
||||||
|
$icon-button-active-hover-color: lighten($icon-button-active-color, 10%);
|
||||||
|
$contained-button-color: $primary-color;
|
||||||
|
$contained-button-hover-color: lighten($contained-button-color, 6%);
|
||||||
|
$contained-button-active-color: lighten($contained-button-color, 10%);
|
||||||
|
$outlined-button-color: $primary-color;
|
||||||
|
$outlined-button-hover-color: transparentize($outlined-button-color, 0.8);
|
||||||
|
$outlined-button-active-color: transparentize($outlined-button-color, 0.7);
|
||||||
|
$text-button-color: $primary-color;
|
||||||
|
$text-button-hover-color: transparentize($text-button-color, 0.7);
|
||||||
|
$text-button-focus-color: transparentize($text-button-color, 0.6);
|
||||||
|
$disabled-button-color: #cccccc;
|
||||||
|
$floating-action-button-color: $primary-color;
|
||||||
|
$floating-action-button-hover-color: lighten($floating-action-button-color, 6%);
|
||||||
|
$floating-action-button-active-color: lighten($floating-action-button-color, 10%);
|
||||||
|
$floating-action-button-icon-color: #ffffff;
|
||||||
|
|
||||||
|
// Toggle color
|
||||||
|
$toggle-thumb-color: #ffffff;
|
||||||
|
$toggle-track-color: darken($toggle-thumb-color, 18%);
|
||||||
|
$toggle-thumb-active-color: #1a73e8;
|
||||||
|
$toggle-track-active-color: lighten($primary-color, 18%);
|
||||||
|
|
||||||
|
// Border color
|
||||||
|
$border-color: #393f4f;
|
||||||
|
$border-hover-color: lighten($border-color, 30%);
|
||||||
|
$border-active-color: $primary-color;
|
||||||
|
|
||||||
|
// Scroll bar color
|
||||||
|
$scroll-bar-thumb-color: lighten($bg-color, 16%);
|
||||||
|
$scroll-bar-thumb-hover-color: lighten($bg-color, 26%);
|
||||||
|
$scroll-bar-thumb-active-color: lighten($bg-color, 32%);
|
||||||
|
|
||||||
|
// App bar color
|
||||||
|
$top-bar-color: #313543;
|
||||||
|
$search-bar-color: lighten($top-bar-color, 6%);
|
||||||
|
$search-bar-focus-color: #ffffff;
|
||||||
|
|
||||||
|
// Tab color
|
||||||
|
$tab-item-color: #d9e1e8;
|
||||||
|
$tab-item-active-color: $primary-color;
|
||||||
|
$tab-bg-color: #1f232b;
|
||||||
|
$tab-bg-hover-color: transparentize($tab-item-active-color, 0.9);
|
||||||
|
$tab-bg-focus-color: transparentize($tab-item-active-color, 0.8);
|
||||||
|
|
||||||
|
// Media indicator color
|
||||||
|
$media-page-indicator-color: #9e9e9e;
|
||||||
|
$media-page-indicator-active-color: #e6e6e6;
|
||||||
|
|
||||||
|
// Progress indicator color
|
||||||
|
$progress-indicator-color: $primary-color;
|
||||||
|
$progress-indicator-track-color: lighten($progress-indicator-color, 30%);
|
||||||
|
$loading-indicator-color: $primary-color;
|
||||||
|
|
||||||
|
// Contents color in read status
|
||||||
|
$read-primary-text-color: transparentize($primary-text-color, 0.3);
|
||||||
|
$read-secondary-text-color: transparentize($secondary-text-color, 0.3);
|
||||||
|
$read-ui-text-color: transparentize($ui-text-color, 0.3);
|
||||||
|
$read-poll-bar-leading-color: transparentize($progress-indicator-color, 0.3);
|
||||||
|
$read-poll-bar-color: transparentize($progress-indicator-track-color, 0.3);
|
@ -0,0 +1,148 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
|
||||||
|
$color-scheme: light;
|
||||||
|
|
||||||
|
// Base color
|
||||||
|
$primary-color: #2b90d9;
|
||||||
|
$secondary-color: #2b90d9;
|
||||||
|
$error-color: #B00020;
|
||||||
|
$verified-color: #4caf50;
|
||||||
|
|
||||||
|
// Text color
|
||||||
|
$primary-text-color: #000000;
|
||||||
|
$secondary-text-color: #5f6368;
|
||||||
|
$ui-text-color: #202124;
|
||||||
|
$section-text-color: $primary-color;
|
||||||
|
$info-text-color: #5e5e5e;
|
||||||
|
$tips-text-color: #c0c0c0;
|
||||||
|
$disabled-text-color: rgba(0,0,0,.54);
|
||||||
|
$link-text-color: #217aba;
|
||||||
|
$menu-text-color: $ui-text-color;
|
||||||
|
$top-bar-text-color: $ui-text-color;
|
||||||
|
$search-bar-text-color: $primary-text-color;
|
||||||
|
$contained-button-text-color: #ffffff;
|
||||||
|
$primary-text-on-shadow-color: #ffffff;
|
||||||
|
$text-field-color: $ui-text-color;
|
||||||
|
|
||||||
|
// Background-color
|
||||||
|
$bg-color: #eff3f5;
|
||||||
|
$menu-bg-color: $bg-color;
|
||||||
|
$menu-bg-hover-color: darken($bg-color, 6%);
|
||||||
|
$menu-bg-active-color: darken($bg-color, 10%);
|
||||||
|
$menu-bg-active-hover-color: darken($bg-color, 16%);
|
||||||
|
$card-bg-color: #ffffff;
|
||||||
|
$card-bg-hover-color: darken($card-bg-color, 6%);
|
||||||
|
$succeeded-card-bg-color: lighten($verified-color, 36%);
|
||||||
|
$list-bg-color: #ffffff;
|
||||||
|
$list-bg-hover-color: darken($list-bg-color, 6%);
|
||||||
|
$list-bg-active-color: darken($list-bg-color, 10%);
|
||||||
|
$list-bg-active-hover-color: darken($list-bg-color, 16%);
|
||||||
|
$list-bg-inactive-color: darken($list-bg-color, 10%);
|
||||||
|
$text-field-bg-color: $card-bg-color;
|
||||||
|
$dropdown-field-bg-color: darken($bg-color, 6%);
|
||||||
|
$dropdown-field-bg-hover-color: darken($dropdown-field-bg-color, 6%);
|
||||||
|
$dropdown-field-bg-active-color: darken($dropdown-field-bg-color, 10%);
|
||||||
|
$verified-bg-color: lighten($verified-color, 20%);
|
||||||
|
$unread-bg-color: transparentize($primary-color, 0.8);
|
||||||
|
|
||||||
|
// Chip color
|
||||||
|
$contained-chip-color: #e0e0e0;
|
||||||
|
$chip-selected-text-color: $primary-color;
|
||||||
|
$contained-chip-hover-color: darken($contained-chip-color, 6%);
|
||||||
|
$contained-chip-selected-color: transparentize($chip-selected-text-color, 0.7);
|
||||||
|
$outlined-chip-color: #ffffff;
|
||||||
|
$outlined-chip-hover-color: darken($outlined-chip-color, 6%);
|
||||||
|
$outlined-chip-selected-color: transparentize($chip-selected-text-color, 0.8);
|
||||||
|
$outlined-chip-selected-border-color: transparentize($chip-selected-text-color, 0.7);
|
||||||
|
|
||||||
|
// Relationship tag color
|
||||||
|
$relationship-tag-color: transparentize(#e0e0e0, 0.2);
|
||||||
|
|
||||||
|
// Badge color
|
||||||
|
$badge-color: $primary-color;
|
||||||
|
//$badge-color: $secondary-color;
|
||||||
|
|
||||||
|
// Icon color
|
||||||
|
$icon-color: #282c37;
|
||||||
|
$icon-hover-color: darken($icon-color, 30%);
|
||||||
|
$icon-bg-hover-color: darken($bg-color, 4%);
|
||||||
|
$icon-bg-active-color: darken($bg-color, 8%);
|
||||||
|
$disabled-icon-color: lighten($icon-color, 16%);
|
||||||
|
$top-bar-icon-color: $icon-color;
|
||||||
|
$top-bar-icon-hover-color: $icon-hover-color;
|
||||||
|
$top-bar-icon-active-color: $icon-hover-color;
|
||||||
|
$top-bar-unread-icon-color: $secondary-color;
|
||||||
|
$media-icon-color: transparentize(#ffffff, 0.4);
|
||||||
|
$media-icon-hover-color: transparentize(#ffffff, 0.2);
|
||||||
|
$media-icon-bg-color: transparentize(#000000, 0.5);
|
||||||
|
$media-icon-bg-hover-color: transparentize(#000000, 0.8);
|
||||||
|
|
||||||
|
// Control color
|
||||||
|
$control-border-color: $icon-color;
|
||||||
|
$control-border-active-color: $primary-color;
|
||||||
|
$control-border-hover-color: transparentize($control-border-active-color, 0.8);
|
||||||
|
|
||||||
|
// Button color
|
||||||
|
$icon-button-color: $icon-color;
|
||||||
|
$icon-button-hover-color: $icon-hover-color;
|
||||||
|
$icon-button-active-color: $primary-color;
|
||||||
|
$icon-button-active-hover-color: lighten($icon-button-active-color, 10%);
|
||||||
|
$contained-button-color: $primary-color;
|
||||||
|
$contained-button-hover-color: lighten($contained-button-color, 6%);
|
||||||
|
$contained-button-active-color: lighten($contained-button-color, 10%);
|
||||||
|
$outlined-button-color: $primary-color;
|
||||||
|
$outlined-button-hover-color: transparentize($outlined-button-color, 0.8);
|
||||||
|
$outlined-button-active-color: transparentize($outlined-button-color, 0.7);
|
||||||
|
$text-button-color: $primary-color;
|
||||||
|
$text-button-hover-color: lighten($text-button-color, 36%);
|
||||||
|
$text-button-focus-color: lighten($text-button-color, 30%);
|
||||||
|
$disabled-button-color: #cccccc;
|
||||||
|
$floating-action-button-color: $primary-color;
|
||||||
|
$floating-action-button-hover-color: lighten($floating-action-button-color, 6%);
|
||||||
|
$floating-action-button-active-color: lighten($floating-action-button-color, 10%);
|
||||||
|
$floating-action-button-icon-color: #ffffff;
|
||||||
|
|
||||||
|
// Toggle color
|
||||||
|
$toggle-thumb-color: #ffffff;
|
||||||
|
$toggle-track-color: darken($toggle-thumb-color, 18%);
|
||||||
|
$toggle-thumb-active-color: $primary-color;
|
||||||
|
$toggle-track-active-color: lighten($primary-color, 26%);
|
||||||
|
|
||||||
|
// Border color
|
||||||
|
$border-color: #dadce0;
|
||||||
|
$border-hover-color: darken($border-color, 30%);
|
||||||
|
$border-active-color: $primary-color;
|
||||||
|
|
||||||
|
// Scroll bar color
|
||||||
|
$scroll-bar-thumb-color: #ccd7e0;
|
||||||
|
$scroll-bar-thumb-hover-color: darken($scroll-bar-thumb-color, 10%);
|
||||||
|
$scroll-bar-thumb-active-color: darken($scroll-bar-thumb-color, 18%);
|
||||||
|
|
||||||
|
// App bar color
|
||||||
|
$top-bar-color: #ffffff;
|
||||||
|
$search-bar-color: #d9e1e8;
|
||||||
|
$search-bar-focus-color: $bg-color;
|
||||||
|
|
||||||
|
// Tab color
|
||||||
|
$tab-item-color: #282c37;
|
||||||
|
$tab-item-active-color: $primary-color;
|
||||||
|
$tab-bg-color: #e6ebf0;
|
||||||
|
$tab-bg-hover-color: transparentize($tab-item-active-color, 0.9);
|
||||||
|
$tab-bg-focus-color: transparentize($tab-item-active-color, 0.8);
|
||||||
|
|
||||||
|
// Media indicator color
|
||||||
|
$media-page-indicator-color: #9e9e9e;
|
||||||
|
$media-page-indicator-active-color: #e6e6e6;
|
||||||
|
|
||||||
|
// Progress indicator color
|
||||||
|
$progress-indicator-color: $primary-color;
|
||||||
|
$progress-indicator-track-color: lighten($progress-indicator-color, 30%);
|
||||||
|
$loading-indicator-color: $primary-color;
|
||||||
|
|
||||||
|
// Contents color in read status
|
||||||
|
$read-primary-text-color: transparentize($primary-text-color, 0.3);
|
||||||
|
$read-secondary-text-color: transparentize($secondary-text-color, 0.3);
|
||||||
|
$read-ui-text-color: transparentize($ui-text-color, 0.3);
|
||||||
|
$read-poll-bar-leading-color: transparentize($progress-indicator-color, 0.3);
|
||||||
|
$read-poll-bar-color: transparentize($progress-indicator-track-color, 0.3);
|
145
app/javascript/styles/mastodon-material/color/plus-classic.scss
Normal file
145
app/javascript/styles/mastodon-material/color/plus-classic.scss
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
$color-scheme: light;
|
||||||
|
|
||||||
|
// Base color
|
||||||
|
$primary-color: #4285f4;
|
||||||
|
$secondary-color: #db4437;
|
||||||
|
$error-color: #B00020;
|
||||||
|
$verified-color: #4caf50;
|
||||||
|
|
||||||
|
// Text color
|
||||||
|
$primary-text-color: #000000;
|
||||||
|
$secondary-text-color: #5f6368;
|
||||||
|
$ui-text-color: #202124;
|
||||||
|
$inverted-text-color: #ffffff;
|
||||||
|
$section-text-color: $primary-color;
|
||||||
|
$info-text-color: #5e5e5e;
|
||||||
|
$tips-text-color: #c0c0c0;
|
||||||
|
$disabled-text-color: rgba(0,0,0,.54);
|
||||||
|
$link-text-color: #4285f4;
|
||||||
|
$menu-text-color: $ui-text-color;
|
||||||
|
$top-bar-text-color: $inverted-text-color;
|
||||||
|
$search-bar-text-color: $primary-text-color;
|
||||||
|
$contained-button-text-color: #ffffff;
|
||||||
|
$primary-text-on-shadow-color: #ffffff;
|
||||||
|
$text-field-color: $ui-text-color;
|
||||||
|
|
||||||
|
// Background-color
|
||||||
|
$bg-color: #fafafa;
|
||||||
|
$menu-bg-color: #ffffff;
|
||||||
|
$menu-bg-hover-color: darken($menu-bg-color, 6%);
|
||||||
|
$menu-bg-active-color: darken($menu-bg-color, 10%);
|
||||||
|
$menu-bg-active-hover-color: darken($menu-bg-color, 16%);
|
||||||
|
$card-bg-color: #ffffff;
|
||||||
|
$card-bg-hover-color: darken($card-bg-color, 6%);
|
||||||
|
$succeeded-card-bg-color: lighten($verified-color, 36%);
|
||||||
|
$list-bg-color: #ffffff;
|
||||||
|
$list-bg-hover-color: darken($list-bg-color, 6%);
|
||||||
|
$list-bg-active-color: darken($list-bg-color, 10%);
|
||||||
|
$list-bg-active-hover-color: darken($list-bg-color, 16%);
|
||||||
|
$list-bg-inactive-color: darken($list-bg-color, 10%);
|
||||||
|
$text-field-bg-color: $card-bg-color;
|
||||||
|
$dropdown-field-bg-color: darken($bg-color, 6%);
|
||||||
|
$dropdown-field-bg-hover-color: darken($dropdown-field-bg-color, 6%);
|
||||||
|
$dropdown-field-bg-active-color: darken($dropdown-field-bg-color, 10%);
|
||||||
|
$verified-bg-color: lighten($verified-color, 20%);
|
||||||
|
$unread-bg-color: transparentize($primary-color, 0.8);
|
||||||
|
|
||||||
|
// Chip color
|
||||||
|
$contained-chip-color: #e0e0e0;
|
||||||
|
$chip-selected-text-color: $primary-color;
|
||||||
|
$contained-chip-hover-color: darken($contained-chip-color, 6%);
|
||||||
|
$contained-chip-selected-color: transparentize($chip-selected-text-color, 0.7);
|
||||||
|
$outlined-chip-color: #ffffff;
|
||||||
|
$outlined-chip-hover-color: darken($outlined-chip-color, 6%);
|
||||||
|
$outlined-chip-selected-color: transparentize($chip-selected-text-color, 0.8);
|
||||||
|
$outlined-chip-selected-border-color: transparentize($chip-selected-text-color, 0.7);
|
||||||
|
|
||||||
|
// Relationship tag color
|
||||||
|
$relationship-tag-color: transparentize(#e0e0e0, 0.2);
|
||||||
|
|
||||||
|
// Badge color
|
||||||
|
$badge-color: $primary-color;
|
||||||
|
//$badge-color: $secondary-color;
|
||||||
|
|
||||||
|
// Icon color
|
||||||
|
$icon-color: #757575;
|
||||||
|
$icon-hover-color: darken($icon-color, 30%);
|
||||||
|
$icon-bg-hover-color: transparentize(#000000, 0.9);
|
||||||
|
$icon-bg-active-color: transparentize(#000000, 0.8);
|
||||||
|
$disabled-icon-color: lighten($icon-color, 16%);
|
||||||
|
$top-bar-icon-color: #ffffff;
|
||||||
|
$top-bar-icon-hover-color: darken($top-bar-icon-color, 10%);
|
||||||
|
$top-bar-icon-active-color: darken($top-bar-icon-color, 18%);
|
||||||
|
$media-icon-color: transparentize(#ffffff, 0.4);
|
||||||
|
$media-icon-hover-color: transparentize(#ffffff, 0.2);
|
||||||
|
$media-icon-bg-color: transparentize(#000000, 0.5);
|
||||||
|
$media-icon-bg-hover-color: transparentize(#000000, 0.8);
|
||||||
|
|
||||||
|
// Control color
|
||||||
|
$control-border-color: $icon-color;
|
||||||
|
$control-border-active-color: $primary-color;
|
||||||
|
$control-border-hover-color: transparentize($control-border-active-color, 0.8);
|
||||||
|
|
||||||
|
// Button color
|
||||||
|
$icon-button-color: $icon-color;
|
||||||
|
$icon-button-hover-color: $icon-hover-color;
|
||||||
|
$icon-button-active-color: $primary-color;
|
||||||
|
$icon-button-active-hover-color: lighten($icon-button-active-color, 10%);
|
||||||
|
$contained-button-color: $primary-color;
|
||||||
|
$contained-button-hover-color: lighten($contained-button-color, 6%);
|
||||||
|
$contained-button-active-color: lighten($contained-button-color, 10%);
|
||||||
|
$outlined-button-color: $primary-color;
|
||||||
|
$outlined-button-hover-color: transparentize($outlined-button-color, 0.8);
|
||||||
|
$outlined-button-active-color: transparentize($outlined-button-color, 0.7);
|
||||||
|
$text-button-color: $primary-color;
|
||||||
|
$text-button-hover-color: transparentize($text-button-color, 0.8);
|
||||||
|
$text-button-focus-color: transparentize($text-button-color, 0.7);
|
||||||
|
$disabled-button-color: #cccccc;
|
||||||
|
$floating-action-button-color: $secondary-color;
|
||||||
|
$floating-action-button-hover-color: lighten($floating-action-button-color, 6%);
|
||||||
|
$floating-action-button-active-color: lighten($floating-action-button-color, 10%);
|
||||||
|
$floating-action-button-icon-color: #ffffff;
|
||||||
|
|
||||||
|
// Toggle color
|
||||||
|
$toggle-thumb-color: #ffffff;
|
||||||
|
$toggle-track-color: darken($toggle-thumb-color, 18%);
|
||||||
|
$toggle-thumb-active-color: #1a73e8;
|
||||||
|
$toggle-track-active-color: lighten($toggle-thumb-active-color, 18%);
|
||||||
|
|
||||||
|
// Border color
|
||||||
|
$border-color: #dadce0;
|
||||||
|
$border-hover-color: darken($border-color, 30%);
|
||||||
|
$border-active-color: $primary-color;
|
||||||
|
|
||||||
|
// Scroll bar color
|
||||||
|
$scroll-bar-thumb-color: darken($bg-color, 20%);
|
||||||
|
$scroll-bar-thumb-hover-color: darken($scroll-bar-thumb-color, 10%);
|
||||||
|
$scroll-bar-thumb-active-color: darken($scroll-bar-thumb-color, 18%);
|
||||||
|
|
||||||
|
// App bar color
|
||||||
|
$top-bar-color: #db4437;
|
||||||
|
$search-bar-color: darken($bg-color, 6%);
|
||||||
|
$search-bar-focus-color: $bg-color;
|
||||||
|
|
||||||
|
// Tab color
|
||||||
|
$tab-item-color: transparentize(#ffffff, 0.5);
|
||||||
|
$tab-item-active-color: #ffffff;
|
||||||
|
$tab-bg-color: $top-bar-color;
|
||||||
|
$tab-bg-hover-color: transparentize($tab-item-active-color, 0.9);
|
||||||
|
$tab-bg-focus-color: transparentize($tab-item-active-color, 0.8);
|
||||||
|
|
||||||
|
// Media indicator color
|
||||||
|
$media-page-indicator-color: #9e9e9e;
|
||||||
|
$media-page-indicator-active-color: #e6e6e6;
|
||||||
|
|
||||||
|
// Progress indicator color
|
||||||
|
$progress-indicator-color: $primary-color;
|
||||||
|
$progress-indicator-track-color: lighten($progress-indicator-color, 30%);
|
||||||
|
$loading-indicator-color: $primary-color;
|
||||||
|
|
||||||
|
// Contents color in read status
|
||||||
|
$read-primary-text-color: transparentize($primary-text-color, 0.3);
|
||||||
|
$read-secondary-text-color: transparentize($secondary-text-color, 0.3);
|
||||||
|
$read-ui-text-color: transparentize($ui-text-color, 0.3);
|
||||||
|
$read-poll-bar-leading-color: transparentize($progress-indicator-color, 0.3);
|
||||||
|
$read-poll-bar-color: transparentize($progress-indicator-track-color, 0.3);
|
148
app/javascript/styles/mastodon-material/color/v1-dark.scss
Normal file
148
app/javascript/styles/mastodon-material/color/v1-dark.scss
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
|
||||||
|
$color-scheme: dark;
|
||||||
|
|
||||||
|
// Base color
|
||||||
|
$primary-color: #4285f4;
|
||||||
|
$secondary-color: #db4437;
|
||||||
|
$error-color: #B00020;
|
||||||
|
$verified-color: #4caf50;
|
||||||
|
|
||||||
|
// Text color
|
||||||
|
$primary-text-color: #ffffff;
|
||||||
|
$secondary-text-color: #9aa0a6;
|
||||||
|
$ui-text-color: #e8eaed;
|
||||||
|
$inverted-text-color: #000000;
|
||||||
|
$section-text-color: $primary-color;
|
||||||
|
$info-text-color: #9aa0a6;
|
||||||
|
$tips-text-color: #c0c0c0;
|
||||||
|
$disabled-text-color: rgba(0,0,0,.54);
|
||||||
|
$link-text-color: #4285f4;
|
||||||
|
$menu-text-color: $ui-text-color;
|
||||||
|
$top-bar-text-color: $ui-text-color;
|
||||||
|
$search-bar-text-color: $inverted-text-color;
|
||||||
|
$contained-button-text-color: #ffffff;
|
||||||
|
$primary-text-on-shadow-color: #ffffff;
|
||||||
|
$text-field-color: $ui-text-color;
|
||||||
|
|
||||||
|
// Background-color
|
||||||
|
$bg-color: #303030;
|
||||||
|
$menu-bg-color: #424242;
|
||||||
|
$menu-bg-hover-color: lighten($menu-bg-color, 6%);
|
||||||
|
$menu-bg-active-color: lighten($menu-bg-color, 10%);
|
||||||
|
$menu-bg-active-hover-color: lighten($menu-bg-color, 16%);
|
||||||
|
$card-bg-color: #424242;
|
||||||
|
$card-bg-hover-color: lighten($card-bg-color, 6%);
|
||||||
|
$succeeded-card-bg-color: lighten($verified-color, 36%);
|
||||||
|
$list-bg-color: #424242;
|
||||||
|
$list-bg-hover-color: lighten($list-bg-color, 6%);
|
||||||
|
$list-bg-active-color: lighten($list-bg-color, 10%);
|
||||||
|
$list-bg-active-hover-color: lighten($list-bg-color, 16%);
|
||||||
|
$list-bg-inactive-color: lighten($list-bg-color, 10%);
|
||||||
|
$text-field-bg-color: $card-bg-color;
|
||||||
|
$dropdown-field-bg-color: lighten($bg-color, 6%);
|
||||||
|
$dropdown-field-bg-hover-color: lighten($dropdown-field-bg-color, 6%);
|
||||||
|
$dropdown-field-bg-active-color: lighten($dropdown-field-bg-color, 10%);
|
||||||
|
$verified-bg-color: darken($verified-color, 20%);
|
||||||
|
$unread-bg-color: transparentize($primary-color, 0.8);
|
||||||
|
|
||||||
|
// Chip color
|
||||||
|
$contained-chip-color: #2e2e2e;
|
||||||
|
$chip-selected-text-color: $primary-color;
|
||||||
|
$contained-chip-hover-color: lighten($contained-chip-color, 6%);
|
||||||
|
$contained-chip-selected-color: transparentize($chip-selected-text-color, 0.7);
|
||||||
|
$outlined-chip-color: #1e1e1e;
|
||||||
|
$outlined-chip-hover-color: lighten($outlined-chip-color, 6%);
|
||||||
|
$outlined-chip-selected-color: transparentize($chip-selected-text-color, 0.8);
|
||||||
|
$outlined-chip-selected-border-color: transparentize($chip-selected-text-color, 0.7);
|
||||||
|
|
||||||
|
// Relationship tag color
|
||||||
|
$relationship-tag-color: transparentize(#2e2e2e, 0.2);
|
||||||
|
|
||||||
|
// Badge color
|
||||||
|
$badge-color: $primary-color;
|
||||||
|
//$badge-color: $secondary-color;
|
||||||
|
|
||||||
|
// Icon color
|
||||||
|
$icon-color: #e2e2e3;
|
||||||
|
$icon-hover-color: #ffffff;
|
||||||
|
$icon-bg-hover-color: transparentize(#ffffff, 0.8);
|
||||||
|
$icon-bg-active-color: transparentize(#ffffff, 0.7);
|
||||||
|
$disabled-icon-color: darken($icon-color, 16%);
|
||||||
|
$top-bar-icon-color: #ffffff;
|
||||||
|
$top-bar-icon-hover-color: lighten($top-bar-icon-color, 10%);
|
||||||
|
$top-bar-icon-active-color: lighten($top-bar-icon-color, 18%);
|
||||||
|
$media-icon-color: transparentize(#ffffff, 0.4);
|
||||||
|
$media-icon-hover-color: transparentize(#ffffff, 0.2);
|
||||||
|
$media-icon-bg-color: transparentize(#000000, 0.5);
|
||||||
|
$media-icon-bg-hover-color: transparentize(#000000, 0.8);
|
||||||
|
|
||||||
|
// Control color
|
||||||
|
$control-border-color: $icon-color;
|
||||||
|
$control-border-active-color: $primary-color;
|
||||||
|
$control-border-hover-color: transparentize($control-border-active-color, 0.8);
|
||||||
|
|
||||||
|
// Button color
|
||||||
|
$icon-button-color: $icon-color;
|
||||||
|
$icon-button-hover-color: $icon-hover-color;
|
||||||
|
$icon-button-active-color: $primary-color;
|
||||||
|
$icon-button-active-hover-color: lighten($icon-button-active-color, 10%);
|
||||||
|
$contained-button-color: $primary-color;
|
||||||
|
$contained-button-hover-color: lighten($contained-button-color, 6%);
|
||||||
|
$contained-button-active-color: lighten($contained-button-color, 10%);
|
||||||
|
$outlined-button-color: $primary-color;
|
||||||
|
$outlined-button-hover-color: transparentize($outlined-button-color, 0.8);
|
||||||
|
$outlined-button-active-color: transparentize($outlined-button-color, 0.7);
|
||||||
|
$text-button-color: $primary-color;
|
||||||
|
$text-button-hover-color: transparentize($text-button-color, 0.8);
|
||||||
|
$text-button-focus-color: transparentize($text-button-color, 0.7);
|
||||||
|
$disabled-button-color: #cccccc;
|
||||||
|
$floating-action-button-color: $primary-color;
|
||||||
|
$floating-action-button-hover-color: lighten($floating-action-button-color, 6%);
|
||||||
|
$floating-action-button-active-color: lighten($floating-action-button-color, 10%);
|
||||||
|
$floating-action-button-icon-color: #ffffff;
|
||||||
|
|
||||||
|
// Toggle color
|
||||||
|
$toggle-thumb-color: #ffffff;
|
||||||
|
$toggle-track-color: darken($toggle-thumb-color, 18%);
|
||||||
|
$toggle-thumb-active-color: #1a73e8;
|
||||||
|
$toggle-track-active-color: lighten($toggle-thumb-active-color, 18%);
|
||||||
|
|
||||||
|
// Border color
|
||||||
|
$border-color: #2e2e2e;
|
||||||
|
$border-hover-color: lighten($border-color, 30%);
|
||||||
|
$border-active-color: $primary-color;
|
||||||
|
|
||||||
|
// Scroll bar color
|
||||||
|
$scroll-bar-thumb-color: lighten($bg-color, 20%);
|
||||||
|
$scroll-bar-thumb-hover-color: lighten($bg-color, 30%);
|
||||||
|
$scroll-bar-thumb-active-color: lighten($bg-color, 38%);
|
||||||
|
|
||||||
|
// App bar color
|
||||||
|
$top-bar-color: #1565C0;
|
||||||
|
$search-bar-color: lighten($bg-color, 6%);
|
||||||
|
$search-bar-focus-color: #ffffff;
|
||||||
|
|
||||||
|
// Tab color
|
||||||
|
$tab-item-color: transparentize(#ffffff, 0.5);
|
||||||
|
$tab-item-active-color: #ffffff;
|
||||||
|
$tab-bg-color: $top-bar-color;
|
||||||
|
$tab-bg-hover-color: transparentize($tab-item-active-color, 0.9);
|
||||||
|
$tab-bg-focus-color: transparentize($tab-item-active-color, 0.8);
|
||||||
|
|
||||||
|
// Media indicator color
|
||||||
|
$media-page-indicator-color: #9e9e9e;
|
||||||
|
$media-page-indicator-active-color: #e6e6e6;
|
||||||
|
|
||||||
|
// Progress indicator color
|
||||||
|
$progress-indicator-color: $primary-color;
|
||||||
|
$progress-indicator-track-color: lighten($progress-indicator-color, 30%);
|
||||||
|
$loading-indicator-color: $primary-color;
|
||||||
|
|
||||||
|
// Contents color in read status
|
||||||
|
$read-primary-text-color: transparentize($primary-text-color, 0.3);
|
||||||
|
$read-secondary-text-color: transparentize($secondary-text-color, 0.3);
|
||||||
|
$read-ui-text-color: transparentize($ui-text-color, 0.3);
|
||||||
|
$read-poll-bar-leading-color: transparentize($progress-indicator-color, 0.3);
|
||||||
|
$read-poll-bar-color: transparentize($progress-indicator-track-color, 0.3);
|
149
app/javascript/styles/mastodon-material/color/v1-light.scss
Normal file
149
app/javascript/styles/mastodon-material/color/v1-light.scss
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
|
||||||
|
$color-scheme: light;
|
||||||
|
|
||||||
|
// Base color
|
||||||
|
$primary-color: #4285f4;
|
||||||
|
$secondary-color: #db4437;
|
||||||
|
$error-color: #B00020;
|
||||||
|
$verified-color: #4caf50;
|
||||||
|
|
||||||
|
// Text color
|
||||||
|
$primary-text-color: #000000;
|
||||||
|
$secondary-text-color: #5f6368;
|
||||||
|
$ui-text-color: #202124;
|
||||||
|
$inverted-text-color: #ffffff;
|
||||||
|
$section-text-color: $primary-color;
|
||||||
|
$info-text-color: #5e5e5e;
|
||||||
|
$tips-text-color: #c0c0c0;
|
||||||
|
$disabled-text-color: rgba(0,0,0,.54);
|
||||||
|
$link-text-color: #4285f4;
|
||||||
|
$menu-text-color: $ui-text-color;
|
||||||
|
$top-bar-text-color: $inverted-text-color;
|
||||||
|
$search-bar-text-color: $primary-text-color;
|
||||||
|
$contained-button-text-color: #ffffff;
|
||||||
|
$primary-text-on-shadow-color: #ffffff;
|
||||||
|
$text-field-color: $ui-text-color;
|
||||||
|
|
||||||
|
// Background-color
|
||||||
|
$bg-color: #fafafa;
|
||||||
|
$menu-bg-color: #ffffff;
|
||||||
|
$menu-bg-hover-color: darken($menu-bg-color, 6%);
|
||||||
|
$menu-bg-active-color: darken($menu-bg-color, 10%);
|
||||||
|
$menu-bg-active-hover-color: darken($menu-bg-color, 16%);
|
||||||
|
$card-bg-color: #ffffff;
|
||||||
|
$card-bg-hover-color: darken($card-bg-color, 6%);
|
||||||
|
$succeeded-card-bg-color: lighten($verified-color, 36%);
|
||||||
|
$list-bg-color: #ffffff;
|
||||||
|
$list-bg-hover-color: darken($list-bg-color, 6%);
|
||||||
|
$list-bg-active-color: darken($list-bg-color, 10%);
|
||||||
|
$list-bg-active-hover-color: darken($list-bg-color, 16%);
|
||||||
|
$list-bg-inactive-color: darken($list-bg-color, 10%);
|
||||||
|
$text-field-bg-color: $card-bg-color;
|
||||||
|
$dropdown-field-bg-color: darken($bg-color, 6%);
|
||||||
|
$dropdown-field-bg-hover-color: darken($dropdown-field-bg-color, 6%);
|
||||||
|
$dropdown-field-bg-active-color: darken($dropdown-field-bg-color, 10%);
|
||||||
|
$verified-bg-color: lighten($verified-color, 20%);
|
||||||
|
$unread-bg-color: transparentize($primary-color, 0.8);
|
||||||
|
|
||||||
|
// Chip color
|
||||||
|
$contained-chip-color: #e0e0e0;
|
||||||
|
$chip-selected-text-color: $primary-color;
|
||||||
|
$contained-chip-hover-color: darken($contained-chip-color, 6%);
|
||||||
|
$contained-chip-selected-color: transparentize($chip-selected-text-color, 0.7);
|
||||||
|
$outlined-chip-color: #ffffff;
|
||||||
|
$outlined-chip-hover-color: darken($outlined-chip-color, 6%);
|
||||||
|
$outlined-chip-selected-color: transparentize($chip-selected-text-color, 0.8);
|
||||||
|
$outlined-chip-selected-border-color: transparentize($chip-selected-text-color, 0.7);
|
||||||
|
|
||||||
|
// Relationship tag color
|
||||||
|
$relationship-tag-color: transparentize(#e0e0e0, 0.2);
|
||||||
|
|
||||||
|
// Badge color
|
||||||
|
$badge-color: $primary-color;
|
||||||
|
//$badge-color: $secondary-color;
|
||||||
|
|
||||||
|
// Icon color
|
||||||
|
$icon-color: #757575;
|
||||||
|
$icon-hover-color: darken($icon-color, 30%);
|
||||||
|
$icon-bg-hover-color: transparentize(#000000, 0.9);
|
||||||
|
$icon-bg-active-color: transparentize(#000000, 0.8);
|
||||||
|
$disabled-icon-color: lighten($icon-color, 16%);
|
||||||
|
$top-bar-icon-color: #ffffff;
|
||||||
|
$top-bar-icon-hover-color: darken($top-bar-icon-color, 10%);
|
||||||
|
$top-bar-icon-active-color: darken($top-bar-icon-color, 18%);
|
||||||
|
$top-bar-unread-icon-color: darken($primary-color, 30%);
|
||||||
|
$media-icon-color: transparentize(#ffffff, 0.4);
|
||||||
|
$media-icon-hover-color: transparentize(#ffffff, 0.2);
|
||||||
|
$media-icon-bg-color: transparentize(#000000, 0.5);
|
||||||
|
$media-icon-bg-hover-color: transparentize(#000000, 0.8);
|
||||||
|
|
||||||
|
// Control color
|
||||||
|
$control-border-color: $icon-color;
|
||||||
|
$control-border-active-color: $primary-color;
|
||||||
|
$control-border-hover-color: transparentize($control-border-active-color, 0.8);
|
||||||
|
|
||||||
|
// Button color
|
||||||
|
$icon-button-color: $icon-color;
|
||||||
|
$icon-button-hover-color: $icon-hover-color;
|
||||||
|
$icon-button-active-color: $primary-color;
|
||||||
|
$icon-button-active-hover-color: lighten($icon-button-active-color, 10%);
|
||||||
|
$contained-button-color: $primary-color;
|
||||||
|
$contained-button-hover-color: lighten($contained-button-color, 6%);
|
||||||
|
$contained-button-active-color: lighten($contained-button-color, 10%);
|
||||||
|
$outlined-button-color: $primary-color;
|
||||||
|
$outlined-button-hover-color: transparentize($outlined-button-color, 0.8);
|
||||||
|
$outlined-button-active-color: transparentize($outlined-button-color, 0.7);
|
||||||
|
$text-button-color: $primary-color;
|
||||||
|
$text-button-hover-color: transparentize($text-button-color, 0.8);
|
||||||
|
$text-button-focus-color: transparentize($text-button-color, 0.7);
|
||||||
|
$disabled-button-color: #cccccc;
|
||||||
|
$floating-action-button-color: $primary-color;
|
||||||
|
$floating-action-button-hover-color: lighten($floating-action-button-color, 6%);
|
||||||
|
$floating-action-button-active-color: lighten($floating-action-button-color, 10%);
|
||||||
|
$floating-action-button-icon-color: #ffffff;
|
||||||
|
|
||||||
|
// Toggle color
|
||||||
|
$toggle-thumb-color: #ffffff;
|
||||||
|
$toggle-track-color: darken($toggle-thumb-color, 18%);
|
||||||
|
$toggle-thumb-active-color: #1a73e8;
|
||||||
|
$toggle-track-active-color: lighten($toggle-thumb-active-color, 18%);
|
||||||
|
|
||||||
|
// Border color
|
||||||
|
$border-color: #dadce0;
|
||||||
|
$border-hover-color: darken($border-color, 30%);
|
||||||
|
$border-active-color: $primary-color;
|
||||||
|
|
||||||
|
// Scroll bar color
|
||||||
|
$scroll-bar-thumb-color: darken($bg-color, 20%);
|
||||||
|
$scroll-bar-thumb-hover-color: darken($scroll-bar-thumb-color, 10%);
|
||||||
|
$scroll-bar-thumb-active-color: darken($scroll-bar-thumb-color, 18%);
|
||||||
|
|
||||||
|
// App bar color
|
||||||
|
$top-bar-color: #2196f3;
|
||||||
|
$search-bar-color: darken($bg-color, 6%);
|
||||||
|
$search-bar-focus-color: $bg-color;
|
||||||
|
|
||||||
|
// Tab color
|
||||||
|
$tab-item-color: transparentize(#ffffff, 0.5);
|
||||||
|
$tab-item-active-color: #ffffff;
|
||||||
|
$tab-bg-color: $top-bar-color;
|
||||||
|
$tab-bg-hover-color: transparentize($tab-item-active-color, 0.9);
|
||||||
|
$tab-bg-focus-color: transparentize($tab-item-active-color, 0.8);
|
||||||
|
|
||||||
|
// Media indicator color
|
||||||
|
$media-page-indicator-color: #9e9e9e;
|
||||||
|
$media-page-indicator-active-color: #e6e6e6;
|
||||||
|
|
||||||
|
// Progress indicator color
|
||||||
|
$progress-indicator-color: $primary-color;
|
||||||
|
$progress-indicator-track-color: lighten($progress-indicator-color, 30%);
|
||||||
|
$loading-indicator-color: $primary-color;
|
||||||
|
|
||||||
|
// Contents color in read status
|
||||||
|
$read-primary-text-color: transparentize($primary-text-color, 0.3);
|
||||||
|
$read-secondary-text-color: transparentize($secondary-text-color, 0.3);
|
||||||
|
$read-ui-text-color: transparentize($ui-text-color, 0.3);
|
||||||
|
$read-poll-bar-leading-color: transparentize($progress-indicator-color, 0.3);
|
||||||
|
$read-poll-bar-color: transparentize($progress-indicator-track-color, 0.3);
|
149
app/javascript/styles/mastodon-material/color/v2-dark.scss
Normal file
149
app/javascript/styles/mastodon-material/color/v2-dark.scss
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
|
||||||
|
$color-scheme: dark;
|
||||||
|
|
||||||
|
// Base color
|
||||||
|
$primary-color: #4285f4;
|
||||||
|
$secondary-color: #db4437;
|
||||||
|
$error-color: #B00020;
|
||||||
|
$verified-color: #4caf50;
|
||||||
|
|
||||||
|
// Text color
|
||||||
|
$primary-text-color: #ffffff;
|
||||||
|
$secondary-text-color: #9aa0a6;
|
||||||
|
$ui-text-color: #e8eaed;
|
||||||
|
$inverted-text-color: #000000;
|
||||||
|
$section-text-color: $primary-color;
|
||||||
|
$info-text-color: #9aa0a6;
|
||||||
|
$tips-text-color: #c0c0c0;
|
||||||
|
$disabled-text-color: rgba(0,0,0,.54);
|
||||||
|
$link-text-color: #4285f4;
|
||||||
|
$menu-text-color: $ui-text-color;
|
||||||
|
$top-bar-text-color: $ui-text-color;
|
||||||
|
$search-bar-text-color: $inverted-text-color;
|
||||||
|
$contained-button-text-color: #ffffff;
|
||||||
|
$primary-text-on-shadow-color: #ffffff;
|
||||||
|
$text-field-color: $ui-text-color;
|
||||||
|
|
||||||
|
// Background-color
|
||||||
|
$bg-color: #121212;
|
||||||
|
$menu-bg-color: #1e1e1e;
|
||||||
|
$menu-bg-hover-color: lighten($menu-bg-color, 6%);
|
||||||
|
$menu-bg-active-color: lighten($menu-bg-color, 10%);
|
||||||
|
$menu-bg-active-hover-color: lighten($menu-bg-color, 16%);
|
||||||
|
$card-bg-color: #1e1e1e;
|
||||||
|
$card-bg-hover-color: lighten($card-bg-color, 6%);
|
||||||
|
$succeeded-card-bg-color: lighten($verified-color, 36%);
|
||||||
|
$list-bg-color: #1e1e1e;
|
||||||
|
$list-bg-hover-color: lighten($list-bg-color, 6%);
|
||||||
|
$list-bg-active-color: lighten($list-bg-color, 10%);
|
||||||
|
$list-bg-active-hover-color: lighten($list-bg-color, 16%);
|
||||||
|
$list-bg-inactive-color: lighten($list-bg-color, 10%);
|
||||||
|
$text-field-bg-color: $card-bg-color;
|
||||||
|
$dropdown-field-bg-color: lighten($bg-color, 6%);
|
||||||
|
$dropdown-field-bg-hover-color: lighten($dropdown-field-bg-color, 6%);
|
||||||
|
$dropdown-field-bg-active-color: lighten($dropdown-field-bg-color, 10%);
|
||||||
|
$verified-bg-color: darken($verified-color, 20%);
|
||||||
|
$unread-bg-color: transparentize($primary-color, 0.8);
|
||||||
|
|
||||||
|
// Chip color
|
||||||
|
$contained-chip-color: #2e2e2e;
|
||||||
|
$chip-selected-text-color: $primary-color;
|
||||||
|
$contained-chip-hover-color: lighten($contained-chip-color, 6%);
|
||||||
|
$contained-chip-selected-color: transparentize($chip-selected-text-color, 0.7);
|
||||||
|
$outlined-chip-color: #1e1e1e;
|
||||||
|
$outlined-chip-hover-color: lighten($outlined-chip-color, 6%);
|
||||||
|
$outlined-chip-selected-color: transparentize($chip-selected-text-color, 0.8);
|
||||||
|
$outlined-chip-selected-border-color: transparentize($chip-selected-text-color, 0.7);
|
||||||
|
|
||||||
|
// Relationship tag color
|
||||||
|
$relationship-tag-color: transparentize(#2e2e2e, 0.2);
|
||||||
|
|
||||||
|
// Badge color
|
||||||
|
$badge-color: $primary-color;
|
||||||
|
//$badge-color: $secondary-color;
|
||||||
|
|
||||||
|
// Icon color
|
||||||
|
$icon-color: #e2e2e3;
|
||||||
|
$icon-hover-color: #ffffff;
|
||||||
|
$icon-bg-hover-color: transparentize(#ffffff, 0.8);
|
||||||
|
$icon-bg-active-color: transparentize(#ffffff, 0.7);
|
||||||
|
$disabled-icon-color: darken($icon-color, 16%);
|
||||||
|
$top-bar-icon-color: $icon-color;
|
||||||
|
$top-bar-icon-hover-color: $icon-hover-color;
|
||||||
|
$top-bar-icon-active-color: $primary-color;
|
||||||
|
$top-bar-unread-icon-color: $secondary-color;
|
||||||
|
$media-icon-color: transparentize(#ffffff, 0.4);
|
||||||
|
$media-icon-hover-color: transparentize(#ffffff, 0.2);
|
||||||
|
$media-icon-bg-color: transparentize(#000000, 0.5);
|
||||||
|
$media-icon-bg-hover-color: transparentize(#000000, 0.8);
|
||||||
|
|
||||||
|
// Control color
|
||||||
|
$control-border-color: $icon-color;
|
||||||
|
$control-border-active-color: $primary-color;
|
||||||
|
$control-border-hover-color: transparentize($control-border-active-color, 0.8);
|
||||||
|
|
||||||
|
// Button color
|
||||||
|
$icon-button-color: $icon-color;
|
||||||
|
$icon-button-hover-color: $icon-hover-color;
|
||||||
|
$icon-button-active-color: $primary-color;
|
||||||
|
$icon-button-active-hover-color: lighten($icon-button-active-color, 10%);
|
||||||
|
$contained-button-color: $primary-color;
|
||||||
|
$contained-button-hover-color: lighten($contained-button-color, 6%);
|
||||||
|
$contained-button-active-color: lighten($contained-button-color, 10%);
|
||||||
|
$outlined-button-color: $primary-color;
|
||||||
|
$outlined-button-hover-color: transparentize($outlined-button-color, 0.8);
|
||||||
|
$outlined-button-active-color: transparentize($outlined-button-color, 0.7);
|
||||||
|
$text-button-color: $primary-color;
|
||||||
|
$text-button-hover-color: transparentize($text-button-color, 0.8);
|
||||||
|
$text-button-focus-color: transparentize($text-button-color, 0.7);
|
||||||
|
$disabled-button-color: #cccccc;
|
||||||
|
$floating-action-button-color: #272727;
|
||||||
|
$floating-action-button-hover-color: lighten($floating-action-button-color, 6%);
|
||||||
|
$floating-action-button-active-color: lighten($floating-action-button-color, 10%);
|
||||||
|
$floating-action-button-icon-color: $primary-color;
|
||||||
|
|
||||||
|
// Toggle color
|
||||||
|
$toggle-thumb-color: #ffffff;
|
||||||
|
$toggle-track-color: darken($toggle-thumb-color, 18%);
|
||||||
|
$toggle-thumb-active-color: #1a73e8;
|
||||||
|
$toggle-track-active-color: lighten($toggle-thumb-active-color, 18%);
|
||||||
|
|
||||||
|
// Border color
|
||||||
|
$border-color: #5f6368;
|
||||||
|
$border-hover-color: lighten($border-color, 30%);
|
||||||
|
$border-active-color: $primary-color;
|
||||||
|
|
||||||
|
// Scroll bar color
|
||||||
|
$scroll-bar-thumb-color: lighten($bg-color, 20%);
|
||||||
|
$scroll-bar-thumb-hover-color: lighten($scroll-bar-thumb-color, 10%);
|
||||||
|
$scroll-bar-thumb-active-color: lighten($scroll-bar-thumb-color, 18%);
|
||||||
|
|
||||||
|
// App bar color
|
||||||
|
$top-bar-color: #272727;
|
||||||
|
$search-bar-color: lighten($top-bar-color, 18%);
|
||||||
|
$search-bar-focus-color: #ffffff;
|
||||||
|
|
||||||
|
// Tab color
|
||||||
|
$tab-item-color: transparentize(#ffffff, 0.5);
|
||||||
|
$tab-item-active-color: $primary-color;
|
||||||
|
$tab-bg-color: $top-bar-color;
|
||||||
|
$tab-bg-hover-color: transparentize($tab-item-active-color, 0.9);
|
||||||
|
$tab-bg-focus-color: transparentize($tab-item-active-color, 0.8);
|
||||||
|
|
||||||
|
// Media indicator color
|
||||||
|
$media-page-indicator-color: #9e9e9e;
|
||||||
|
$media-page-indicator-active-color: #e6e6e6;
|
||||||
|
|
||||||
|
// Progress indicator color
|
||||||
|
$progress-indicator-color: $primary-color;
|
||||||
|
$progress-indicator-track-color: lighten($progress-indicator-color, 30%);
|
||||||
|
$loading-indicator-color: $primary-color;
|
||||||
|
|
||||||
|
// Contents color in read status
|
||||||
|
$read-primary-text-color: transparentize($primary-text-color, 0.3);
|
||||||
|
$read-secondary-text-color: transparentize($secondary-text-color, 0.3);
|
||||||
|
$read-ui-text-color: transparentize($ui-text-color, 0.3);
|
||||||
|
$read-poll-bar-leading-color: transparentize($progress-indicator-color, 0.3);
|
||||||
|
$read-poll-bar-color: transparentize($progress-indicator-track-color, 0.3);
|
149
app/javascript/styles/mastodon-material/color/v2-light.scss
Normal file
149
app/javascript/styles/mastodon-material/color/v2-light.scss
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
|
||||||
|
$color-scheme: light;
|
||||||
|
|
||||||
|
// Base color
|
||||||
|
$primary-color: #4285f4;
|
||||||
|
$secondary-color: #db4437;
|
||||||
|
$error-color: #B00020;
|
||||||
|
$verified-color: #4caf50;
|
||||||
|
|
||||||
|
// Text color
|
||||||
|
$primary-text-color: #000000;
|
||||||
|
$secondary-text-color: #5f6368;
|
||||||
|
$ui-text-color: #202124;
|
||||||
|
$inverted-text-color: #ffffff;
|
||||||
|
$section-text-color: $primary-color;
|
||||||
|
$info-text-color: #5e5e5e;
|
||||||
|
$tips-text-color: #c0c0c0;
|
||||||
|
$disabled-text-color: rgba(0,0,0,.54);
|
||||||
|
$link-text-color: #4285f4;
|
||||||
|
$menu-text-color: $ui-text-color;
|
||||||
|
$top-bar-text-color: $ui-text-color;
|
||||||
|
$search-bar-text-color: $primary-text-color;
|
||||||
|
$contained-button-text-color: #ffffff;
|
||||||
|
$primary-text-on-shadow-color: #ffffff;
|
||||||
|
$text-field-color: $ui-text-color;
|
||||||
|
|
||||||
|
// Background-color
|
||||||
|
$bg-color: #ffffff;
|
||||||
|
$menu-bg-color: $bg-color;
|
||||||
|
$menu-bg-hover-color: darken($menu-bg-color, 6%);
|
||||||
|
$menu-bg-active-color: darken($menu-bg-color, 10%);
|
||||||
|
$menu-bg-active-hover-color: darken($menu-bg-color, 16%);
|
||||||
|
$card-bg-color: $bg-color;
|
||||||
|
$card-bg-hover-color: darken($card-bg-color, 6%);
|
||||||
|
$succeeded-card-bg-color: lighten($verified-color, 36%);
|
||||||
|
$list-bg-color: $bg-color;
|
||||||
|
$list-bg-hover-color: darken($list-bg-color, 6%);
|
||||||
|
$list-bg-active-color: darken($list-bg-color, 10%);
|
||||||
|
$list-bg-active-hover-color: darken($list-bg-color, 16%);
|
||||||
|
$list-bg-inactive-color: darken($list-bg-color, 10%);
|
||||||
|
$text-field-bg-color: $card-bg-color;
|
||||||
|
$dropdown-field-bg-color: darken($bg-color, 6%);
|
||||||
|
$dropdown-field-bg-hover-color: darken($dropdown-field-bg-color, 6%);
|
||||||
|
$dropdown-field-bg-active-color: darken($dropdown-field-bg-color, 10%);
|
||||||
|
$verified-bg-color: lighten($verified-color, 20%);
|
||||||
|
$unread-bg-color: transparentize($primary-color, 0.8);
|
||||||
|
|
||||||
|
// Chip color
|
||||||
|
$contained-chip-color: #e0e0e0;
|
||||||
|
$chip-selected-text-color: $primary-color;
|
||||||
|
$contained-chip-hover-color: darken($contained-chip-color, 6%);
|
||||||
|
$contained-chip-selected-color: transparentize($chip-selected-text-color, 0.7);
|
||||||
|
$outlined-chip-color: #ffffff;
|
||||||
|
$outlined-chip-hover-color: darken($outlined-chip-color, 6%);
|
||||||
|
$outlined-chip-selected-color: transparentize($chip-selected-text-color, 0.8);
|
||||||
|
$outlined-chip-selected-border-color: transparentize($chip-selected-text-color, 0.7);
|
||||||
|
|
||||||
|
// Relationship tag color
|
||||||
|
$relationship-tag-color: transparentize(#e0e0e0, 0.2);
|
||||||
|
|
||||||
|
// Badge color
|
||||||
|
$badge-color: $primary-color;
|
||||||
|
//$badge-color: $secondary-color;
|
||||||
|
|
||||||
|
// Icon color
|
||||||
|
$icon-color: #757575;
|
||||||
|
$icon-hover-color: darken($icon-color, 30%);
|
||||||
|
$icon-bg-hover-color: transparentize(#000000, 0.9);
|
||||||
|
$icon-bg-active-color: transparentize(#000000, 0.8);
|
||||||
|
$disabled-icon-color: lighten($icon-color, 16%);
|
||||||
|
$top-bar-icon-color: $icon-color;
|
||||||
|
$top-bar-icon-hover-color: $icon-hover-color;
|
||||||
|
$top-bar-icon-active-color: $primary-color;
|
||||||
|
$top-bar-unread-icon-color: $secondary-color;
|
||||||
|
$media-icon-color: transparentize(#ffffff, 0.4);
|
||||||
|
$media-icon-hover-color: transparentize(#ffffff, 0.2);
|
||||||
|
$media-icon-bg-color: transparentize(#000000, 0.5);
|
||||||
|
$media-icon-bg-hover-color: transparentize(#000000, 0.8);
|
||||||
|
|
||||||
|
// Control color
|
||||||
|
$control-border-color: $icon-color;
|
||||||
|
$control-border-active-color: $primary-color;
|
||||||
|
$control-border-hover-color: transparentize($control-border-active-color, 0.8);
|
||||||
|
|
||||||
|
// Button color
|
||||||
|
$icon-button-color: $icon-color;
|
||||||
|
$icon-button-hover-color: $icon-hover-color;
|
||||||
|
$icon-button-active-color: $primary-color;
|
||||||
|
$icon-button-active-hover-color: lighten($icon-button-active-color, 10%);
|
||||||
|
$contained-button-color: $primary-color;
|
||||||
|
$contained-button-hover-color: lighten($contained-button-color, 6%);
|
||||||
|
$contained-button-active-color: lighten($contained-button-color, 10%);
|
||||||
|
$outlined-button-color: $primary-color;
|
||||||
|
$outlined-button-hover-color: transparentize($outlined-button-color, 0.8);
|
||||||
|
$outlined-button-active-color: transparentize($outlined-button-color, 0.7);
|
||||||
|
$text-button-color: $primary-color;
|
||||||
|
$text-button-hover-color: transparentize($text-button-color, 0.8);
|
||||||
|
$text-button-focus-color: transparentize($text-button-color, 0.7);
|
||||||
|
$disabled-button-color: #cccccc;
|
||||||
|
$floating-action-button-color: #ffffff;
|
||||||
|
$floating-action-button-hover-color: darken($floating-action-button-color, 6%);
|
||||||
|
$floating-action-button-active-color: darken($floating-action-button-color, 10%);
|
||||||
|
$floating-action-button-icon-color: $primary-color;
|
||||||
|
|
||||||
|
// Toggle color
|
||||||
|
$toggle-thumb-color: #ffffff;
|
||||||
|
$toggle-track-color: darken($toggle-thumb-color, 18%);
|
||||||
|
$toggle-thumb-active-color: #1a73e8;
|
||||||
|
$toggle-track-active-color: lighten($toggle-thumb-active-color, 18%);
|
||||||
|
|
||||||
|
// Border color
|
||||||
|
$border-color: #dadce0;
|
||||||
|
$border-hover-color: darken($border-color, 30%);
|
||||||
|
$border-active-color: $primary-color;
|
||||||
|
|
||||||
|
// Scroll bar color
|
||||||
|
$scroll-bar-thumb-color: darken($bg-color, 20%);
|
||||||
|
$scroll-bar-thumb-hover-color: darken($bg-color, 30%);
|
||||||
|
$scroll-bar-thumb-active-color: darken($bg-color, 38%);
|
||||||
|
|
||||||
|
// App bar color
|
||||||
|
$top-bar-color: $bg-color;
|
||||||
|
$search-bar-color: darken($bg-color, 6%);
|
||||||
|
$search-bar-focus-color: $bg-color;
|
||||||
|
|
||||||
|
// Tab color
|
||||||
|
$tab-item-color: transparentize(#000000, 0.5);
|
||||||
|
$tab-item-active-color: $primary-color;
|
||||||
|
$tab-bg-color: $top-bar-color;
|
||||||
|
$tab-bg-hover-color: transparentize($tab-item-active-color, 0.9);
|
||||||
|
$tab-bg-focus-color: transparentize($tab-item-active-color, 0.8);
|
||||||
|
|
||||||
|
// Media indicator color
|
||||||
|
$media-page-indicator-color: #9e9e9e;
|
||||||
|
$media-page-indicator-active-color: #e6e6e6;
|
||||||
|
|
||||||
|
// Progress indicator color
|
||||||
|
$progress-indicator-color: $primary-color;
|
||||||
|
$progress-indicator-track-color: lighten($progress-indicator-color, 30%);
|
||||||
|
$loading-indicator-color: $primary-color;
|
||||||
|
|
||||||
|
// Contents color in read status
|
||||||
|
$read-primary-text-color: transparentize($primary-text-color, 0.3);
|
||||||
|
$read-secondary-text-color: transparentize($secondary-text-color, 0.3);
|
||||||
|
$read-ui-text-color: transparentize($ui-text-color, 0.3);
|
||||||
|
$read-poll-bar-leading-color: transparentize($progress-indicator-color, 0.3);
|
||||||
|
$read-poll-bar-color: transparentize($progress-indicator-track-color, 0.3);
|
@ -0,0 +1,83 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
@import '../theme/mixins';
|
||||||
|
|
||||||
|
|
||||||
|
// Navigation bar radius
|
||||||
|
$nav-bar-radius: 2px;
|
||||||
|
|
||||||
|
|
||||||
|
// Search bar radius
|
||||||
|
$search-bar-radius: 2px;
|
||||||
|
|
||||||
|
|
||||||
|
// Bar radius settings
|
||||||
|
$bar-radius: 0;
|
||||||
|
|
||||||
|
|
||||||
|
// Button radius settings
|
||||||
|
$button-radius: 2px;
|
||||||
|
|
||||||
|
|
||||||
|
// Card radius settings
|
||||||
|
$card-radius: 2px;
|
||||||
|
|
||||||
|
|
||||||
|
// Dialog radius settings
|
||||||
|
$dialog-radius: 2px;
|
||||||
|
|
||||||
|
|
||||||
|
// Menu radius settings
|
||||||
|
$menu-radius: 2px;
|
||||||
|
|
||||||
|
|
||||||
|
// Table radius settings
|
||||||
|
$table-radius: 2px;
|
||||||
|
|
||||||
|
|
||||||
|
// Media radius settings
|
||||||
|
$media-radius: 0;
|
||||||
|
|
||||||
|
|
||||||
|
// Navigation drawer item settings
|
||||||
|
$nav-drawer-item-radius: 0;
|
||||||
|
|
||||||
|
|
||||||
|
// Avatar cropping settings
|
||||||
|
$avatar-radius: 50%; // Rounded cropping
|
||||||
|
//$avatar-radius: 2px // Material v1 square
|
||||||
|
//$avatar-radius: $card-radius // Fit to card radius
|
||||||
|
|
||||||
|
|
||||||
|
// Tab indicator thickness
|
||||||
|
$tab-indicator-thickness: 2px;
|
||||||
|
//$tab-indicator-thickness: 4px;
|
||||||
|
|
||||||
|
|
||||||
|
// Chip settings
|
||||||
|
$chip-type: contained; // Material v1 styled contained chip
|
||||||
|
|
||||||
|
|
||||||
|
// Button on statuses
|
||||||
|
$button-on-status: text; // Material v1 styled text button
|
||||||
|
//$button-on-status: outlined; // Material v2 styled outlined button
|
||||||
|
|
||||||
|
|
||||||
|
// Button shadow
|
||||||
|
$button-shadow: true; // Material v1 styled colored button with shadow
|
||||||
|
|
||||||
|
|
||||||
|
// Floating Action Button size
|
||||||
|
$fab-size: 56px;
|
||||||
|
|
||||||
|
|
||||||
|
// CW text field border
|
||||||
|
$cw-area-border: underlined;
|
||||||
|
//$cw-area-border: none;
|
||||||
|
|
||||||
|
// Compose text field border
|
||||||
|
$compose-area-border: none;
|
||||||
|
//$compose-area-border: underlined;
|
||||||
|
|
||||||
|
// Other text field border
|
||||||
|
$other-text-area-border: underlined;
|
||||||
|
//$other-text-area-border: none;
|
@ -0,0 +1,97 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
@import '../theme/mixins';
|
||||||
|
|
||||||
|
|
||||||
|
// Navigation bar radius
|
||||||
|
$nav-bar-radius: 8px;
|
||||||
|
//$nav-bar-radius: 28px; // full radius
|
||||||
|
|
||||||
|
|
||||||
|
// Search bar radius
|
||||||
|
$search-bar-radius: 8px;
|
||||||
|
//$search-bar-radius: 21px; // full rounded
|
||||||
|
|
||||||
|
|
||||||
|
// Bar radius settings
|
||||||
|
$bar-radius: 0;
|
||||||
|
//$bar-radius: 8px;
|
||||||
|
|
||||||
|
|
||||||
|
// Button radius settings
|
||||||
|
$button-radius: 8px;
|
||||||
|
|
||||||
|
|
||||||
|
// Card radius settings
|
||||||
|
$card-radius: 8px;
|
||||||
|
|
||||||
|
|
||||||
|
// Dialog radius settings
|
||||||
|
$dialog-radius: 8px;
|
||||||
|
|
||||||
|
|
||||||
|
// Menu radius settings
|
||||||
|
$menu-radius: 8px;
|
||||||
|
|
||||||
|
|
||||||
|
// Table radius settings
|
||||||
|
$table-radius: 8px;
|
||||||
|
//$table-radius: 2px;
|
||||||
|
|
||||||
|
|
||||||
|
// Media radius settings
|
||||||
|
$media-radius: 0;
|
||||||
|
//$media-radius: 2px;
|
||||||
|
|
||||||
|
|
||||||
|
// Navigation drawer item settings
|
||||||
|
$nav-drawer-item-radius: 8px; // corner rounded
|
||||||
|
//$nav-drawer-item-radius: 32px; // full rounded
|
||||||
|
//$nav-drawer-item-radius: 32px 0 0 32px; // left rounded
|
||||||
|
|
||||||
|
|
||||||
|
// Avatar cropping settings
|
||||||
|
$avatar-radius: 50%; // Rounded cropping
|
||||||
|
//$avatar-radius: $card-radius // Fit to card radius
|
||||||
|
//$avatar-radius: 2px // Material v1 square
|
||||||
|
//$avatar-radius: 8px // Material v2 square
|
||||||
|
|
||||||
|
|
||||||
|
// Tab indicator thickness
|
||||||
|
//$tab-indicator-thickness: 2px;
|
||||||
|
$tab-indicator-thickness: 4px;
|
||||||
|
|
||||||
|
|
||||||
|
// Chip settings
|
||||||
|
$chip-type: outlined; // Material v2 styled outlined chip
|
||||||
|
//$outlined-chip: contained; // Material v1 styled contained chip
|
||||||
|
|
||||||
|
|
||||||
|
// Button on statuses
|
||||||
|
$button-on-status: text; // Material v1 styled text button
|
||||||
|
//$button-on-status: outlined; // Material v2 styled outlined button
|
||||||
|
|
||||||
|
|
||||||
|
// Button shadow
|
||||||
|
$button-shadow: true; // Material v1 styled colored button with shadow
|
||||||
|
//$button-shadow: false; // Material v2 styled colored button without shadow
|
||||||
|
|
||||||
|
|
||||||
|
// Floating Action Button size
|
||||||
|
$fab-size: 56px; // Regular
|
||||||
|
//$fab-size: 40px; // Mini
|
||||||
|
|
||||||
|
|
||||||
|
// CW text field border
|
||||||
|
$cw-area-border: none;
|
||||||
|
//$cw-area-border: underlined;
|
||||||
|
//$cw-area-border: outlined;
|
||||||
|
|
||||||
|
// Compose text field border
|
||||||
|
$compose-area-border: none;
|
||||||
|
//$compose-area-border: underlined;
|
||||||
|
//$compose-area-border: outlined;
|
||||||
|
|
||||||
|
// Other text field border
|
||||||
|
$other-text-area-border: underlined;
|
||||||
|
//$other-text-area-border: outlined;
|
||||||
|
//$other-text-area-border: none;
|
21
app/javascript/styles/mastodon-material/plugins/astarte.scss
Normal file
21
app/javascript/styles/mastodon-material/plugins/astarte.scss
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
.announcements {
|
||||||
|
li {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: $card-bg-color;
|
||||||
|
color: $primary-text-color;
|
||||||
|
border-radius: $card-radius;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__icon {
|
||||||
|
margin: 0;
|
||||||
|
right: 16px;
|
||||||
|
height: 20px;
|
||||||
|
|
||||||
|
.icon-button {
|
||||||
|
@include icon-button;
|
||||||
|
|
||||||
|
&:hover { background: transparent }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
app/javascript/styles/mastodon-material/plugins/cards.scss
Normal file
45
app/javascript/styles/mastodon-material/plugins/cards.scss
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
.column {
|
||||||
|
box-shadow: none !important;
|
||||||
|
margin: 2px 0 !important;
|
||||||
|
padding: 8px !important;
|
||||||
|
|
||||||
|
> .scrollable { background: $bg-color }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 415px) {
|
||||||
|
.notification.notification,
|
||||||
|
.status {
|
||||||
|
border-radius: 0;
|
||||||
|
|
||||||
|
&__wrapper.status__wrapper {
|
||||||
|
margin: 8px 0;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.columns-area__panels__main { box-shadow: none }
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
border-bottom: 0;
|
||||||
|
border-radius: $card-radius;
|
||||||
|
background: $card-bg-color;
|
||||||
|
|
||||||
|
&__prepend { padding: 8px 8px 2px 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
.account { border-bottom: 0 !important }
|
||||||
|
|
||||||
|
.status__wrapper.status__wrapper,
|
||||||
|
.notification.notification {
|
||||||
|
@include shadow-1dp;
|
||||||
|
margin: 8px 2px;
|
||||||
|
border-radius: $card-radius;
|
||||||
|
background: $card-bg-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
.status__wrapper.status__wrapper { box-shadow: none !important }
|
||||||
|
|
||||||
|
&__filter-bar { @include non-overflow-shadow-4dp }
|
||||||
|
}
|
97
app/javascript/styles/mastodon-material/plugins/dense.scss
Normal file
97
app/javascript/styles/mastodon-material/plugins/dense.scss
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
$dense: true;
|
||||||
|
|
||||||
|
.drawer {
|
||||||
|
&__header { height: 48px }
|
||||||
|
|
||||||
|
&__tab { margin: 6px auto 6px }
|
||||||
|
}
|
||||||
|
|
||||||
|
.column { margin: 10px 5px }
|
||||||
|
|
||||||
|
.column-header {
|
||||||
|
height: 48px;
|
||||||
|
|
||||||
|
> button { padding: 12px 16px }
|
||||||
|
|
||||||
|
&__buttons { height: 48px }
|
||||||
|
|
||||||
|
&__button { margin: 6px }
|
||||||
|
|
||||||
|
&__back-button { margin: 6px }
|
||||||
|
}
|
||||||
|
|
||||||
|
.column-subheading { padding: 8px 16px }
|
||||||
|
|
||||||
|
.column-link { padding: 12px 16px }
|
||||||
|
|
||||||
|
.tabs-bar__link { padding: 12px 16px 8px 16px }
|
||||||
|
|
||||||
|
.notification__filter-bar button { padding: 12px 0 }
|
||||||
|
|
||||||
|
.getting-started__footer {
|
||||||
|
padding: 12px 16px;
|
||||||
|
|
||||||
|
p { margin-bottom: 12px }
|
||||||
|
}
|
||||||
|
|
||||||
|
.compose-form {
|
||||||
|
.compose-form {
|
||||||
|
&__publish { padding-top: 8px }
|
||||||
|
|
||||||
|
&__buttons-wrapper {
|
||||||
|
.icon-button,
|
||||||
|
.text-icon-button { padding: 4px }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
padding: 8px 8px 4px 66px;
|
||||||
|
|
||||||
|
&__expand { width: 66px }
|
||||||
|
|
||||||
|
&__avatar {
|
||||||
|
left: 8px;
|
||||||
|
top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
padding-top: 2px;
|
||||||
|
|
||||||
|
&__spoiler-link,
|
||||||
|
&__read-more-button {
|
||||||
|
height: auto;
|
||||||
|
padding: 4px 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__action-bar-button {
|
||||||
|
margin: 0 4px;
|
||||||
|
|
||||||
|
&.icon-button { padding: 4px }
|
||||||
|
}
|
||||||
|
|
||||||
|
&__action-bar-dropdown {
|
||||||
|
width: 28px !important;
|
||||||
|
height: 28px !important;
|
||||||
|
margin: 0 4px;
|
||||||
|
|
||||||
|
.icon-button { padding: 4px }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 630px) {
|
||||||
|
.columns-area--mobile .status { padding: 8px 8px 8px 78px }
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailed-status {
|
||||||
|
padding: 14px;
|
||||||
|
|
||||||
|
&__meta { margin-top: 14px }
|
||||||
|
|
||||||
|
&__action-bar { padding: 10px 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
.reply-indicator { padding: 8px }
|
||||||
|
|
||||||
|
.button.button--block { margin: 6px 0 }
|
@ -0,0 +1,27 @@
|
|||||||
|
.drawer__tab.short-label {
|
||||||
|
padding: 8px;
|
||||||
|
|
||||||
|
.drawer__tab__short-label {
|
||||||
|
overflow-x: visible;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
i { margin: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabs-bar__link span { margin-left: 0 }
|
||||||
|
|
||||||
|
.tabs-bar.bottom-bar {
|
||||||
|
@include shadow-4dp;
|
||||||
|
|
||||||
|
.tabs-bar {
|
||||||
|
&__link,
|
||||||
|
&__link.active { border-bottom: 4px solid transparent }
|
||||||
|
}
|
||||||
|
|
||||||
|
&__link span { margin-left: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1190px) {
|
||||||
|
.tabs-bar__wrapper .tabs-bar.bottom-bar { width: 100% }
|
||||||
|
}
|
72
app/javascript/styles/mastodon-material/plugins/plus.scss
Normal file
72
app/javascript/styles/mastodon-material/plugins/plus.scss
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
$name: "Plus";
|
||||||
|
$plugin-version: "1.0.0";
|
||||||
|
$target-version: "1.0.0";
|
||||||
|
$website: "";
|
||||||
|
@include version-check($name, $plugin-version, $target-version, $website);
|
||||||
|
|
||||||
|
.status__action-bar-button {
|
||||||
|
background: #eeeeee;
|
||||||
|
width: 28px !important;
|
||||||
|
height: 28px !important;
|
||||||
|
|
||||||
|
.fa { vertical-align: bottom }
|
||||||
|
}
|
||||||
|
|
||||||
|
// exception
|
||||||
|
.media-modal__overlay .picture-in-picture__footer .icon-button,
|
||||||
|
.status__action-bar-button.icon-button--with-counter { background: transparent }
|
||||||
|
|
||||||
|
// favorite icon
|
||||||
|
.star-icon.active,
|
||||||
|
.star-icon.icon-button.active.activate,
|
||||||
|
.notification__favourite-icon-wrapper .star-icon { background: #db4437 }
|
||||||
|
|
||||||
|
.notification__favourite-icon-wrapper {
|
||||||
|
left: -34px;
|
||||||
|
|
||||||
|
.star-icon {
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
vertical-align: baseline;
|
||||||
|
font-size: 18px;
|
||||||
|
|
||||||
|
&.fa-fw::before { vertical-align: middle }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fedibird bottom bar in mobile
|
||||||
|
.tabs-bar.bottom-bar {
|
||||||
|
background: #212121;
|
||||||
|
|
||||||
|
.tabs-bar__link {
|
||||||
|
color: transparentize(#ffffff, 0.5);
|
||||||
|
|
||||||
|
&.active { color: #ffffff }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quote
|
||||||
|
.quote-status {
|
||||||
|
border-radius: 0;
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid $border-color;
|
||||||
|
padding: 16px 16px 16px 48px;
|
||||||
|
margin-top: 16px;
|
||||||
|
|
||||||
|
.detailed-status__display-avatar,
|
||||||
|
.status__avatar {
|
||||||
|
left: 0 !important;
|
||||||
|
top: 16px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $dense {
|
||||||
|
.quote-status {
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 16px;
|
||||||
|
|
||||||
|
.detailed-status__display-avatar,
|
||||||
|
.status__avatar { top: 8px !important }
|
||||||
|
}
|
||||||
|
}
|
77
app/javascript/styles/mastodon-material/plugins/quote.scss
Normal file
77
app/javascript/styles/mastodon-material/plugins/quote.scss
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
$name: "Quote";
|
||||||
|
$plugin-version: "1.0.0";
|
||||||
|
$target-version: "1.0.0";
|
||||||
|
$website: "";
|
||||||
|
@include version-check($name, $plugin-version, $target-version, $website);
|
||||||
|
|
||||||
|
.quote-indicator,
|
||||||
|
.reply-indicator {
|
||||||
|
@include card-elevation(false);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
|
||||||
|
&__header { margin-bottom: 4px }
|
||||||
|
|
||||||
|
&__display-avatar {
|
||||||
|
margin-right: 8px;
|
||||||
|
|
||||||
|
.account__avatar {
|
||||||
|
width: 40px !important;
|
||||||
|
height: 40px !important;
|
||||||
|
background-size: 40px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
padding-top: 4px;
|
||||||
|
color: $primary-text-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote-status {
|
||||||
|
@include card-elevation(false);
|
||||||
|
background: none;
|
||||||
|
padding: 16px 16px 16px 72px;
|
||||||
|
|
||||||
|
.detailed-status__display-avatar,
|
||||||
|
.status__avatar {
|
||||||
|
top: 16px !important;
|
||||||
|
left: 16px !important;
|
||||||
|
width: 40px !important;
|
||||||
|
height: 40px !important;
|
||||||
|
|
||||||
|
&>div {
|
||||||
|
width: 40px !important;
|
||||||
|
height: 40px !important;
|
||||||
|
background-size: 40px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.status__content { padding-top: 8px }
|
||||||
|
|
||||||
|
.detailed-status__display-name {
|
||||||
|
span,
|
||||||
|
strong { display: block }
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-name {
|
||||||
|
padding-left: 0;
|
||||||
|
|
||||||
|
&__account { color: $secondary-text-color }
|
||||||
|
}
|
||||||
|
|
||||||
|
&>.unlisted-quote>button { color: $ui-text-color }
|
||||||
|
}
|
||||||
|
|
||||||
|
@if $dense {
|
||||||
|
.quote-status {
|
||||||
|
padding: 8px 8px 8px 58px;
|
||||||
|
margin-top: 4px;
|
||||||
|
|
||||||
|
.detailed-status__display-avatar,
|
||||||
|
.status__avatar {
|
||||||
|
top: 8px !important;
|
||||||
|
left: 8px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
// Add your customization below
|
@ -0,0 +1,3 @@
|
|||||||
|
// Add your settings below
|
||||||
|
|
||||||
|
@import '../../color/mastodon-dark.scss';
|
@ -0,0 +1,3 @@
|
|||||||
|
// Add your icon customization below
|
||||||
|
|
||||||
|
$icon-font-source: self;
|
@ -0,0 +1 @@
|
|||||||
|
// Add your customization below
|
@ -0,0 +1,11 @@
|
|||||||
|
@charset "UTF-8";
|
||||||
|
|
||||||
|
@import '../../theme/base_config', '../../theme/base_icon_config';
|
||||||
|
@import 'config', 'icon_config';
|
||||||
|
@import 'color', 'layout';
|
||||||
|
|
||||||
|
@import '../../theme/functions', '../../theme/mixins';
|
||||||
|
@import '../../theme/theme';
|
||||||
|
@import 'plugins';
|
||||||
|
|
||||||
|
@import '../../theme/material-icons';
|
@ -0,0 +1,6 @@
|
|||||||
|
@import '../../plugins/astarte';
|
||||||
|
//@import '../../plugins/cards';
|
||||||
|
//@import '../../plugins/dense';
|
||||||
|
@import '../../plugins/fedibird';
|
||||||
|
@import '../../plugins/quote';
|
||||||
|
//@import '../../plugins/plus';
|
@ -0,0 +1 @@
|
|||||||
|
// Add your customization below
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user