Merge commit '31abef8917879917a330419fe3981a2fb7f35b69' into glitch-soc/merge-upstream
Conflicts: - `app/services/post_status_service.rb`: Upstream added a line adjacent to one that had been modified due to local-only posting. Added upstream's change. - `tsconfig.json`: Upstream updated Typescript and updated `tsconfig` in the process by changing paths, where glitch-soc had extra paths. Updated as upstream did.
This commit is contained in:
commit
79958d9442
2
.github/actions/setup-ruby/action.yml
vendored
2
.github/actions/setup-ruby/action.yml
vendored
@ -14,7 +14,7 @@ runs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libicu-dev libidn11-dev libvips42 ${{ inputs.additional-system-dependencies }}
|
sudo apt-get install --no-install-recommends -y libicu-dev libidn11-dev libvips42 ${{ inputs.additional-system-dependencies }}
|
||||||
|
|
||||||
- name: Set up Ruby
|
- name: Set up Ruby
|
||||||
uses: ruby/setup-ruby@4eb9f110bac952a8b68ecf92e3b5c7a987594ba6 # v1
|
uses: ruby/setup-ruby@4eb9f110bac952a8b68ecf92e3b5c7a987594ba6 # v1
|
||||||
|
|||||||
10
.github/workflows/test-ruby.yml
vendored
10
.github/workflows/test-ruby.yml
vendored
@ -124,7 +124,6 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ruby-version:
|
ruby-version:
|
||||||
- '3.2'
|
|
||||||
- '3.3'
|
- '3.3'
|
||||||
- '.ruby-version'
|
- '.ruby-version'
|
||||||
steps:
|
steps:
|
||||||
@ -217,7 +216,6 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ruby-version:
|
ruby-version:
|
||||||
- '3.2'
|
|
||||||
- '3.3'
|
- '3.3'
|
||||||
- '.ruby-version'
|
- '.ruby-version'
|
||||||
|
|
||||||
@ -348,7 +346,6 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
ruby-version:
|
ruby-version:
|
||||||
- '3.2'
|
|
||||||
- '3.3'
|
- '3.3'
|
||||||
- '.ruby-version'
|
- '.ruby-version'
|
||||||
search-image:
|
search-image:
|
||||||
@ -387,10 +384,3 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: test-search-logs-${{ matrix.ruby-version }}
|
name: test-search-logs-${{ matrix.ruby-version }}
|
||||||
path: log/
|
path: log/
|
||||||
|
|
||||||
- name: Archive test screenshots
|
|
||||||
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
|
|
||||||
if: failure()
|
|
||||||
with:
|
|
||||||
name: test-search-screenshots
|
|
||||||
path: tmp/capybara/
|
|
||||||
|
|||||||
@ -8,7 +8,7 @@ AllCops:
|
|||||||
- lib/mastodon/migration_helpers.rb
|
- lib/mastodon/migration_helpers.rb
|
||||||
ExtraDetails: true
|
ExtraDetails: true
|
||||||
NewCops: enable
|
NewCops: enable
|
||||||
TargetRubyVersion: 3.2 # Oldest supported ruby version
|
TargetRubyVersion: 3.3 # Oldest supported ruby version
|
||||||
|
|
||||||
inherit_from:
|
inherit_from:
|
||||||
- .rubocop/layout.yml
|
- .rubocop/layout.yml
|
||||||
|
|||||||
@ -137,11 +137,7 @@ const preview: Preview = {
|
|||||||
}, [currentLocale, currentLocaleData]);
|
}, [currentLocale, currentLocaleData]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IntlProvider
|
<IntlProvider locale={currentLocale} messages={currentLocaleData}>
|
||||||
locale={currentLocale}
|
|
||||||
messages={currentLocaleData}
|
|
||||||
textComponent='span'
|
|
||||||
>
|
|
||||||
<Story />
|
<Story />
|
||||||
</IntlProvider>
|
</IntlProvider>
|
||||||
);
|
);
|
||||||
|
|||||||
2
Gemfile
2
Gemfile
@ -1,7 +1,7 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
ruby '>= 3.2.0', '< 3.5.0'
|
ruby '>= 3.3.0', '< 3.5.0'
|
||||||
|
|
||||||
gem 'propshaft'
|
gem 'propshaft'
|
||||||
gem 'puma', '~> 7.0'
|
gem 'puma', '~> 7.0'
|
||||||
|
|||||||
@ -72,7 +72,7 @@ Mastodon is a **free, open-source social network server** based on [ActivityPub]
|
|||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
- **Ruby** 3.2+
|
- **Ruby** 3.3+
|
||||||
- **PostgreSQL** 14+
|
- **PostgreSQL** 14+
|
||||||
- **Redis** 7.0+
|
- **Redis** 7.0+
|
||||||
- **Node.js** 20+
|
- **Node.js** 20+
|
||||||
|
|||||||
@ -0,0 +1,26 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::Accounts::EmailSubscriptionsController < Api::BaseController
|
||||||
|
before_action :set_account
|
||||||
|
before_action :require_feature_enabled!
|
||||||
|
before_action :require_account_permissions!
|
||||||
|
|
||||||
|
def create
|
||||||
|
@account.email_subscriptions.create!(email: params[:email], locale: I18n.locale)
|
||||||
|
render_empty
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_account
|
||||||
|
@account = Account.local.find(params[:account_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
def require_feature_enabled!
|
||||||
|
head 404 unless Mastodon::Feature.email_subscriptions_enabled?
|
||||||
|
end
|
||||||
|
|
||||||
|
def require_account_permissions!
|
||||||
|
head 404 if @account.unavailable? || !@account.user_can?(:manage_email_subscriptions) || !@account.user_email_subscriptions_enabled?
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -53,19 +53,21 @@ module SignatureVerification
|
|||||||
|
|
||||||
raise Mastodon::SignatureVerificationError, 'Request not signed' unless signed_request?
|
raise Mastodon::SignatureVerificationError, 'Request not signed' unless signed_request?
|
||||||
|
|
||||||
actor = actor_from_key_id
|
keypair = keypair_from_key_id
|
||||||
|
|
||||||
raise Mastodon::SignatureVerificationError, "Public key not found for key #{signature_key_id}" if actor.nil?
|
raise Mastodon::SignatureVerificationError, "Public key not found for key #{signature_key_id}" if keypair.nil?
|
||||||
|
|
||||||
return (@signed_request_actor = actor) if signed_request.verified?(actor)
|
check_keypair_validity!(keypair)
|
||||||
|
return (@signed_request_actor = keypair.actor) if signed_request.verified?(keypair)
|
||||||
|
|
||||||
actor = stoplight_wrapper.run { actor_refresh_key!(actor) }
|
keypair = stoplight_wrapper.run { keypair_refresh_key!(keypair) }
|
||||||
|
|
||||||
raise Mastodon::SignatureVerificationError, "Could not refresh public key #{signature_key_id}" if actor.nil?
|
raise Mastodon::SignatureVerificationError, "Could not refresh public key #{signature_key_id}" if keypair.nil?
|
||||||
|
|
||||||
return (@signed_request_actor = actor) if signed_request.verified?(actor)
|
check_keypair_validity!(keypair)
|
||||||
|
return (@signed_request_actor = keypair.actor) if signed_request.verified?(keypair)
|
||||||
|
|
||||||
fail_with! "Verification failed for #{actor.to_log_human_identifier} #{actor.uri}"
|
fail_with! "Verification failed for #{keypair.actor.to_log_human_identifier} #{keypair.actor.uri} #{keypair.uri}"
|
||||||
rescue Mastodon::MalformedHeaderError => e
|
rescue Mastodon::MalformedHeaderError => e
|
||||||
@signature_verification_failure_code = 400
|
@signature_verification_failure_code = 400
|
||||||
fail_with! e.message
|
fail_with! e.message
|
||||||
@ -89,7 +91,7 @@ module SignatureVerification
|
|||||||
@signed_request_actor = nil
|
@signed_request_actor = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def actor_from_key_id
|
def keypair_from_key_id
|
||||||
key_id = signed_request.key_id
|
key_id = signed_request.key_id
|
||||||
domain = key_id.start_with?('acct:') ? key_id.split('@').last : key_id
|
domain = key_id.start_with?('acct:') ? key_id.split('@').last : key_id
|
||||||
|
|
||||||
@ -101,9 +103,10 @@ module SignatureVerification
|
|||||||
if key_id.start_with?('acct:')
|
if key_id.start_with?('acct:')
|
||||||
stoplight_wrapper.run { ResolveAccountService.new.call(key_id.delete_prefix('acct:'), suppress_errors: false) }
|
stoplight_wrapper.run { ResolveAccountService.new.call(key_id.delete_prefix('acct:'), suppress_errors: false) }
|
||||||
elsif !ActivityPub::TagManager.instance.local_uri?(key_id)
|
elsif !ActivityPub::TagManager.instance.local_uri?(key_id)
|
||||||
account = ActivityPub::TagManager.instance.uri_to_actor(key_id)
|
keypair = Keypair.from_keyid(key_id)
|
||||||
account ||= stoplight_wrapper.run { ActivityPub::FetchRemoteKeyService.new.call(key_id, suppress_errors: false) }
|
return keypair if keypair.present?
|
||||||
account
|
|
||||||
|
stoplight_wrapper.run { ActivityPub::FetchRemoteKeyService.new.call(key_id, suppress_errors: false) }
|
||||||
end
|
end
|
||||||
rescue Mastodon::PrivateNetworkAddressError => e
|
rescue Mastodon::PrivateNetworkAddressError => e
|
||||||
raise Mastodon::SignatureVerificationError, "Requests to private network addresses are disallowed (tried to query #{e.host})"
|
raise Mastodon::SignatureVerificationError, "Requests to private network addresses are disallowed (tried to query #{e.host})"
|
||||||
@ -120,14 +123,20 @@ module SignatureVerification
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def actor_refresh_key!(actor)
|
def keypair_refresh_key!(keypair)
|
||||||
return if actor.local? || !actor.activitypub?
|
# TODO: this currently only is concerned with refreshing the actor and returning the legacy key, this needs to be reworked
|
||||||
return actor.refresh! if actor.respond_to?(:refresh!) && actor.possibly_stale?
|
return if keypair.actor.local? || !keypair.actor.activitypub?
|
||||||
|
return keypair.actor.refresh! if keypair.actor.respond_to?(:refresh!) && keypair.actor.possibly_stale?
|
||||||
|
|
||||||
ActivityPub::FetchRemoteActorService.new.call(actor.uri, only_key: true, suppress_errors: false)
|
Keypair.from_legacy_account(ActivityPub::FetchRemoteActorService.new.call(keypair.actor.uri, only_key: true, suppress_errors: false))
|
||||||
rescue Mastodon::PrivateNetworkAddressError => e
|
rescue Mastodon::PrivateNetworkAddressError => e
|
||||||
raise Mastodon::SignatureVerificationError, "Requests to private network addresses are disallowed (tried to query #{e.host})"
|
raise Mastodon::SignatureVerificationError, "Requests to private network addresses are disallowed (tried to query #{e.host})"
|
||||||
rescue Mastodon::HostValidationError, ActivityPub::FetchRemoteActorService::Error, Webfinger::Error => e
|
rescue Mastodon::HostValidationError, ActivityPub::FetchRemoteActorService::Error, Webfinger::Error => e
|
||||||
raise Mastodon::SignatureVerificationError, e.message
|
raise Mastodon::SignatureVerificationError, e.message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def check_keypair_validity!(keypair)
|
||||||
|
raise Mastodon::SignatureVerification, "Key #{signature_key_id} is revoked" if keypair.revoked?
|
||||||
|
raise Mastodon::SignatureVerification, "Key #{signature_key_id} has expired" if keypair.expired?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -0,0 +1,17 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class EmailSubscriptions::ConfirmationsController < ApplicationController
|
||||||
|
layout 'auth'
|
||||||
|
|
||||||
|
before_action :set_email_subscription
|
||||||
|
|
||||||
|
def show
|
||||||
|
@email_subscription.confirm! unless @email_subscription.confirmed?
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_email_subscription
|
||||||
|
@email_subscription = EmailSubscription.find_by!(confirmation_token: params[:confirmation_token])
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -1,39 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class MailSubscriptionsController < ApplicationController
|
|
||||||
layout 'auth'
|
|
||||||
|
|
||||||
skip_before_action :require_functional!
|
|
||||||
|
|
||||||
before_action :set_user
|
|
||||||
before_action :set_type
|
|
||||||
|
|
||||||
protect_from_forgery with: :null_session
|
|
||||||
|
|
||||||
def show; end
|
|
||||||
|
|
||||||
def create
|
|
||||||
@user.settings[email_type_from_param] = false
|
|
||||||
@user.save!
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def set_user
|
|
||||||
@user = GlobalID::Locator.locate_signed(params[:token], for: 'unsubscribe')
|
|
||||||
not_found unless @user
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_type
|
|
||||||
@type = email_type_from_param
|
|
||||||
end
|
|
||||||
|
|
||||||
def email_type_from_param
|
|
||||||
case params[:type]
|
|
||||||
when 'follow', 'reblog', 'favourite', 'mention', 'follow_request'
|
|
||||||
"notification_emails.#{params[:type]}"
|
|
||||||
else
|
|
||||||
not_found
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
class Settings::PrivacyController < Settings::BaseController
|
class Settings::PrivacyController < Settings::BaseController
|
||||||
before_action :set_account
|
before_action :set_account
|
||||||
|
before_action :set_email_subscriptions_count
|
||||||
|
|
||||||
def show; end
|
def show; end
|
||||||
|
|
||||||
@ -24,4 +25,8 @@ class Settings::PrivacyController < Settings::BaseController
|
|||||||
def set_account
|
def set_account
|
||||||
@account = current_account
|
@account = current_account
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def set_email_subscriptions_count
|
||||||
|
@email_subscriptions_count = with_read_replica { @account.email_subscriptions.confirmed.count }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
58
app/controllers/unsubscriptions_controller.rb
Normal file
58
app/controllers/unsubscriptions_controller.rb
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class UnsubscriptionsController < ApplicationController
|
||||||
|
layout 'auth'
|
||||||
|
|
||||||
|
skip_before_action :require_functional!
|
||||||
|
|
||||||
|
before_action :set_recipient
|
||||||
|
before_action :set_type
|
||||||
|
before_action :set_scope
|
||||||
|
before_action :require_type_if_user!
|
||||||
|
|
||||||
|
protect_from_forgery with: :null_session
|
||||||
|
|
||||||
|
def show; end
|
||||||
|
|
||||||
|
def create
|
||||||
|
case @scope
|
||||||
|
when :user
|
||||||
|
@recipient.settings[@type] = false
|
||||||
|
@recipient.save!
|
||||||
|
when :email_subscription
|
||||||
|
@recipient.destroy!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_recipient
|
||||||
|
@recipient = GlobalID::Locator.locate_signed(params[:token], for: 'unsubscribe')
|
||||||
|
not_found unless @recipient
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_scope
|
||||||
|
if @recipient.is_a?(User)
|
||||||
|
@scope = :user
|
||||||
|
elsif @recipient.is_a?(EmailSubscription)
|
||||||
|
@scope = :email_subscription
|
||||||
|
else
|
||||||
|
not_found
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_type
|
||||||
|
@type = email_type_from_param
|
||||||
|
end
|
||||||
|
|
||||||
|
def require_type_if_user!
|
||||||
|
not_found if @recipient.is_a?(User) && @type.blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
def email_type_from_param
|
||||||
|
case params[:type]
|
||||||
|
when 'follow', 'reblog', 'favourite', 'mention', 'follow_request'
|
||||||
|
"notification_emails.#{params[:type]}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -1,12 +1,14 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { injectIntl, defineMessages } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import api from 'mastodon/api';
|
import api from 'mastodon/api';
|
||||||
|
|
||||||
|
import { injectIntl } from '../intl';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
legal: { id: 'report.categories.legal', defaultMessage: 'Legal' },
|
legal: { id: 'report.categories.legal', defaultMessage: 'Legal' },
|
||||||
other: { id: 'report.categories.other', defaultMessage: 'Other' },
|
other: { id: 'report.categories.other', defaultMessage: 'Other' },
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { useState, useCallback, useRef, useId, Fragment } from 'react';
|
import { useState, useCallback, useRef, useId } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage, useIntl } from 'react-intl';
|
import { FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
@ -84,7 +84,6 @@ export const AltTextBadge: React.FC<{ description: string }> = ({
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='alt_text_badge.title'
|
id='alt_text_badge.title'
|
||||||
defaultMessage='Alt text'
|
defaultMessage='Alt text'
|
||||||
tagName={Fragment}
|
|
||||||
/>
|
/>
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
align-items: start;
|
align-items: start;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
background-color: var(--color-bg-brand-softer);
|
background-color: var(--color-bg-brand-softest);
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
border-radius: 12px;
|
border-radius: 12px;
|
||||||
}
|
}
|
||||||
@ -86,11 +86,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.variantSubtle {
|
.variantSubtle {
|
||||||
border: 1px solid var(--color-bg-brand-softer);
|
border: 1px solid var(--color-bg-brand-softest);
|
||||||
background-color: var(--color-bg-primary);
|
background-color: var(--color-bg-primary);
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
background-color: var(--color-bg-brand-softer);
|
background-color: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,11 +105,11 @@
|
|||||||
|
|
||||||
.variantInverted {
|
.variantInverted {
|
||||||
background-color: var(--color-bg-inverted);
|
background-color: var(--color-bg-inverted);
|
||||||
color: var(--color-text-on-inverted);
|
color: var(--color-text-inverted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.variantSuccess {
|
.variantSuccess {
|
||||||
background-color: var(--color-bg-success-softer);
|
background-color: var(--color-bg-success-softest);
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
background-color: var(--color-bg-success-soft);
|
background-color: var(--color-bg-success-soft);
|
||||||
@ -117,7 +117,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.variantWarning {
|
.variantWarning {
|
||||||
background-color: var(--color-bg-warning-softer);
|
background-color: var(--color-bg-warning-softest);
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
background-color: var(--color-bg-warning-soft);
|
background-color: var(--color-bg-warning-soft);
|
||||||
@ -125,7 +125,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.variantError {
|
.variantError {
|
||||||
background-color: var(--color-bg-error-softer);
|
background-color: var(--color-bg-error-softest);
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
background-color: var(--color-bg-error-soft);
|
background-color: var(--color-bg-error-soft);
|
||||||
|
|||||||
@ -61,6 +61,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
[data-has-error='true'] & {
|
[data-has-error='true'] & {
|
||||||
border-color: var(--color-text-error);
|
border-color: var(--color-border-error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,11 +32,11 @@
|
|||||||
&:focus:user-invalid,
|
&:focus:user-invalid,
|
||||||
&:required:user-invalid,
|
&:required:user-invalid,
|
||||||
[data-has-error='true'] & {
|
[data-has-error='true'] & {
|
||||||
outline-color: var(--color-text-error);
|
outline-color: var(--color-border-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
outline-color: var(--color-text-brand);
|
outline-color: var(--color-border-brand);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:required:user-valid {
|
&:required:user-valid {
|
||||||
|
|||||||
26
app/javascript/mastodon/components/intl.tsx
Normal file
26
app/javascript/mastodon/components/intl.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import type { ComponentClass } from 'react';
|
||||||
|
|
||||||
|
import { useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
interface IntlHocProps<TProps extends Record<string, unknown>> {
|
||||||
|
component: ComponentClass<TProps>;
|
||||||
|
props: TProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const IntlHoc = <TProps extends Record<string, unknown>>({
|
||||||
|
component: Component,
|
||||||
|
props,
|
||||||
|
}: IntlHocProps<TProps>) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
return <Component {...props} intl={intl} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const injectIntl = <TProps extends Record<string, unknown>>(
|
||||||
|
Component: ComponentClass<TProps>,
|
||||||
|
) => {
|
||||||
|
const WrappedComponent = (props: Omit<TProps, 'intl'>) => (
|
||||||
|
<IntlHoc component={Component} props={props as TProps} />
|
||||||
|
);
|
||||||
|
WrappedComponent.displayName = `injectIntl(${(Component.displayName ?? Component.name) || 'Component'})`;
|
||||||
|
return WrappedComponent;
|
||||||
|
};
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
import { FormattedMessage, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
@ -14,6 +14,8 @@ import { ShortNumber } from 'mastodon/components/short_number';
|
|||||||
import { Skeleton } from 'mastodon/components/skeleton';
|
import { Skeleton } from 'mastodon/components/skeleton';
|
||||||
import { domain } from 'mastodon/initial_state';
|
import { domain } from 'mastodon/initial_state';
|
||||||
|
|
||||||
|
import { injectIntl } from './intl';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
aboutActiveUsers: { id: 'server_banner.about_active_users', defaultMessage: 'People using this server during the last 30 days (Monthly Active Users)' },
|
aboutActiveUsers: { id: 'server_banner.about_active_users', defaultMessage: 'People using this server during the last 30 days (Monthly Active Users)' },
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { injectIntl, defineMessages, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
@ -24,6 +24,7 @@ import { MediaGallery, Video, Audio } from '../features/ui/util/async-components
|
|||||||
import { SensitiveMediaContext } from '../features/ui/util/sensitive_media_context';
|
import { SensitiveMediaContext } from '../features/ui/util/sensitive_media_context';
|
||||||
import { displayMedia } from '../initial_state';
|
import { displayMedia } from '../initial_state';
|
||||||
|
|
||||||
|
import { injectIntl } from './intl';
|
||||||
import { StatusHeader } from './status/header'
|
import { StatusHeader } from './status/header'
|
||||||
import { LinkedDisplayName } from './display_name';
|
import { LinkedDisplayName } from './display_name';
|
||||||
import { getHashtagBarForStatus } from './hashtag_bar';
|
import { getHashtagBarForStatus } from './hashtag_bar';
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
|
|
||||||
@ -23,10 +23,12 @@ import { Dropdown } from 'mastodon/components/dropdown_menu';
|
|||||||
import { me, quickBoosting } from '../../initial_state';
|
import { me, quickBoosting } from '../../initial_state';
|
||||||
|
|
||||||
import { IconButton } from '../icon_button';
|
import { IconButton } from '../icon_button';
|
||||||
|
import { injectIntl } from '../intl';
|
||||||
import { BoostButton } from '../status/boost_button';
|
import { BoostButton } from '../status/boost_button';
|
||||||
import { RemoveQuoteHint } from './remove_quote_hint';
|
import { RemoveQuoteHint } from './remove_quote_hint';
|
||||||
import { quoteItemState, selectStatusState } from '../status/boost_button_utils';
|
import { quoteItemState, selectStatusState } from '../status/boost_button_utils';
|
||||||
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||||
redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
|
redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage, injectIntl } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import { withRouter } from 'react-router-dom';
|
import { withRouter } from 'react-router-dom';
|
||||||
@ -16,6 +16,7 @@ import { identityContextPropShape, withIdentity } from 'mastodon/identity_contex
|
|||||||
import { languages as preloadedLanguages } from 'mastodon/initial_state';
|
import { languages as preloadedLanguages } from 'mastodon/initial_state';
|
||||||
|
|
||||||
import { EmojiHTML } from './emoji/html';
|
import { EmojiHTML } from './emoji/html';
|
||||||
|
import { injectIntl } from './intl';
|
||||||
import { HandledLink } from './status/handled_link';
|
import { HandledLink } from './status/handled_link';
|
||||||
|
|
||||||
const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
|
const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
|
||||||
|
|||||||
@ -335,7 +335,6 @@ export const QuotedStatus: React.FC<QuotedStatusProps> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='status__quote'>
|
<div className='status__quote'>
|
||||||
{/* @ts-expect-error Status is not yet typed */}
|
|
||||||
<StatusContainer
|
<StatusContainer
|
||||||
isQuotedPost
|
isQuotedPost
|
||||||
id={quotedStatusId}
|
id={quotedStatusId}
|
||||||
|
|||||||
@ -22,8 +22,8 @@ button.tag:focus-visible {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.active {
|
.active {
|
||||||
border-color: var(--color-text-brand);
|
border-color: var(--color-border-brand);
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,3 @@
|
|||||||
import { injectIntl } from 'react-intl';
|
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -43,6 +41,7 @@ import {
|
|||||||
undoStatusTranslation,
|
undoStatusTranslation,
|
||||||
} from '../actions/statuses';
|
} from '../actions/statuses';
|
||||||
import { setStatusQuotePolicy } from '../actions/statuses_typed';
|
import { setStatusQuotePolicy } from '../actions/statuses_typed';
|
||||||
|
import { injectIntl } from '../components/intl';
|
||||||
import Status from '../components/status';
|
import Status from '../components/status';
|
||||||
import { deleteModal } from '../initial_state';
|
import { deleteModal } from '../initial_state';
|
||||||
import { makeGetStatus, makeGetPictureInPicture } from '../selectors';
|
import { makeGetStatus, makeGetPictureInPicture } from '../selectors';
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'mastodon/actions/server';
|
import { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'mastodon/actions/server';
|
||||||
import { Account } from 'mastodon/components/account';
|
import { Account } from 'mastodon/components/account';
|
||||||
import Column from 'mastodon/components/column';
|
import Column from 'mastodon/components/column';
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { useCallback, useState } from 'react';
|
|||||||
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import { CharacterCounter } from '@/mastodon/components/character_counter';
|
||||||
import { Details } from '@/mastodon/components/details';
|
import { Details } from '@/mastodon/components/details';
|
||||||
import { TextAreaField } from '@/mastodon/components/form_fields';
|
import { TextAreaField } from '@/mastodon/components/form_fields';
|
||||||
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
|
import { LoadingIndicator } from '@/mastodon/components/loading_indicator';
|
||||||
@ -84,7 +85,12 @@ export const ImageAltTextField: FC<{
|
|||||||
const altLimit = useAppSelector(
|
const altLimit = useAppSelector(
|
||||||
(state) =>
|
(state) =>
|
||||||
state.server.getIn(
|
state.server.getIn(
|
||||||
['server', 'configuration', 'media_attachments', 'description_limit'],
|
[
|
||||||
|
'server',
|
||||||
|
'configuration',
|
||||||
|
'accounts',
|
||||||
|
'max_header_description_length',
|
||||||
|
],
|
||||||
150,
|
150,
|
||||||
) as number,
|
) as number,
|
||||||
);
|
);
|
||||||
@ -100,23 +106,26 @@ export const ImageAltTextField: FC<{
|
|||||||
<>
|
<>
|
||||||
<img src={imageSrc} alt='' className={classes.altImage} />
|
<img src={imageSrc} alt='' className={classes.altImage} />
|
||||||
|
|
||||||
<TextAreaField
|
<div>
|
||||||
label={
|
<TextAreaField
|
||||||
<FormattedMessage
|
label={
|
||||||
id='account_edit.image_alt_modal.text_label'
|
<FormattedMessage
|
||||||
defaultMessage='Alt text'
|
id='account_edit.image_alt_modal.text_label'
|
||||||
/>
|
defaultMessage='Alt text'
|
||||||
}
|
/>
|
||||||
hint={
|
}
|
||||||
<FormattedMessage
|
hint={
|
||||||
id='account_edit.image_alt_modal.text_hint'
|
<FormattedMessage
|
||||||
defaultMessage='Alt text helps screen reader users to understand your content.'
|
id='account_edit.image_alt_modal.text_hint'
|
||||||
/>
|
defaultMessage='Alt text helps screen reader users to understand your content.'
|
||||||
}
|
/>
|
||||||
onChange={handleChange}
|
}
|
||||||
value={altText}
|
onChange={handleChange}
|
||||||
maxLength={altLimit}
|
value={altText}
|
||||||
/>
|
maxLength={altLimit}
|
||||||
|
/>
|
||||||
|
<CharacterCounter currentString={altText} maxLength={altLimit} />
|
||||||
|
</div>
|
||||||
|
|
||||||
{!hideTip && (
|
{!hideTip && (
|
||||||
<Details
|
<Details
|
||||||
@ -130,7 +139,7 @@ export const ImageAltTextField: FC<{
|
|||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='account_edit.image_alt_modal.details_content'
|
id='account_edit.image_alt_modal.details_content'
|
||||||
defaultMessage='DO: <ul> <li>Describe yourself as pictured</li> <li>Use third person language (e.g. “Alex” instead of “me”)</li> <li>Be succinct – a few words is often enough</li> </ul> DON’T: <ul> <li>Start with “Photo of” – it’s redundant for screen readers</li> </ul> EXAMPLE: <ul> <li>“Alex wearing a green shirt and glasses”</li> </ul>'
|
defaultMessage='DO: <ul> <li>Describe yourself as pictured</li> <li>Use third person language (e.g. “Alex” instead of “me”)</li> <li>Be succinct – a few words is often enough</li> </ul> DON’T: <ul> <li>Start with “Photo of” – it’s redundant for screen readers</li> </ul> EXAMPLE: <ul> <li>“Alex wearing a green shirt and glasses”</li></ul>'
|
||||||
values={{
|
values={{
|
||||||
ul: (chunks) => <ul>{chunks}</ul>,
|
ul: (chunks) => <ul>{chunks}</ul>,
|
||||||
li: (chunks) => <li>{chunks}</li>,
|
li: (chunks) => <li>{chunks}</li>,
|
||||||
|
|||||||
@ -58,7 +58,7 @@
|
|||||||
transition: background 0.2s ease-in-out;
|
transition: background 0.2s ease-in-out;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
|
|||||||
@ -160,7 +160,7 @@
|
|||||||
&:active,
|
&:active,
|
||||||
&:focus,
|
&:focus,
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--color-bg-brand-softer);
|
background-color: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:disabled {
|
&:disabled {
|
||||||
@ -177,7 +177,7 @@
|
|||||||
|
|
||||||
.deleteButton {
|
.deleteButton {
|
||||||
--default-icon-color: var(--color-text-error);
|
--default-icon-color: var(--color-text-error);
|
||||||
--hover-bg-color: var(--color-bg-error-base-hover);
|
--hover-bg-color: var(--color-bg-error-base);
|
||||||
--hover-icon-color: var(--color-text-on-error-base);
|
--hover-icon-color: var(--color-text-on-error-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +201,7 @@
|
|||||||
&,
|
&,
|
||||||
&:global(.active) {
|
&:global(.active) {
|
||||||
// Overrides the transparent background added by default with .active
|
// Overrides the transparent background added by default with .active
|
||||||
--hover-bg-color: var(--color-bg-brand-softer-solid);
|
--hover-bg-color: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
@ -93,7 +93,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
@ -184,7 +184,7 @@ $button-fallback-breakpoint: $button-breakpoint + 55px;
|
|||||||
|
|
||||||
.badgeMuted {
|
.badgeMuted {
|
||||||
background-color: var(--color-bg-inverted);
|
background-color: var(--color-bg-inverted);
|
||||||
color: var(--color-text-on-inverted);
|
color: var(--color-text-inverted);
|
||||||
}
|
}
|
||||||
|
|
||||||
.badgeBlocked {
|
.badgeBlocked {
|
||||||
@ -270,7 +270,7 @@ svg.badgeIcon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fieldVerified {
|
.fieldVerified {
|
||||||
background-color: var(--color-bg-success-softer);
|
background-color: var(--color-bg-success-softest);
|
||||||
|
|
||||||
dt {
|
dt {
|
||||||
padding-right: 24px;
|
padding-right: 24px;
|
||||||
@ -292,8 +292,8 @@ svg.badgeIcon {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.fieldOverflowButton {
|
.fieldOverflowButton {
|
||||||
--default-bg-color: var(--color-bg-secondary-solid);
|
--default-bg-color: var(--color-bg-secondary);
|
||||||
--hover-bg-color: var(--color-bg-brand-softer-solid);
|
--hover-bg-color: var(--color-bg-brand-softest);
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 8px;
|
right: 8px;
|
||||||
@ -413,7 +413,7 @@ svg.badgeIcon {
|
|||||||
|
|
||||||
:global(.active) {
|
:global(.active) {
|
||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
border-bottom: 4px solid var(--color-text-brand);
|
border-bottom: 4px solid var(--color-border-brand);
|
||||||
padding-bottom: 14px;
|
padding-bottom: 14px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -59,6 +59,7 @@ export const InfoButton: React.FC = () => {
|
|||||||
>
|
>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='info_button.what_is_alt_text'
|
id='info_button.what_is_alt_text'
|
||||||
|
// eslint-disable-next-line formatjs/prefer-full-sentence
|
||||||
defaultMessage='<h1>What is alt text?</h1>
|
defaultMessage='<h1>What is alt text?</h1>
|
||||||
|
|
||||||
<p>Alt text provides image descriptions for people with vision impairments, low-bandwidth connections, or those seeking extra context.</p>
|
<p>Alt text provides image descriptions for people with vision impairments, low-bandwidth connections, or those seeking extra context.</p>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
@ -8,6 +8,7 @@ import { connect } from 'react-redux';
|
|||||||
|
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import BlockIcon from '@/material-icons/400-24px/block-fill.svg?react';
|
import BlockIcon from '@/material-icons/400-24px/block-fill.svg?react';
|
||||||
import { Account } from 'mastodon/components/account';
|
import { Account } from 'mastodon/components/account';
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Fragment, useCallback, useRef, useState } from 'react';
|
import { useCallback, useRef, useState } from 'react';
|
||||||
|
|
||||||
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
@ -100,14 +100,12 @@ const RevokeControls: React.FC<{
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='collections.detail.accept_inclusion'
|
id='collections.detail.accept_inclusion'
|
||||||
defaultMessage='Okay'
|
defaultMessage='Okay'
|
||||||
tagName={Fragment}
|
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
<Button secondary onClick={confirmRevoke}>
|
<Button secondary onClick={confirmRevoke}>
|
||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='collections.detail.revoke_inclusion'
|
id='collections.detail.revoke_inclusion'
|
||||||
defaultMessage='Remove me'
|
defaultMessage='Remove me'
|
||||||
tagName={Fragment}
|
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@ -143,7 +141,6 @@ const SensitiveScreen: React.FC<{
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='content_warning.show'
|
id='content_warning.show'
|
||||||
defaultMessage='Show anyway'
|
defaultMessage='Show anyway'
|
||||||
tagName={Fragment}
|
|
||||||
/>
|
/>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@ -205,7 +202,6 @@ export const CollectionAccountsList: React.FC<{
|
|||||||
values={{
|
values={{
|
||||||
author: <SimpleAuthorName id={collection.account_id} />,
|
author: <SimpleAuthorName id={collection.account_id} />,
|
||||||
}}
|
}}
|
||||||
tagName={Fragment}
|
|
||||||
/>
|
/>
|
||||||
</h3>
|
</h3>
|
||||||
<Article
|
<Article
|
||||||
@ -231,7 +227,6 @@ export const CollectionAccountsList: React.FC<{
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='collections.detail.other_accounts_in_collection'
|
id='collections.detail.other_accounts_in_collection'
|
||||||
defaultMessage='Others in this collection:'
|
defaultMessage='Others in this collection:'
|
||||||
tagName={Fragment}
|
|
||||||
/>
|
/>
|
||||||
</h3>
|
</h3>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Fragment, useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage, useIntl } from 'react-intl';
|
import { FormattedMessage, useIntl } from 'react-intl';
|
||||||
|
|
||||||
@ -413,7 +413,6 @@ const LanguageField: React.FC = () => {
|
|||||||
<FormattedMessage
|
<FormattedMessage
|
||||||
id='collections.collection_language_none'
|
id='collections.collection_language_none'
|
||||||
defaultMessage='None'
|
defaultMessage='None'
|
||||||
tagName={Fragment}
|
|
||||||
/>
|
/>
|
||||||
</option>
|
</option>
|
||||||
{languages?.map(([code, name, localName]) => (
|
{languages?.map(([code, name, localName]) => (
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { injectIntl, FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
|
|
||||||
import SettingToggle from '../../notifications/components/setting_toggle';
|
import SettingToggle from '../../notifications/components/setting_toggle';
|
||||||
|
|
||||||
class ColumnSettings extends PureComponent {
|
class ColumnSettings extends PureComponent {
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
import PeopleIcon from '@/material-icons/400-24px/group.svg?react';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||||
import { domain, localLiveFeedAccess } from 'mastodon/initial_state';
|
import { domain, localLiveFeedAccess } from 'mastodon/initial_state';
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { createRef } from 'react';
|
import { createRef } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ import { missingAltTextModal } from 'mastodon/initial_state';
|
|||||||
import AutosuggestInput from 'mastodon/components/autosuggest_input';
|
import AutosuggestInput from 'mastodon/components/autosuggest_input';
|
||||||
import AutosuggestTextarea from 'mastodon/components/autosuggest_textarea';
|
import AutosuggestTextarea from 'mastodon/components/autosuggest_textarea';
|
||||||
import { Button } from 'mastodon/components/button';
|
import { Button } from 'mastodon/components/button';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import EmojiPickerDropdown from '../containers/emoji_picker_dropdown_container';
|
import EmojiPickerDropdown from '../containers/emoji_picker_dropdown_container';
|
||||||
import PollButtonContainer from '../containers/poll_button_container';
|
import PollButtonContainer from '../containers/poll_button_container';
|
||||||
import SpoilerButtonContainer from '../containers/spoiler_button_container';
|
import SpoilerButtonContainer from '../containers/spoiler_button_container';
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
@ -12,6 +12,7 @@ import Overlay from 'react-overlays/Overlay';
|
|||||||
|
|
||||||
import MoodIcon from '@/material-icons/400-20px/mood.svg?react';
|
import MoodIcon from '@/material-icons/400-20px/mood.svg?react';
|
||||||
import { IconButton } from 'mastodon/components/icon_button';
|
import { IconButton } from 'mastodon/components/icon_button';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
|
|
||||||
import { buildCustomEmojis, categoriesFromEmojis } from '../../emoji/emoji';
|
import { buildCustomEmojis, categoriesFromEmojis } from '../../emoji/emoji';
|
||||||
import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components';
|
import { EmojiPicker as EmojiPickerAsync } from '../../ui/util/async-components';
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import BarChart4BarsIcon from '@/material-icons/400-20px/bar_chart_4_bars.svg?react';
|
import BarChart4BarsIcon from '@/material-icons/400-20px/bar_chart_4_bars.svg?react';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
|
|
||||||
import { IconButton } from '../../../components/icon_button';
|
import { IconButton } from '../../../components/icon_button';
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import PhotoLibraryIcon from '@/material-icons/400-20px/photo_library.svg?react';
|
import PhotoLibraryIcon from '@/material-icons/400-20px/photo_library.svg?react';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import { IconButton } from 'mastodon/components/icon_button';
|
import { IconButton } from 'mastodon/components/icon_button';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import { injectIntl, defineMessages } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import WarningIcon from '@/material-icons/400-20px/warning.svg?react';
|
import WarningIcon from '@/material-icons/400-20px/warning.svg?react';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import { IconButton } from 'mastodon/components/icon_button';
|
import { IconButton } from 'mastodon/components/icon_button';
|
||||||
|
|
||||||
import { changeComposeSpoilerness } from '../../../actions/compose';
|
import { changeComposeSpoilerness } from '../../../actions/compose';
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
@ -15,6 +15,7 @@ import { fetchFavourites, expandFavourites } from 'mastodon/actions/interactions
|
|||||||
import { Account } from 'mastodon/components/account';
|
import { Account } from 'mastodon/components/account';
|
||||||
import ColumnHeader from 'mastodon/components/column_header';
|
import ColumnHeader from 'mastodon/components/column_header';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||||
import ScrollableList from 'mastodon/components/scrollable_list';
|
import ScrollableList from 'mastodon/components/scrollable_list';
|
||||||
import Column from 'mastodon/features/ui/components/column';
|
import Column from 'mastodon/features/ui/components/column';
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
@ -9,6 +9,7 @@ import fuzzysort from 'fuzzysort';
|
|||||||
|
|
||||||
import AddIcon from '@/material-icons/400-24px/add.svg?react';
|
import AddIcon from '@/material-icons/400-24px/add.svg?react';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import { toServerSideType } from 'mastodon/utils/filters';
|
import { toServerSideType } from 'mastodon/utils/filters';
|
||||||
import { loupeIcon, deleteIcon } from 'mastodon/utils/icons';
|
import { loupeIcon, deleteIcon } from 'mastodon/utils/icons';
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
@ -13,6 +13,7 @@ import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
|||||||
import { Avatar } from '@/mastodon/components/avatar';
|
import { Avatar } from '@/mastodon/components/avatar';
|
||||||
import { DisplayName } from '@/mastodon/components/display_name';
|
import { DisplayName } from '@/mastodon/components/display_name';
|
||||||
import { IconButton } from '@/mastodon/components/icon_button';
|
import { IconButton } from '@/mastodon/components/icon_button';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import { EmojiHTML } from '@/mastodon/components/emoji/html';
|
import { EmojiHTML } from '@/mastodon/components/emoji/html';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
@ -11,6 +11,7 @@ import { connect } from 'react-redux';
|
|||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
import PersonAddIcon from '@/material-icons/400-24px/person_add.svg?react';
|
import PersonAddIcon from '@/material-icons/400-24px/person_add.svg?react';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
|
|
||||||
import { fetchFollowRequests, expandFollowRequests } from '../../actions/accounts';
|
import { fetchFollowRequests, expandFollowRequests } from '../../actions/accounts';
|
||||||
import ScrollableList from '../../components/scrollable_list';
|
import ScrollableList from '../../components/scrollable_list';
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
@ -9,6 +9,8 @@ import { NonceProvider } from 'react-select';
|
|||||||
import AsyncSelect from 'react-select/async';
|
import AsyncSelect from 'react-select/async';
|
||||||
import Toggle from 'react-toggle';
|
import Toggle from 'react-toggle';
|
||||||
|
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
|
|
||||||
import SettingToggle from '../../notifications/components/setting_toggle';
|
import SettingToggle from '../../notifications/components/setting_toggle';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
@ -10,6 +10,7 @@ import { connect } from 'react-redux';
|
|||||||
|
|
||||||
import CampaignIcon from '@/material-icons/400-24px/campaign.svg?react';
|
import CampaignIcon from '@/material-icons/400-24px/campaign.svg?react';
|
||||||
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
import HomeIcon from '@/material-icons/400-24px/home-fill.svg?react';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import { SymbolLogo } from 'mastodon/components/logo';
|
import { SymbolLogo } from 'mastodon/components/logo';
|
||||||
import { fetchAnnouncements, toggleShowAnnouncements } from 'mastodon/actions/announcements';
|
import { fetchAnnouncements, toggleShowAnnouncements } from 'mastodon/actions/announcements';
|
||||||
import { IconWithBadge } from 'mastodon/components/icon_with_badge';
|
import { IconWithBadge } from 'mastodon/components/icon_with_badge';
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
@ -9,6 +9,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||||||
import InfoIcon from '@/material-icons/400-24px/info.svg?react';
|
import InfoIcon from '@/material-icons/400-24px/info.svg?react';
|
||||||
import Column from 'mastodon/components/column';
|
import Column from 'mastodon/components/column';
|
||||||
import ColumnHeader from 'mastodon/components/column_header';
|
import ColumnHeader from 'mastodon/components/column_header';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
heading: { id: 'keyboard_shortcuts.heading', defaultMessage: 'Keyboard Shortcuts' },
|
heading: { id: 'keyboard_shortcuts.heading', defaultMessage: 'Keyboard Shortcuts' },
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
@ -12,6 +12,7 @@ import { debounce } from 'lodash';
|
|||||||
|
|
||||||
import VolumeOffIcon from '@/material-icons/400-24px/volume_off.svg?react';
|
import VolumeOffIcon from '@/material-icons/400-24px/volume_off.svg?react';
|
||||||
import { Account } from 'mastodon/components/account';
|
import { Account } from 'mastodon/components/account';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
|
|
||||||
import { fetchMutes, expandMutes } from '../../actions/mutes';
|
import { fetchMutes, expandMutes } from '../../actions/mutes';
|
||||||
import { LoadingIndicator } from '../../components/loading_indicator';
|
import { LoadingIndicator } from '../../components/loading_indicator';
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
@ -12,6 +12,7 @@ import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
|||||||
import { Avatar } from 'mastodon/components/avatar';
|
import { Avatar } from 'mastodon/components/avatar';
|
||||||
import { DisplayName } from 'mastodon/components/display_name';
|
import { DisplayName } from 'mastodon/components/display_name';
|
||||||
import { IconButton } from 'mastodon/components/icon_button';
|
import { IconButton } from 'mastodon/components/icon_button';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
|
authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { injectIntl, FormattedMessage, defineMessages } from 'react-intl';
|
import { FormattedMessage, defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Link, withRouter } from 'react-router-dom';
|
import { Link, withRouter } from 'react-router-dom';
|
||||||
@ -20,6 +20,7 @@ import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
|||||||
import { Account } from 'mastodon/components/account';
|
import { Account } from 'mastodon/components/account';
|
||||||
import { LinkedDisplayName } from '@/mastodon/components/display_name';
|
import { LinkedDisplayName } from '@/mastodon/components/display_name';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import { Hotkeys } from 'mastodon/components/hotkeys';
|
import { Hotkeys } from 'mastodon/components/hotkeys';
|
||||||
import { StatusQuoteManager } from 'mastodon/components/status_quoted';
|
import { StatusQuoteManager } from 'mastodon/components/status_quoted';
|
||||||
import { me } from 'mastodon/initial_state';
|
import { me } from 'mastodon/initial_state';
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import { AvatarOverlay } from 'mastodon/components/avatar_overlay';
|
import { AvatarOverlay } from 'mastodon/components/avatar_overlay';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||||
|
|
||||||
// This needs to be kept in sync with app/models/report.rb
|
// This needs to be kept in sync with app/models/report.rb
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { openModal } from 'mastodon/actions/modal';
|
import { openModal } from 'mastodon/actions/modal';
|
||||||
import { fetchNotifications , setNotificationsFilter } from 'mastodon/actions/notification_groups';
|
import { fetchNotifications , setNotificationsFilter } from 'mastodon/actions/notification_groups';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
|
|
||||||
import { showAlert } from '../../../actions/alerts';
|
import { showAlert } from '../../../actions/alerts';
|
||||||
import { requestBrowserPermission } from '../../../actions/notifications';
|
import { requestBrowserPermission } from '../../../actions/notifications';
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
@ -9,6 +9,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import PushPinIcon from '@/material-icons/400-24px/push_pin.svg?react';
|
import PushPinIcon from '@/material-icons/400-24px/push_pin.svg?react';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import { getStatusList } from 'mastodon/selectors';
|
import { getStatusList } from 'mastodon/selectors';
|
||||||
|
|
||||||
import { fetchPinnedStatuses } from '../../actions/pin_statuses';
|
import { fetchPinnedStatuses } from '../../actions/pin_statuses';
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { injectIntl, FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
|
|
||||||
import SettingToggle from '../../notifications/components/setting_toggle';
|
import SettingToggle from '../../notifications/components/setting_toggle';
|
||||||
|
|
||||||
class ColumnSettings extends PureComponent {
|
class ColumnSettings extends PureComponent {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
@ -9,6 +9,7 @@ import { connect } from 'react-redux';
|
|||||||
|
|
||||||
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
import PublicIcon from '@/material-icons/400-24px/public.svg?react';
|
||||||
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||||
import { domain, localLiveFeedAccess, remoteLiveFeedAccess } from 'mastodon/initial_state';
|
import { domain, localLiveFeedAccess, remoteLiveFeedAccess } from 'mastodon/initial_state';
|
||||||
import { canViewFeed } from 'mastodon/permissions';
|
import { canViewFeed } from 'mastodon/permissions';
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
@ -13,6 +13,7 @@ import { debounce } from 'lodash';
|
|||||||
import RefreshIcon from '@/material-icons/400-24px/refresh.svg?react';
|
import RefreshIcon from '@/material-icons/400-24px/refresh.svg?react';
|
||||||
import { Account } from 'mastodon/components/account';
|
import { Account } from 'mastodon/components/account';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
|
|
||||||
import { fetchReblogs, expandReblogs } from '../../actions/interactions';
|
import { fetchReblogs, expandReblogs } from '../../actions/interactions';
|
||||||
import ColumnHeader from '../../components/column_header';
|
import ColumnHeader from '../../components/column_header';
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { List as ImmutableList } from 'immutable';
|
import { List as ImmutableList } from 'immutable';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { Button } from 'mastodon/components/button';
|
import { Button } from 'mastodon/components/button';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
|
|
||||||
import Option from './components/option';
|
import Option from './components/option';
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
@ -13,6 +13,7 @@ import ReplyIcon from '@/material-icons/400-24px/reply.svg?react';
|
|||||||
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
|
import ReplyAllIcon from '@/material-icons/400-24px/reply_all.svg?react';
|
||||||
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
|
||||||
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
|
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
@ -15,6 +15,7 @@ import VisibilityIcon from '@/material-icons/400-24px/visibility.svg?react';
|
|||||||
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
|
import VisibilityOffIcon from '@/material-icons/400-24px/visibility_off.svg?react';
|
||||||
import { Hotkeys } from 'mastodon/components/hotkeys';
|
import { Hotkeys } from 'mastodon/components/hotkeys';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||||
import { ScrollContainer } from 'mastodon/containers/scroll_container';
|
import { ScrollContainer } from 'mastodon/containers/scroll_container';
|
||||||
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
import BundleColumnError from 'mastodon/features/ui/components/bundle_column_error';
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { createSelector } from '@reduxjs/toolkit';
|
import { createSelector } from '@reduxjs/toolkit';
|
||||||
import { is, List as ImmutableList, Set as ImmutableSet } from 'immutable';
|
import { is, List as ImmutableList, Set as ImmutableSet } from 'immutable';
|
||||||
@ -12,6 +12,7 @@ import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
|||||||
import { followAccount } from 'mastodon/actions/accounts';
|
import { followAccount } from 'mastodon/actions/accounts';
|
||||||
import { Button } from 'mastodon/components/button';
|
import { Button } from 'mastodon/components/button';
|
||||||
import { IconButton } from 'mastodon/components/icon_button';
|
import { IconButton } from 'mastodon/components/icon_button';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import Option from 'mastodon/features/report/components/option';
|
import Option from 'mastodon/features/report/components/option';
|
||||||
import { languages as preloadedLanguages } from 'mastodon/initial_state';
|
import { languages as preloadedLanguages } from 'mastodon/initial_state';
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { injectIntl, FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
@ -9,6 +9,7 @@ import { Link } from 'react-router-dom';
|
|||||||
|
|
||||||
import { Button } from 'mastodon/components/button';
|
import { Button } from 'mastodon/components/button';
|
||||||
import Column from 'mastodon/components/column';
|
import Column from 'mastodon/components/column';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import { GIF } from 'mastodon/components/gif';
|
import { GIF } from 'mastodon/components/gif';
|
||||||
|
|
||||||
class CopyButton extends PureComponent {
|
class CopyButton extends PureComponent {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
@ -9,6 +9,7 @@ import CloseIcon from '@/material-icons/400-24px/close.svg?react';
|
|||||||
import { fetchFilters, createFilter, createFilterStatus } from 'mastodon/actions/filters';
|
import { fetchFilters, createFilter, createFilterStatus } from 'mastodon/actions/filters';
|
||||||
import { fetchStatus } from 'mastodon/actions/statuses';
|
import { fetchStatus } from 'mastodon/actions/statuses';
|
||||||
import { IconButton } from 'mastodon/components/icon_button';
|
import { IconButton } from 'mastodon/components/icon_button';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import AddedToFilter from 'mastodon/features/filters/added_to_filter';
|
import AddedToFilter from 'mastodon/features/filters/added_to_filter';
|
||||||
import SelectFilter from 'mastodon/features/filters/select_filter';
|
import SelectFilter from 'mastodon/features/filters/select_filter';
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
|
|
||||||
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import { OrderedSet } from 'immutable';
|
import { OrderedSet } from 'immutable';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
@ -12,6 +12,7 @@ import { submitReport } from 'mastodon/actions/reports';
|
|||||||
import { fetchServer } from 'mastodon/actions/server';
|
import { fetchServer } from 'mastodon/actions/server';
|
||||||
import { expandAccountTimeline } from 'mastodon/actions/timelines';
|
import { expandAccountTimeline } from 'mastodon/actions/timelines';
|
||||||
import { IconButton } from 'mastodon/components/icon_button';
|
import { IconButton } from 'mastodon/components/icon_button';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import Category from 'mastodon/features/report/category';
|
import Category from 'mastodon/features/report/category';
|
||||||
import Comment from 'mastodon/features/report/comment';
|
import Comment from 'mastodon/features/report/comment';
|
||||||
import Rules from 'mastodon/features/report/rules';
|
import Rules from 'mastodon/features/report/rules';
|
||||||
|
|||||||
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
&:focus-within {
|
&:focus-within {
|
||||||
outline: var(--outline-focus-default);
|
outline: var(--outline-focus-default);
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
|
|
||||||
:any-link {
|
:any-link {
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import { PureComponent } from 'react';
|
import { PureComponent } from 'react';
|
||||||
|
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Redirect, Route, withRouter } from 'react-router-dom';
|
import { Redirect, Route, withRouter } from 'react-router-dom';
|
||||||
@ -16,6 +16,7 @@ import { synchronouslySubmitMarkers, submitMarkers, fetchMarkers } from 'mastodo
|
|||||||
import { fetchNotifications } from 'mastodon/actions/notification_groups';
|
import { fetchNotifications } from 'mastodon/actions/notification_groups';
|
||||||
import { INTRODUCTION_VERSION } from 'mastodon/actions/onboarding';
|
import { INTRODUCTION_VERSION } from 'mastodon/actions/onboarding';
|
||||||
import { AlertsController } from 'mastodon/components/alerts_controller';
|
import { AlertsController } from 'mastodon/components/alerts_controller';
|
||||||
|
import { injectIntl } from '@/mastodon/components/intl';
|
||||||
import { Hotkeys } from 'mastodon/components/hotkeys';
|
import { Hotkeys } from 'mastodon/components/hotkeys';
|
||||||
import { HoverCardController } from 'mastodon/components/hover_card_controller';
|
import { HoverCardController } from 'mastodon/components/hover_card_controller';
|
||||||
import { PictureInPicture } from 'mastodon/features/picture_in_picture';
|
import { PictureInPicture } from 'mastodon/features/picture_in_picture';
|
||||||
|
|||||||
@ -50,7 +50,6 @@ export const IntlProvider: React.FC<
|
|||||||
locale={locale}
|
locale={locale}
|
||||||
messages={messages}
|
messages={messages}
|
||||||
onError={onProviderError}
|
onError={onProviderError}
|
||||||
textComponent='span'
|
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// import { shouldPolyfill as shouldPolyfillCanonicalLocales } from '@formatjs/intl-getcanonicallocales/should-polyfill';
|
// import { shouldPolyfill as shouldPolyfillCanonicalLocales } from '@formatjs/intl-getcanonicallocales/should-polyfill';
|
||||||
// import { shouldPolyfill as shouldPolyfillLocale } from '@formatjs/intl-locale/should-polyfill';
|
// import { shouldPolyfill as shouldPolyfillLocale } from '@formatjs/intl-locale/should-polyfill';
|
||||||
import { shouldPolyfill as shoudPolyfillPluralRules } from '@formatjs/intl-pluralrules/should-polyfill';
|
import { shouldPolyfill as shoudPolyfillPluralRules } from '@formatjs/intl-pluralrules/should-polyfill.js';
|
||||||
// import { shouldPolyfill as shouldPolyfillNumberFormat } from '@formatjs/intl-numberformat/should-polyfill';
|
// import { shouldPolyfill as shouldPolyfillNumberFormat } from '@formatjs/intl-numberformat/should-polyfill';
|
||||||
// import { shouldPolyfill as shouldPolyfillIntlDateTimeFormat } from '@formatjs/intl-datetimeformat/should-polyfill';
|
// import { shouldPolyfill as shouldPolyfillIntlDateTimeFormat } from '@formatjs/intl-datetimeformat/should-polyfill';
|
||||||
// import { shouldPolyfill as shouldPolyfillIntlRelativeTimeFormat } from '@formatjs/intl-relativetimeformat/should-polyfill';
|
// import { shouldPolyfill as shouldPolyfillIntlRelativeTimeFormat } from '@formatjs/intl-relativetimeformat/should-polyfill';
|
||||||
@ -54,7 +54,7 @@ async function loadIntlPluralRulesPolyfills(locale: string) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Load the polyfill 1st BEFORE loading data
|
// Load the polyfill 1st BEFORE loading data
|
||||||
await import('@formatjs/intl-pluralrules/polyfill-force');
|
await import('@formatjs/intl-pluralrules/polyfill-force.js');
|
||||||
await import(
|
await import(
|
||||||
`../../../../node_modules/@formatjs/intl-pluralrules/locale-data/${unsupportedLocale}.js`
|
`../../../../node_modules/@formatjs/intl-pluralrules/locale-data/${unsupportedLocale}.js`
|
||||||
);
|
);
|
||||||
|
|||||||
@ -721,6 +721,52 @@ table + p {
|
|||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Banner item
|
||||||
|
.email-banner-table {
|
||||||
|
border-radius: 12px;
|
||||||
|
background-color: #1b001f;
|
||||||
|
background-image: url('../../images/mailer-new/common/header-bg-start.png');
|
||||||
|
background-position: left top;
|
||||||
|
background-repeat: repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-banner-td {
|
||||||
|
padding: 24px 24px 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-banner-text-td {
|
||||||
|
p {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 16.8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-desktop-flex {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-btn-table {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-btn-td {
|
||||||
|
mso-padding-alt: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.email-btn-a {
|
||||||
|
color: #181820;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div + div {
|
||||||
|
margin-inline-start: auto;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Checklist item
|
// Checklist item
|
||||||
.email-checklist-wrapper-td {
|
.email-checklist-wrapper-td {
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
|
|||||||
@ -7,7 +7,7 @@
|
|||||||
background: var(--color-bg-secondary);
|
background: var(--color-bg-secondary);
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid var(--color-border-on-bg-secondary);
|
border: 1px solid var(--color-border-primary);
|
||||||
font-size: 17px;
|
font-size: 17px;
|
||||||
line-height: normal;
|
line-height: normal;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
&:active,
|
&:active,
|
||||||
&:focus {
|
&:focus {
|
||||||
.card__bar {
|
.card__bar {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -220,8 +220,8 @@
|
|||||||
.information-badge {
|
.information-badge {
|
||||||
&.superapp {
|
&.superapp {
|
||||||
color: var(--color-text-success);
|
color: var(--color-text-success);
|
||||||
background-color: var(--color-bg-success-softer);
|
background-color: var(--color-bg-success-softest);
|
||||||
border-color: var(--color-border-on-bg-success-softer);
|
border-color: var(--color-border-success-soft);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +229,7 @@
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
padding-inline-end: 8px;
|
padding-inline-end: 8px;
|
||||||
border: 1px solid var(--color-text-brand);
|
border: 1px solid var(--color-border-brand);
|
||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@ -255,8 +255,8 @@
|
|||||||
|
|
||||||
.simple_form .not_recommended {
|
.simple_form .not_recommended {
|
||||||
color: var(--color-text-error);
|
color: var(--color-text-error);
|
||||||
background-color: var(--color-bg-error-softer);
|
background-color: var(--color-bg-error-softest);
|
||||||
border-color: var(--color-border-on-bg-error-softer);
|
border-color: var(--color-border-error-soft);
|
||||||
}
|
}
|
||||||
|
|
||||||
.account__header__fields {
|
.account__header__fields {
|
||||||
@ -310,8 +310,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.verified {
|
.verified {
|
||||||
border: 1px solid var(--color-border-on-bg-success-softer);
|
border: 1px solid var(--color-border-success-soft);
|
||||||
background: var(--color-bg-success-softer);
|
background: var(--color-bg-success-softest);
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--color-text-success);
|
color: var(--color-text-success);
|
||||||
|
|||||||
@ -68,7 +68,7 @@ $content-width: 840px;
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
|
|
||||||
.material-close {
|
.material-close {
|
||||||
@ -438,7 +438,7 @@ $content-width: 840px;
|
|||||||
}
|
}
|
||||||
|
|
||||||
ul .simple-navigation-active-leaf a {
|
ul .simple-navigation-active-leaf a {
|
||||||
border-bottom-color: var(--color-text-brand);
|
border-bottom-color: var(--color-border-brand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -499,7 +499,7 @@ body,
|
|||||||
|
|
||||||
kbd {
|
kbd {
|
||||||
font-family: Courier, monospace;
|
font-family: Courier, monospace;
|
||||||
background-color: var(--color-bg-brand-softer);
|
background-color: var(--color-bg-brand-softest);
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
padding-bottom: 2px;
|
padding-bottom: 2px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
@ -566,7 +566,7 @@ kbd {
|
|||||||
|
|
||||||
&.selected {
|
&.selected {
|
||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
border-bottom: 2px solid var(--color-text-brand);
|
border-bottom: 2px solid var(--color-border-brand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -845,14 +845,14 @@ a.name-tag,
|
|||||||
|
|
||||||
.speech-bubble {
|
.speech-bubble {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
border-inline-start: 4px solid var(--color-text-brand);
|
border-inline-start: 4px solid var(--color-border-brand);
|
||||||
|
|
||||||
&.positive {
|
&.positive {
|
||||||
border-color: var(--color-text-success);
|
border-color: var(--color-text-success);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.negative {
|
&.negative {
|
||||||
border-color: var(--color-text-error);
|
border-color: var(--color-border-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.warning {
|
&.warning {
|
||||||
@ -1314,7 +1314,7 @@ a.sparkline {
|
|||||||
&:hover,
|
&:hover,
|
||||||
&:focus,
|
&:focus,
|
||||||
&:active {
|
&:active {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1938,7 +1938,7 @@ a.sparkline {
|
|||||||
width: calc(1.375rem + 1px);
|
width: calc(1.375rem + 1px);
|
||||||
height: calc(1.375rem + 1px);
|
height: calc(1.375rem + 1px);
|
||||||
background: var(--color-bg-primary);
|
background: var(--color-bg-primary);
|
||||||
border: 1px solid var(--color-text-brand);
|
border: 1px solid var(--color-border-brand);
|
||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
@ -2022,8 +2022,8 @@ a.sparkline {
|
|||||||
display: block;
|
display: block;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
border: 1px solid var(--color-border-on-bg-brand-softer);
|
border: 1px solid var(--color-border-brand-soft);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 8px 13px;
|
padding: 8px 13px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
html {
|
html {
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
background: var(--color-bg-ambient);
|
background: var(--color-bg-primary);
|
||||||
|
|
||||||
&.custom-scrollbars {
|
&.custom-scrollbars {
|
||||||
scrollbar-color: var(--color-text-secondary) var(--color-bg-secondary);
|
scrollbar-color: var(--color-text-secondary) var(--color-bg-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
--outline-focus-default: 2px solid var(--color-text-brand);
|
--outline-focus-default: 2px solid var(--color-border-brand);
|
||||||
--avatar-border-radius: 8px;
|
--avatar-border-radius: 8px;
|
||||||
--max-media-height-small: 460px;
|
--max-media-height-small: 460px;
|
||||||
--max-media-height-large: 566px;
|
--max-media-height-large: 566px;
|
||||||
@ -46,7 +46,7 @@ html.has-modal {
|
|||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: $font-sans-serif, sans-serif;
|
font-family: $font-sans-serif, sans-serif;
|
||||||
background: var(--color-bg-ambient);
|
background: var(--color-bg-primary);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
|||||||
@ -114,7 +114,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
outline: 2px solid var(--color-bg-brand-base);
|
outline: var(--outline-focus-default);
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,12 +169,12 @@
|
|||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
padding: 6px 17px;
|
padding: 6px 17px;
|
||||||
border: 1px solid var(--color-text-brand);
|
border: 1px solid var(--color-border-brand);
|
||||||
|
|
||||||
&:active,
|
&:active,
|
||||||
&:focus,
|
&:focus,
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: var(--color-text-brand);
|
border-color: var(--color-border-brand);
|
||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
@ -184,7 +184,7 @@
|
|||||||
&:active,
|
&:active,
|
||||||
&:focus,
|
&:focus,
|
||||||
&:hover {
|
&:hover {
|
||||||
border-color: var(--color-text-error);
|
border-color: var(--color-border-error);
|
||||||
color: var(--color-text-error);
|
color: var(--color-text-error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -284,8 +284,8 @@
|
|||||||
--default-icon-color: var(--color-text-secondary);
|
--default-icon-color: var(--color-text-secondary);
|
||||||
--default-bg-color: transparent;
|
--default-bg-color: transparent;
|
||||||
--hover-icon-color: var(--color-text-primary);
|
--hover-icon-color: var(--color-text-primary);
|
||||||
--hover-bg-color: var(--color-bg-brand-softer);
|
--hover-bg-color: var(--color-bg-brand-softest);
|
||||||
--focus-outline-color: var(--color-text-brand);
|
--focus-outline-color: var(--color-border-brand);
|
||||||
|
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
color: var(--default-icon-color);
|
color: var(--default-icon-color);
|
||||||
@ -364,8 +364,8 @@
|
|||||||
&.copied {
|
&.copied {
|
||||||
color: var(--color-text-success);
|
color: var(--color-text-success);
|
||||||
transition: none;
|
transition: none;
|
||||||
background-color: var(--color-bg-success-softer);
|
background-color: var(--color-bg-success-softest);
|
||||||
border-color: var(--color-border-on-bg-brand-softer);
|
border-color: var(--color-border-success-soft);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,21 +537,21 @@ body > [data-popper-placement] {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 0 1 auto;
|
flex: 0 1 auto;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid var(--color-border-on-bg-secondary);
|
border: 1px solid var(--color-border-primary);
|
||||||
transition: border-color 300ms linear;
|
transition: border-color 300ms linear;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: var(--color-bg-secondary);
|
background: var(--color-bg-secondary);
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
transition: none;
|
transition: none;
|
||||||
border-color: var(--color-text-brand);
|
border-color: var(--color-border-brand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&__warning {
|
&__warning {
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
background: var(--color-bg-warning-softer);
|
background: var(--color-bg-warning-softest);
|
||||||
border: 1px solid var(--color-border-on-bg-warning-softer);
|
border: 1px solid var(--color-border-warning-soft);
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
@ -619,7 +619,7 @@ body > [data-popper-placement] {
|
|||||||
|
|
||||||
.spoiler-input__input {
|
.spoiler-input__input {
|
||||||
padding: 12px 12px - 5px;
|
padding: 12px 12px - 5px;
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -885,7 +885,7 @@ body > [data-popper-placement] {
|
|||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
letter-spacing: 0.1px;
|
letter-spacing: 0.1px;
|
||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
background-color: var(--color-bg-secondary-solid);
|
background-color: var(--color-bg-secondary);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@ -917,7 +917,7 @@ body > [data-popper-placement] {
|
|||||||
gap: 4px;
|
gap: 4px;
|
||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 1px solid var(--color-text-brand);
|
border: 1px solid var(--color-border-brand);
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
@ -1464,9 +1464,9 @@ body > [data-popper-placement] {
|
|||||||
|
|
||||||
.focusable {
|
.focusable {
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
outline: 2px solid var(--color-text-brand);
|
outline: 2px solid var(--color-border-brand);
|
||||||
outline-offset: -2px;
|
outline-offset: -2px;
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1587,7 +1587,7 @@ body > [data-popper-placement] {
|
|||||||
content: '';
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
animation: fade 0.7s reverse both 0.3s;
|
animation: fade 0.7s reverse both 0.3s;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
@ -1739,7 +1739,7 @@ body > [data-popper-placement] {
|
|||||||
.notification-ungrouped--direct,
|
.notification-ungrouped--direct,
|
||||||
.notification-group--direct,
|
.notification-group--direct,
|
||||||
.notification-group--annual-report {
|
.notification-group--annual-report {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
background: var(--color-bg-brand-soft);
|
background: var(--color-bg-brand-soft);
|
||||||
@ -1877,7 +1877,7 @@ body > [data-popper-placement] {
|
|||||||
.detailed-status__wrapper-direct {
|
.detailed-status__wrapper-direct {
|
||||||
.detailed-status,
|
.detailed-status,
|
||||||
.detailed-status__action-bar {
|
.detailed-status__action-bar {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status__prepend {
|
.status__prepend {
|
||||||
@ -1932,7 +1932,7 @@ body > [data-popper-placement] {
|
|||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
letter-spacing: 0.25px;
|
letter-spacing: 0.25px;
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
@ -2060,7 +2060,7 @@ body > [data-popper-placement] {
|
|||||||
|
|
||||||
&__domain-pill {
|
&__domain-pill {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 0;
|
border: 0;
|
||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
@ -2121,7 +2121,7 @@ body > [data-popper-placement] {
|
|||||||
|
|
||||||
&__handle {
|
&__handle {
|
||||||
border: 2px dashed var(--color-border-on-brand-softer);
|
border: 2px dashed var(--color-border-on-brand-softer);
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
padding: 12px 8px;
|
padding: 12px 8px;
|
||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@ -2903,11 +2903,15 @@ a.account__display-name {
|
|||||||
&:focus,
|
&:focus,
|
||||||
&:hover,
|
&:hover,
|
||||||
&:active {
|
&:active {
|
||||||
&:not(:disabled, [aria-disabled='true']) {
|
&:where(:not(:disabled, [aria-disabled='true'])) {
|
||||||
background: var(--color-bg-secondary);
|
background: var(--color-bg-brand-softest);
|
||||||
outline: 0;
|
outline: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:focus-visible {
|
||||||
|
outline: var(--outline-focus-default);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
button:disabled,
|
button:disabled,
|
||||||
@ -3072,7 +3076,7 @@ a.account__display-name {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
border-top-color: var(--color-text-brand);
|
border-top-color: var(--color-border-brand);
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3087,7 +3091,7 @@ a.account__display-name {
|
|||||||
border-top: 0;
|
border-top: 0;
|
||||||
|
|
||||||
@media screen and (min-width: $no-gap-breakpoint) {
|
@media screen and (min-width: $no-gap-breakpoint) {
|
||||||
border-top: 10px solid var(--color-bg-ambient);
|
border-top: 10px solid var(--color-bg-primary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3586,7 +3590,7 @@ a.account__display-name {
|
|||||||
&.focused {
|
&.focused {
|
||||||
transition: none;
|
transition: none;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
border-color: var(--color-text-brand);
|
border-color: var(--color-border-brand);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.copied {
|
&.copied {
|
||||||
@ -3986,25 +3990,14 @@ a.account__display-name {
|
|||||||
height: 20px;
|
height: 20px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
background-color: rgb(from var(--color-bg-brand-softer) r g b / 50%);
|
background-color: var(--color-bg-tertiary);
|
||||||
border: 1px solid rgb(from var(--color-text-brand) r g b / 50%);
|
border: 1px solid var(--color-border-primary);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
.react-toggle:hover:not(.react-toggle--disabled) & {
|
|
||||||
background-color: rgb(
|
|
||||||
from var(--color-bg-brand-softer) r g b /
|
|
||||||
calc(50% + var(--overlay-strength-brand))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-toggle--checked & {
|
.react-toggle--checked & {
|
||||||
background-color: var(--color-bg-brand-base);
|
background-color: var(--color-bg-brand-base);
|
||||||
border-color: var(--color-bg-brand-base);
|
border-color: var(--color-bg-brand-base);
|
||||||
}
|
}
|
||||||
|
|
||||||
.react-toggle--checked:not(.react-toggle--disabled):hover & {
|
|
||||||
background-color: var(--color-bg-brand-base-hover);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.react-toggle-track-check,
|
.react-toggle-track-check,
|
||||||
@ -4020,6 +4013,7 @@ a.account__display-name {
|
|||||||
height: 16px;
|
height: 16px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: var(--color-text-on-brand-base);
|
background-color: var(--color-text-on-brand-base);
|
||||||
|
box-shadow: 0 2px 4px 0 color-mix(var(--color-black), transparent 75%);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
transition: all 0.25s ease;
|
transition: all 0.25s ease;
|
||||||
transition-property: border-color, left;
|
transition-property: border-color, left;
|
||||||
@ -4081,8 +4075,8 @@ a.account__display-name {
|
|||||||
|
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
outline: none;
|
outline: none;
|
||||||
border-color: var(--color-text-brand);
|
border-color: var(--color-border-brand);
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
|
|
||||||
&--logo {
|
&--logo {
|
||||||
@ -4457,7 +4451,7 @@ a.status-card {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
outline: 2px solid var(--color-text-brand);
|
outline: var(--outline-focus-default);
|
||||||
outline-offset: -2px;
|
outline-offset: -2px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4545,7 +4539,7 @@ a.status-card {
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
box-shadow: 0 1px 0 var(--color-bg-brand-softer);
|
box-shadow: 0 1px 0 var(--color-bg-brand-softest);
|
||||||
|
|
||||||
&::before {
|
&::before {
|
||||||
display: block;
|
display: block;
|
||||||
@ -4655,7 +4649,7 @@ a.status-card {
|
|||||||
&.active {
|
&.active {
|
||||||
.column-header__icon {
|
.column-header__icon {
|
||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
text-shadow: 0 0 10px var(--color-bg-brand-softer);
|
text-shadow: 0 0 10px var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5150,7 +5144,7 @@ a.status-card {
|
|||||||
&:hover,
|
&:hover,
|
||||||
&:active,
|
&:active,
|
||||||
&:focus {
|
&:focus {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
color: color-mix(
|
color: color-mix(
|
||||||
in oklab,
|
in oklab,
|
||||||
var(--color-text-primary),
|
var(--color-text-primary),
|
||||||
@ -5752,7 +5746,7 @@ a.status-card {
|
|||||||
.visibility-modal {
|
.visibility-modal {
|
||||||
&__quote-warning {
|
&__quote-warning {
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
background: var(--color-bg-warning-softer);
|
background: var(--color-bg-warning-softest);
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
|
||||||
@ -5787,7 +5781,7 @@ a.status-card {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
background: var(--color-bg-secondary-solid);
|
background: var(--color-bg-secondary);
|
||||||
border: 1px solid var(--color-border-primary);
|
border: 1px solid var(--color-border-primary);
|
||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -5848,7 +5842,8 @@ a.status-card {
|
|||||||
|
|
||||||
.icon-button {
|
.icon-button {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
color: var(--color-text-secondary);
|
|
||||||
|
--default-icon-color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
@ -5901,7 +5896,10 @@ a.status-card {
|
|||||||
background: var(--color-bg-brand-base);
|
background: var(--color-bg-brand-base);
|
||||||
|
|
||||||
.icon-button {
|
.icon-button {
|
||||||
color: inherit;
|
--default-icon-color: inherit;
|
||||||
|
--default-bg-color: transparent;
|
||||||
|
--hover-icon-color: inherit;
|
||||||
|
--hover-bg-color: var(--color-bg-brand-base-hover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6000,7 +5998,7 @@ a.status-card {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:focus-visible {
|
&:focus-visible {
|
||||||
box-shadow: 0 0 0 2px var(--color-text-brand);
|
box-shadow: 0 0 0 2px var(--color-border-brand);
|
||||||
}
|
}
|
||||||
|
|
||||||
&[aria-hidden='true'] {
|
&[aria-hidden='true'] {
|
||||||
@ -6254,9 +6252,7 @@ a.status-card {
|
|||||||
--default-icon-color: var(--color-text-on-media);
|
--default-icon-color: var(--color-text-on-media);
|
||||||
--default-bg-color: transparent;
|
--default-bg-color: transparent;
|
||||||
--hover-icon-color: var(--color-text-on-media);
|
--hover-icon-color: var(--color-text-on-media);
|
||||||
--hover-bg-color: rgb(
|
--hover-bg-color: rgb(from var(--color-text-on-media) r g b / 10%);
|
||||||
from var(--color-text-on-media) r g b / var(--overlay-strength-brand)
|
|
||||||
);
|
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
filter: var(--overlay-icon-shadow);
|
filter: var(--overlay-icon-shadow);
|
||||||
@ -6272,8 +6268,7 @@ a.status-card {
|
|||||||
--default-icon-color: var(--color-text-favourite-highlight);
|
--default-icon-color: var(--color-text-favourite-highlight);
|
||||||
--hover-icon-color: var(--color-text-favourite-highlight);
|
--hover-icon-color: var(--color-text-favourite-highlight);
|
||||||
--hover-bg-color: rgb(
|
--hover-bg-color: rgb(
|
||||||
from var(--color-text-favourite-highlight) r g b /
|
from var(--color-text-favourite-highlight) r g b / 10%
|
||||||
var(--overlay-strength-brand)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6918,7 +6913,7 @@ a.status-card {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.button.button-secondary {
|
.button.button-secondary {
|
||||||
border-color: var(--color-text-error);
|
border-color: var(--color-border-error);
|
||||||
color: var(--color-text-error);
|
color: var(--color-text-error);
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
|
|
||||||
@ -6986,7 +6981,7 @@ a.status-card {
|
|||||||
&:hover,
|
&:hover,
|
||||||
&:active,
|
&:active,
|
||||||
&:focus {
|
&:focus {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7431,7 +7426,7 @@ a.status-card {
|
|||||||
inset: 2px;
|
inset: 2px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
border: 2px solid var(--color-text-on-inverted);
|
border: 2px solid var(--color-text-inverted);
|
||||||
outline: 2px solid var(--color-bg-inverted);
|
outline: 2px solid var(--color-bg-inverted);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
@ -8150,7 +8145,7 @@ a.status-card {
|
|||||||
|
|
||||||
&.checked,
|
&.checked,
|
||||||
&.indeterminate {
|
&.indeterminate {
|
||||||
border-color: var(--color-text-brand);
|
border-color: var(--color-border-brand);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
@ -8691,7 +8686,7 @@ noscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8953,7 +8948,7 @@ noscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__root {
|
&__root {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-end;
|
align-items: flex-end;
|
||||||
@ -9037,13 +9032,13 @@ noscript {
|
|||||||
|
|
||||||
&__item {
|
&__item {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
color: color-mix(
|
color: color-mix(
|
||||||
in oklab,
|
in oklab,
|
||||||
var(--color-text-primary),
|
var(--color-text-primary),
|
||||||
var(--color-text-secondary)
|
var(--color-text-secondary)
|
||||||
);
|
);
|
||||||
border: 1px solid var(--color-border-on-bg-brand-softer);
|
border: 1px solid var(--color-border-brand-soft);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
@ -9092,8 +9087,8 @@ noscript {
|
|||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
background-color: var(--color-bg-brand-softer);
|
background-color: var(--color-bg-brand-softest);
|
||||||
border-color: var(--color-text-brand);
|
border-color: var(--color-border-brand);
|
||||||
transition: all 100ms ease-in;
|
transition: all 100ms ease-in;
|
||||||
transition-property: background-color, color;
|
transition-property: background-color, color;
|
||||||
}
|
}
|
||||||
@ -9156,7 +9151,7 @@ noscript {
|
|||||||
inset-inline-start: 0;
|
inset-inline-start: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-inline-start: 4px solid var(--color-text-brand);
|
border-inline-start: 4px solid var(--color-border-brand);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -9679,7 +9674,7 @@ noscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.invalid &__input {
|
&.invalid &__input {
|
||||||
border-color: var(--color-text-error);
|
border-color: var(--color-border-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.expanded .search__popout {
|
&.expanded .search__popout {
|
||||||
@ -9950,8 +9945,8 @@ noscript {
|
|||||||
margin: 10px;
|
margin: 10px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid var(--color-border-on-bg-brand-softer);
|
border: 1px solid var(--color-border-brand-soft);
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|
||||||
@ -10017,8 +10012,8 @@ noscript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.warning-banner {
|
.warning-banner {
|
||||||
border: 1px solid var(--color-border-on-bg-error-softer);
|
border: 1px solid var(--color-border-error-soft);
|
||||||
background: var(--color-bg-error-softer);
|
background: var(--color-bg-error-softest);
|
||||||
|
|
||||||
&__message {
|
&__message {
|
||||||
h1 {
|
h1 {
|
||||||
@ -10354,7 +10349,7 @@ noscript {
|
|||||||
width: auto;
|
width: auto;
|
||||||
padding: 15px;
|
padding: 15px;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--color-text-on-inverted);
|
color: var(--color-text-inverted);
|
||||||
background: var(--color-bg-inverted);
|
background: var(--color-bg-inverted);
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@ -10408,7 +10403,7 @@ noscript {
|
|||||||
&:hover,
|
&:hover,
|
||||||
&:focus,
|
&:focus,
|
||||||
&:active {
|
&:active {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10510,13 +10505,16 @@ noscript {
|
|||||||
color: inherit;
|
color: inherit;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus,
|
&:focus,
|
||||||
&:active {
|
&:active,
|
||||||
|
.focusable:focus-visible &,
|
||||||
|
.detailed-status__wrapper-direct .detailed-status &,
|
||||||
|
.status__wrapper-direct & {
|
||||||
background: var(--color-bg-brand-soft);
|
background: var(--color-bg-brand-soft);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -10536,10 +10534,10 @@ noscript {
|
|||||||
padding: 16px 0;
|
padding: 16px 0;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
border-bottom: 1px solid var(--color-border-primary);
|
border-bottom: 1px solid var(--color-border-primary);
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
|
|
||||||
&.focusable:focus-visible {
|
&.focusable:focus-visible {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
@ -11201,7 +11199,7 @@ noscript {
|
|||||||
inset-inline-start: 0;
|
inset-inline-start: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-inline-start: 4px solid var(--color-text-brand);
|
border-inline-start: 4px solid var(--color-border-brand);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11398,8 +11396,8 @@ noscript {
|
|||||||
display: block;
|
display: block;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: var(--color-text-primary);
|
color: var(--color-text-primary);
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
border: 1px solid var(--color-border-on-bg-brand-softer);
|
border: 1px solid var(--color-border-brand-soft);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 8px (5px + 8px);
|
padding: 8px (5px + 8px);
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
&:hover,
|
&:hover,
|
||||||
&:focus,
|
&:focus,
|
||||||
&:active {
|
&:active {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,12 +100,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.positive {
|
&.positive {
|
||||||
background: var(--color-bg-success-softer);
|
background: var(--color-bg-success-softest);
|
||||||
color: var(--color-text-success);
|
color: var(--color-text-success);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.negative {
|
&.negative {
|
||||||
background: var(--color-bg-error-softer);
|
background: var(--color-bg-error-softest);
|
||||||
color: var(--color-text-error);
|
color: var(--color-text-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -168,7 +168,7 @@
|
|||||||
inset-inline-start: 0;
|
inset-inline-start: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background-color: var(--color-bg-brand-softer);
|
background-color: var(--color-bg-brand-softest);
|
||||||
border-radius: 100%;
|
border-radius: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,7 +32,7 @@ code {
|
|||||||
display: block;
|
display: block;
|
||||||
background: linear-gradient(
|
background: linear-gradient(
|
||||||
to bottom,
|
to bottom,
|
||||||
var(--color-bg-secondary-solid),
|
var(--color-bg-secondary),
|
||||||
transparent
|
transparent
|
||||||
);
|
);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -573,7 +573,7 @@ code {
|
|||||||
input[type='datetime-local'] {
|
input[type='datetime-local'] {
|
||||||
&:focus:user-invalid:not(:placeholder-shown),
|
&:focus:user-invalid:not(:placeholder-shown),
|
||||||
&:required:user-invalid:not(:placeholder-shown) {
|
&:required:user-invalid:not(:placeholder-shown) {
|
||||||
border-color: var(--color-text-error);
|
border-color: var(--color-border-error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -763,7 +763,7 @@ code {
|
|||||||
input[type='datetime-local'],
|
input[type='datetime-local'],
|
||||||
textarea,
|
textarea,
|
||||||
select {
|
select {
|
||||||
border-color: var(--color-text-error);
|
border-color: var(--color-border-error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -801,27 +801,27 @@ code {
|
|||||||
.flash-message {
|
.flash-message {
|
||||||
color: var(--color-text-brand);
|
color: var(--color-text-brand);
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: 1px solid var(--color-text-brand);
|
border: 1px solid var(--color-border-brand);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 15px 10px;
|
padding: 15px 10px;
|
||||||
margin-bottom: 30px;
|
margin-bottom: 30px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
&.notice {
|
&.notice {
|
||||||
border: 1px solid var(--color-border-on-bg-success-softer);
|
border: 1px solid var(--color-border-success-soft);
|
||||||
background: var(--color-bg-success-softer);
|
background: var(--color-bg-success-softest);
|
||||||
color: var(--color-text-success);
|
color: var(--color-text-success);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.warning {
|
&.warning {
|
||||||
border: 1px solid var(--color-border-on-bg-warning-softer);
|
border: 1px solid var(--color-border-warning-soft);
|
||||||
background: var(--color-bg-warning-softer);
|
background: var(--color-bg-warning-softest);
|
||||||
color: var(--color-text-warning);
|
color: var(--color-text-warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.alert {
|
&.alert {
|
||||||
border: 1px solid var(--color-border-on-bg-error-softer);
|
border: 1px solid var(--color-border-error-soft);
|
||||||
background: var(--color-bg-error-softer);
|
background: var(--color-bg-error-softest);
|
||||||
color: var(--color-text-error);
|
color: var(--color-text-error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -887,7 +887,7 @@ code {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1373,7 +1373,7 @@ code {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--color-bg-brand-softer);
|
background-color: var(--color-bg-brand-softest);
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
@ -1404,7 +1404,7 @@ code {
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.invalid img {
|
&.invalid img {
|
||||||
outline: 1px solid var(--color-text-error);
|
outline: 1px solid var(--color-border-error);
|
||||||
outline-offset: -1px;
|
outline-offset: -1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1414,7 +1414,7 @@ code {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: var(--color-bg-error-softer);
|
background: var(--color-bg-error-softest);
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
@use 'variables' as *;
|
@use 'variables' as *;
|
||||||
|
|
||||||
.modal-layout {
|
.modal-layout {
|
||||||
background: var(--color-bg-brand-softer);
|
background: var(--color-bg-brand-softest);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
@ -42,7 +42,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
inset: auto 0 0;
|
inset: auto 0 0;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
background-color: var(--color-bg-brand-softer);
|
background-color: var(--color-bg-brand-softest);
|
||||||
|
|
||||||
/* Decorative zig-zag pattern at the bottom of the page */
|
/* Decorative zig-zag pattern at the bottom of the page */
|
||||||
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 234.80078 31.757813" width="234.80078" height="31.757812"><path d="M19.599609 0c-1.05 0-2.10039.375-2.90039 1.125L0 16.925781v14.832031h234.80078V17.025391l-16.5-15.900391c-1.6-1.5-4.20078-1.5-5.80078 0l-13.80078 13.099609c-1.6 1.5-4.19883 1.5-5.79883 0L179.09961 1.125c-1.6-1.5-4.19883-1.5-5.79883 0L159.5 14.224609c-1.6 1.5-4.20078 1.5-5.80078 0L139.90039 1.125c-1.6-1.5-4.20078-1.5-5.80078 0l-13.79883 13.099609c-1.6 1.5-4.20078 1.5-5.80078 0L100.69922 1.125c-1.600001-1.5-4.198829-1.5-5.798829 0l-13.59961 13.099609c-1.6 1.5-4.200781 1.5-5.800781 0L61.699219 1.125c-1.6-1.5-4.198828-1.5-5.798828 0L42.099609 14.224609c-1.6 1.5-4.198828 1.5-5.798828 0L22.5 1.125C21.7.375 20.649609 0 19.599609 0z" fill="black"/></svg>');
|
mask-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 234.80078 31.757813" width="234.80078" height="31.757812"><path d="M19.599609 0c-1.05 0-2.10039.375-2.90039 1.125L0 16.925781v14.832031h234.80078V17.025391l-16.5-15.900391c-1.6-1.5-4.20078-1.5-5.80078 0l-13.80078 13.099609c-1.6 1.5-4.19883 1.5-5.79883 0L179.09961 1.125c-1.6-1.5-4.19883-1.5-5.79883 0L159.5 14.224609c-1.6 1.5-4.20078 1.5-5.80078 0L139.90039 1.125c-1.6-1.5-4.20078-1.5-5.80078 0l-13.79883 13.099609c-1.6 1.5-4.20078 1.5-5.80078 0L100.69922 1.125c-1.600001-1.5-4.198829-1.5-5.798829 0l-13.59961 13.099609c-1.6 1.5-4.200781 1.5-5.800781 0L61.699219 1.125c-1.6-1.5-4.198828-1.5-5.798828 0L42.099609 14.224609c-1.6 1.5-4.198828 1.5-5.798828 0L22.5 1.125C21.7.375 20.649609 0 19.599609 0z" fill="black"/></svg>');
|
||||||
|
|||||||
@ -86,7 +86,7 @@
|
|||||||
padding: 8px 12px;
|
padding: 8px 12px;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
border-color: var(--color-text-brand);
|
border-color: var(--color-border-brand);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (width <= 600px) {
|
@media screen and (width <= 600px) {
|
||||||
|
|||||||
@ -93,6 +93,26 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.mini-table {
|
||||||
|
border-top: 1px solid var(--color-border-primary);
|
||||||
|
width: 50%;
|
||||||
|
|
||||||
|
& > tbody > tr > th,
|
||||||
|
& > tbody > tr > td {
|
||||||
|
padding: 12px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > tbody > tr > th {
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > tbody > tr > td {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&.batch-table {
|
&.batch-table {
|
||||||
& > thead > tr > th {
|
& > thead > tr > th {
|
||||||
background: var(--color-bg-primary);
|
background: var(--color-bg-primary);
|
||||||
|
|||||||
@ -1,40 +1,69 @@
|
|||||||
@mixin palette {
|
@mixin palette {
|
||||||
--color-black: #000;
|
|
||||||
--color-grey-950: #181820;
|
|
||||||
--color-grey-800: #3a3a50;
|
|
||||||
--color-grey-700: #44445f;
|
|
||||||
--color-grey-600: #535374;
|
|
||||||
--color-grey-500: #67678e;
|
|
||||||
--color-grey-400: #88a;
|
|
||||||
--color-grey-300: #b2b1c8;
|
|
||||||
--color-grey-200: #d7d6e1;
|
|
||||||
--color-grey-100: #eeedf3;
|
|
||||||
--color-grey-50: #f6f6f9;
|
|
||||||
--color-white: #fff;
|
--color-white: #fff;
|
||||||
--color-indigo-700: #5638cc;
|
--color-black: #000;
|
||||||
--color-indigo-600: #6147e6;
|
|
||||||
--color-indigo-400: #8280f9;
|
// Grey
|
||||||
--color-indigo-300: #a5abfd;
|
--color-grey-50: #f6f6f9;
|
||||||
--color-indigo-200: #c8cdfe;
|
--color-grey-100: #eeedf3;
|
||||||
--color-indigo-100: #e0e3ff;
|
--color-grey-200: #d7d6e1;
|
||||||
|
--color-grey-300: #b2b1c8;
|
||||||
|
--color-grey-400: #88a;
|
||||||
|
--color-grey-500: #67678e;
|
||||||
|
--color-grey-600: #535374;
|
||||||
|
--color-grey-700: #44445f;
|
||||||
|
--color-grey-800: #3a3a50;
|
||||||
|
--color-grey-900: #21212c;
|
||||||
|
--color-grey-950: #181820;
|
||||||
|
|
||||||
|
// Indigo
|
||||||
--color-indigo-50: #f0f1ff;
|
--color-indigo-50: #f0f1ff;
|
||||||
|
--color-indigo-100: #e0e3ff;
|
||||||
|
--color-indigo-200: #c8cdfe;
|
||||||
|
--color-indigo-300: #a5abfd;
|
||||||
|
--color-indigo-400: #8280f9;
|
||||||
|
--color-indigo-500: #7263f2;
|
||||||
|
--color-indigo-600: #6147e6;
|
||||||
|
--color-indigo-700: #5638cc;
|
||||||
|
--color-indigo-800: #48359c;
|
||||||
|
--color-indigo-900: #3d317c;
|
||||||
|
--color-indigo-950: #261e48;
|
||||||
|
|
||||||
|
// Red
|
||||||
--color-red-50: #fef2f2;
|
--color-red-50: #fef2f2;
|
||||||
--color-red-100: #ffe2e2;
|
--color-red-100: #ffe2e2;
|
||||||
|
--color-red-200: #ffc9c9;
|
||||||
--color-red-300: #ffa2a2;
|
--color-red-300: #ffa2a2;
|
||||||
|
--color-red-400: #ff6467;
|
||||||
|
--color-red-500: #fb2c36;
|
||||||
|
--color-red-600: #e7000b;
|
||||||
|
--color-red-700: #c10007;
|
||||||
--color-red-800: #9f0712;
|
--color-red-800: #9f0712;
|
||||||
--color-red-900: #82181a;
|
--color-red-900: #82181a;
|
||||||
--color-red-950: #460809;
|
--color-red-950: #460809;
|
||||||
|
|
||||||
|
// Yellow
|
||||||
--color-yellow-50: #fffbeb;
|
--color-yellow-50: #fffbeb;
|
||||||
--color-yellow-100: #fef3c6;
|
--color-yellow-100: #fef3c6;
|
||||||
|
--color-yellow-200: #fee685;
|
||||||
|
--color-yellow-300: #ffd230;
|
||||||
--color-yellow-400: #ffb900;
|
--color-yellow-400: #ffb900;
|
||||||
|
--color-yellow-500: #fe9a00;
|
||||||
--color-yellow-600: #e17100;
|
--color-yellow-600: #e17100;
|
||||||
--color-yellow-700: #bb4d00;
|
--color-yellow-700: #bb4d00;
|
||||||
|
--color-yellow-800: #973c00;
|
||||||
--color-yellow-900: #7b3306;
|
--color-yellow-900: #7b3306;
|
||||||
--color-yellow-950: #461901;
|
--color-yellow-950: #461901;
|
||||||
|
|
||||||
|
// Green
|
||||||
--color-green-50: #f0fdf4;
|
--color-green-50: #f0fdf4;
|
||||||
--color-green-100: #dcfce7;
|
--color-green-100: #dcfce7;
|
||||||
|
--color-green-200: #b9f8cf;
|
||||||
|
--color-green-300: #7bf1a8;
|
||||||
--color-green-400: #05df72;
|
--color-green-400: #05df72;
|
||||||
|
--color-green-500: #00c950;
|
||||||
--color-green-600: #00a63e;
|
--color-green-600: #00a63e;
|
||||||
|
--color-green-700: #008236;
|
||||||
|
--color-green-800: #016630;
|
||||||
--color-green-900: #0d542b;
|
--color-green-900: #0d542b;
|
||||||
--color-green-950: #032e15;
|
--color-green-950: #032e15;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,150 +5,92 @@
|
|||||||
|
|
||||||
--color-text-primary: var(--color-grey-100);
|
--color-text-primary: var(--color-grey-100);
|
||||||
--color-text-secondary: var(--color-grey-300);
|
--color-text-secondary: var(--color-grey-300);
|
||||||
--color-text-tertiary: var(--color-grey-400);
|
--color-text-tertiary: var(--color-grey-400); // legacy
|
||||||
--color-text-on-inverted: var(--color-grey-950);
|
--color-text-inverted: var(--color-grey-950);
|
||||||
--color-text-brand: var(--color-indigo-300);
|
--color-text-brand: var(--color-indigo-300);
|
||||||
--color-text-brand-soft: color-mix(
|
--color-text-brand-soft: color-mix(
|
||||||
in oklab,
|
in oklab,
|
||||||
var(--color-text-primary),
|
var(--color-text-primary),
|
||||||
var(--color-text-brand)
|
var(--color-text-brand)
|
||||||
);
|
); // legacy
|
||||||
|
|
||||||
--color-text-on-brand-base: var(--color-white);
|
--color-text-on-brand-base: var(--color-white);
|
||||||
--color-text-brand-on-inverted: var(--color-indigo-600);
|
--color-text-brand-on-inverted: var(--color-indigo-600); // legacy
|
||||||
--color-text-error: var(--color-red-300);
|
--color-text-error: var(--color-red-300);
|
||||||
--color-text-on-error-base: var(--color-white);
|
--color-text-on-error-base: var(--color-white);
|
||||||
--color-text-warning: var(--color-yellow-400);
|
--color-text-warning: var(--color-yellow-400); // legacy
|
||||||
--color-text-on-warning-base: var(--color-white);
|
--color-text-on-warning-base: var(--color-white);
|
||||||
--color-text-success: var(--color-green-400);
|
--color-text-success: var(--color-green-400); // legacy
|
||||||
--color-text-on-success-base: var(--color-white);
|
--color-text-on-success-base: var(--color-white);
|
||||||
--color-text-disabled: var(--color-grey-600);
|
--color-text-disabled: var(--color-grey-600); // legacy
|
||||||
--color-text-on-disabled: var(--color-grey-400);
|
--color-text-on-disabled: var(--color-grey-400); // legacy
|
||||||
--color-text-bookmark-highlight: var(--color-text-error);
|
--color-text-bookmark-highlight: var(--color-text-error); // legacy
|
||||||
--color-text-favourite-highlight: var(--color-text-warning);
|
--color-text-favourite-highlight: var(--color-text-warning); // legacy
|
||||||
--color-text-on-media: var(--color-white);
|
--color-text-on-media: var(--color-white); // legacy
|
||||||
--color-text-status-links: color-mix(
|
--color-text-status-links: color-mix(
|
||||||
in oklab,
|
in oklab,
|
||||||
var(--color-text-primary),
|
var(--color-text-primary),
|
||||||
var(--color-text-secondary)
|
var(--color-text-secondary)
|
||||||
);
|
); // legacy
|
||||||
|
|
||||||
/* BACKGROUND TOKENS */
|
/* BACKGROUND TOKENS */
|
||||||
|
|
||||||
// Neutrals
|
// Neutrals
|
||||||
--color-bg-primary: var(--color-grey-950);
|
--color-bg-primary: var(--color-grey-950);
|
||||||
--overlay-strength-secondary: 4%;
|
--overlay-strength-secondary: 4%;
|
||||||
--color-bg-secondary-base: var(--color-white);
|
--color-bg-secondary: var(--color-grey-900);
|
||||||
--color-bg-secondary: #{utils.css-alpha(
|
--color-bg-tertiary: var(--color-grey-800); // legacy
|
||||||
var(--color-bg-secondary-base),
|
|
||||||
var(--overlay-strength-secondary)
|
|
||||||
)};
|
|
||||||
--color-bg-secondary-solid: color-mix(
|
|
||||||
in srgb,
|
|
||||||
var(--color-bg-primary),
|
|
||||||
var(--color-bg-secondary-base) var(--overlay-strength-secondary)
|
|
||||||
);
|
|
||||||
--color-bg-tertiary: color-mix(
|
|
||||||
in oklab,
|
|
||||||
var(--color-bg-primary),
|
|
||||||
var(--color-bg-secondary-base) calc(2 * var(--overlay-strength-secondary))
|
|
||||||
);
|
|
||||||
|
|
||||||
// Utility
|
// Utility
|
||||||
--color-bg-ambient: var(--color-bg-primary);
|
|
||||||
--color-bg-inverted: var(--color-grey-50);
|
--color-bg-inverted: var(--color-grey-50);
|
||||||
--color-bg-media-base: var(--color-black);
|
--color-bg-overlay-base: #{utils.css-alpha(var(--color-grey-950), 60%)};
|
||||||
--color-bg-media-strength: 65%;
|
--color-bg-overlay-highlight: #{utils.css-alpha(var(--color-white), 5%)};
|
||||||
--color-bg-media: #{utils.css-alpha(
|
--color-bg-overlay: var(--color-black); // legacy
|
||||||
var(--color-bg-media-base),
|
--color-bg-media-base: var(--color-black); // legacy
|
||||||
var(--color-bg-media-strength)
|
--color-bg-media: #{utils.css-alpha(var(--color-bg-media-base), 65%)}; // legacy
|
||||||
)};
|
--color-bg-disabled: var(--color-grey-700); // legacy
|
||||||
--color-bg-overlay: var(--color-black);
|
|
||||||
--color-bg-disabled: var(--color-grey-700);
|
|
||||||
|
|
||||||
// Brand
|
// Brand
|
||||||
--overlay-strength-brand: 22%;
|
|
||||||
--color-bg-brand-base: var(--color-indigo-700);
|
--color-bg-brand-base: var(--color-indigo-700);
|
||||||
--color-bg-brand-base-hover: color-mix(
|
--color-bg-brand-base-hover: var(--color-indigo-800); // legacy
|
||||||
in oklab,
|
--color-bg-brand-soft: var(--color-indigo-900);
|
||||||
var(--color-bg-brand-base),
|
--color-bg-brand-softest: var(--color-indigo-950);
|
||||||
var(--color-bg-primary) var(--overlay-strength-brand)
|
|
||||||
);
|
|
||||||
--color-bg-brand-soft: #{utils.css-alpha(
|
|
||||||
#6f4df5,
|
|
||||||
calc(var(--overlay-strength-brand) * 2)
|
|
||||||
)};
|
|
||||||
--color-bg-brand-softer: #{utils.css-alpha(
|
|
||||||
var(--color-bg-brand-base),
|
|
||||||
var(--overlay-strength-brand)
|
|
||||||
)};
|
|
||||||
--color-bg-brand-softer-solid: color-mix(
|
|
||||||
in srgb,
|
|
||||||
var(--color-bg-primary),
|
|
||||||
var(--color-bg-brand-base) var(--overlay-strength-brand)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Error
|
// Error
|
||||||
--overlay-strength-error: 10%;
|
--color-bg-error-base: var(--color-red-700);
|
||||||
--color-bg-error-base: var(--color-red-800);
|
--color-bg-error-base-hover: var(--color-red-800); // legacy
|
||||||
--color-bg-error-base-hover: color-mix(
|
|
||||||
in oklab,
|
|
||||||
var(--color-bg-error-base),
|
|
||||||
var(--color-bg-primary) var(--overlay-strength-error)
|
|
||||||
);
|
|
||||||
--color-bg-error-soft: var(--color-red-900);
|
--color-bg-error-soft: var(--color-red-900);
|
||||||
--color-bg-error-softer: var(--color-red-950);
|
--color-bg-error-softest: var(--color-red-950);
|
||||||
|
|
||||||
// Warning
|
// Warning
|
||||||
--overlay-strength-warning: 10%;
|
--color-bg-warning-base: var(--color-yellow-700); // legacy
|
||||||
--color-bg-warning-base: var(--color-yellow-700);
|
|
||||||
--color-bg-warning-base-hover: color-mix(
|
|
||||||
in oklab,
|
|
||||||
var(--color-bg-warning-base),
|
|
||||||
var(--color-bg-primary) var(--overlay-strength-warning)
|
|
||||||
);
|
|
||||||
--color-bg-warning-soft: var(--color-yellow-900);
|
--color-bg-warning-soft: var(--color-yellow-900);
|
||||||
--color-bg-warning-softer: var(--color-yellow-950);
|
--color-bg-warning-softest: var(--color-yellow-950);
|
||||||
|
|
||||||
// Success
|
// Success
|
||||||
--overlay-strength-success: 15%;
|
--color-bg-success-base: var(--color-green-600); // legacy
|
||||||
--color-bg-success-base: var(--color-green-600);
|
|
||||||
--color-bg-success-base-hover: color-mix(
|
|
||||||
in oklab,
|
|
||||||
var(--color-bg-success-base),
|
|
||||||
var(--color-bg-primary) var(--overlay-strength-success)
|
|
||||||
);
|
|
||||||
--color-bg-success-soft: var(--color-green-900);
|
--color-bg-success-soft: var(--color-green-900);
|
||||||
--color-bg-success-softer: var(--color-green-950);
|
--color-bg-success-softest: var(--color-green-950);
|
||||||
|
|
||||||
/* BORDER TOKENS */
|
/* BORDER TOKENS */
|
||||||
|
|
||||||
--border-strength-primary: 18%;
|
--color-border-primary: var(--color-grey-800);
|
||||||
--color-border-primary: #{utils.css-alpha(
|
--color-border-brand: var(--color-text-brand);
|
||||||
var(--color-indigo-200),
|
--color-border-brand-soft: var(--color-indigo-800);
|
||||||
var(--border-strength-primary)
|
--color-border-error: var(--color-red-300);
|
||||||
)};
|
--color-border-media: rgb(252 248 255 / 15%); // legacy
|
||||||
--color-border-media: rgb(252 248 255 / 15%);
|
--color-border-error-soft: #{utils.css-alpha(var(--color-border-error), 50%)}; // legacy
|
||||||
--color-border-verified: rgb(220, 3, 240);
|
--color-border-warning-soft: #{utils.css-alpha(
|
||||||
--color-border-on-bg-secondary: #{utils.css-alpha(
|
|
||||||
var(--color-indigo-200),
|
|
||||||
calc(var(--border-strength-primary) / 1.5)
|
|
||||||
)};
|
|
||||||
--color-border-on-bg-brand-softer: var(--color-border-primary);
|
|
||||||
--color-border-on-bg-error-softer: #{utils.css-alpha(
|
|
||||||
var(--color-text-error),
|
|
||||||
50%
|
|
||||||
)};
|
|
||||||
--color-border-on-bg-warning-softer: #{utils.css-alpha(
|
|
||||||
var(--color-text-warning),
|
var(--color-text-warning),
|
||||||
50%
|
50%
|
||||||
)};
|
)}; // legacy
|
||||||
--color-border-on-bg-success-softer: #{utils.css-alpha(
|
|
||||||
|
--color-border-success-soft: #{utils.css-alpha(
|
||||||
var(--color-text-success),
|
var(--color-text-success),
|
||||||
50%
|
50%
|
||||||
)};
|
)}; // legacy
|
||||||
--color-border-on-bg-inverted: var(--color-border-primary);
|
|
||||||
|
|
||||||
/* SHADOW TOKENS */
|
/* SHADOW TOKENS (LEGACY) */
|
||||||
|
|
||||||
--shadow-strength-primary: 80%;
|
--shadow-strength-primary: 80%;
|
||||||
--color-shadow-primary: #{utils.css-alpha(
|
--color-shadow-primary: #{utils.css-alpha(
|
||||||
@ -160,16 +102,16 @@
|
|||||||
0 8px 10px -6px var(--color-shadow-primary);
|
0 8px 10px -6px var(--color-shadow-primary);
|
||||||
--overlay-icon-shadow: drop-shadow(0 0 8px var(--color-shadow-primary));
|
--overlay-icon-shadow: drop-shadow(0 0 8px var(--color-shadow-primary));
|
||||||
|
|
||||||
/* GRAPHS/CHARTS TOKENS */
|
/* GRAPHS/CHARTS TOKENS (LEGACY) */
|
||||||
|
|
||||||
--color-graph-primary-stroke: var(--color-text-brand);
|
--color-graph-primary-stroke: var(--color-text-brand);
|
||||||
--color-graph-primary-fill: var(--color-bg-brand-softer);
|
--color-graph-primary-fill: var(--color-bg-brand-softest);
|
||||||
--color-graph-warning-stroke: var(--color-text-warning);
|
--color-graph-warning-stroke: var(--color-text-warning);
|
||||||
--color-graph-warning-fill: var(--color-bg-warning-softer);
|
--color-graph-warning-fill: var(--color-bg-warning-softest);
|
||||||
--color-graph-disabled-stroke: var(--color-text-disabled);
|
--color-graph-disabled-stroke: var(--color-text-disabled);
|
||||||
--color-graph-disabled-fill: var(--color-bg-disabled);
|
--color-graph-disabled-fill: var(--color-bg-disabled);
|
||||||
|
|
||||||
/* LEGACY TOKENS */
|
/* RICH TEXT TOKENS (LEGACY) */
|
||||||
|
|
||||||
--rich-text-container-color: rgb(87 24 60 / 100%);
|
--rich-text-container-color: rgb(87 24 60 / 100%);
|
||||||
--rich-text-text-color: rgb(255 175 212 / 100%);
|
--rich-text-text-color: rgb(255 175 212 / 100%);
|
||||||
@ -182,7 +124,9 @@
|
|||||||
--color-text-primary: var(--color-grey-50);
|
--color-text-primary: var(--color-grey-50);
|
||||||
--color-text-status-links: var(--color-text-brand);
|
--color-text-status-links: var(--color-text-brand);
|
||||||
|
|
||||||
/* BORDER TOKENS */
|
/* BACKGROUND TOKENS */
|
||||||
|
--color-bg-error-base: var(--color-red-800);
|
||||||
|
|
||||||
--border-strength-primary: 30%;
|
/* BORDER TOKENS */
|
||||||
|
--color-border-primary: var(--color-grey-600);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,145 +5,88 @@
|
|||||||
|
|
||||||
--color-text-primary: var(--color-grey-950);
|
--color-text-primary: var(--color-grey-950);
|
||||||
--color-text-secondary: var(--color-grey-600);
|
--color-text-secondary: var(--color-grey-600);
|
||||||
--color-text-tertiary: var(--color-grey-500);
|
--color-text-tertiary: var(--color-grey-500); // legacy
|
||||||
--color-text-on-inverted: var(--color-white);
|
--color-text-inverted: var(--color-white);
|
||||||
--color-text-brand: var(--color-indigo-700);
|
--color-text-brand: var(--color-indigo-700);
|
||||||
--color-text-brand-soft: color-mix(
|
--color-text-brand-soft: color-mix(
|
||||||
in oklab,
|
in oklab,
|
||||||
var(--color-text-primary),
|
var(--color-text-primary),
|
||||||
var(--color-text-brand)
|
var(--color-text-brand)
|
||||||
);
|
); // legacy
|
||||||
|
|
||||||
--color-text-on-brand-base: var(--color-white);
|
--color-text-on-brand-base: var(--color-white);
|
||||||
--color-text-brand-on-inverted: var(--color-indigo-400);
|
--color-text-brand-on-inverted: var(--color-indigo-400); // legacy
|
||||||
--color-text-error: var(--color-red-800);
|
--color-text-error: var(--color-red-800);
|
||||||
--color-text-on-error-base: var(--color-white);
|
--color-text-on-error-base: var(--color-white);
|
||||||
--color-text-warning: var(--color-yellow-600);
|
--color-text-warning: var(--color-yellow-600); // legacy
|
||||||
--color-text-on-warning-base: var(--color-white);
|
--color-text-on-warning-base: var(--color-white);
|
||||||
--color-text-success: var(--color-green-600);
|
--color-text-success: var(--color-green-600); // legacy
|
||||||
--color-text-on-success-base: var(--color-white);
|
--color-text-on-success-base: var(--color-white);
|
||||||
--color-text-disabled: var(--color-grey-300);
|
--color-text-disabled: var(--color-grey-300); // legacy
|
||||||
--color-text-on-disabled: var(--color-grey-200);
|
--color-text-on-disabled: var(--color-grey-200); // legacy
|
||||||
--color-text-bookmark-highlight: var(--color-text-error);
|
--color-text-bookmark-highlight: var(--color-text-error); // legacy
|
||||||
--color-text-favourite-highlight: var(--color-text-warning);
|
--color-text-favourite-highlight: var(--color-text-warning); // legacy
|
||||||
--color-text-on-media: var(--color-white);
|
--color-text-on-media: var(--color-white); // legacy
|
||||||
--color-text-status-links: var(--color-text-brand);
|
--color-text-status-links: var(--color-text-brand); // legacy
|
||||||
|
|
||||||
/* BACKGROUND TOKENS */
|
/* BACKGROUND TOKENS */
|
||||||
|
|
||||||
// Neutrals
|
// Neutrals
|
||||||
--color-bg-primary: var(--color-white);
|
--color-bg-primary: var(--color-white);
|
||||||
--overlay-strength-secondary: 4%;
|
--overlay-strength-secondary: 4%;
|
||||||
--color-bg-secondary-base: #000550;
|
--color-bg-secondary: var(--color-grey-50);
|
||||||
--color-bg-secondary: #{color-mix(
|
--color-bg-tertiary: var(--color-grey-100); // legacy
|
||||||
in oklab,
|
|
||||||
var(--color-bg-primary),
|
|
||||||
var(--color-bg-secondary-base) var(--overlay-strength-secondary)
|
|
||||||
)};
|
|
||||||
--color-bg-secondary-solid: #{color-mix(
|
|
||||||
in srgb,
|
|
||||||
var(--color-bg-primary),
|
|
||||||
var(--color-bg-secondary-base) var(--overlay-strength-secondary)
|
|
||||||
)};
|
|
||||||
--color-bg-tertiary: #{color-mix(
|
|
||||||
in oklab,
|
|
||||||
var(--color-bg-primary),
|
|
||||||
var(--color-bg-secondary-base) calc(2 * var(--overlay-strength-secondary))
|
|
||||||
)};
|
|
||||||
|
|
||||||
// Utility
|
// Utility
|
||||||
--color-bg-ambient: var(--color-bg-primary);
|
|
||||||
--color-bg-inverted: var(--color-grey-950);
|
--color-bg-inverted: var(--color-grey-950);
|
||||||
--color-bg-media-base: var(--color-black);
|
--color-bg-overlay-base: #{utils.css-alpha(var(--color-grey-950), 60%)};
|
||||||
--color-bg-media-strength: 65%;
|
--color-bg-overlay-highlight: #{utils.css-alpha(var(--color-grey-950), 5%)};
|
||||||
--color-bg-media: #{utils.css-alpha(
|
--color-bg-overlay: var(--color-bg-primary); // legacy
|
||||||
var(--color-bg-media-base),
|
--color-bg-media-base: var(--color-black); // legacy
|
||||||
var(--color-bg-media-strength)
|
--color-bg-media: #{utils.css-alpha(var(--color-bg-media-base), 65%)}; // legacy
|
||||||
)};
|
--color-bg-disabled: var(--color-grey-400); // legacy
|
||||||
--color-bg-overlay: var(--color-bg-primary);
|
|
||||||
--color-bg-disabled: var(--color-grey-400);
|
|
||||||
|
|
||||||
// Brand
|
// Brand
|
||||||
--overlay-strength-brand: 6%;
|
|
||||||
--color-bg-brand-base: var(--color-indigo-700);
|
--color-bg-brand-base: var(--color-indigo-700);
|
||||||
--color-bg-brand-base-hover: color-mix(
|
--color-bg-brand-base-hover: var(--color-indigo-800); // legacy
|
||||||
in oklab,
|
--color-bg-brand-soft: var(--color-indigo-100);
|
||||||
var(--color-bg-brand-base),
|
--color-bg-brand-softest: var(--color-indigo-50);
|
||||||
black var(--overlay-strength-brand)
|
|
||||||
);
|
|
||||||
--color-bg-brand-soft: #{utils.css-alpha(
|
|
||||||
#0012d8,
|
|
||||||
calc(var(--overlay-strength-brand) * 2)
|
|
||||||
)};
|
|
||||||
--color-bg-brand-softer: #{utils.css-alpha(
|
|
||||||
#0012d8,
|
|
||||||
var(--overlay-strength-brand)
|
|
||||||
)};
|
|
||||||
--color-bg-brand-softer-solid: color-mix(
|
|
||||||
in srgb,
|
|
||||||
var(--color-bg-primary),
|
|
||||||
var(--color-bg-brand-base) var(--overlay-strength-brand)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Error
|
// Error
|
||||||
--overlay-strength-error: 5%;
|
--color-bg-error-base: var(--color-red-700);
|
||||||
--color-bg-error-base: var(--color-red-800);
|
--color-bg-error-base-hover: var(--color-red-800); // legacy
|
||||||
--color-bg-error-base-hover: color-mix(
|
|
||||||
in oklab,
|
|
||||||
var(--color-bg-error-base),
|
|
||||||
black var(--overlay-strength-error)
|
|
||||||
);
|
|
||||||
--color-bg-error-soft: var(--color-red-100);
|
--color-bg-error-soft: var(--color-red-100);
|
||||||
--color-bg-error-softer: var(--color-red-50);
|
--color-bg-error-softest: var(--color-red-50);
|
||||||
|
|
||||||
// Warning
|
// Warning
|
||||||
--overlay-strength-warning: 10%;
|
--color-bg-warning-base: var(--color-yellow-700); // legacy
|
||||||
--color-bg-warning-base: var(--color-yellow-700);
|
|
||||||
--color-bg-warning-base-hover: color-mix(
|
|
||||||
in oklab,
|
|
||||||
var(--color-bg-warning-base),
|
|
||||||
black var(--overlay-strength-warning)
|
|
||||||
);
|
|
||||||
--color-bg-warning-soft: var(--color-yellow-100);
|
--color-bg-warning-soft: var(--color-yellow-100);
|
||||||
--color-bg-warning-softer: var(--color-yellow-50);
|
--color-bg-warning-softest: var(--color-yellow-50);
|
||||||
|
|
||||||
// Success
|
// Success
|
||||||
--overlay-strength-success: 15%;
|
--color-bg-success-base: var(--color-green-600); // legacy
|
||||||
--color-bg-success-base: var(--color-green-600);
|
|
||||||
--color-bg-success-base-hover: color-mix(
|
|
||||||
in oklab,
|
|
||||||
var(--color-bg-success-base),
|
|
||||||
black var(--overlay-strength-success)
|
|
||||||
);
|
|
||||||
--color-bg-success-soft: var(--color-green-100);
|
--color-bg-success-soft: var(--color-green-100);
|
||||||
--color-bg-success-softer: var(--color-green-50);
|
--color-bg-success-softest: var(--color-green-50);
|
||||||
|
|
||||||
/* BORDER TOKENS */
|
/* BORDER TOKENS */
|
||||||
|
|
||||||
--border-strength-primary: 15%;
|
--color-border-primary: var(--color-grey-200);
|
||||||
--color-border-primary: color-mix(
|
--color-border-brand: var(--color-text-brand);
|
||||||
in oklab,
|
--color-border-brand-soft: var(--color-indigo-200);
|
||||||
var(--color-bg-primary),
|
--color-border-error: var(--color-red-700);
|
||||||
var(--color-grey-950) var(--border-strength-primary)
|
--color-border-media: rgb(252 248 255 / 15%); // legacy
|
||||||
);
|
--color-border-error-soft: #{utils.css-alpha(var(--color-text-error), 50%)}; // legacy
|
||||||
--color-border-media: rgb(252 248 255 / 15%);
|
--color-border-warning-soft: #{utils.css-alpha(
|
||||||
--color-border-verified: rgb(220, 3, 240);
|
|
||||||
--color-border-on-bg-secondary: var(--color-grey-200);
|
|
||||||
--color-border-on-bg-brand-softer: var(--color-indigo-200);
|
|
||||||
--color-border-on-bg-error-softer: #{utils.css-alpha(
|
|
||||||
var(--color-text-error),
|
|
||||||
50%
|
|
||||||
)};
|
|
||||||
--color-border-on-bg-warning-softer: #{utils.css-alpha(
|
|
||||||
var(--color-text-warning),
|
var(--color-text-warning),
|
||||||
50%
|
50%
|
||||||
)};
|
)}; // legacy
|
||||||
--color-border-on-bg-success-softer: #{utils.css-alpha(
|
|
||||||
|
--color-border-success-soft: #{utils.css-alpha(
|
||||||
var(--color-text-success),
|
var(--color-text-success),
|
||||||
50%
|
50%
|
||||||
)};
|
)}; // legacy
|
||||||
--color-border-on-bg-inverted: var(--color-border-primary);
|
|
||||||
|
|
||||||
/* SHADOW TOKENS */
|
/* SHADOW TOKENS (LEGACY) */
|
||||||
|
|
||||||
--shadow-strength-primary: 30%;
|
--shadow-strength-primary: 30%;
|
||||||
--color-shadow-primary: #{utils.css-alpha(
|
--color-shadow-primary: #{utils.css-alpha(
|
||||||
@ -155,16 +98,16 @@
|
|||||||
0 8px 10px -6px var(--color-shadow-primary);
|
0 8px 10px -6px var(--color-shadow-primary);
|
||||||
--overlay-icon-shadow: drop-shadow(0 0 8px var(--color-shadow-primary));
|
--overlay-icon-shadow: drop-shadow(0 0 8px var(--color-shadow-primary));
|
||||||
|
|
||||||
/* GRAPHS/CHARTS TOKENS */
|
/* GRAPHS/CHARTS TOKENS (LEGACY) */
|
||||||
|
|
||||||
--color-graph-primary-stroke: var(--color-text-brand);
|
--color-graph-primary-stroke: var(--color-text-brand);
|
||||||
--color-graph-primary-fill: var(--color-bg-brand-softer);
|
--color-graph-primary-fill: var(--color-bg-brand-softest);
|
||||||
--color-graph-warning-stroke: var(--color-text-warning);
|
--color-graph-warning-stroke: var(--color-text-warning);
|
||||||
--color-graph-warning-fill: var(--color-bg-warning-softer);
|
--color-graph-warning-fill: var(--color-bg-warning-softest);
|
||||||
--color-graph-disabled-stroke: var(--color-text-disabled);
|
--color-graph-disabled-stroke: var(--color-text-disabled);
|
||||||
--color-graph-disabled-fill: var(--color-bg-disabled);
|
--color-graph-disabled-fill: var(--color-bg-disabled);
|
||||||
|
|
||||||
/* LEGACY TOKENS */
|
/* RICH TEXT TOKENS (LEGACY) */
|
||||||
|
|
||||||
--rich-text-container-color: rgb(255 216 231 / 100%);
|
--rich-text-container-color: rgb(255 216 231 / 100%);
|
||||||
--rich-text-text-color: rgb(114 47 83 / 100%);
|
--rich-text-text-color: rgb(114 47 83 / 100%);
|
||||||
@ -179,8 +122,9 @@
|
|||||||
--color-text-tertiary: var(--color-grey-700);
|
--color-text-tertiary: var(--color-grey-700);
|
||||||
--color-text-brand: var(--color-indigo-600);
|
--color-text-brand: var(--color-indigo-600);
|
||||||
|
|
||||||
/* BORDER TOKENS */
|
/* BACKGROUND TOKENS */
|
||||||
|
--color-bg-error-base: var(--color-red-800);
|
||||||
|
|
||||||
--border-strength-primary: 30%;
|
/* BORDER TOKENS */
|
||||||
--color-border-on-bg-secondary: var(--color-grey-300);
|
--color-border-primary: var(--color-grey-300);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -69,7 +69,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.active .avatar-stack .account__avatar {
|
&.active .avatar-stack .account__avatar {
|
||||||
border-color: var(--color-text-brand);
|
border-color: var(--color-border-brand);
|
||||||
}
|
}
|
||||||
|
|
||||||
.trends__item__current {
|
.trends__item__current {
|
||||||
|
|||||||
@ -23,8 +23,7 @@ class ActivityPub::Activity::FeatureRequest < ActivityPub::Activity
|
|||||||
|
|
||||||
def accept_request!
|
def accept_request!
|
||||||
collection_item = @collection.collection_items.create!(
|
collection_item = @collection.collection_items.create!(
|
||||||
account: @featured_account,
|
collection_item_attributes(:accepted)
|
||||||
state: :accepted
|
|
||||||
)
|
)
|
||||||
|
|
||||||
queue_delivery!(collection_item, ActivityPub::AcceptFeatureRequestSerializer)
|
queue_delivery!(collection_item, ActivityPub::AcceptFeatureRequestSerializer)
|
||||||
@ -32,13 +31,16 @@ class ActivityPub::Activity::FeatureRequest < ActivityPub::Activity
|
|||||||
|
|
||||||
def reject_request!
|
def reject_request!
|
||||||
collection_item = @collection.collection_items.build(
|
collection_item = @collection.collection_items.build(
|
||||||
account: @featured_account,
|
collection_item_attributes(:rejected)
|
||||||
state: :rejected
|
|
||||||
)
|
)
|
||||||
|
|
||||||
queue_delivery!(collection_item, ActivityPub::RejectFeatureRequestSerializer)
|
queue_delivery!(collection_item, ActivityPub::RejectFeatureRequestSerializer)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def collection_item_attributes(state = :accepted)
|
||||||
|
{ account: @featured_account, activity_uri: @json['id'], state: }
|
||||||
|
end
|
||||||
|
|
||||||
def queue_delivery!(collection_item, serializer)
|
def queue_delivery!(collection_item, serializer)
|
||||||
json = JSON.generate(serialize_payload(collection_item, serializer))
|
json = JSON.generate(serialize_payload(collection_item, serializer))
|
||||||
ActivityPub::DeliveryWorker.perform_async(json, @featured_account.id, @account.inbox_url)
|
ActivityPub::DeliveryWorker.perform_async(json, @featured_account.id, @account.inbox_url)
|
||||||
|
|||||||
@ -19,16 +19,15 @@ class ActivityPub::LinkedDataSignature
|
|||||||
|
|
||||||
return unless type == 'RsaSignature2017'
|
return unless type == 'RsaSignature2017'
|
||||||
|
|
||||||
creator = ActivityPub::TagManager.instance.uri_to_actor(creator_uri)
|
keypair = Keypair.from_keyid(creator_uri)
|
||||||
creator = ActivityPub::FetchRemoteKeyService.new.call(creator_uri) if creator&.public_key.blank?
|
keypair = ActivityPub::FetchRemoteKeyService.new.call(creator_uri) if keypair&.public_key.blank?
|
||||||
|
return if keypair.nil? || !keypair.usable?
|
||||||
return if creator.nil?
|
|
||||||
|
|
||||||
options_hash = hash(@json['signature'].without('type', 'id', 'signatureValue').merge('@context' => CONTEXT))
|
options_hash = hash(@json['signature'].without('type', 'id', 'signatureValue').merge('@context' => CONTEXT))
|
||||||
document_hash = hash(@json.without('signature'))
|
document_hash = hash(@json.without('signature'))
|
||||||
to_be_verified = options_hash + document_hash
|
to_be_verified = options_hash + document_hash
|
||||||
|
|
||||||
creator if creator.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), Base64.decode64(signature), to_be_verified)
|
keypair.actor if keypair.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), Base64.decode64(signature), to_be_verified)
|
||||||
rescue OpenSSL::PKey::RSAError
|
rescue OpenSSL::PKey::RSAError
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|||||||
@ -23,14 +23,14 @@ class SignedRequest
|
|||||||
%w(rsa-sha256 hs2019).include?(signature_algorithm)
|
%w(rsa-sha256 hs2019).include?(signature_algorithm)
|
||||||
end
|
end
|
||||||
|
|
||||||
def verified?(actor)
|
def verified?(keypair)
|
||||||
signature = Base64.decode64(signature_params['signature'])
|
signature = Base64.decode64(signature_params['signature'])
|
||||||
compare_signed_string = build_signed_string(include_query_string: true)
|
compare_signed_string = build_signed_string(include_query_string: true)
|
||||||
|
|
||||||
return true unless verify_signature(actor, signature, compare_signed_string).nil?
|
return true unless verify_signature(keypair, signature, compare_signed_string).nil?
|
||||||
|
|
||||||
compare_signed_string = build_signed_string(include_query_string: false)
|
compare_signed_string = build_signed_string(include_query_string: false)
|
||||||
return true unless verify_signature(actor, signature, compare_signed_string).nil?
|
return true unless verify_signature(keypair, signature, compare_signed_string).nil?
|
||||||
|
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
@ -99,8 +99,8 @@ class SignedRequest
|
|||||||
signature_params.fetch('headers', signature_algorithm == 'hs2019' ? '(created)' : 'date').downcase.split
|
signature_params.fetch('headers', signature_algorithm == 'hs2019' ? '(created)' : 'date').downcase.split
|
||||||
end
|
end
|
||||||
|
|
||||||
def verify_signature(actor, signature, compare_signed_string)
|
def verify_signature(keypair, signature, compare_signed_string)
|
||||||
true if actor.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), signature, compare_signed_string)
|
true if keypair.keypair.public_key.verify(OpenSSL::Digest.new('SHA256'), signature, compare_signed_string)
|
||||||
rescue OpenSSL::PKey::RSAError
|
rescue OpenSSL::PKey::RSAError
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
@ -170,8 +170,8 @@ class SignedRequest
|
|||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
def verified?(actor)
|
def verified?(keypair)
|
||||||
key = Linzer.new_rsa_v1_5_sha256_public_key(actor.public_key)
|
key = Linzer.new_rsa_v1_5_sha256_public_key(keypair.public_key)
|
||||||
|
|
||||||
Linzer.verify(key, @message, @signature)
|
Linzer.verify(key, @message, @signature)
|
||||||
rescue Linzer::VerifyError
|
rescue Linzer::VerifyError
|
||||||
@ -243,7 +243,7 @@ class SignedRequest
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def verified?(actor)
|
def verified?(keypair)
|
||||||
missing_signature_parameters = @signature.missing_signature_parameters
|
missing_signature_parameters = @signature.missing_signature_parameters
|
||||||
raise Mastodon::SignatureVerificationError, "Incompatible request signature. #{missing_signature_parameters.to_sentence} are required" if missing_signature_parameters
|
raise Mastodon::SignatureVerificationError, "Incompatible request signature. #{missing_signature_parameters.to_sentence} are required" if missing_signature_parameters
|
||||||
raise Mastodon::SignatureVerificationError, 'Unsupported signature algorithm (only rsa-sha256 and hs2019 are supported)' unless @signature.algorithm_supported?
|
raise Mastodon::SignatureVerificationError, 'Unsupported signature algorithm (only rsa-sha256 and hs2019 are supported)' unless @signature.algorithm_supported?
|
||||||
@ -251,7 +251,7 @@ class SignedRequest
|
|||||||
|
|
||||||
@signature.verify_signature_strength!
|
@signature.verify_signature_strength!
|
||||||
@signature.verify_body_digest!
|
@signature.verify_body_digest!
|
||||||
@signature.verified?(actor)
|
@signature.verified?(keypair)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|||||||
66
app/mailers/email_subscription_mailer.rb
Normal file
66
app/mailers/email_subscription_mailer.rb
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class EmailSubscriptionMailer < ApplicationMailer
|
||||||
|
include BulkMailSettingsConcern
|
||||||
|
include Redisable
|
||||||
|
|
||||||
|
layout 'mailer'
|
||||||
|
|
||||||
|
helper :accounts
|
||||||
|
helper :routing
|
||||||
|
helper :statuses
|
||||||
|
|
||||||
|
before_action :set_subscription
|
||||||
|
before_action :set_unsubscribe_url
|
||||||
|
before_action :set_instance
|
||||||
|
before_action :set_skip_preferences_link
|
||||||
|
|
||||||
|
after_action :use_bulk_mail_delivery_settings, except: [:confirmation]
|
||||||
|
after_action :set_list_headers
|
||||||
|
|
||||||
|
default to: -> { @subscription.email }
|
||||||
|
|
||||||
|
def confirmation
|
||||||
|
I18n.with_locale(locale) do
|
||||||
|
mail subject: default_i18n_subject
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def notification(statuses)
|
||||||
|
@statuses = statuses
|
||||||
|
|
||||||
|
I18n.with_locale(locale) do
|
||||||
|
mail subject: default_i18n_subject(count: @statuses.size, name: @subscription.account.display_name, excerpt: @statuses.first.text.truncate(17))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_list_headers
|
||||||
|
headers(
|
||||||
|
'List-ID' => "<#{@subscription.account.username}.#{Rails.configuration.x.local_domain}>",
|
||||||
|
'List-Unsubscribe-Post' => 'List-Unsubscribe=One-Click',
|
||||||
|
'List-Unsubscribe' => "<#{@unsubscribe_url}>"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_subscription
|
||||||
|
@subscription = params[:subscription]
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_unsubscribe_url
|
||||||
|
@unsubscribe_url = unsubscribe_url(token: @subscription.to_sgid(for: 'unsubscribe').to_s)
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_instance
|
||||||
|
@instance = Rails.configuration.x.local_domain
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_skip_preferences_link
|
||||||
|
@skip_preferences_link = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def locale
|
||||||
|
@subscription.locale.presence || I18n.default_locale
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -193,8 +193,10 @@ class Account < ApplicationRecord
|
|||||||
:role,
|
:role,
|
||||||
:locale,
|
:locale,
|
||||||
:shows_application?,
|
:shows_application?,
|
||||||
|
:email_subscriptions_enabled?,
|
||||||
:prefers_noindex?,
|
:prefers_noindex?,
|
||||||
:time_zone,
|
:time_zone,
|
||||||
|
:can?,
|
||||||
to: :user,
|
to: :user,
|
||||||
prefix: true,
|
prefix: true,
|
||||||
allow_nil: true
|
allow_nil: true
|
||||||
|
|||||||
@ -37,6 +37,8 @@ module Account::Associations
|
|||||||
has_many :scheduled_statuses
|
has_many :scheduled_statuses
|
||||||
has_many :status_pins
|
has_many :status_pins
|
||||||
has_many :statuses
|
has_many :statuses
|
||||||
|
has_many :keypairs
|
||||||
|
has_many :email_subscriptions
|
||||||
|
|
||||||
has_one :deletion_request, class_name: 'AccountDeletionRequest'
|
has_one :deletion_request, class_name: 'AccountDeletionRequest'
|
||||||
has_one :follow_recommendation_suppression
|
has_one :follow_recommendation_suppression
|
||||||
|
|||||||
@ -15,6 +15,10 @@ module User::HasSettings
|
|||||||
settings['noindex']
|
settings['noindex']
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def email_subscriptions_enabled?
|
||||||
|
settings['email_subscriptions']
|
||||||
|
end
|
||||||
|
|
||||||
def preferred_posting_language
|
def preferred_posting_language
|
||||||
valid_locale_cascade(settings['default_language'], locale, I18n.locale)
|
valid_locale_cascade(settings['default_language'], locale, I18n.locale)
|
||||||
end
|
end
|
||||||
|
|||||||
48
app/models/email_subscription.rb
Normal file
48
app/models/email_subscription.rb
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: email_subscriptions
|
||||||
|
#
|
||||||
|
# id :bigint(8) not null, primary key
|
||||||
|
# confirmation_token :string
|
||||||
|
# confirmed_at :datetime
|
||||||
|
# email :string not null
|
||||||
|
# locale :string not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# account_id :bigint(8) not null
|
||||||
|
#
|
||||||
|
|
||||||
|
class EmailSubscription < ApplicationRecord
|
||||||
|
belongs_to :account
|
||||||
|
|
||||||
|
normalizes :email, with: ->(str) { str.squish.downcase }
|
||||||
|
|
||||||
|
validates :email, presence: true, email_address: true, uniqueness: { scope: :account_id }
|
||||||
|
|
||||||
|
scope :confirmed, -> { where.not(confirmed_at: nil) }
|
||||||
|
scope :unconfirmed, -> { where(confirmed_at: nil) }
|
||||||
|
|
||||||
|
before_create :set_confirmation_token
|
||||||
|
|
||||||
|
after_create_commit :send_confirmation_email
|
||||||
|
|
||||||
|
def confirmed?
|
||||||
|
confirmed_at.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def confirm!
|
||||||
|
touch(:confirmed_at)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def set_confirmation_token
|
||||||
|
self.confirmation_token = Devise.friendly_token unless confirmed?
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_confirmation_email
|
||||||
|
EmailSubscriptionMailer.with(subscription: self).confirmation.deliver_later
|
||||||
|
end
|
||||||
|
end
|
||||||
77
app/models/keypair.rb
Normal file
77
app/models/keypair.rb
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: keypairs
|
||||||
|
#
|
||||||
|
# id :bigint(8) not null, primary key
|
||||||
|
# expires_at :datetime
|
||||||
|
# private_key :string
|
||||||
|
# public_key :string not null
|
||||||
|
# revoked :boolean default(FALSE), not null
|
||||||
|
# type :integer not null
|
||||||
|
# uri :string not null
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
# account_id :bigint(8) not null
|
||||||
|
#
|
||||||
|
|
||||||
|
class Keypair < ApplicationRecord
|
||||||
|
include Expireable
|
||||||
|
|
||||||
|
self.inheritance_column = nil
|
||||||
|
|
||||||
|
encrypts :private_key
|
||||||
|
|
||||||
|
belongs_to :account
|
||||||
|
|
||||||
|
enum :type, { rsa: 0 }
|
||||||
|
|
||||||
|
attr_accessor :require_private_key
|
||||||
|
|
||||||
|
validates :uri, presence: true, uniqueness: true
|
||||||
|
validates :public_key, presence: true
|
||||||
|
validates :private_key, presence: true, if: -> { account.local? }
|
||||||
|
|
||||||
|
# NOTE: this should be true in production, but tests heavily rely on remote accounts having a keypair
|
||||||
|
validates :private_key, absence: true, if: -> { account.remote? && !require_private_key }
|
||||||
|
|
||||||
|
scope :unexpired, -> { where(expires_at: nil).or(where.not(expires_at: ..Time.now.utc)) }
|
||||||
|
scope :usable, -> { unexpired.where(revoked: false) }
|
||||||
|
|
||||||
|
alias actor account
|
||||||
|
|
||||||
|
def keypair
|
||||||
|
@keypair ||= begin
|
||||||
|
case type
|
||||||
|
when 'rsa'
|
||||||
|
OpenSSL::PKey::RSA.new(private_key || public_key)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def usable?
|
||||||
|
!revoked? && !expired?
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_keyid(uri)
|
||||||
|
keypair = find_by(uri: uri)
|
||||||
|
return keypair unless keypair.nil?
|
||||||
|
|
||||||
|
# No keypair found, try the old way we used to store RSA keypairs
|
||||||
|
account = ActivityPub::TagManager.instance.uri_to_actor(uri)
|
||||||
|
return if account&.public_key.blank?
|
||||||
|
|
||||||
|
from_legacy_account(account, uri: uri)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_legacy_account(account, uri: nil)
|
||||||
|
Keypair.new(
|
||||||
|
account:,
|
||||||
|
uri: uri.presence || ActivityPub::TagManager.instance.key_uri_for(account),
|
||||||
|
public_key: account.public_key,
|
||||||
|
private_key: account.private_key,
|
||||||
|
type: :rsa
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -39,6 +39,7 @@ class UserRole < ApplicationRecord
|
|||||||
delete_user_data: (1 << 19),
|
delete_user_data: (1 << 19),
|
||||||
view_feeds: (1 << 20),
|
view_feeds: (1 << 20),
|
||||||
invite_bypass_approval: (1 << 21),
|
invite_bypass_approval: (1 << 21),
|
||||||
|
manage_email_subscriptions: (1 << 22),
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
EVERYONE_ROLE_ID = -99
|
EVERYONE_ROLE_ID = -99
|
||||||
@ -60,6 +61,10 @@ class UserRole < ApplicationRecord
|
|||||||
invite_bypass_approval
|
invite_bypass_approval
|
||||||
).freeze,
|
).freeze,
|
||||||
|
|
||||||
|
email: %i(
|
||||||
|
manage_email_subscriptions
|
||||||
|
).freeze,
|
||||||
|
|
||||||
moderation: %i(
|
moderation: %i(
|
||||||
view_dashboard
|
view_dashboard
|
||||||
view_audit_log
|
view_audit_log
|
||||||
|
|||||||
@ -19,6 +19,7 @@ class UserSettings
|
|||||||
setting :default_content_type, default: 'text/plain'
|
setting :default_content_type, default: 'text/plain'
|
||||||
setting :hide_followers_count, default: false
|
setting :hide_followers_count, default: false
|
||||||
setting :default_quote_policy, default: 'public', in: %w(public followers nobody)
|
setting :default_quote_policy, default: 'public', in: %w(public followers nobody)
|
||||||
|
setting :email_subscriptions, default: false
|
||||||
|
|
||||||
setting_inverse_alias :indexable, :noindex
|
setting_inverse_alias :indexable, :noindex
|
||||||
setting_inverse_alias :show_followers_count, :hide_followers_count
|
setting_inverse_alias :show_followers_count, :hide_followers_count
|
||||||
|
|||||||
@ -32,7 +32,7 @@ class ActivityPub::FeaturedCollectionSerializer < ActivityPub::Serializer
|
|||||||
end
|
end
|
||||||
|
|
||||||
def total_items
|
def total_items
|
||||||
object.collection_items.size
|
object.accepted_collection_items.size
|
||||||
end
|
end
|
||||||
|
|
||||||
def published
|
def published
|
||||||
|
|||||||
@ -22,6 +22,7 @@ class REST::AccountSerializer < ActiveModel::Serializer
|
|||||||
attribute :memorial, if: :memorial?
|
attribute :memorial, if: :memorial?
|
||||||
|
|
||||||
attribute :feature_approval, if: -> { Mastodon::Feature.collections_enabled? }
|
attribute :feature_approval, if: -> { Mastodon::Feature.collections_enabled? }
|
||||||
|
attribute :email_subscriptions, if: -> { Mastodon::Feature.email_subscriptions_enabled? }
|
||||||
|
|
||||||
class AccountDecorator < SimpleDelegator
|
class AccountDecorator < SimpleDelegator
|
||||||
def self.model_name
|
def self.model_name
|
||||||
@ -180,4 +181,8 @@ class REST::AccountSerializer < ActiveModel::Serializer
|
|||||||
current_user: object.feature_policy_for_account(current_user&.account),
|
current_user: object.feature_policy_for_account(current_user&.account),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def email_subscriptions
|
||||||
|
object.user_can?(:manage_email_subscriptions) && object.user_email_subscriptions_enabled?
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -14,7 +14,7 @@ class ActivityPub::FetchRemoteKeyService < BaseService
|
|||||||
raise Error, "Unable to fetch key JSON at #{uri}" if @json.nil?
|
raise Error, "Unable to fetch key JSON at #{uri}" if @json.nil?
|
||||||
raise Error, "Unsupported JSON-LD context for document #{uri}" unless supported_context?(@json) || (supported_security_context?(@json) && @json['owner'].present? && !actor_type?)
|
raise Error, "Unsupported JSON-LD context for document #{uri}" unless supported_context?(@json) || (supported_security_context?(@json) && @json['owner'].present? && !actor_type?)
|
||||||
raise Error, "Unexpected object type for key #{uri}" unless expected_type?
|
raise Error, "Unexpected object type for key #{uri}" unless expected_type?
|
||||||
return find_actor(@json['id'], @json, suppress_errors) if actor_type?
|
return Keypair.from_legacy_account(find_actor(@json['id'], @json, suppress_errors), uri: uri) if actor_type?
|
||||||
|
|
||||||
@owner = fetch_resource(owner_uri, true)
|
@owner = fetch_resource(owner_uri, true)
|
||||||
|
|
||||||
@ -23,7 +23,8 @@ class ActivityPub::FetchRemoteKeyService < BaseService
|
|||||||
raise Error, "Unexpected object type for actor #{owner_uri} (expected any of: #{SUPPORTED_TYPES})" unless expected_owner_type?
|
raise Error, "Unexpected object type for actor #{owner_uri} (expected any of: #{SUPPORTED_TYPES})" unless expected_owner_type?
|
||||||
raise Error, "publicKey id for #{owner_uri} does not correspond to #{@json['id']}" unless confirmed_owner?
|
raise Error, "publicKey id for #{owner_uri} does not correspond to #{@json['id']}" unless confirmed_owner?
|
||||||
|
|
||||||
find_actor(owner_uri, @owner, suppress_errors)
|
# TODO: change to fetch and persist key
|
||||||
|
Keypair.from_legacy_account(find_actor(owner_uri, @owner, suppress_errors), uri: uri)
|
||||||
rescue Error => e
|
rescue Error => e
|
||||||
Rails.logger.debug { "Fetching key #{uri} failed: #{e.message}" }
|
Rails.logger.debug { "Fetching key #{uri} failed: #{e.message}" }
|
||||||
raise unless suppress_errors
|
raise unless suppress_errors
|
||||||
|
|||||||
@ -6,6 +6,7 @@ class ActivityPub::ProcessAccountService < BaseService
|
|||||||
include Redisable
|
include Redisable
|
||||||
include Lockable
|
include Lockable
|
||||||
|
|
||||||
|
MAX_PUBLIC_KEYS = 10
|
||||||
MAX_PROFILE_FIELDS = 50
|
MAX_PROFILE_FIELDS = 50
|
||||||
SUBDOMAINS_RATELIMIT = 10
|
SUBDOMAINS_RATELIMIT = 10
|
||||||
DISCOVERIES_PER_REQUEST = 400
|
DISCOVERIES_PER_REQUEST = 400
|
||||||
@ -33,8 +34,8 @@ class ActivityPub::ProcessAccountService < BaseService
|
|||||||
with_redis_lock("process_account:#{@uri}") do
|
with_redis_lock("process_account:#{@uri}") do
|
||||||
@account = Account.remote.find_by(uri: @uri) if @options[:only_key]
|
@account = Account.remote.find_by(uri: @uri) if @options[:only_key]
|
||||||
@account ||= Account.find_remote(@username, @domain)
|
@account ||= Account.find_remote(@username, @domain)
|
||||||
@old_public_key = @account&.public_key
|
@old_public_keys = @account.present? ? (@account.keypairs.pluck(:public_key) + [@account.public_key.presence].compact) : []
|
||||||
@old_protocol = @account&.protocol
|
@old_protocol = @account&.protocol
|
||||||
@suspension_changed = false
|
@suspension_changed = false
|
||||||
|
|
||||||
if @account.nil?
|
if @account.nil?
|
||||||
@ -56,8 +57,9 @@ class ActivityPub::ProcessAccountService < BaseService
|
|||||||
end
|
end
|
||||||
|
|
||||||
after_protocol_change! if protocol_changed?
|
after_protocol_change! if protocol_changed?
|
||||||
after_key_change! if key_changed? && !@options[:signed_with_known_key]
|
after_key_change! if all_public_keys_changed? && !@options[:signed_with_known_key]
|
||||||
clear_tombstones! if key_changed?
|
# TODO: maybe tie tombstones to specific keys? i.e. we don't need to keep tombstones if all keys changed
|
||||||
|
clear_tombstones! if all_public_keys_changed?
|
||||||
after_suspension_change! if suspension_changed?
|
after_suspension_change! if suspension_changed?
|
||||||
|
|
||||||
unless @options[:only_key] || @account.suspended?
|
unless @options[:only_key] || @account.suspended?
|
||||||
@ -145,7 +147,11 @@ class ActivityPub::ProcessAccountService < BaseService
|
|||||||
end
|
end
|
||||||
|
|
||||||
def set_fetchable_key!
|
def set_fetchable_key!
|
||||||
@account.public_key = public_key || ''
|
@account.keypairs.upsert_all(public_keys, unique_by: :uri)
|
||||||
|
@account.keypairs.where.not(uri: public_keys.pluck(:uri)).delete_all
|
||||||
|
|
||||||
|
# Unset legacy public key attribute
|
||||||
|
@account.public_key = ''
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_fetchable_attributes!
|
def set_fetchable_attributes!
|
||||||
@ -257,14 +263,35 @@ class ActivityPub::ProcessAccountService < BaseService
|
|||||||
[url, description]
|
[url, description]
|
||||||
end
|
end
|
||||||
|
|
||||||
def public_key
|
def public_keys
|
||||||
value = first_of_value(@json['publicKey'])
|
# TODO: handle FEP-521a
|
||||||
|
|
||||||
return if value.nil?
|
@public_keys ||= as_array(@json['publicKey']).take(MAX_PUBLIC_KEYS).filter_map do |value|
|
||||||
return value['publicKeyPem'] if value.is_a?(Hash)
|
next if value.nil?
|
||||||
|
|
||||||
key = fetch_resource_without_id_validation(value)
|
if value.is_a?(Hash)
|
||||||
key['publicKeyPem'] if key
|
next unless value['owner'] == @account.uri
|
||||||
|
|
||||||
|
key = value['publicKeyPem']
|
||||||
|
value = value['id']
|
||||||
|
|
||||||
|
# Key is contained within the actor document, no need to fetch anything else
|
||||||
|
next { type: :rsa, public_key: key, uri: value } if value.split('#').first == @account.uri
|
||||||
|
end
|
||||||
|
|
||||||
|
key_id = value
|
||||||
|
|
||||||
|
# Key is fetched without ID validation because of a GoToSocial bug
|
||||||
|
value = fetch_resource_without_id_validation(key_id)
|
||||||
|
|
||||||
|
# Special handling for GoToSocial which returns the whole actor for the key ID
|
||||||
|
value = first_of_value(value['publicKey']) if value.is_a?(Hash) && value.key?('publicKey')
|
||||||
|
|
||||||
|
next unless value['owner'] == @account.uri
|
||||||
|
|
||||||
|
value['publicKeyPem']
|
||||||
|
{ type: :rsa, public_key: :key, uri: key_id }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def url
|
def url
|
||||||
@ -353,8 +380,8 @@ class ActivityPub::ProcessAccountService < BaseService
|
|||||||
@domain_block = DomainBlock.rule_for(@domain)
|
@domain_block = DomainBlock.rule_for(@domain)
|
||||||
end
|
end
|
||||||
|
|
||||||
def key_changed?
|
def all_public_keys_changed?
|
||||||
!@old_public_key.nil? && @old_public_key != @account.public_key
|
!@old_public_keys.empty? && @account.keypairs.none? { |keypair| keypair.usable? && @old_public_keys.include?(keypair.public_key) }
|
||||||
end
|
end
|
||||||
|
|
||||||
def suspension_changed?
|
def suspension_changed?
|
||||||
|
|||||||
@ -51,7 +51,7 @@ class ActivityPub::ProcessFeaturedCollectionService
|
|||||||
items = @json['orderedItems'] || []
|
items = @json['orderedItems'] || []
|
||||||
items.take(ITEMS_LIMIT).each_with_index do |item_json, index|
|
items.take(ITEMS_LIMIT).each_with_index do |item_json, index|
|
||||||
uris << value_or_id(item_json)
|
uris << value_or_id(item_json)
|
||||||
ActivityPub::ProcessFeaturedItemWorker.perform_async(@collection.id, item_json, index, @request_id)
|
ActivityPub::ProcessFeaturedItemWorker.perform_async(@collection.id, item_json, index + 1, @request_id)
|
||||||
end
|
end
|
||||||
uris.compact!
|
uris.compact!
|
||||||
@collection.collection_items.where.not(uri: uris).delete_all
|
@collection.collection_items.where.not(uri: uris).delete_all
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user