mirror of
https://git.bsd.gay/fef/nyastodon.git
synced 2024-10-18 17:25:04 +02:00
merge with catstodon/main
This commit is contained in:
commit
60d54db810
|
@ -251,6 +251,11 @@ SMTP_FROM_ADDRESS=notifications@example.com
|
|||
# Maximum allowed character count
|
||||
MAX_TOOT_CHARS=500
|
||||
|
||||
# Maximum allowed hashtags to follow in a feed column
|
||||
# Note that setting this value higher may cause significant
|
||||
# database load
|
||||
MAX_FEED_HASHTAGS=4
|
||||
|
||||
# Maximum number of pinned posts
|
||||
MAX_PINNED_TOOTS=5
|
||||
|
||||
|
|
4
.github/workflows/build-security.yml
vendored
4
.github/workflows/build-security.yml
vendored
|
@ -36,7 +36,7 @@ jobs:
|
|||
tags: |
|
||||
type=raw,value=edge
|
||||
type=raw,value=nightly
|
||||
type=schedule,pattern=${{ needs.compute-suffix.outputs.prerelease }}
|
||||
type=raw,value=${{ needs.compute-suffix.outputs.prerelease }}
|
||||
secrets: inherit
|
||||
|
||||
build-image-streaming:
|
||||
|
@ -57,5 +57,5 @@ jobs:
|
|||
tags: |
|
||||
type=raw,value=edge
|
||||
type=raw,value=nightly
|
||||
type=schedule,pattern=${{ needs.compute-suffix.outputs.prerelease }}
|
||||
type=raw,value=${{ needs.compute-suffix.outputs.prerelease }}
|
||||
secrets: inherit
|
||||
|
|
95
CHANGELOG.md
95
CHANGELOG.md
|
@ -2,6 +2,101 @@
|
|||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [4.2.7] - 2024-02-16
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix OmniAuth tests and edge cases in error handling ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29201), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/29207))
|
||||
- Fix new installs by upgrading to the latest release of the `nsa` gem, instead of a no longer existing commit ([mjankowski](https://github.com/mastodon/mastodon/pull/29065))
|
||||
|
||||
### Security
|
||||
|
||||
- Fix insufficient checking of remote posts ([GHSA-jhrq-qvrm-qr36](https://github.com/mastodon/mastodon/security/advisories/GHSA-jhrq-qvrm-qr36))
|
||||
|
||||
## [4.2.6] - 2024-02-14
|
||||
|
||||
### Security
|
||||
|
||||
- Update the `sidekiq-unique-jobs` dependency (see [GHSA-cmh9-rx85-xj38](https://github.com/mhenrixon/sidekiq-unique-jobs/security/advisories/GHSA-cmh9-rx85-xj38))
|
||||
In addition, we have disabled the web interface for `sidekiq-unique-jobs` out of caution.
|
||||
If you need it, you can re-enable it by setting `ENABLE_SIDEKIQ_UNIQUE_JOBS_UI=true`.
|
||||
If you only need to clear all locks, you can now use `bundle exec rake sidekiq_unique_jobs:delete_all_locks`.
|
||||
- Update the `nokogiri` dependency (see [GHSA-xc9x-jj77-9p9j](https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-xc9x-jj77-9p9j))
|
||||
- Disable administrative Doorkeeper routes ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/29187))
|
||||
- Fix ongoing streaming sessions not being invalidated when applications get deleted in some cases ([GHSA-7w3c-p9j8-mq3x](https://github.com/mastodon/mastodon/security/advisories/GHSA-7w3c-p9j8-mq3x))
|
||||
In some rare cases, the streaming server was not notified of access tokens revocation on application deletion.
|
||||
- Change external authentication behavior to never reattach a new identity to an existing user by default ([GHSA-vm39-j3vx-pch3](https://github.com/mastodon/mastodon/security/advisories/GHSA-vm39-j3vx-pch3))
|
||||
Up until now, Mastodon has allowed new identities from external authentication providers to attach to an existing local user based on their verified e-mail address.
|
||||
This allowed upgrading users from a database-stored password to an external authentication provider, or move from one authentication provider to another.
|
||||
However, this behavior may be unexpected, and means that when multiple authentication providers are configured, the overall security would be that of the least secure authentication provider.
|
||||
For these reasons, this behavior is now locked under the `ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH` environment variable.
|
||||
In addition, regardless of this environment variable, Mastodon will refuse to attach two identities from the same authentication provider to the same account.
|
||||
|
||||
## [4.2.5] - 2024-02-01
|
||||
|
||||
### Security
|
||||
|
||||
- Fix insufficient origin validation (CVE-2024-23832, [GHSA-3fjr-858r-92rw](https://github.com/mastodon/mastodon/security/advisories/GHSA-3fjr-858r-92rw))
|
||||
|
||||
## [4.2.4] - 2024-01-24
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix error when processing remote files with unusually long names ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28823))
|
||||
- Fix processing of compacted single-item JSON-LD collections ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28816))
|
||||
- Retry 401 errors on replies fetching ([ShadowJonathan](https://github.com/mastodon/mastodon/pull/28788))
|
||||
- Fix `RecordNotUnique` errors in LinkCrawlWorker ([tribela](https://github.com/mastodon/mastodon/pull/28748))
|
||||
- Fix Mastodon not correctly processing HTTP Signatures with query strings ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28443), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/28476))
|
||||
- Fix potential redirection loop of streaming endpoint ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28665))
|
||||
- Fix streaming API redirection ignoring the port of `streaming_api_base_url` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28558))
|
||||
- Fix error when processing link preview with an array as `inLanguage` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28252))
|
||||
- Fix unsupported time zone or locale preventing sign-up ([Gargron](https://github.com/mastodon/mastodon/pull/28035))
|
||||
- Fix "Hide these posts from home" list setting not refreshing when switching lists ([brianholley](https://github.com/mastodon/mastodon/pull/27763))
|
||||
- Fix missing background behind dismissable banner in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/27479))
|
||||
- Fix line wrapping of language selection button with long locale codes ([gunchleoc](https://github.com/mastodon/mastodon/pull/27100), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27127))
|
||||
- Fix `Undo Announce` activity not being sent to non-follower authors ([MitarashiDango](https://github.com/mastodon/mastodon/pull/18482))
|
||||
- Fix N+1s because of association preloaders not actually getting called ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28339))
|
||||
- Fix empty column explainer getting cropped under certain conditions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28337))
|
||||
- Fix `LinkCrawlWorker` error when encountering empty OEmbed response ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28268))
|
||||
- Fix call to inefficient `delete_matched` cache method in domain blocks ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28367))
|
||||
|
||||
### Security
|
||||
|
||||
- Add rate-limit of TOTP authentication attempts at controller level ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28801))
|
||||
|
||||
## [4.2.3] - 2023-12-05
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix dependency on `json-canonicalization` version that has been made unavailable since last release
|
||||
|
||||
## [4.2.2] - 2023-12-04
|
||||
|
||||
### Changed
|
||||
|
||||
- Change dismissed banners to be stored server-side ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27055))
|
||||
- Change GIF max matrix size error to explicitly mention GIF files ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27927))
|
||||
- Change `Follow` activities delivery to bypass availability check ([ShadowJonathan](https://github.com/mastodon/mastodon/pull/27586))
|
||||
- Change single-column navigation notice to be displayed outside of the logo container ([renchap](https://github.com/mastodon/mastodon/pull/27462), [renchap](https://github.com/mastodon/mastodon/pull/27476))
|
||||
- Change Content-Security-Policy to be tighter on media paths ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26889))
|
||||
- Change post language code to include country code when relevant ([gunchleoc](https://github.com/mastodon/mastodon/pull/27099), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27207))
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix upper border radius of onboarding columns ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27890))
|
||||
- Fix incoming status creation date not being restricted to standard ISO8601 ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27655), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/28081))
|
||||
- Fix some posts from threads received out-of-order sometimes not being inserted into timelines ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27653))
|
||||
- Fix posts from force-sensitized accounts being able to trend ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27620))
|
||||
- Fix error when trying to delete already-deleted file with OpenStack Swift ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27569))
|
||||
- Fix batch attachment deletion when using OpenStack Swift ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27554))
|
||||
- Fix processing LDSigned activities from actors with unknown public keys ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27474))
|
||||
- Fix error and incorrect URLs in `/api/v1/accounts/:id/featured_tags` for remote accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27459))
|
||||
- Fix report processing notice not mentioning the report number when performing a custom action ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27442))
|
||||
- Fix handling of `inLanguage` attribute in preview card processing ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27423))
|
||||
- Fix own posts being removed from home timeline when unfollowing a used hashtag ([kmycode](https://github.com/mastodon/mastodon/pull/27391))
|
||||
- Fix some link anchors being recognized as hashtags ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27271), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27584))
|
||||
- Fix format-dependent redirects being cached regardless of requested format ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27634))
|
||||
|
||||
## [4.2.1] - 2023-10-10
|
||||
|
||||
### Added
|
||||
|
|
4
Gemfile
4
Gemfile
|
@ -26,7 +26,7 @@ gem 'blurhash', '~> 0.1'
|
|||
|
||||
gem 'active_model_serializers', '~> 0.10'
|
||||
gem 'addressable', '~> 2.8'
|
||||
gem 'bootsnap', '~> 1.17.0', require: false
|
||||
gem 'bootsnap', '~> 1.18.0', require: false
|
||||
gem 'browser'
|
||||
gem 'charlock_holmes', '~> 0.7.7'
|
||||
gem 'chewy', '~> 7.3'
|
||||
|
@ -63,7 +63,7 @@ gem 'kaminari', '~> 1.2'
|
|||
gem 'link_header', '~> 0.0'
|
||||
gem 'mime-types', '~> 3.5.0', require: 'mime/types/columnar'
|
||||
gem 'nokogiri', '~> 1.15'
|
||||
gem 'nsa', github: 'jhawthorn/nsa', ref: 'e020fcc3a54d993ab45b7194d89ab720296c111b'
|
||||
gem 'nsa'
|
||||
gem 'oj', '~> 3.14'
|
||||
gem 'ox', '~> 2.14'
|
||||
gem 'parslet'
|
||||
|
|
51
Gemfile.lock
51
Gemfile.lock
|
@ -7,17 +7,6 @@ GIT
|
|||
hkdf (~> 0.2)
|
||||
jwt (~> 2.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/jhawthorn/nsa.git
|
||||
revision: e020fcc3a54d993ab45b7194d89ab720296c111b
|
||||
ref: e020fcc3a54d993ab45b7194d89ab720296c111b
|
||||
specs:
|
||||
nsa (0.2.8)
|
||||
activesupport (>= 4.2, < 7.2)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
sidekiq (>= 3.5)
|
||||
statsd-ruby (~> 1.4, >= 1.4.0)
|
||||
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
|
@ -155,9 +144,9 @@ GEM
|
|||
binding_of_caller (1.0.0)
|
||||
debug_inspector (>= 0.0.1)
|
||||
blurhash (0.1.7)
|
||||
bootsnap (1.17.1)
|
||||
bootsnap (1.18.3)
|
||||
msgpack (~> 1.2)
|
||||
brakeman (6.1.1)
|
||||
brakeman (6.1.2)
|
||||
racc
|
||||
browser (5.3.1)
|
||||
brpoplpush-redis_script (0.1.3)
|
||||
|
@ -167,11 +156,11 @@ GEM
|
|||
bundler-audit (0.9.1)
|
||||
bundler (>= 1.2.0, < 3)
|
||||
thor (~> 1.0)
|
||||
capybara (3.39.2)
|
||||
capybara (3.40.0)
|
||||
addressable
|
||||
matrix
|
||||
mini_mime (>= 0.1.3)
|
||||
nokogiri (~> 1.8)
|
||||
nokogiri (~> 1.11)
|
||||
rack (>= 1.6.0)
|
||||
rack-test (>= 0.6.3)
|
||||
regexp_parser (>= 1.5, < 3.0)
|
||||
|
@ -180,7 +169,7 @@ GEM
|
|||
activesupport
|
||||
cbor (0.5.9.6)
|
||||
charlock_holmes (0.7.7)
|
||||
chewy (7.5.0)
|
||||
chewy (7.5.1)
|
||||
activesupport (>= 5.2)
|
||||
elasticsearch (>= 7.12.0, < 7.14.0)
|
||||
elasticsearch-dsl
|
||||
|
@ -193,7 +182,8 @@ GEM
|
|||
cose (1.3.0)
|
||||
cbor (~> 0.5.9)
|
||||
openssl-signature_algorithm (~> 1.0)
|
||||
crack (0.4.5)
|
||||
crack (0.4.6)
|
||||
bigdecimal
|
||||
rexml
|
||||
crass (1.0.6)
|
||||
css_parser (1.14.0)
|
||||
|
@ -319,13 +309,13 @@ GEM
|
|||
activesupport (>= 5.1)
|
||||
haml (>= 4.0.6)
|
||||
railties (>= 5.1)
|
||||
haml_lint (0.55.0)
|
||||
haml_lint (0.56.0)
|
||||
haml (>= 5.0)
|
||||
parallel (~> 1.10)
|
||||
rainbow
|
||||
rubocop (>= 1.0)
|
||||
sysexits (~> 1.1)
|
||||
hashdiff (1.0.1)
|
||||
hashdiff (1.1.0)
|
||||
hashie (5.0.0)
|
||||
hcaptcha (7.1.0)
|
||||
json
|
||||
|
@ -361,7 +351,7 @@ GEM
|
|||
terminal-table (>= 1.5.1)
|
||||
idn-ruby (0.1.5)
|
||||
io-console (0.7.2)
|
||||
irb (1.11.1)
|
||||
irb (1.11.2)
|
||||
rdoc
|
||||
reline (>= 0.4.2)
|
||||
jmespath (1.6.2)
|
||||
|
@ -465,9 +455,14 @@ GEM
|
|||
net-smtp (0.4.0.1)
|
||||
net-protocol
|
||||
nio4r (2.5.9)
|
||||
nokogiri (1.16.0)
|
||||
nokogiri (1.16.2)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
racc (~> 1.4)
|
||||
nsa (0.3.0)
|
||||
activesupport (>= 4.2, < 7.2)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
sidekiq (>= 3.5)
|
||||
statsd-ruby (~> 1.4, >= 1.4.0)
|
||||
oj (3.16.3)
|
||||
bigdecimal (>= 3.0)
|
||||
omniauth (2.1.1)
|
||||
|
@ -510,8 +505,8 @@ GEM
|
|||
parslet (2.0.0)
|
||||
pastel (0.8.0)
|
||||
tty-color (~> 0.5)
|
||||
pg (1.5.4)
|
||||
pghero (3.4.0)
|
||||
pg (1.5.5)
|
||||
pghero (3.4.1)
|
||||
activerecord (>= 6)
|
||||
posix-spawn (0.3.15)
|
||||
premailer (1.21.0)
|
||||
|
@ -712,7 +707,7 @@ GEM
|
|||
rufus-scheduler (~> 3.2)
|
||||
sidekiq (>= 6, < 8)
|
||||
tilt (>= 1.4.0)
|
||||
sidekiq-unique-jobs (7.1.31)
|
||||
sidekiq-unique-jobs (7.1.33)
|
||||
brpoplpush-redis_script (> 0.1.1, <= 2.0.0)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.5)
|
||||
redis (< 5.0)
|
||||
|
@ -771,7 +766,7 @@ GEM
|
|||
unf (~> 0.1.0)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
tzinfo-data (1.2023.4)
|
||||
tzinfo-data (1.2024.1)
|
||||
tzinfo (>= 1.0.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
|
@ -798,7 +793,7 @@ GEM
|
|||
webfinger (1.2.0)
|
||||
activesupport
|
||||
httpclient (>= 2.4)
|
||||
webmock (3.19.1)
|
||||
webmock (3.20.0)
|
||||
addressable (>= 2.8.0)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
|
@ -829,7 +824,7 @@ DEPENDENCIES
|
|||
better_errors (~> 2.9)
|
||||
binding_of_caller (~> 1.0)
|
||||
blurhash (~> 0.1)
|
||||
bootsnap (~> 1.17.0)
|
||||
bootsnap (~> 1.18.0)
|
||||
brakeman (~> 6.0)
|
||||
browser
|
||||
bundler-audit (~> 0.9)
|
||||
|
@ -886,7 +881,7 @@ DEPENDENCIES
|
|||
net-http (~> 0.4.0)
|
||||
net-ldap (~> 0.18)
|
||||
nokogiri (~> 1.15)
|
||||
nsa!
|
||||
nsa
|
||||
oj (~> 3.14)
|
||||
omniauth (~> 2.0)
|
||||
omniauth-cas (~> 3.0.0.beta.1)
|
||||
|
|
|
@ -7,7 +7,7 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
|||
def self.provides_callback_for(provider)
|
||||
define_method provider do
|
||||
@provider = provider
|
||||
@user = User.find_for_oauth(request.env['omniauth.auth'], current_user)
|
||||
@user = User.find_for_omniauth(request.env['omniauth.auth'], current_user)
|
||||
|
||||
if @user.persisted?
|
||||
record_login_activity
|
||||
|
@ -17,6 +17,9 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
|||
session["devise.#{provider}_data"] = request.env['omniauth.auth']
|
||||
redirect_to new_user_registration_url
|
||||
end
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
flash[:alert] = I18n.t('devise.failure.omniauth_user_creation_failure') if is_navigational_format?
|
||||
redirect_to new_user_session_url
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -174,7 +174,19 @@ module JsonLdHelper
|
|||
build_request(uri, on_behalf_of, options: request_options).perform do |response|
|
||||
raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response) || !raise_on_temporary_error
|
||||
|
||||
body_to_json(response.body_with_limit) if response.code == 200
|
||||
body_to_json(response.body_with_limit) if response.code == 200 && valid_activitypub_content_type?(response)
|
||||
end
|
||||
end
|
||||
|
||||
def valid_activitypub_content_type?(response)
|
||||
return true if response.mime_type == 'application/activity+json'
|
||||
|
||||
# When the mime type is `application/ld+json`, we need to check the profile,
|
||||
# but `http.rb` does not parse it for us.
|
||||
return false unless response.mime_type == 'application/ld+json'
|
||||
|
||||
response.headers[HTTP::Headers::CONTENT_TYPE]&.split(';')&.map(&:strip)&.any? do |str|
|
||||
str.start_with?('profile="') && str[9...-1].split.include?('https://www.w3.org/ns/activitystreams')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -338,7 +338,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
|||
onClick={this.handleNoOp} // EmojiPickerDropdown handles that
|
||||
title={intl.formatMessage(messages.react)}
|
||||
disabled={!canReact}
|
||||
icon='add-reaction'
|
||||
icon='add_reaction'
|
||||
iconComponent={AddReactionIcon}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -131,7 +131,7 @@ export default class StatusPrepend extends PureComponent {
|
|||
iconComponent = StarIcon;
|
||||
break;
|
||||
case 'reaction':
|
||||
iconId = 'add-reaction';
|
||||
iconId = 'add_reaction';
|
||||
iconComponent = AddReactionIcon;
|
||||
break;
|
||||
case 'featured':
|
||||
|
|
|
@ -15,7 +15,7 @@ export default class AutosuggestAccount extends ImmutablePureComponent {
|
|||
|
||||
return (
|
||||
<div className='autosuggest-account' title={account.get('acct')}>
|
||||
<div className='autosuggest-account-icon'><Avatar account={account} size={24} /></div>
|
||||
<div className='autosuggest-account-icon'><Avatar account={account} size={18} /></div>
|
||||
<DisplayName account={account} inline />
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -70,7 +70,6 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
singleColumn: PropTypes.bool,
|
||||
lang: PropTypes.string,
|
||||
advancedOptions: ImmutablePropTypes.map,
|
||||
layout: PropTypes.string,
|
||||
media: ImmutablePropTypes.list,
|
||||
sideArm: PropTypes.string,
|
||||
sensitive: PropTypes.bool,
|
||||
|
@ -259,7 +258,6 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
intl,
|
||||
advancedOptions,
|
||||
isSubmitting,
|
||||
layout,
|
||||
onChangeSpoilerness,
|
||||
onPaste,
|
||||
privacy,
|
||||
|
@ -313,7 +311,7 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||
onSuggestionSelected={this.onSuggestionSelected}
|
||||
onPaste={onPaste}
|
||||
autoFocus={!showSearch && !isMobile(window.innerWidth, layout)}
|
||||
autoFocus={!showSearch && !isMobile(window.innerWidth)}
|
||||
lang={this.props.lang}
|
||||
>
|
||||
<TextareaIcons advancedOptions={advancedOptions} />
|
||||
|
|
|
@ -38,8 +38,8 @@ export default class NavigationBar extends ImmutablePureComponent {
|
|||
|
||||
{ profileLink !== undefined && (
|
||||
<a
|
||||
className='edit'
|
||||
href={profileLink}
|
||||
className='navigation-bar__profile-edit'
|
||||
><FormattedMessage id='navigation_bar.edit_profile' defaultMessage='Edit profile' /></a>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -76,7 +76,7 @@ class SearchResults extends ImmutablePureComponent {
|
|||
|
||||
|
||||
return (
|
||||
<div className='drawer--results'>
|
||||
<div className='search-results'>
|
||||
<header className='search-results__header'>
|
||||
<Icon id='search' icon={SearchIcon} />
|
||||
<FormattedMessage id='explore.search_results' defaultMessage='Search results' />
|
||||
|
|
|
@ -71,7 +71,6 @@ const mapStateToProps = state => ({
|
|||
isInReply: state.getIn(['compose', 'in_reply_to']) !== null,
|
||||
lang: state.getIn(['compose', 'language']),
|
||||
advancedOptions: state.getIn(['compose', 'advanced_options']),
|
||||
layout: state.getIn(['local_settings', 'layout']),
|
||||
media: state.getIn(['compose', 'media_attachments']),
|
||||
sideArm: sideArmPrivacy(state),
|
||||
sensitive: state.getIn(['compose', 'sensitive']),
|
||||
|
|
|
@ -9,6 +9,8 @@ import { NonceProvider } from 'react-select';
|
|||
import AsyncSelect from 'react-select/async';
|
||||
import Toggle from 'react-toggle';
|
||||
|
||||
import { maxFeedHashtags } from 'flavours/glitch/initial_state';
|
||||
|
||||
import SettingToggle from '../../notifications/components/setting_toggle';
|
||||
|
||||
const messages = defineMessages({
|
||||
|
@ -46,9 +48,9 @@ class ColumnSettings extends PureComponent {
|
|||
onSelect = mode => value => {
|
||||
const oldValue = this.tags(mode);
|
||||
|
||||
// Prevent changes that add more than 4 tags, but allow removing
|
||||
// tags that were already added before
|
||||
if ((value.length > 4) && !(value < oldValue)) {
|
||||
// Prevent changes that add more than the number of configured
|
||||
// tags, but allow removing tags that were already added before
|
||||
if ((value.length > maxFeedHashtags) && !(value < oldValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ class NewListForm extends PureComponent {
|
|||
|
||||
<Button
|
||||
disabled={disabled || !value}
|
||||
title={title}
|
||||
text={title}
|
||||
onClick={this.handleClick}
|
||||
/>
|
||||
</form>
|
||||
|
|
|
@ -89,7 +89,7 @@ class FilterBar extends PureComponent {
|
|||
onClick={this.onClick('reaction')}
|
||||
title={intl.formatMessage(tooltips.reactions)}
|
||||
>
|
||||
<Icon id='add-reaction' icon={AddReactionIcon} />
|
||||
<Icon id='add_reaction' icon={AddReactionIcon} />
|
||||
</button>
|
||||
<button
|
||||
className={selectedFilter === 'reblog' ? 'active' : ''}
|
||||
|
|
|
@ -244,11 +244,11 @@ class ActionBar extends PureComponent {
|
|||
const canReact = signedIn && status.get('reactions').filter(r => r.get('count') > 0 && r.get('me')).size < maxReactions;
|
||||
const reactButton = (
|
||||
<IconButton
|
||||
className='add-reaction'
|
||||
className='add-reaction-icon'
|
||||
onClick={this.handleNoOp} // EmojiPickerDropdown handles that
|
||||
title={intl.formatMessage(messages.react)}
|
||||
disabled={!canReact}
|
||||
icon='add-reaction'
|
||||
icon='add_reaction'
|
||||
iconComponent={AddReactionIcon}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -9,6 +9,10 @@ import { connect } from 'react-redux';
|
|||
import Atrament from 'atrament'; // the doodling library
|
||||
import { debounce, mapValues } from 'lodash';
|
||||
|
||||
import ColorsIcon from '@/material-icons/400-24px/colors.svg?react';
|
||||
import DeleteIcon from '@/material-icons/400-24px/delete.svg?react';
|
||||
import EditIcon from '@/material-icons/400-24px/edit.svg?react';
|
||||
import UndoIcon from '@/material-icons/400-24px/undo.svg?react';
|
||||
import { doodleSet, uploadCompose } from 'flavours/glitch/actions/compose';
|
||||
import { Button } from 'flavours/glitch/components/button';
|
||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||
|
@ -584,10 +588,10 @@ class DoodleModal extends ImmutablePureComponent {
|
|||
</div>
|
||||
</div>
|
||||
<div className='doodle-toolbar'>
|
||||
<IconButton icon='pencil' title='Draw' label='Draw' onClick={this.setModeDraw} size={18} active={this.mode === 'draw'} inverted />
|
||||
<IconButton icon='bath' title='Fill' label='Fill' onClick={this.setModeFill} size={18} active={this.mode === 'fill'} inverted />
|
||||
<IconButton icon='undo' title='Undo' label='Undo' onClick={this.undo} size={18} inverted />
|
||||
<IconButton icon='trash' title='Clear' label='Clear' onClick={this.handleClearBtn} size={18} inverted />
|
||||
<IconButton icon='pencil' iconComponent={EditIcon} title='Draw' label='Draw' onClick={this.setModeDraw} size={18} active={this.mode === 'draw'} inverted />
|
||||
<IconButton icon='bath' iconComponent={ColorsIcon} title='Fill' label='Fill' onClick={this.setModeFill} size={18} active={this.mode === 'fill'} inverted />
|
||||
<IconButton icon='undo' iconComponent={UndoIcon} title='Undo' label='Undo' onClick={this.undo} size={18} inverted />
|
||||
<IconButton icon='trash' iconComponent={DeleteIcon} title='Clear' label='Clear' onClick={this.handleClearBtn} size={18} inverted />
|
||||
</div>
|
||||
<div className='doodle-palette'>
|
||||
{
|
||||
|
|
|
@ -602,18 +602,7 @@ class UI extends PureComponent {
|
|||
const { draggingOver } = this.state;
|
||||
const { children, isWide, location, dropdownMenuIsOpen, layout, moved } = this.props;
|
||||
|
||||
const columnsClass = layout => {
|
||||
switch (layout) {
|
||||
case 'single':
|
||||
return 'single-column';
|
||||
case 'multiple':
|
||||
return 'multi-columns';
|
||||
default:
|
||||
return 'auto-columns';
|
||||
}
|
||||
};
|
||||
|
||||
const className = classNames('ui', columnsClass(layout), {
|
||||
const className = classNames('ui', {
|
||||
'wide': isWide,
|
||||
'system-font': this.props.systemFontUi,
|
||||
'hicolor-privacy-icons': this.props.hicolorPrivacyIcons,
|
||||
|
|
|
@ -78,6 +78,7 @@ export const hasMultiColumnPath = initialPath === '/'
|
|||
* @property {InitialStateMeta} meta
|
||||
* @property {object} local_settings
|
||||
* @property {number} max_toot_chars
|
||||
* @property {number} max_feed_hashtags
|
||||
* @property {PollLimits} poll_limits
|
||||
* @property {number} max_reactions
|
||||
*/
|
||||
|
@ -144,6 +145,7 @@ export const sso_redirect = getMeta('sso_redirect');
|
|||
|
||||
// Glitch-soc-specific settings
|
||||
export const maxChars = (initialState && initialState.max_toot_chars) || 500;
|
||||
export const maxFeedHashtags = (initialState && initialState.max_feed_hashtags) || 4;
|
||||
export const favouriteModal = getMeta('favourite_modal');
|
||||
export const pollLimits = (initialState && initialState.poll_limits);
|
||||
export const defaultContentType = getMeta('default_content_type');
|
||||
|
|
|
@ -1,4 +1,51 @@
|
|||
{
|
||||
"about.fork_disclaimer": "Glitch-soc adalah perangkat lunak sumber terbuka yang merupakan fork dari Mastodon.",
|
||||
"account.disclaimer_full": "Informasi di bawah ini mungkin tidak mencerminkan profil pengguna secara lengkap.",
|
||||
"account.follows": "Mengikuti",
|
||||
"account.joined": "Bergabung {date}",
|
||||
"account.suspended_disclaimer_full": "Pengguna ini telah ditangguhkan oleh moderator.",
|
||||
"account.view_full_profile": "Tampilkan profil lengkap",
|
||||
"advanced_options.icon_title": "Opsi lanjutan",
|
||||
"advanced_options.local-only.long": "Jangan mengunggah ke instance lain",
|
||||
"advanced_options.local-only.short": "Hanya lokal",
|
||||
"advanced_options.local-only.tooltip": "Postingan ini hanya untuk lokal",
|
||||
"advanced_options.threaded_mode.long": "Secara otomatis membuka balasan pada postingan",
|
||||
"advanced_options.threaded_mode.short": "Mode Utasan",
|
||||
"advanced_options.threaded_mode.tooltip": "Mode utasan dinyalakan",
|
||||
"boost_modal.missing_description": "Toot ini berisi beberapa media tanpa deskripsi",
|
||||
"column.favourited_by": "Disukai oleh",
|
||||
"column.heading": "Lainnya",
|
||||
"column.reblogged_by": "Dibagikan oleh",
|
||||
"column.subheading": "Opsi lain-lain",
|
||||
"column_header.profile": "Profil",
|
||||
"column_subheading.lists": "Daftar",
|
||||
"column_subheading.navigation": "Penelusuran",
|
||||
"community.column_settings.allow_local_only": "Tampilkan toot lokal saja",
|
||||
"compose.attach": "Lampirkan...",
|
||||
"compose.attach.doodle": "Gambar sesuatu",
|
||||
"compose.attach.upload": "Unggah file",
|
||||
"compose.content-type.html": "HTML",
|
||||
"compose.content-type.markdown": "Bahasa Markdown",
|
||||
"compose.content-type.plain": "Teks biasa",
|
||||
"compose_form.poll.multiple_choices": "Izinkan beberapa pilihan",
|
||||
"compose_form.poll.single_choice": "Izinkan hanya satu pilihan",
|
||||
"compose_form.spoiler": "Sembunyikan teks di balik peringatan",
|
||||
"confirmation_modal.do_not_ask_again": "Jangan minta konfirmasi lagi",
|
||||
"confirmations.deprecated_settings.confirm": "Gunakan preferensi Mastodon",
|
||||
"confirmations.deprecated_settings.message": "Beberapa {app_settings} khusus perangkat Glitch-soc yang Anda gunakan telah digantikan oleh {preferences} Mastodon dan akan diganti:",
|
||||
"confirmations.missing_media_description.confirm": "Tetap kirim",
|
||||
"confirmations.missing_media_description.edit": "Sunting media",
|
||||
"confirmations.missing_media_description.message": "Setidaknya satu lampiran media tidak memiliki deskripsi. Pertimbangkan untuk mendeskripsikan semua lampiran media untuk pengguna tunanetra sebelum mengirim toot Anda.",
|
||||
"confirmations.unfilter.author": "Penulis",
|
||||
"confirmations.unfilter.confirm": "Tampilkan",
|
||||
"confirmations.unfilter.edit_filter": "Ubah saringan",
|
||||
"content-type.change": "Jenis konten",
|
||||
"direct.group_by_conversations": "Grupkan berdasarkan percakapan",
|
||||
"endorsed_accounts_editor.endorsed_accounts": "Akun pilihan",
|
||||
"favourite_modal.combo": "Anda dapat menekan {combo} untuk melewati ini lain kali",
|
||||
"firehose.column_settings.allow_local_only": "Tampilkan postingan khusus lokal di \"Semua\"",
|
||||
"home.column_settings.advanced": "Lanjutan",
|
||||
"home.column_settings.filter_regex": "Saring dengan ekspresi reguler",
|
||||
"settings.content_warnings": "Content warnings",
|
||||
"settings.preferences": "Preferences"
|
||||
}
|
||||
|
|
|
@ -1,4 +1,52 @@
|
|||
{
|
||||
"account.follows": "Följer",
|
||||
"account.joined": "Gick med {date}",
|
||||
"account.suspended_disclaimer_full": "Denna användare har stängts av av en moderator.",
|
||||
"account.view_full_profile": "Visa full profil",
|
||||
"advanced_options.icon_title": "Avancerade inställningar",
|
||||
"advanced_options.local-only.long": "Lägg inte ut på andra instanser",
|
||||
"advanced_options.local-only.short": "Endast lokalt",
|
||||
"advanced_options.local-only.tooltip": "Detta inlägg är endast tillgängligt lokalt",
|
||||
"advanced_options.threaded_mode.long": "Öppnar automatiskt ett svar vid publicering",
|
||||
"advanced_options.threaded_mode.short": "Tråd-läge",
|
||||
"advanced_options.threaded_mode.tooltip": "Tråd-läge på",
|
||||
"boost_modal.missing_description": "Denna toot innehåller viss media utan beskrivning",
|
||||
"column.favourited_by": "Favoritmarkerad av",
|
||||
"column.heading": "Övrigt",
|
||||
"column.reblogged_by": "Boostad av",
|
||||
"column.subheading": "Övriga val",
|
||||
"column_header.profile": "Profil",
|
||||
"column_subheading.lists": "Listor",
|
||||
"column_subheading.navigation": "Navigering",
|
||||
"community.column_settings.allow_local_only": "Visa endast lokala toots",
|
||||
"compose.attach": "Bifoga...",
|
||||
"compose.attach.doodle": "Rita något",
|
||||
"compose.attach.upload": "Ladda upp en fil",
|
||||
"compose.content-type.html": "HTML",
|
||||
"compose.content-type.markdown": "Markdown",
|
||||
"compose.content-type.plain": "Klartext",
|
||||
"compose_form.poll.multiple_choices": "Tillåt flera val",
|
||||
"compose_form.poll.single_choice": "Tillåt ett val",
|
||||
"compose_form.spoiler": "Göm text bakom varning",
|
||||
"confirmation_modal.do_not_ask_again": "Fråga mig inte igen",
|
||||
"confirmations.deprecated_settings.confirm": "Använd Mastodon-preferenser",
|
||||
"confirmations.deprecated_settings.message": "Några av de glitch-soc-enhetsspecifika {app_settings} som du använder har ersatts av Mastodon-{preferences} och kommer att åsidosättas:",
|
||||
"confirmations.missing_media_description.confirm": "Lägg ut ändå",
|
||||
"confirmations.missing_media_description.edit": "Redigera media",
|
||||
"confirmations.missing_media_description.message": "Minst en mediebilaga saknar beskrivning. Överväg att beskriva all media för synskadade innan du skickar din toot.",
|
||||
"confirmations.unfilter.author": "Användare",
|
||||
"confirmations.unfilter.confirm": "Visa",
|
||||
"confirmations.unfilter.edit_filter": "Redigera filter",
|
||||
"confirmations.unfilter.filters": "Matchande {count, plural, one {filter} other {filters}}",
|
||||
"content-type.change": "Innehållstyp",
|
||||
"direct.group_by_conversations": "Sortera efter konversation",
|
||||
"endorsed_accounts_editor.endorsed_accounts": "Utvalda konton",
|
||||
"favourite_modal.combo": "Du kan trycka på {combo} för att skippa detta nästa gång",
|
||||
"firehose.column_settings.allow_local_only": "Visa endast lokala inlägg i \"Alla\"",
|
||||
"home.column_settings.advanced": "Avancerat",
|
||||
"home.column_settings.filter_regex": "Filtrera bort med reguljära uttryck",
|
||||
"home.column_settings.show_direct": "Visa privata omnämningar",
|
||||
"home.settings": "Kolumninställningar",
|
||||
"settings.content_warnings": "Content warnings",
|
||||
"settings.preferences": "Preferences"
|
||||
}
|
||||
|
|
|
@ -14,9 +14,7 @@ const initialState = ImmutableMap({
|
|||
export default function meta(state = initialState, action) {
|
||||
switch(action.type) {
|
||||
case STORE_HYDRATE:
|
||||
return state.merge(action.state.get('meta'))
|
||||
.set('permissions', action.state.getIn(['role', 'permissions']))
|
||||
.set('layout', layoutFromWindow(action.state.getIn(['local_settings', 'layout'])));
|
||||
return state.merge(action.state.get('meta')).set('permissions', action.state.getIn(['role', 'permissions']));
|
||||
case changeLayout.type:
|
||||
return state.set('layout', action.payload.layout);
|
||||
default:
|
||||
|
|
|
@ -10,37 +10,6 @@
|
|||
background-size: $size $size;
|
||||
}
|
||||
|
||||
@mixin single-column($media, $parent: '&') {
|
||||
.auto-columns #{$parent} {
|
||||
@media #{$media} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
.single-column #{$parent} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin limited-single-column($media, $parent: '&') {
|
||||
.auto-columns #{$parent},
|
||||
.single-column #{$parent} {
|
||||
@media #{$media} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@mixin multi-columns($media, $parent: '&') {
|
||||
.auto-columns #{$parent} {
|
||||
@media #{$media} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
.multi-columns #{$parent} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin fullwidth-gallery {
|
||||
&.full-width {
|
||||
margin-left: -14px;
|
||||
|
|
9973
app/javascript/flavours/glitch/styles/components.scss
Normal file
9973
app/javascript/flavours/glitch/styles/components.scss
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,298 +0,0 @@
|
|||
.image {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&__preview {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
&.loaded &__preview {
|
||||
display: none;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&.loaded img {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.link-footer {
|
||||
flex: 0 0 auto;
|
||||
padding: 10px;
|
||||
padding-top: 20px;
|
||||
z-index: 1;
|
||||
font-size: 13px;
|
||||
|
||||
p {
|
||||
color: $dark-text-color;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.version {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $dark-text-color;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.about {
|
||||
padding: 20px;
|
||||
|
||||
@media screen and (min-width: $no-gap-breakpoint) {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
color: $dark-text-color;
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
margin-bottom: 30px;
|
||||
|
||||
&__hero {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
aspect-ratio: 1.9;
|
||||
background: lighten($ui-base-color, 4%);
|
||||
border-radius: 8px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
h1,
|
||||
p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
line-height: 1.5;
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__meta {
|
||||
background: lighten($ui-base-color, 4%);
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
font-size: 15px;
|
||||
|
||||
&__column {
|
||||
box-sizing: border-box;
|
||||
width: 50%;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
&__divider {
|
||||
width: 0;
|
||||
border: 0;
|
||||
border-style: solid;
|
||||
border-color: lighten($ui-base-color, 8%);
|
||||
border-left-width: 1px;
|
||||
min-height: calc(100% - 60px);
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 15px;
|
||||
text-transform: uppercase;
|
||||
color: $darker-text-color;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
@media screen and (width <= 600px) {
|
||||
display: block;
|
||||
|
||||
h4 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__column {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__divider {
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
border-left-width: 0;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.layout-multiple-columns & {
|
||||
display: block;
|
||||
|
||||
h4 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&__column {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&__divider {
|
||||
min-height: 0;
|
||||
width: 100%;
|
||||
border-left-width: 0;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__mail {
|
||||
color: $primary-text-color;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.link-footer {
|
||||
padding: 0;
|
||||
margin-top: 60px;
|
||||
text-align: center;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
|
||||
@media screen and (min-width: $no-gap-breakpoint) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.account {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.account__avatar-wrapper {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
||||
.account__relationship {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__section {
|
||||
margin-bottom: 10px;
|
||||
|
||||
&__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 17px;
|
||||
font-weight: 600;
|
||||
line-height: 22px;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
background: lighten($ui-base-color, 4%);
|
||||
color: $highlight-text-color;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.active &__title {
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
&__body {
|
||||
border: 1px solid lighten($ui-base-color, 4%);
|
||||
border-top: 0;
|
||||
padding: 20px;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
&__domain-blocks {
|
||||
margin-top: 30px;
|
||||
background: darken($ui-base-color, 4%);
|
||||
border: 1px solid lighten($ui-base-color, 4%);
|
||||
border-radius: 4px;
|
||||
|
||||
&__domain {
|
||||
border-bottom: 1px solid lighten($ui-base-color, 4%);
|
||||
padding: 10px;
|
||||
font-size: 15px;
|
||||
color: $darker-text-color;
|
||||
|
||||
&:nth-child(2n) {
|
||||
background: darken($ui-base-color, 2%);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: space-between;
|
||||
font-weight: 500;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
h6 {
|
||||
color: $secondary-text-color;
|
||||
font-size: inherit;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
p {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,845 +0,0 @@
|
|||
.account {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
|
||||
.account__display-name {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: $darker-text-color;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
|
||||
.display-name {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.display-name strong {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
&--minimal {
|
||||
.account__display-name {
|
||||
.display-name {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.display-name strong {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__note {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
-webkit-box-orient: vertical;
|
||||
margin-top: 10px;
|
||||
color: $darker-text-color;
|
||||
|
||||
&--missing {
|
||||
color: $dark-text-color;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.account__wrapper {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.account__avatar-wrapper {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.account__avatar {
|
||||
@include avatar-radius;
|
||||
|
||||
display: block;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
&-inline {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-inline-end: 5px;
|
||||
}
|
||||
|
||||
&-composite {
|
||||
@include avatar-radius;
|
||||
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
& > div {
|
||||
@include avatar-radius;
|
||||
|
||||
float: left;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.account__avatar {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
&__label {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
inset-inline-start: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: $primary-text-color;
|
||||
text-shadow: 1px 1px 2px $base-shadow-color;
|
||||
font-weight: 700;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.account__avatar-overlay {
|
||||
position: relative;
|
||||
|
||||
&-overlay {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
inset-inline-end: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.account__relationship {
|
||||
white-space: nowrap;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.account__header__wrapper {
|
||||
flex: 0 0 auto;
|
||||
background: lighten($ui-base-color, 4%);
|
||||
}
|
||||
|
||||
.account__disclaimer {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
gap: 5px;
|
||||
color: $dark-text-color;
|
||||
align-items: center;
|
||||
|
||||
strong {
|
||||
font-weight: 500;
|
||||
|
||||
@each $lang in $cjk-langs {
|
||||
&:lang(#{$lang}) {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.account__action-bar {
|
||||
border-top: 1px solid lighten($ui-base-color, 8%);
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
line-height: 36px;
|
||||
overflow: hidden;
|
||||
flex: 0 0 auto;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.account__action-bar-links {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.account__action-bar__tab {
|
||||
text-decoration: none;
|
||||
overflow: hidden;
|
||||
flex: 0 1 100%;
|
||||
border-inline-start: 1px solid lighten($ui-base-color, 8%);
|
||||
padding: 10px 0;
|
||||
border-bottom: 4px solid transparent;
|
||||
|
||||
&:first-child {
|
||||
border-inline-start: 0;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border-bottom: 4px solid $ui-highlight-color;
|
||||
}
|
||||
|
||||
& > span {
|
||||
display: block;
|
||||
text-transform: uppercase;
|
||||
font-size: 11px;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
|
||||
strong {
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: $primary-text-color;
|
||||
|
||||
@each $lang in $cjk-langs {
|
||||
&:lang(#{$lang}) {
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abbr {
|
||||
color: $highlight-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.account-authorize {
|
||||
padding: 14px 10px;
|
||||
|
||||
.detailed-status__display-name {
|
||||
display: block;
|
||||
margin-bottom: 15px;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.account-authorize__avatar {
|
||||
float: left;
|
||||
margin-inline-end: 10px;
|
||||
}
|
||||
|
||||
.notification__report {
|
||||
padding: 8px 10px;
|
||||
padding-inline-start: 68px;
|
||||
position: relative;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
min-height: 54px;
|
||||
|
||||
&__details {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: $darker-text-color;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
|
||||
strong {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
&__avatar {
|
||||
position: absolute;
|
||||
inset-inline-start: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.notification__message {
|
||||
margin-inline-start: 42px;
|
||||
padding-top: 8px;
|
||||
padding-inline-start: 26px;
|
||||
cursor: default;
|
||||
color: $darker-text-color;
|
||||
font-size: 15px;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
color: $highlight-text-color;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.icon-star {
|
||||
color: $gold-star;
|
||||
}
|
||||
|
||||
> span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.account--panel {
|
||||
background: lighten($ui-base-color, 4%);
|
||||
border-top: 1px solid lighten($ui-base-color, 8%);
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.account--panel__button,
|
||||
.detailed-status__button {
|
||||
flex: 1 1 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.detailed-status__button .emoji-button {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.relationship-tag {
|
||||
color: $white;
|
||||
margin-bottom: 4px;
|
||||
display: block;
|
||||
background-color: rgba($black, 0.45);
|
||||
text-transform: uppercase;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
padding: 4px;
|
||||
border-radius: 4px;
|
||||
opacity: 0.7;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.account-gallery__container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
padding: 4px 2px;
|
||||
}
|
||||
|
||||
.account-gallery__item {
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin: 2px;
|
||||
|
||||
&__icons {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
inset-inline-start: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.notification__filter-bar,
|
||||
.account__section-headline {
|
||||
background: darken($ui-base-color, 4%);
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
cursor: default;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
|
||||
button {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button,
|
||||
a {
|
||||
display: block;
|
||||
flex: 1 1 auto;
|
||||
color: $darker-text-color;
|
||||
padding: 15px 0;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
|
||||
&.active {
|
||||
color: $primary-text-color;
|
||||
|
||||
&::before {
|
||||
display: block;
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
border-radius: 4px;
|
||||
background: $highlight-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.directory__section-headline {
|
||||
background: darken($ui-base-color, 2%);
|
||||
border-bottom-color: transparent;
|
||||
|
||||
a,
|
||||
button {
|
||||
&.active {
|
||||
&::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::after {
|
||||
border-color: transparent transparent darken($ui-base-color, 7%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.account__moved-note {
|
||||
padding: 14px 10px;
|
||||
padding-bottom: 16px;
|
||||
background: lighten($ui-base-color, 4%);
|
||||
border-top: 1px solid lighten($ui-base-color, 8%);
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
|
||||
&__message {
|
||||
position: relative;
|
||||
margin-inline-start: 58px;
|
||||
color: $dark-text-color;
|
||||
padding: 8px 0;
|
||||
padding-top: 0;
|
||||
padding-bottom: 4px;
|
||||
font-size: 14px;
|
||||
|
||||
> span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
&__icon-wrapper {
|
||||
inset-inline-start: -26px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.detailed-status__display-avatar {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.detailed-status__display-name {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.account__header__content {
|
||||
color: $darker-text-color;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
overflow: hidden;
|
||||
word-break: normal;
|
||||
word-wrap: break-word;
|
||||
|
||||
p {
|
||||
margin-bottom: 20px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.account__header {
|
||||
overflow: hidden;
|
||||
|
||||
&.inactive {
|
||||
opacity: 0.5;
|
||||
|
||||
.account__header__image,
|
||||
.account__avatar {
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
}
|
||||
|
||||
&__info {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
inset-inline-start: 10px;
|
||||
}
|
||||
|
||||
&__image {
|
||||
overflow: hidden;
|
||||
height: 145px;
|
||||
position: relative;
|
||||
background: darken($ui-base-color, 4%);
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__bar {
|
||||
position: relative;
|
||||
background: lighten($ui-base-color, 4%);
|
||||
padding: 5px;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 12%);
|
||||
|
||||
.avatar {
|
||||
display: block;
|
||||
flex: 0 0 auto;
|
||||
width: 94px;
|
||||
|
||||
.account__avatar {
|
||||
background: darken($ui-base-color, 8%);
|
||||
border: 2px solid lighten($ui-base-color, 4%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__badges {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
|
||||
.account-role {
|
||||
line-height: unset;
|
||||
}
|
||||
}
|
||||
|
||||
&__tabs {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
padding: 7px 10px;
|
||||
margin-top: -55px;
|
||||
gap: 8px;
|
||||
overflow: hidden;
|
||||
margin-inline-start: -2px; // aligns the pfp with content below
|
||||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding-top: 55px;
|
||||
overflow: hidden;
|
||||
|
||||
.button {
|
||||
flex-shrink: 1;
|
||||
white-space: nowrap;
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint) {
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
border: 1px solid lighten($ui-base-color, 12%);
|
||||
border-radius: 4px;
|
||||
box-sizing: content-box;
|
||||
padding: 5px;
|
||||
|
||||
.icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__name {
|
||||
padding: 5px 10px;
|
||||
|
||||
.emojione {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
color: $primary-text-color;
|
||||
font-weight: 500;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
small {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
color: $darker-text-color;
|
||||
font-weight: 400;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
span {
|
||||
user-select: all;
|
||||
}
|
||||
|
||||
.icon-lock {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.spacer {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
}
|
||||
|
||||
&__bio {
|
||||
overflow: hidden;
|
||||
margin: 0 -5px;
|
||||
|
||||
.account__header__content {
|
||||
padding: 20px 15px;
|
||||
padding-bottom: 5px;
|
||||
color: $primary-text-color;
|
||||
}
|
||||
|
||||
.account__header__joined {
|
||||
font-size: 14px;
|
||||
padding: 5px 15px;
|
||||
color: $darker-text-color;
|
||||
|
||||
.columns-area--mobile & {
|
||||
padding-inline-start: 20px;
|
||||
padding-inline-end: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.account__header__fields {
|
||||
margin: 0;
|
||||
border-top: 1px solid lighten($ui-base-color, 12%);
|
||||
|
||||
a {
|
||||
color: lighten($ui-highlight-color, 8%);
|
||||
}
|
||||
|
||||
dl:first-child .verified {
|
||||
border-radius: 0 4px 0 0;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
dd {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.verified a {
|
||||
color: $valid-value-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__extra {
|
||||
margin-top: 4px;
|
||||
|
||||
&__links {
|
||||
font-size: 14px;
|
||||
color: $darker-text-color;
|
||||
padding: 10px 0;
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
color: $darker-text-color;
|
||||
text-decoration: none;
|
||||
padding: 5px 10px;
|
||||
font-weight: 500;
|
||||
|
||||
strong {
|
||||
font-weight: 700;
|
||||
color: $primary-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__account-note {
|
||||
margin: 0 -5px;
|
||||
padding: 10px 15px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
border-top: 1px solid lighten($ui-base-color, 12%);
|
||||
border-bottom: 1px solid lighten($ui-base-color, 12%);
|
||||
|
||||
label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: $darker-text-color;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
&__content {
|
||||
white-space: pre-wrap;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
textarea {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
width: calc(100% + 20px);
|
||||
color: $secondary-text-color;
|
||||
background: $ui-base-color;
|
||||
padding: 10px;
|
||||
margin: 0 -10px;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
resize: none;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
border-radius: 4px;
|
||||
|
||||
&::placeholder {
|
||||
color: $dark-text-color;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.account__contents {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.account__details {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 1em;
|
||||
}
|
||||
|
||||
.verified-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: $valid-value-color;
|
||||
gap: 4px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
> span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.moved-account-banner,
|
||||
.follow-request-banner,
|
||||
.account-memorial-banner {
|
||||
padding: 20px;
|
||||
background: lighten($ui-base-color, 4%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
|
||||
&__message {
|
||||
color: $darker-text-color;
|
||||
padding: 8px 0;
|
||||
padding-top: 0;
|
||||
padding-bottom: 4px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
&__action {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.detailed-status__display-name {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.follow-request-banner .button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.account-memorial-banner__message {
|
||||
margin-bottom: 0;
|
||||
}
|
|
@ -1,233 +0,0 @@
|
|||
.announcements__item__content {
|
||||
word-wrap: break-word;
|
||||
overflow-y: auto;
|
||||
|
||||
.emojione {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: -3px 0 0;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 10px;
|
||||
white-space: pre-wrap;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: $secondary-text-color;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&.mention {
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
|
||||
span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.unhandled-link {
|
||||
color: $highlight-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.announcements {
|
||||
background: lighten($ui-base-color, 8%);
|
||||
font-size: 13px;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
|
||||
&__mastodon {
|
||||
width: 124px;
|
||||
flex: 0 0 auto;
|
||||
|
||||
@media screen and (max-width: 124px + 300px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&__container {
|
||||
width: calc(100% - 124px);
|
||||
flex: 0 0 auto;
|
||||
position: relative;
|
||||
|
||||
@media screen and (max-width: 124px + 300px) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&__item {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 15px;
|
||||
position: relative;
|
||||
font-size: 15px;
|
||||
line-height: 20px;
|
||||
word-wrap: break-word;
|
||||
font-weight: 400;
|
||||
max-height: 50vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&__range {
|
||||
display: block;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
padding-inline-end: 18px;
|
||||
}
|
||||
|
||||
&__unread {
|
||||
position: absolute;
|
||||
top: 19px;
|
||||
inset-inline-end: 19px;
|
||||
display: block;
|
||||
background: $highlight-text-color;
|
||||
border-radius: 50%;
|
||||
width: 0.625rem;
|
||||
height: 0.625rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__pagination {
|
||||
padding: 15px;
|
||||
color: $darker-text-color;
|
||||
position: absolute;
|
||||
bottom: 3px;
|
||||
inset-inline-end: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.layout-multiple-columns .announcements__mastodon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.layout-multiple-columns .announcements__container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.reactions-bar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin-top: 15px;
|
||||
margin-inline-start: -2px;
|
||||
width: calc(100% - (90px - 33px));
|
||||
|
||||
&__item {
|
||||
flex-shrink: 0;
|
||||
background: lighten($ui-base-color, 12%);
|
||||
border: 0;
|
||||
border-radius: 3px;
|
||||
margin: 2px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
padding: 0 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
transition: all 100ms ease-in;
|
||||
transition-property: background-color, color;
|
||||
|
||||
&__emoji {
|
||||
display: block;
|
||||
margin: 3px 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-width: auto;
|
||||
min-height: auto;
|
||||
vertical-align: bottom;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
&__count {
|
||||
display: block;
|
||||
min-width: 9px;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
text-align: center;
|
||||
margin-inline-start: 6px;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
background: lighten($ui-base-color, 16%);
|
||||
transition: all 200ms ease-out;
|
||||
transition-property: background-color, color;
|
||||
|
||||
&__count {
|
||||
color: lighten($darker-text-color, 4%);
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
transition: all 100ms ease-in;
|
||||
transition-property: background-color, color;
|
||||
background-color: mix(
|
||||
lighten($ui-base-color, 12%),
|
||||
$ui-highlight-color,
|
||||
80%
|
||||
);
|
||||
|
||||
.reactions-bar__item__count {
|
||||
color: lighten($highlight-text-color, 8%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-picker-dropdown {
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
&:hover .emoji-button {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.emoji-button {
|
||||
color: $darker-text-color;
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
width: auto;
|
||||
flex-shrink: 0;
|
||||
padding: 0 6px;
|
||||
height: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
opacity: 0.5;
|
||||
transition: all 100ms ease-in;
|
||||
transition-property: background-color, color;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
opacity: 1;
|
||||
color: lighten($darker-text-color, 4%);
|
||||
transition: all 200ms ease-out;
|
||||
transition-property: background-color, color;
|
||||
}
|
||||
}
|
||||
|
||||
&--empty {
|
||||
.emoji-button {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,668 +0,0 @@
|
|||
.compose-form {
|
||||
padding: 10px;
|
||||
|
||||
.emoji-picker-dropdown {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-end: 0;
|
||||
|
||||
::-webkit-scrollbar-track:hover,
|
||||
::-webkit-scrollbar-track:active {
|
||||
background-color: rgba($base-overlay-background, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.character-counter {
|
||||
cursor: default;
|
||||
font-family: $font-sans-serif, sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: $lighter-text-color;
|
||||
|
||||
&.character-counter--over {
|
||||
color: $warning-red;
|
||||
}
|
||||
}
|
||||
|
||||
.no-reduce-motion .spoiler-input {
|
||||
transition:
|
||||
height 0.4s ease,
|
||||
opacity 0.4s ease;
|
||||
}
|
||||
|
||||
.spoiler-input {
|
||||
height: 0;
|
||||
transform-origin: bottom;
|
||||
opacity: 0;
|
||||
|
||||
&.spoiler-input--visible {
|
||||
height: 36px;
|
||||
margin-bottom: 11px;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
input {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
outline: 0;
|
||||
color: $inverted-text-color;
|
||||
background: $simple-background-color;
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
resize: vertical;
|
||||
|
||||
&::placeholder {
|
||||
color: $dark-text-color;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
@include single-column('screen and (max-width: 630px)') {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.compose-form__warning {
|
||||
color: $inverted-text-color;
|
||||
margin-bottom: 15px;
|
||||
background: $ui-primary-color;
|
||||
box-shadow: 0 2px 6px rgba($base-shadow-color, 0.3);
|
||||
padding: 8px 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
|
||||
a {
|
||||
color: $lighter-text-color;
|
||||
font-weight: 500;
|
||||
text-decoration: underline;
|
||||
|
||||
&:active,
|
||||
&:focus,
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.compose-form__sensitive-button {
|
||||
padding: 10px;
|
||||
padding-top: 0;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
|
||||
&.active {
|
||||
color: $highlight-text-color;
|
||||
}
|
||||
|
||||
input[type='checkbox'] {
|
||||
appearance: none;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
border: 1px solid $ui-primary-color;
|
||||
box-sizing: border-box;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
flex: 0 0 auto;
|
||||
margin-inline-start: 5px;
|
||||
margin-inline-end: 10px;
|
||||
top: -1px;
|
||||
border-radius: 4px;
|
||||
vertical-align: middle;
|
||||
cursor: inherit;
|
||||
|
||||
&:checked {
|
||||
border-color: $highlight-text-color;
|
||||
background: $highlight-text-color
|
||||
url("data:image/svg+xml;utf8,<svg width='18' height='18' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M4.5 8.5L8 12l6-6' stroke='white' stroke-width='1.5'/></svg>")
|
||||
center center no-repeat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reply-indicator {
|
||||
margin: 0 0 10px;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
background: $ui-primary-color;
|
||||
min-height: 23px;
|
||||
overflow-y: auto;
|
||||
flex: 0 2 auto;
|
||||
}
|
||||
|
||||
.reply-indicator__header {
|
||||
margin-bottom: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.reply-indicator__cancel {
|
||||
float: right;
|
||||
line-height: 24px;
|
||||
}
|
||||
|
||||
.reply-indicator__display-name {
|
||||
color: $inverted-text-color;
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
line-height: 24px;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
|
||||
& > .display-name {
|
||||
line-height: unset;
|
||||
height: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.reply-indicator__display-avatar {
|
||||
float: left;
|
||||
margin-inline-end: 5px;
|
||||
}
|
||||
|
||||
.reply-indicator__content {
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
word-wrap: break-word;
|
||||
font-weight: 400;
|
||||
overflow: hidden;
|
||||
padding-top: 5px;
|
||||
color: $inverted-text-color;
|
||||
white-space: pre-wrap;
|
||||
|
||||
p,
|
||||
pre {
|
||||
margin-bottom: 20px;
|
||||
white-space: pre-wrap;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: $lighter-text-color;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
&.mention {
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
|
||||
span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.emojione {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: -5px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
.compose-form .compose-form__autosuggest-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.compose-form .autosuggest-textarea,
|
||||
.compose-form .autosuggest-input {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
label {
|
||||
.autosuggest-textarea__textarea {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
border-radius: 4px 4px 0 0;
|
||||
padding: 10px 32px 0 10px;
|
||||
width: 100%;
|
||||
min-height: 100px;
|
||||
outline: 0;
|
||||
color: $inverted-text-color;
|
||||
background: $simple-background-color;
|
||||
font-size: 14px;
|
||||
font-family: inherit;
|
||||
resize: none;
|
||||
scrollbar-color: initial;
|
||||
|
||||
&::placeholder {
|
||||
color: $dark-text-color;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
all: unset;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
@include single-column('screen and (max-width: 630px)') {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@include limited-single-column('screen and (max-width: 600px)') {
|
||||
height: 100px !important; // prevent auto-resize textarea
|
||||
resize: vertical;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.compose-form__textarea-icons {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 29px;
|
||||
inset-inline-end: 5px;
|
||||
bottom: 5px;
|
||||
overflow: hidden;
|
||||
|
||||
& > .textarea_icon {
|
||||
display: block;
|
||||
margin-top: 2px;
|
||||
margin-inline-start: 2px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
color: $lighter-text-color;
|
||||
font-size: 18px;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
.autosuggest-textarea__suggestions-wrapper {
|
||||
position: relative;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.autosuggest-textarea__suggestions {
|
||||
box-sizing: border-box;
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
width: 100%;
|
||||
z-index: 99;
|
||||
box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4);
|
||||
background: $ui-secondary-color;
|
||||
border-radius: 0 0 4px 4px;
|
||||
color: $inverted-text-color;
|
||||
font-size: 14px;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.autosuggest-textarea__suggestions--visible {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.autosuggest-textarea__suggestions__item {
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active,
|
||||
&.selected {
|
||||
background: darken($ui-secondary-color, 10%);
|
||||
}
|
||||
|
||||
.autosuggest-account,
|
||||
.autosuggest-emoji,
|
||||
.autosuggest-hashtag {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
line-height: 18px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.autosuggest-hashtag {
|
||||
justify-content: space-between;
|
||||
|
||||
&__name {
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&__uses {
|
||||
flex: 0 0 auto;
|
||||
text-align: end;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.autosuggest-account-icon,
|
||||
.autosuggest-emoji img {
|
||||
margin-inline-end: 8px;
|
||||
}
|
||||
|
||||
.autosuggest-account .display-name > span {
|
||||
color: $lighter-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.compose-form__upload-wrapper {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.compose-form__uploads-wrapper {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
font-family: inherit;
|
||||
padding: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.compose-form__upload {
|
||||
flex: 1 1 0;
|
||||
margin: 5px;
|
||||
min-width: 40%;
|
||||
|
||||
.compose-form__upload-thumbnail {
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
height: 140px;
|
||||
width: 100%;
|
||||
background-color: $base-shadow-color;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
overflow: hidden;
|
||||
|
||||
& > .close {
|
||||
mix-blend-mode: difference;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
flex: 0 1 auto;
|
||||
color: $secondary-text-color;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
padding: 10px;
|
||||
font-family: inherit;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
color: lighten($secondary-text-color, 7%);
|
||||
}
|
||||
}
|
||||
|
||||
&__warning {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
bottom: 0;
|
||||
inset-inline-start: 0;
|
||||
inset-inline-end: 0;
|
||||
box-sizing: border-box;
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba($base-shadow-color, 0.8) 0,
|
||||
rgba($base-shadow-color, 0.35) 80%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.compose-form__upload__actions {
|
||||
background: linear-gradient(
|
||||
180deg,
|
||||
rgba($base-shadow-color, 0.8) 0,
|
||||
rgba($base-shadow-color, 0.35) 80%,
|
||||
transparent
|
||||
);
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.upload-progress {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
color: $darker-text-color;
|
||||
overflow: hidden;
|
||||
gap: 10px;
|
||||
|
||||
.icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
.upload-progress__message {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.upload-progress__backdrop {
|
||||
position: relative;
|
||||
margin-top: 5px;
|
||||
border-radius: 6px;
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
background: darken($simple-background-color, 8%);
|
||||
}
|
||||
|
||||
.upload-progress__tracker {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
height: 6px;
|
||||
border-radius: 6px;
|
||||
background: $ui-highlight-color;
|
||||
}
|
||||
|
||||
.compose-form__modifiers {
|
||||
color: $inverted-text-color;
|
||||
font-family: inherit;
|
||||
font-size: 14px;
|
||||
background: $simple-background-color;
|
||||
}
|
||||
|
||||
.compose-form__buttons-wrapper {
|
||||
padding: 10px;
|
||||
background: darken($simple-background-color, 8%);
|
||||
border-radius: 0 0 4px 4px;
|
||||
height: 27px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.compose-form__buttons {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
gap: 2px;
|
||||
|
||||
.icon-button {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
& .icon-button,
|
||||
& .text-icon-button {
|
||||
box-sizing: content-box;
|
||||
padding: 0 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.character-counter__wrapper {
|
||||
align-self: center;
|
||||
margin-inline-end: 4px;
|
||||
}
|
||||
|
||||
.privacy-dropdown__dropdown {
|
||||
border-radius: 4px;
|
||||
box-shadow: var(--dropdown-shadow);
|
||||
background: $simple-background-color;
|
||||
overflow: hidden;
|
||||
transform-origin: 50% 0;
|
||||
}
|
||||
|
||||
.privacy-dropdown__option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
color: $inverted-text-color;
|
||||
cursor: pointer;
|
||||
|
||||
.privacy-dropdown__option__content {
|
||||
flex: 1 1 auto;
|
||||
color: $lighter-text-color;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-inline-start: 10px;
|
||||
}
|
||||
|
||||
strong {
|
||||
display: block;
|
||||
color: $inverted-text-color;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
background: $ui-highlight-color;
|
||||
color: $primary-text-color;
|
||||
|
||||
.privacy-dropdown__option__content {
|
||||
color: $primary-text-color;
|
||||
|
||||
strong {
|
||||
color: $primary-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.active:hover {
|
||||
background: lighten($ui-highlight-color, 4%);
|
||||
}
|
||||
}
|
||||
|
||||
.compose-form__publish {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
min-width: 0;
|
||||
flex: 0 0 auto;
|
||||
column-gap: 5px;
|
||||
|
||||
.compose-form__publish-button-wrapper {
|
||||
padding-top: 10px;
|
||||
|
||||
button {
|
||||
padding: 7px 10px;
|
||||
text-align: center;
|
||||
|
||||
& > span {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.side_arm {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.language-dropdown {
|
||||
&__dropdown {
|
||||
background: $simple-background-color;
|
||||
box-shadow: var(--dropdown-shadow);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
z-index: 2;
|
||||
|
||||
&.top {
|
||||
transform-origin: 50% 100%;
|
||||
}
|
||||
|
||||
&.bottom {
|
||||
transform-origin: 50% 0;
|
||||
}
|
||||
|
||||
.emoji-mart-search {
|
||||
padding-inline-end: 10px;
|
||||
}
|
||||
|
||||
.emoji-mart-search-icon {
|
||||
inset-inline-end: 10px + 5px;
|
||||
}
|
||||
|
||||
.emoji-mart-scroll {
|
||||
padding: 0 10px 10px;
|
||||
}
|
||||
|
||||
&__results {
|
||||
&__item {
|
||||
cursor: pointer;
|
||||
color: $inverted-text-color;
|
||||
font-weight: 500;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
|
||||
&:focus,
|
||||
&:active,
|
||||
&:hover {
|
||||
background: $ui-secondary-color;
|
||||
}
|
||||
|
||||
&__common-name {
|
||||
color: $darker-text-color;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: $ui-highlight-color;
|
||||
color: $primary-text-color;
|
||||
outline: 0;
|
||||
|
||||
.language-dropdown__dropdown__results__item__common-name {
|
||||
color: $secondary-text-color;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: lighten($ui-highlight-color, 4%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
.scrollable .account-card {
|
||||
margin: 10px;
|
||||
background: lighten($ui-base-color, 8%);
|
||||
}
|
||||
|
||||
.scrollable .account-card__title__avatar {
|
||||
img,
|
||||
.account__avatar {
|
||||
border-color: lighten($ui-base-color, 8%);
|
||||
}
|
||||
}
|
||||
|
||||
.scrollable .account-card__bio::after {
|
||||
background: linear-gradient(
|
||||
to left,
|
||||
lighten($ui-base-color, 8%),
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
.filter-form {
|
||||
background: $ui-base-color;
|
||||
|
||||
&__column {
|
||||
padding: 10px 15px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.radio-button {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.radio-button {
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
padding: 6px 0;
|
||||
line-height: 18px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: pointer;
|
||||
|
||||
input[type='radio'],
|
||||
input[type='checkbox'] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&__input {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
border: 1px solid $ui-primary-color;
|
||||
box-sizing: border-box;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
flex: 0 0 auto;
|
||||
margin-inline-end: 10px;
|
||||
top: -1px;
|
||||
border-radius: 50%;
|
||||
vertical-align: middle;
|
||||
|
||||
&.checked {
|
||||
border-color: lighten($ui-highlight-color, 4%);
|
||||
background: lighten($ui-highlight-color, 4%);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
.domain {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
|
||||
.domain__domain-name {
|
||||
flex: 1 1 auto;
|
||||
display: block;
|
||||
color: $primary-text-color;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.domain__wrapper {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.domain_buttons {
|
||||
height: 18px;
|
||||
padding: 10px;
|
||||
white-space: nowrap;
|
||||
}
|
|
@ -1,309 +0,0 @@
|
|||
.drawer {
|
||||
width: 300px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: hidden;
|
||||
padding: 10px 5px;
|
||||
flex: none;
|
||||
|
||||
&:first-child {
|
||||
padding-inline-start: 10px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
padding-inline-end: 10px;
|
||||
}
|
||||
|
||||
@include single-column('screen and (max-width: 630px)') {
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
@include limited-single-column('screen and (max-width: 630px)') {
|
||||
&,
|
||||
&:first-child,
|
||||
&:last-child {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.wide & {
|
||||
min-width: 300px;
|
||||
max-width: 400px;
|
||||
flex: 1 1 200px;
|
||||
}
|
||||
|
||||
@include single-column('screen and (max-width: 630px)') {
|
||||
:root & {
|
||||
// Overrides `.wide` for single-column view
|
||||
flex: auto;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
max-width: none;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.react-swipeable-view-container & {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer__tab {
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
padding: 13px 3px 11px;
|
||||
color: $darker-text-color;
|
||||
text-decoration: none;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-bottom: 2px solid transparent;
|
||||
}
|
||||
|
||||
.drawer__header {
|
||||
flex: none;
|
||||
font-size: 16px;
|
||||
background: $ui-base-color;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
|
||||
a:hover {
|
||||
background: lighten($ui-base-color, 3%);
|
||||
}
|
||||
}
|
||||
|
||||
.search {
|
||||
position: relative;
|
||||
margin-bottom: 10px;
|
||||
flex: none;
|
||||
|
||||
@include limited-single-column(
|
||||
'screen and (max-width: #{$no-gap-breakpoint})'
|
||||
) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@include single-column('screen and (max-width: 630px)') {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-bar {
|
||||
padding: 10px;
|
||||
color: $darker-text-color;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.acct {
|
||||
display: block;
|
||||
color: $secondary-text-color;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.navigation-bar__actions {
|
||||
position: relative;
|
||||
|
||||
.compose__action-bar .icon-button {
|
||||
pointer-events: auto;
|
||||
transform: scale(1, 1) translate(0, 0);
|
||||
opacity: 1;
|
||||
|
||||
.icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-bar__profile {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1 1 auto;
|
||||
margin-inline-start: 8px;
|
||||
}
|
||||
|
||||
.drawer--results {
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.search-results__section {
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
&__header {
|
||||
background: darken($ui-base-color, 4%);
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
padding: 15px;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: $darker-text-color;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
h3 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
button {
|
||||
color: $highlight-text-color;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: 0;
|
||||
font: inherit;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.account:last-child,
|
||||
& > div:last-child .status {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
& > .hashtag {
|
||||
display: block;
|
||||
padding: 10px;
|
||||
color: $secondary-text-color;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
color: lighten($secondary-text-color, 4%);
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.drawer__pager {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
flex-grow: 1;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.drawer__inner {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
background: $ui-base-color;
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
&.darker {
|
||||
background: $ui-base-color;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer__inner__mastodon {
|
||||
background: $ui-base-color
|
||||
url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 234.80078 31.757813" width="234.80078" height="31.757812"><path d="M19.599609 0c-1.05 0-2.10039.375-2.90039 1.125L0 16.925781v14.832031h234.80078V17.025391l-16.5-15.900391c-1.6-1.5-4.20078-1.5-5.80078 0l-13.80078 13.099609c-1.6 1.5-4.19883 1.5-5.79883 0L179.09961 1.125c-1.6-1.5-4.19883-1.5-5.79883 0L159.5 14.224609c-1.6 1.5-4.20078 1.5-5.80078 0L139.90039 1.125c-1.6-1.5-4.20078-1.5-5.80078 0l-13.79883 13.099609c-1.6 1.5-4.20078 1.5-5.80078 0L100.69922 1.125c-1.600001-1.5-4.198829-1.5-5.798829 0l-13.59961 13.099609c-1.6 1.5-4.200781 1.5-5.800781 0L61.699219 1.125c-1.6-1.5-4.198828-1.5-5.798828 0L42.099609 14.224609c-1.6 1.5-4.198828 1.5-5.798828 0L22.5 1.125C21.7.375 20.649609 0 19.599609 0z" fill="#{hex-color($ui-base-color)}"/></svg>')
|
||||
no-repeat bottom / 100% auto;
|
||||
flex: 1;
|
||||
min-height: 47px;
|
||||
display: none;
|
||||
|
||||
> img {
|
||||
display: block;
|
||||
object-fit: contain;
|
||||
object-position: bottom left;
|
||||
width: 85%;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
> .mastodon {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
cursor: inherit;
|
||||
}
|
||||
|
||||
@media screen and (height >= 640px) {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.pseudo-drawer {
|
||||
background: lighten($ui-base-color, 13%);
|
||||
font-size: 13px;
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.drawer__backdrop {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba($base-overlay-background, 0.5);
|
||||
}
|
||||
|
||||
@for $i from 0 through 3 {
|
||||
.mbstobon-#{$i} .drawer__inner__mastodon {
|
||||
@if $i == 3 {
|
||||
background:
|
||||
url('~flavours/glitch/images/wave-drawer.png')
|
||||
no-repeat
|
||||
bottom /
|
||||
100%
|
||||
auto,
|
||||
$ui-base-color;
|
||||
} @else {
|
||||
background:
|
||||
url('~flavours/glitch/images/wave-drawer-glitched.png')
|
||||
no-repeat
|
||||
bottom /
|
||||
100%
|
||||
auto,
|
||||
$ui-base-color;
|
||||
}
|
||||
|
||||
& > .mastodon {
|
||||
background: url('~flavours/glitch/images/mbstobon-ui-#{$i}.png')
|
||||
no-repeat
|
||||
left
|
||||
bottom /
|
||||
contain;
|
||||
|
||||
@if $i != 3 {
|
||||
filter: contrast(50%) brightness(50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
.emojione {
|
||||
font-size: inherit;
|
||||
vertical-align: middle;
|
||||
object-fit: contain;
|
||||
margin: -0.2ex 0.15em 0.2ex;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
||||
img {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-picker-dropdown__menu {
|
||||
background: $simple-background-color;
|
||||
position: relative;
|
||||
box-shadow: var(--dropdown-shadow);
|
||||
border-radius: 4px;
|
||||
margin-top: 5px;
|
||||
z-index: 2;
|
||||
|
||||
.emoji-mart-scroll {
|
||||
transition: opacity 200ms ease;
|
||||
}
|
||||
|
||||
&.selecting .emoji-mart-scroll {
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-picker-dropdown__modifiers {
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
inset-inline-end: 11px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.emoji-picker-dropdown__modifiers__menu {
|
||||
position: absolute;
|
||||
z-index: 4;
|
||||
top: -4px;
|
||||
inset-inline-start: -8px;
|
||||
background: $simple-background-color;
|
||||
border-radius: 4px;
|
||||
box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
|
||||
overflow: hidden;
|
||||
|
||||
button {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
border: 0;
|
||||
padding: 4px 8px;
|
||||
background: transparent;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
background: rgba($ui-secondary-color, 0.4);
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-mart-emoji {
|
||||
height: 22px;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-mart-emoji {
|
||||
span {
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-button {
|
||||
display: block;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 2px;
|
||||
padding-inline-start: 2px;
|
||||
padding-inline-end: 5px;
|
||||
outline: 0;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
filter: grayscale(100%);
|
||||
opacity: 0.8;
|
||||
display: block;
|
||||
margin: 0;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
img {
|
||||
opacity: 1;
|
||||
filter: none;
|
||||
border-radius: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
img {
|
||||
outline: $ui-button-icon-focus-outline;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
.account-card__header {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.explore__search-header {
|
||||
background: darken($ui-base-color, 4%);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
|
||||
.search {
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.search__input {
|
||||
border: 1px solid lighten($ui-base-color, 8%);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.search__popout {
|
||||
border: 1px solid lighten($ui-base-color, 8%);
|
||||
}
|
||||
|
||||
.search .icon {
|
||||
top: 9px;
|
||||
inset-inline-end: 10px;
|
||||
color: $dark-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
.explore__search-results {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: $ui-base-color;
|
||||
border-bottom-left-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
.story {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: $primary-text-color;
|
||||
text-decoration: none;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
gap: 15px;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
color: $highlight-text-color;
|
||||
|
||||
.story__details__publisher,
|
||||
.story__details__shared {
|
||||
color: $highlight-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__details {
|
||||
flex: 1 1 auto;
|
||||
|
||||
&__publisher {
|
||||
color: $darker-text-color;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
&__title {
|
||||
font-size: 19px;
|
||||
line-height: 24px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
&__shared {
|
||||
color: $darker-text-color;
|
||||
}
|
||||
|
||||
strong {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
&__thumbnail {
|
||||
flex: 0 0 auto;
|
||||
position: relative;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
|
||||
.skeleton {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: 8px;
|
||||
display: block;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
&__preview {
|
||||
border-radius: 8px;
|
||||
display: block;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: fill;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
z-index: 0;
|
||||
|
||||
&--hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.expanded {
|
||||
flex-direction: column;
|
||||
|
||||
.story__thumbnail {
|
||||
order: 1;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
aspect-ratio: 1.91 / 1;
|
||||
}
|
||||
|
||||
.story__details {
|
||||
order: 2;
|
||||
width: 100%;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
@import 'misc';
|
||||
@import 'accounts';
|
||||
@import 'domains';
|
||||
@import 'status';
|
||||
@import 'modal';
|
||||
@import 'compose_form';
|
||||
@import 'columns';
|
||||
@import 'regeneration_indicator';
|
||||
@import 'directory';
|
||||
@import 'search';
|
||||
@import 'emoji';
|
||||
@import 'doodle';
|
||||
@import 'drawer';
|
||||
@import 'media';
|
||||
@import 'sensitive';
|
||||
@import 'lists';
|
||||
@import 'emoji_picker';
|
||||
@import 'local_settings';
|
||||
@import 'single_column';
|
||||
@import 'announcements';
|
||||
@import 'explore';
|
||||
@import 'signed_out';
|
||||
@import 'privacy_policy';
|
||||
@import 'about';
|
|
@ -1,94 +0,0 @@
|
|||
.list-editor {
|
||||
background: $ui-base-color;
|
||||
flex-direction: column;
|
||||
border-radius: 8px;
|
||||
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
|
||||
width: 380px;
|
||||
overflow: hidden;
|
||||
|
||||
@media screen and (width <= 420px) {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
h4 {
|
||||
padding: 15px 0;
|
||||
background: lighten($ui-base-color, 13%);
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.drawer__pager {
|
||||
height: 50vh;
|
||||
}
|
||||
|
||||
.drawer__inner {
|
||||
border-radius: 0 0 8px 8px;
|
||||
|
||||
&.backdrop {
|
||||
width: calc(100% - 60px);
|
||||
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
|
||||
border-radius: 0 0 0 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&__accounts {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.account__display-name {
|
||||
&:hover strong {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.account__avatar {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.search {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.list-adder {
|
||||
background: $ui-base-color;
|
||||
flex-direction: column;
|
||||
border-radius: 8px;
|
||||
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
|
||||
width: 380px;
|
||||
overflow: hidden;
|
||||
|
||||
@media screen and (width <= 420px) {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
&__account {
|
||||
background: lighten($ui-base-color, 13%);
|
||||
}
|
||||
|
||||
&__lists {
|
||||
background: lighten($ui-base-color, 13%);
|
||||
height: 50vh;
|
||||
border-radius: 0 0 8px 8px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.list {
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
}
|
||||
|
||||
.list__wrapper {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.list__display-name {
|
||||
flex: 1 1 auto;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
font-size: 16px;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
|
@ -1,802 +0,0 @@
|
|||
.video-error-cover {
|
||||
align-items: center;
|
||||
background: $base-overlay-background;
|
||||
color: $primary-text-color;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
margin-top: 8px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.media-spoiler {
|
||||
background: $base-overlay-background;
|
||||
color: $darker-text-color;
|
||||
border: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
color: lighten($darker-text-color, 8%);
|
||||
}
|
||||
|
||||
.status__content > & {
|
||||
margin-top: 15px; // Add margin when used bare for NSFW video player
|
||||
}
|
||||
@include fullwidth-gallery;
|
||||
}
|
||||
|
||||
.media-spoiler__warning {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.media-spoiler__trigger {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.media-gallery__item__badges {
|
||||
position: absolute;
|
||||
bottom: 6px;
|
||||
inset-inline-start: 6px;
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.media-gallery__alt__label,
|
||||
.media-gallery__gifv__label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: $white;
|
||||
background: rgba($black, 0.65);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
line-height: 18px;
|
||||
|
||||
.icon {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.media-gallery {
|
||||
box-sizing: border-box;
|
||||
margin-top: 8px;
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
min-height: 64px;
|
||||
display: grid;
|
||||
grid-template-columns: 50% 50%;
|
||||
grid-template-rows: 50% 50%;
|
||||
gap: 2px;
|
||||
|
||||
@include fullwidth-gallery;
|
||||
}
|
||||
|
||||
.media-gallery__item {
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
|
||||
&--tall {
|
||||
grid-row: span 2;
|
||||
}
|
||||
|
||||
&--wide {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.full-width & {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
&.letterbox {
|
||||
background: $base-shadow-color;
|
||||
}
|
||||
}
|
||||
|
||||
.media-gallery__item-thumbnail {
|
||||
cursor: zoom-in;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: $secondary-text-color;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
&,
|
||||
img {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: contain;
|
||||
|
||||
&:not(.letterbox) {
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.media-gallery__preview {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
z-index: 0;
|
||||
background: $base-overlay-background;
|
||||
|
||||
&--hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.media-gallery__gifv {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.media-gallery__item-gifv-thumbnail {
|
||||
cursor: zoom-in;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: contain;
|
||||
user-select: none;
|
||||
|
||||
&:not(.letterbox) {
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.media-gallery__item-thumbnail-label {
|
||||
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.video-modal__container {
|
||||
max-width: 100vw;
|
||||
max-height: 100vh;
|
||||
}
|
||||
|
||||
.audio-modal__container {
|
||||
width: 50vw;
|
||||
}
|
||||
|
||||
.media-modal {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
&__close,
|
||||
&__zoom-button {
|
||||
color: rgba($white, 0.7);
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
color: $white;
|
||||
background-color: rgba($white, 0.15);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: rgba($white, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.media-modal__closer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
inset-inline-end: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.media-modal__navigation {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
inset-inline-end: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.3s linear;
|
||||
will-change: opacity;
|
||||
|
||||
* {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
&.media-modal__navigation--hidden {
|
||||
opacity: 0;
|
||||
|
||||
* {
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.media-modal__nav {
|
||||
background: transparent;
|
||||
box-sizing: border-box;
|
||||
border: 0;
|
||||
color: rgba($white, 0.7);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 24px;
|
||||
height: 20vmax;
|
||||
margin: auto 0;
|
||||
padding: 30px 15px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.media-modal__nav--left {
|
||||
inset-inline-start: 0;
|
||||
}
|
||||
|
||||
.media-modal__nav--right {
|
||||
inset-inline-end: 0;
|
||||
}
|
||||
|
||||
.media-modal__overlay {
|
||||
max-width: 600px;
|
||||
position: absolute;
|
||||
inset-inline-start: 0;
|
||||
inset-inline-end: 0;
|
||||
bottom: 0;
|
||||
margin: 0 auto;
|
||||
|
||||
.picture-in-picture__footer {
|
||||
border-radius: 0;
|
||||
background: transparent;
|
||||
padding: 20px 0;
|
||||
|
||||
.icon-button {
|
||||
color: $white;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
color: $white;
|
||||
background-color: rgba($white, 0.15);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background-color: rgba($white, 0.3);
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: $highlight-text-color;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
background: rgba($highlight-text-color, 0.15);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: rgba($highlight-text-color, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
&.star-icon.active {
|
||||
color: $gold-star;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
background: rgba($gold-star, 0.15);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: rgba($gold-star, 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
color: $white;
|
||||
background-color: transparent;
|
||||
cursor: default;
|
||||
opacity: 0.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.media-modal__pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.media-modal__page-dot {
|
||||
flex: 0 0 auto;
|
||||
background-color: $white;
|
||||
opacity: 0.4;
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
border-radius: 50%;
|
||||
margin: 0 4px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 0;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.media-modal__close {
|
||||
position: absolute;
|
||||
inset-inline-end: 8px;
|
||||
top: 8px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.detailed,
|
||||
.fullscreen {
|
||||
.video-player__volume__current,
|
||||
.video-player__volume::before {
|
||||
bottom: 27px;
|
||||
}
|
||||
|
||||
.video-player__volume__handle {
|
||||
bottom: 23px;
|
||||
}
|
||||
}
|
||||
|
||||
.audio-player {
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
background: darken($ui-base-color, 8%);
|
||||
border-radius: 4px;
|
||||
padding-bottom: 44px;
|
||||
width: 100%;
|
||||
|
||||
&.editable {
|
||||
border-radius: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.inactive {
|
||||
audio,
|
||||
.video-player__controls {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.video-player__volume::before,
|
||||
.video-player__seek::before {
|
||||
background: currentColor;
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
.video-player__seek__buffer {
|
||||
background: currentColor;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.video-player__buttons button,
|
||||
.video-player__buttons a {
|
||||
color: currentColor;
|
||||
opacity: 0.75;
|
||||
|
||||
&:active,
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: currentColor;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.video-player__time-sep,
|
||||
.video-player__time-total,
|
||||
.video-player__time-current {
|
||||
color: currentColor;
|
||||
}
|
||||
|
||||
.video-player__seek::before,
|
||||
.video-player__seek__buffer,
|
||||
.video-player__seek__progress {
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.video-player__seek__handle {
|
||||
top: -4px;
|
||||
}
|
||||
|
||||
.video-player__controls {
|
||||
padding-top: 10px;
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.video-player {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
background: $base-shadow-color;
|
||||
max-width: 100%;
|
||||
border-radius: 4px;
|
||||
box-sizing: border-box;
|
||||
color: $white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&.editable {
|
||||
border-radius: 0;
|
||||
height: 100% !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
.detailed-status & {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@include fullwidth-gallery;
|
||||
|
||||
video {
|
||||
display: block;
|
||||
max-width: 100vw;
|
||||
max-height: 80vh;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
&.fullscreen {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
margin: 0;
|
||||
|
||||
video {
|
||||
max-width: 100% !important;
|
||||
max-height: 100% !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.inline {
|
||||
video {
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
&__controls {
|
||||
position: absolute;
|
||||
direction: ltr;
|
||||
z-index: 2;
|
||||
bottom: 0;
|
||||
inset-inline-start: 0;
|
||||
inset-inline-end: 0;
|
||||
box-sizing: border-box;
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba($base-shadow-color, 0.85) 0,
|
||||
rgba($base-shadow-color, 0.45) 60%,
|
||||
transparent
|
||||
);
|
||||
padding: 0 15px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.1s ease;
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&.inactive {
|
||||
video,
|
||||
.video-player__controls {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&__spoiler {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 4;
|
||||
border: 0;
|
||||
background: $base-shadow-color;
|
||||
color: $darker-text-color;
|
||||
transition: none;
|
||||
pointer-events: none;
|
||||
|
||||
&.active {
|
||||
display: block;
|
||||
pointer-events: auto;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
color: lighten($darker-text-color, 7%);
|
||||
}
|
||||
}
|
||||
|
||||
&__title {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&__subtitle {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
&__buttons-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-bottom: 8px;
|
||||
margin: 0 -5px;
|
||||
|
||||
.video-player__download__icon {
|
||||
color: inherit;
|
||||
|
||||
.fa,
|
||||
&:active .fa,
|
||||
&:hover .fa,
|
||||
&:focus .fa {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
flex: 0 1 auto;
|
||||
min-width: 30px;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
gap: 5px;
|
||||
|
||||
.player-button {
|
||||
display: inline-block;
|
||||
outline: 0;
|
||||
padding: 5px;
|
||||
flex: 0 0 auto;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: rgba($white, 0.75);
|
||||
|
||||
&:active,
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__time {
|
||||
display: inline;
|
||||
flex: 0 1 auto;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
&__time-sep,
|
||||
&__time-total,
|
||||
&__time-current {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
&__time-current {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&__time-sep {
|
||||
display: inline-block;
|
||||
margin: 0 6px;
|
||||
}
|
||||
|
||||
&__time-sep,
|
||||
&__time-total {
|
||||
color: $white;
|
||||
}
|
||||
|
||||
&__volume {
|
||||
flex: 0 0 auto;
|
||||
display: inline-flex;
|
||||
cursor: pointer;
|
||||
height: 24px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
.no-reduce-motion & {
|
||||
transition: all 100ms linear;
|
||||
}
|
||||
|
||||
&.active {
|
||||
overflow: visible;
|
||||
width: 50px;
|
||||
margin-inline-end: 16px;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 50px;
|
||||
background: rgba($white, 0.35);
|
||||
border-radius: 4px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 4px;
|
||||
inset-inline-start: 0;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
|
||||
&__current {
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 4px;
|
||||
border-radius: 4px;
|
||||
inset-inline-start: 0;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
background: lighten($ui-highlight-color, 8%);
|
||||
}
|
||||
|
||||
&__handle {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
border-radius: 50%;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
top: 50%;
|
||||
inset-inline-start: 0;
|
||||
margin-inline-start: -6px;
|
||||
transform: translate(0, -50%);
|
||||
background: lighten($ui-highlight-color, 8%);
|
||||
box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
|
||||
opacity: 0;
|
||||
|
||||
.no-reduce-motion & {
|
||||
transition: opacity 100ms linear;
|
||||
}
|
||||
}
|
||||
|
||||
&.active &__handle {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&__link {
|
||||
padding: 2px 10px;
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: $white;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__seek {
|
||||
cursor: pointer;
|
||||
height: 24px;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 100%;
|
||||
background: rgba($white, 0.35);
|
||||
border-radius: 4px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 4px;
|
||||
top: 14px;
|
||||
}
|
||||
|
||||
&__progress,
|
||||
&__buffer {
|
||||
display: block;
|
||||
position: absolute;
|
||||
height: 4px;
|
||||
border-radius: 4px;
|
||||
top: 14px;
|
||||
background: lighten($ui-highlight-color, 8%);
|
||||
}
|
||||
|
||||
&__buffer {
|
||||
background: rgba($white, 0.2);
|
||||
}
|
||||
|
||||
&__handle {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
opacity: 0;
|
||||
border-radius: 50%;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
top: 10px;
|
||||
margin-inline-start: -6px;
|
||||
background: lighten($ui-highlight-color, 8%);
|
||||
box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
|
||||
|
||||
.no-reduce-motion & {
|
||||
transition: opacity 0.1s ease;
|
||||
}
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.video-player__seek__handle {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.detailed,
|
||||
&.fullscreen {
|
||||
.video-player__buttons {
|
||||
.player-button {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.gifv {
|
||||
video {
|
||||
max-width: 100vw;
|
||||
max-height: 80vh;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,209 +0,0 @@
|
|||
.privacy-policy {
|
||||
background: $ui-base-color;
|
||||
padding: 20px;
|
||||
|
||||
@media screen and (min-width: $no-gap-breakpoint) {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
&__body {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.prose {
|
||||
color: $secondary-text-color;
|
||||
font-size: 15px;
|
||||
line-height: 22px;
|
||||
|
||||
p,
|
||||
ul,
|
||||
ol {
|
||||
margin-top: 1.25em;
|
||||
margin-bottom: 1.25em;
|
||||
}
|
||||
|
||||
img {
|
||||
margin-top: 2em;
|
||||
margin-bottom: 2em;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
video {
|
||||
margin-top: 2em;
|
||||
margin-bottom: 2em;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin-top: 2em;
|
||||
margin-bottom: 2em;
|
||||
|
||||
figcaption {
|
||||
font-size: 0.875em;
|
||||
line-height: 1.4285714;
|
||||
margin-top: 0.8571429em;
|
||||
}
|
||||
}
|
||||
|
||||
figure > * {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
margin-top: 0;
|
||||
margin-bottom: 1em;
|
||||
line-height: 1.33;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.25em;
|
||||
margin-top: 1.6em;
|
||||
margin-bottom: 0.6em;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin-top: 1.5em;
|
||||
margin-bottom: 0.5em;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
ol {
|
||||
counter-reset: list-counter;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
ol > li {
|
||||
counter-increment: list-counter;
|
||||
|
||||
&::before {
|
||||
content: counter(list-counter) '.';
|
||||
position: absolute;
|
||||
inset-inline-start: 0;
|
||||
}
|
||||
}
|
||||
|
||||
ul > li::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
background-color: $darker-text-color;
|
||||
border-radius: 50%;
|
||||
width: 0.375em;
|
||||
height: 0.375em;
|
||||
top: 0.5em;
|
||||
inset-inline-start: 0.25em;
|
||||
}
|
||||
|
||||
ul > li,
|
||||
ol > li {
|
||||
position: relative;
|
||||
padding-inline-start: 1.75em;
|
||||
}
|
||||
|
||||
& > ul > li p {
|
||||
margin-top: 0.75em;
|
||||
margin-bottom: 0.75em;
|
||||
}
|
||||
|
||||
& > ul > li > *:first-child {
|
||||
margin-top: 1.25em;
|
||||
}
|
||||
|
||||
& > ul > li > *:last-child {
|
||||
margin-bottom: 1.25em;
|
||||
}
|
||||
|
||||
& > ol > li > *:first-child {
|
||||
margin-top: 1.25em;
|
||||
}
|
||||
|
||||
& > ol > li > *:last-child {
|
||||
margin-bottom: 1.25em;
|
||||
}
|
||||
|
||||
ul ul,
|
||||
ul ol,
|
||||
ol ul,
|
||||
ol ol {
|
||||
margin-top: 0.75em;
|
||||
margin-bottom: 0.75em;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
strong,
|
||||
b {
|
||||
color: $primary-text-color;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
em,
|
||||
i {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $highlight-text-color;
|
||||
text-decoration: underline;
|
||||
|
||||
&:focus,
|
||||
&:hover,
|
||||
&:active {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 0.875em;
|
||||
background: darken($ui-base-color, 8%);
|
||||
border-radius: 4px;
|
||||
padding: 0.2em 0.3em;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
border-top: 1px solid lighten($ui-base-color, 4%);
|
||||
margin-top: 3em;
|
||||
margin-bottom: 3em;
|
||||
}
|
||||
|
||||
hr + * {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h2 + * {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h3 + * {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
h4 + *,
|
||||
h5 + *,
|
||||
h6 + * {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
& > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
& > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
.regeneration-indicator {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: $dark-text-color;
|
||||
background: $ui-base-color;
|
||||
cursor: default;
|
||||
display: flex;
|
||||
flex: 1 1 auto;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
|
||||
&__figure {
|
||||
&,
|
||||
img {
|
||||
display: block;
|
||||
width: auto;
|
||||
height: 160px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&--without-header {
|
||||
padding-top: 20px + 48px;
|
||||
}
|
||||
|
||||
&__label {
|
||||
margin-top: 30px;
|
||||
|
||||
strong {
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
color: $dark-text-color;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,329 +0,0 @@
|
|||
.search {
|
||||
margin-bottom: 10px;
|
||||
position: relative;
|
||||
|
||||
&__popout {
|
||||
box-sizing: border-box;
|
||||
display: none;
|
||||
position: absolute;
|
||||
inset-inline-start: 0;
|
||||
margin-top: -2px;
|
||||
width: 100%;
|
||||
background: $ui-base-color;
|
||||
border-radius: 0 0 4px 4px;
|
||||
box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4);
|
||||
z-index: 99;
|
||||
font-size: 13px;
|
||||
padding: 15px 5px;
|
||||
|
||||
h4 {
|
||||
text-transform: uppercase;
|
||||
color: $dark-text-color;
|
||||
font-weight: 500;
|
||||
padding: 0 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
&__menu {
|
||||
margin-bottom: 20px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&__message {
|
||||
color: $dark-text-color;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
border: 0;
|
||||
font: inherit;
|
||||
background: transparent;
|
||||
color: $darker-text-color;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
text-align: start;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
&--flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.icon-button {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active,
|
||||
&.selected {
|
||||
background: $ui-highlight-color;
|
||||
color: $primary-text-color;
|
||||
|
||||
.icon-button {
|
||||
color: $primary-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
mark {
|
||||
background: transparent;
|
||||
font-weight: 700;
|
||||
color: $primary-text-color;
|
||||
}
|
||||
|
||||
span {
|
||||
overflow: inherit;
|
||||
text-overflow: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
.search__popout {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search__input {
|
||||
@include search-input;
|
||||
|
||||
display: block;
|
||||
padding: 15px;
|
||||
padding-inline-end: 30px;
|
||||
line-height: 18px;
|
||||
font-size: 16px;
|
||||
|
||||
&::placeholder {
|
||||
color: lighten($darker-text-color, 4%);
|
||||
}
|
||||
|
||||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&::-moz-focus-inner,
|
||||
&:focus,
|
||||
&:active {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
background: lighten($ui-base-color, 4%);
|
||||
}
|
||||
}
|
||||
|
||||
.search__icon {
|
||||
&::-moz-focus-inner {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&::-moz-focus-inner,
|
||||
&:focus {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
top: 13px;
|
||||
inset-inline-end: 10px;
|
||||
display: inline-block;
|
||||
opacity: 0;
|
||||
transition: all 100ms linear;
|
||||
transition-property: color, transform, opacity;
|
||||
font-size: 18px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
color: $secondary-text-color;
|
||||
cursor: default;
|
||||
pointer-events: none;
|
||||
|
||||
&.active {
|
||||
pointer-events: auto;
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-search {
|
||||
transform: rotate(0deg);
|
||||
|
||||
&.active {
|
||||
pointer-events: auto;
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-times-circle {
|
||||
top: 17px;
|
||||
transform: rotate(0deg);
|
||||
color: $action-button-color;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
transform: rotate(90deg);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: lighten($action-button-color, 7%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search-results__header {
|
||||
color: $dark-text-color;
|
||||
background: lighten($ui-base-color, 2%);
|
||||
padding: 15px;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
cursor: default;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.search-results__info {
|
||||
padding: 20px;
|
||||
color: $darker-text-color;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.trends {
|
||||
&__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
gap: 15px;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
&__name {
|
||||
flex: 1 1 auto;
|
||||
color: $dark-text-color;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
strong {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $darker-text-color;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover,
|
||||
&:focus,
|
||||
&:active {
|
||||
span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__current {
|
||||
flex: 0 0 auto;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
text-align: end;
|
||||
color: $secondary-text-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&__sparkline {
|
||||
flex: 0 0 auto;
|
||||
width: 50px;
|
||||
|
||||
path:first-child {
|
||||
fill: rgba($highlight-text-color, 0.25) !important;
|
||||
fill-opacity: 1 !important;
|
||||
}
|
||||
|
||||
path:last-child {
|
||||
stroke: lighten($highlight-text-color, 6%) !important;
|
||||
fill: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
&--requires-review {
|
||||
.trends__item__name {
|
||||
color: $gold-star;
|
||||
|
||||
a {
|
||||
color: $gold-star;
|
||||
}
|
||||
}
|
||||
|
||||
.trends__item__current {
|
||||
color: $gold-star;
|
||||
}
|
||||
|
||||
.trends__item__sparkline {
|
||||
path:first-child {
|
||||
fill: rgba($gold-star, 0.25) !important;
|
||||
}
|
||||
|
||||
path:last-child {
|
||||
stroke: lighten($gold-star, 6%) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
.trends__item__name {
|
||||
color: lighten($ui-base-color, 12%);
|
||||
|
||||
a {
|
||||
color: lighten($ui-base-color, 12%);
|
||||
}
|
||||
}
|
||||
|
||||
.trends__item__current {
|
||||
color: lighten($ui-base-color, 12%);
|
||||
}
|
||||
|
||||
.trends__item__sparkline {
|
||||
path:first-child {
|
||||
fill: rgba(lighten($ui-base-color, 12%), 0.25) !important;
|
||||
}
|
||||
|
||||
path:last-child {
|
||||
stroke: lighten(lighten($ui-base-color, 12%), 6%) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&--compact &__item {
|
||||
padding: 10px;
|
||||
padding-inline-end: 28px;
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
.sensitive-info {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
inset-inline-start: 4px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.sensitive-marker {
|
||||
margin: 0 3px;
|
||||
border-radius: 2px;
|
||||
padding: 2px 6px;
|
||||
color: rgba($primary-text-color, 0.8);
|
||||
background: rgba($base-overlay-background, 0.5);
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
text-transform: uppercase;
|
||||
opacity: 0.9;
|
||||
transition: opacity 0.1s ease;
|
||||
|
||||
.media-gallery:hover & {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
.sign-in-banner {
|
||||
padding: 10px;
|
||||
|
||||
p {
|
||||
color: $darker-text-color;
|
||||
margin-bottom: 20px;
|
||||
|
||||
a {
|
||||
color: $secondary-text-color;
|
||||
text-decoration: none;
|
||||
unicode-bidi: isolate;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.server-banner {
|
||||
padding: 20px 0;
|
||||
|
||||
&__introduction {
|
||||
color: $darker-text-color;
|
||||
margin-bottom: 20px;
|
||||
|
||||
strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover,
|
||||
&:active,
|
||||
&:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__hero {
|
||||
display: block;
|
||||
border-radius: 4px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin-bottom: 20px;
|
||||
aspect-ratio: 1.9;
|
||||
border: 0;
|
||||
background: $ui-base-color;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
&__description {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
&__meta {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
max-width: 100%;
|
||||
|
||||
&__column {
|
||||
flex: 0 0 auto;
|
||||
width: calc(50% - 5px);
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
&__number {
|
||||
font-weight: 600;
|
||||
color: $primary-text-color;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&__number-label {
|
||||
color: $darker-text-color;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h4 {
|
||||
text-transform: uppercase;
|
||||
color: $darker-text-color;
|
||||
margin-bottom: 10px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.account {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.account__avatar-wrapper {
|
||||
margin-inline-start: 0;
|
||||
}
|
||||
|
||||
.spacer {
|
||||
margin: 10px 0;
|
||||
}
|
||||
}
|
|
@ -1,319 +0,0 @@
|
|||
.compose-panel {
|
||||
width: 285px;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(100% - 10px);
|
||||
overflow-y: hidden;
|
||||
|
||||
.hero-widget {
|
||||
box-shadow: none;
|
||||
|
||||
&__text,
|
||||
&__img,
|
||||
&__img img {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
&__text {
|
||||
padding: 15px;
|
||||
color: $secondary-text-color;
|
||||
|
||||
strong {
|
||||
font-weight: 700;
|
||||
color: $primary-text-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.search__input {
|
||||
line-height: 18px;
|
||||
font-size: 16px;
|
||||
padding: 15px;
|
||||
padding-inline-end: 30px;
|
||||
}
|
||||
|
||||
.search__icon .fa {
|
||||
top: 15px;
|
||||
}
|
||||
|
||||
.navigation-bar {
|
||||
flex: 0 1 48px;
|
||||
}
|
||||
|
||||
.compose-form {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 310px;
|
||||
}
|
||||
|
||||
.compose-form__autosuggest-wrapper {
|
||||
overflow-y: auto;
|
||||
background-color: $white;
|
||||
border-radius: 4px 4px 0 0;
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
|
||||
.autosuggest-textarea__textarea {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-panel {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
height: calc(100% - 20px);
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
& > a {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 30px;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.navigation-panel,
|
||||
.compose-panel {
|
||||
hr {
|
||||
flex: 0 0 auto;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
border-top: 1px solid lighten($ui-base-color, 4%);
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.flex-spacer {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.columns-area--mobile {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
|
||||
.column,
|
||||
.drawer {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.account-card {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.filter-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.autosuggest-textarea__textarea {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.search__input {
|
||||
line-height: 18px;
|
||||
font-size: 16px;
|
||||
padding: 15px;
|
||||
padding-inline-end: 30px;
|
||||
}
|
||||
|
||||
.search__icon .fa {
|
||||
top: 15px;
|
||||
}
|
||||
|
||||
.scrollable {
|
||||
overflow: visible;
|
||||
|
||||
@supports (display: grid) {
|
||||
contain: content;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: $no-gap-breakpoint) {
|
||||
padding: 10px 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.detailed-status {
|
||||
padding: 15px;
|
||||
|
||||
.media-gallery,
|
||||
.video-player,
|
||||
.audio-player {
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
.account__header__bar {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.navigation-bar,
|
||||
.compose-form {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.compose-form .compose-form__publish .compose-form__publish-button-wrapper {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.notification__report {
|
||||
padding: 15px;
|
||||
padding-inline-start: (48px + 15px * 2);
|
||||
min-height: 48px + 2px;
|
||||
|
||||
&__avatar {
|
||||
inset-inline-start: 15px;
|
||||
top: 17px;
|
||||
}
|
||||
}
|
||||
|
||||
.status {
|
||||
padding: 15px;
|
||||
min-height: 48px + 2px;
|
||||
|
||||
.media-gallery,
|
||||
&__action-bar,
|
||||
.video-player,
|
||||
.audio-player {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.account {
|
||||
padding: 15px 10px;
|
||||
|
||||
&__header__bio {
|
||||
margin: 0 -10px;
|
||||
}
|
||||
}
|
||||
|
||||
.notification {
|
||||
&__message {
|
||||
padding-top: 15px;
|
||||
}
|
||||
|
||||
.status {
|
||||
padding-top: 8px;
|
||||
}
|
||||
|
||||
.account {
|
||||
padding-top: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: $no-gap-breakpoint) {
|
||||
.react-swipeable-view-container .columns-area--mobile {
|
||||
height: calc(100% - 10px) !important;
|
||||
}
|
||||
|
||||
.getting-started__wrapper {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.search-page .search {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navigation-panel__legal {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint - 1px) {
|
||||
$sidebar-width: 285px;
|
||||
|
||||
.columns-area__panels__main {
|
||||
width: calc(100% - $sidebar-width);
|
||||
}
|
||||
|
||||
.columns-area__panels {
|
||||
min-height: calc(100vh - $ui-header-height);
|
||||
}
|
||||
|
||||
.columns-area__panels__pane--navigational {
|
||||
min-width: $sidebar-width;
|
||||
|
||||
.columns-area__panels__pane__inner {
|
||||
width: $sidebar-width;
|
||||
}
|
||||
|
||||
.navigation-panel {
|
||||
margin: 0;
|
||||
background: $ui-base-color;
|
||||
border-inline-start: 1px solid lighten($ui-base-color, 8%);
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.navigation-panel__sign-in-banner,
|
||||
.navigation-panel__logo,
|
||||
.navigation-panel__banner,
|
||||
.getting-started__trends {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.column-link__icon {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.layout-single-column .ui__header {
|
||||
display: flex;
|
||||
background: $ui-base-color;
|
||||
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||
}
|
||||
|
||||
.column-header,
|
||||
.column-back-button,
|
||||
.scrollable,
|
||||
.error-column {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint - 285px - 1px) {
|
||||
$sidebar-width: 55px;
|
||||
|
||||
.columns-area__panels__main {
|
||||
width: calc(100% - $sidebar-width);
|
||||
}
|
||||
|
||||
.columns-area__panels__pane--navigational {
|
||||
min-width: $sidebar-width;
|
||||
|
||||
.columns-area__panels__pane__inner {
|
||||
width: $sidebar-width;
|
||||
}
|
||||
|
||||
.column-link span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.list-panel {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.explore__search-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint - 1px) {
|
||||
.columns-area__panels__pane--compositional {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.explore__search-header {
|
||||
display: flex;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,14 +1,14 @@
|
|||
.emoji-mart {
|
||||
font-size: 13px;
|
||||
display: inline-block;
|
||||
color: $inverted-text-color;
|
||||
|
||||
&,
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
line-height: 1.15;
|
||||
}
|
||||
|
||||
font-size: 13px;
|
||||
display: inline-block;
|
||||
color: $inverted-text-color;
|
||||
|
||||
.emoji-mart-emoji {
|
||||
padding: 6px;
|
||||
}
|
||||
|
@ -64,17 +64,17 @@
|
|||
}
|
||||
|
||||
.emoji-mart-anchor-bar {
|
||||
bottom: 0;
|
||||
bottom: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-mart-anchor-bar {
|
||||
position: absolute;
|
||||
bottom: -3px;
|
||||
bottom: -5px;
|
||||
inset-inline-start: 0;
|
||||
width: 100%;
|
||||
height: 3px;
|
||||
background-color: darken($ui-highlight-color, 3%);
|
||||
height: 4px;
|
||||
background-color: $highlight-text-color;
|
||||
}
|
||||
|
||||
.emoji-mart-anchors {
|
||||
|
@ -173,7 +173,7 @@
|
|||
}
|
||||
|
||||
&:hover::before {
|
||||
z-index: 0;
|
||||
z-index: -1;
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
|
@ -13,8 +13,9 @@
|
|||
@import 'forms';
|
||||
@import 'accounts';
|
||||
@import 'statuses';
|
||||
@import 'components/index';
|
||||
@import 'components';
|
||||
@import 'polls';
|
||||
@import 'emoji_picker';
|
||||
@import 'about';
|
||||
@import 'tables';
|
||||
@import 'admin';
|
||||
|
@ -22,3 +23,5 @@
|
|||
@import 'rtl';
|
||||
@import 'dashboard';
|
||||
@import 'rich_text';
|
||||
@import 'glitch_local_settings';
|
||||
@import 'glitch_doodle';
|
||||
|
|
|
@ -51,16 +51,6 @@ export const UNBOOKMARK_REQUEST = 'UNBOOKMARKED_REQUEST';
|
|||
export const UNBOOKMARK_SUCCESS = 'UNBOOKMARKED_SUCCESS';
|
||||
export const UNBOOKMARK_FAIL = 'UNBOOKMARKED_FAIL';
|
||||
|
||||
export const REACTION_UPDATE = 'REACTION_UPDATE';
|
||||
|
||||
export const REACTION_ADD_REQUEST = 'REACTION_ADD_REQUEST';
|
||||
export const REACTION_ADD_SUCCESS = 'REACTION_ADD_SUCCESS';
|
||||
export const REACTION_ADD_FAIL = 'REACTION_ADD_FAIL';
|
||||
|
||||
export const REACTION_REMOVE_REQUEST = 'REACTION_REMOVE_REQUEST';
|
||||
export const REACTION_REMOVE_SUCCESS = 'REACTION_REMOVE_SUCCESS';
|
||||
export const REACTION_REMOVE_FAIL = 'REACTION_REMOVE_FAIL';
|
||||
|
||||
export function reblog(status, visibility) {
|
||||
return function (dispatch, getState) {
|
||||
dispatch(reblogRequest(status));
|
||||
|
@ -526,89 +516,3 @@ export function unpinFail(status, error) {
|
|||
skipLoading: true,
|
||||
};
|
||||
}
|
||||
|
||||
export function addReaction(statusId, name, url) {
|
||||
return (dispatch, getState) => {
|
||||
const status = getState().get('statuses').get(statusId);
|
||||
let alreadyAdded = false;
|
||||
if (status) {
|
||||
const reaction = status.get('reactions').find(x => x.get('name') === name);
|
||||
if (reaction && reaction.get('me')) {
|
||||
alreadyAdded = true;
|
||||
}
|
||||
}
|
||||
if (!alreadyAdded) {
|
||||
dispatch(addReactionRequest(statusId, name, url));
|
||||
}
|
||||
|
||||
api(getState).post(`/api/v1/statuses/${statusId}/react/${encodeURIComponent(name)}`).then(() => {
|
||||
dispatch(addReactionSuccess(statusId, name));
|
||||
}).catch(err => {
|
||||
if (!alreadyAdded) {
|
||||
dispatch(addReactionFail(statusId, name, err));
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function addReactionRequest(statusId, name, url) {
|
||||
return {
|
||||
type: REACTION_ADD_REQUEST,
|
||||
id: statusId,
|
||||
name,
|
||||
url,
|
||||
};
|
||||
}
|
||||
|
||||
export function addReactionSuccess(statusId, name) {
|
||||
return {
|
||||
type: REACTION_ADD_SUCCESS,
|
||||
id: statusId,
|
||||
name,
|
||||
};
|
||||
}
|
||||
|
||||
export function addReactionFail(statusId, name, error) {
|
||||
return {
|
||||
type: REACTION_ADD_FAIL,
|
||||
id: statusId,
|
||||
name,
|
||||
error,
|
||||
};
|
||||
}
|
||||
|
||||
export function removeReaction(statusId, name) {
|
||||
return (dispatch, getState) => {
|
||||
dispatch(removeReactionRequest(statusId, name));
|
||||
|
||||
api(getState).post(`/api/v1/statuses/${statusId}/unreact/${encodeURIComponent(name)}`).then(() => {
|
||||
dispatch(removeReactionSuccess(statusId, name));
|
||||
}).catch(err => {
|
||||
dispatch(removeReactionFail(statusId, name, err));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function removeReactionRequest(statusId, name) {
|
||||
return {
|
||||
type: REACTION_REMOVE_REQUEST,
|
||||
id: statusId,
|
||||
name,
|
||||
};
|
||||
}
|
||||
|
||||
export function removeReactionSuccess(statusId, name) {
|
||||
return {
|
||||
type: REACTION_REMOVE_SUCCESS,
|
||||
id: statusId,
|
||||
name,
|
||||
};
|
||||
}
|
||||
|
||||
export function removeReactionFail(statusId, name) {
|
||||
return {
|
||||
type: REACTION_REMOVE_FAIL,
|
||||
id: statusId,
|
||||
name,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -130,7 +130,6 @@ const excludeTypesFromFilter = filter => {
|
|||
'follow',
|
||||
'follow_request',
|
||||
'favourite',
|
||||
'reaction',
|
||||
'reblog',
|
||||
'mention',
|
||||
'poll',
|
||||
|
|
|
@ -1,170 +0,0 @@
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { autoPlayGif, reduceMotion } from '../initial_state';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
import TransitionMotion from 'react-motion/lib/TransitionMotion';
|
||||
import classNames from 'classnames';
|
||||
import React from 'react';
|
||||
import unicodeMapping from '../features/emoji/emoji_unicode_mapping_light';
|
||||
import { AnimatedNumber } from './animated_number';
|
||||
import { assetHost } from '../utils/config';
|
||||
|
||||
export default class StatusReactions extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
statusId: PropTypes.string.isRequired,
|
||||
reactions: ImmutablePropTypes.list.isRequired,
|
||||
numVisible: PropTypes.number,
|
||||
addReaction: PropTypes.func.isRequired,
|
||||
canReact: PropTypes.bool.isRequired,
|
||||
removeReaction: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
willEnter() {
|
||||
return { scale: reduceMotion ? 1 : 0 };
|
||||
}
|
||||
|
||||
willLeave() {
|
||||
return { scale: reduceMotion ? 0 : spring(0, { stiffness: 170, damping: 26 }) };
|
||||
}
|
||||
|
||||
render() {
|
||||
const { reactions, numVisible } = this.props;
|
||||
let visibleReactions = reactions
|
||||
.filter(x => x.get('count') > 0)
|
||||
.sort((a, b) => b.get('count') - a.get('count'));
|
||||
|
||||
if (numVisible >= 0) {
|
||||
visibleReactions = visibleReactions.filter((_, i) => i < numVisible);
|
||||
}
|
||||
|
||||
const styles = visibleReactions.map(reaction => ({
|
||||
key: reaction.get('name'),
|
||||
data: reaction,
|
||||
style: { scale: reduceMotion ? 1 : spring(1, { stiffness: 150, damping: 13 }) },
|
||||
})).toArray();
|
||||
|
||||
return (
|
||||
<TransitionMotion styles={styles} willEnter={this.willEnter} willLeave={this.willLeave}>
|
||||
{items => (
|
||||
<div className={classNames('reactions-bar', { 'reactions-bar--empty': visibleReactions.isEmpty() })}>
|
||||
{items.map(({ key, data, style }) => (
|
||||
<Reaction
|
||||
key={key}
|
||||
statusId={this.props.statusId}
|
||||
reaction={data}
|
||||
style={{ transform: `scale(${style.scale})`, position: style.scale < 0.5 ? 'absolute' : 'static' }}
|
||||
addReaction={this.props.addReaction}
|
||||
removeReaction={this.props.removeReaction}
|
||||
canReact={this.props.canReact}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</TransitionMotion>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Reaction extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
statusId: PropTypes.string,
|
||||
reaction: ImmutablePropTypes.map.isRequired,
|
||||
addReaction: PropTypes.func.isRequired,
|
||||
removeReaction: PropTypes.func.isRequired,
|
||||
canReact: PropTypes.bool.isRequired,
|
||||
style: PropTypes.object,
|
||||
};
|
||||
|
||||
state = {
|
||||
hovered: false,
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
const { reaction, statusId, addReaction, removeReaction } = this.props;
|
||||
|
||||
if (reaction.get('me')) {
|
||||
removeReaction(statusId, reaction.get('name'));
|
||||
} else {
|
||||
addReaction(statusId, reaction.get('name'));
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseEnter = () => this.setState({ hovered: true })
|
||||
|
||||
handleMouseLeave = () => this.setState({ hovered: false })
|
||||
|
||||
render() {
|
||||
const { reaction } = this.props;
|
||||
|
||||
return (
|
||||
<button
|
||||
className={classNames('reactions-bar__item', { active: reaction.get('me') })}
|
||||
onClick={this.handleClick}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
disabled={!this.props.canReact}
|
||||
style={this.props.style}
|
||||
>
|
||||
<span className='reactions-bar__item__emoji'>
|
||||
<Emoji
|
||||
hovered={this.state.hovered}
|
||||
emoji={reaction.get('name')}
|
||||
url={reaction.get('url')}
|
||||
staticUrl={reaction.get('static_url')}
|
||||
/>
|
||||
</span>
|
||||
<span className='reactions-bar__item__count'>
|
||||
<AnimatedNumber value={reaction.get('count')} />
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Emoji extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
emoji: PropTypes.string.isRequired,
|
||||
hovered: PropTypes.bool.isRequired,
|
||||
url: PropTypes.string,
|
||||
staticUrl: PropTypes.string,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { emoji, hovered, url, staticUrl } = this.props;
|
||||
|
||||
if (unicodeMapping[emoji]) {
|
||||
const { filename, shortCode } = unicodeMapping[this.props.emoji];
|
||||
const title = shortCode ? `:${shortCode}:` : '';
|
||||
|
||||
return (
|
||||
<img
|
||||
draggable='false'
|
||||
className='emojione'
|
||||
alt={emoji}
|
||||
title={title}
|
||||
src={`${assetHost}/emoji/${filename}.svg`}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
const filename = (autoPlayGif || hovered) ? url : staticUrl;
|
||||
const shortCode = `:${emoji}:`;
|
||||
|
||||
return (
|
||||
<img
|
||||
draggable='false'
|
||||
className='emojione custom-emoji'
|
||||
alt={shortCode}
|
||||
title={shortCode}
|
||||
src={filename}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -30,8 +30,6 @@ import {
|
|||
unbookmark,
|
||||
pin,
|
||||
unpin,
|
||||
addReaction,
|
||||
removeReaction,
|
||||
} from '../actions/interactions';
|
||||
import { openModal } from '../actions/modal';
|
||||
import { initMuteModal } from '../actions/mutes';
|
||||
|
@ -137,14 +135,6 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
|
|||
}
|
||||
},
|
||||
|
||||
onReactionAdd (statusId, name, url) {
|
||||
dispatch(addReaction(statusId, name, url));
|
||||
},
|
||||
|
||||
onReactionRemove (statusId, name) {
|
||||
dispatch(removeReaction(statusId, name));
|
||||
},
|
||||
|
||||
onEmbed (status) {
|
||||
dispatch(openModal({
|
||||
modalType: 'EMBED',
|
||||
|
|
|
@ -322,7 +322,6 @@ class EmojiPickerDropdown extends PureComponent {
|
|||
onSkinTone: PropTypes.func.isRequired,
|
||||
skinTone: PropTypes.number.isRequired,
|
||||
button: PropTypes.node,
|
||||
disabled: PropTypes.bool,
|
||||
};
|
||||
|
||||
state = {
|
||||
|
@ -356,7 +355,7 @@ class EmojiPickerDropdown extends PureComponent {
|
|||
};
|
||||
|
||||
onToggle = (e) => {
|
||||
if (!this.state.disabled && !this.state.loading && (!e.key || e.key === 'Enter')) {
|
||||
if (!this.state.loading && (!e.key || e.key === 'Enter')) {
|
||||
if (this.state.active) {
|
||||
this.onHideDropdown();
|
||||
} else {
|
||||
|
|
|
@ -119,17 +119,6 @@ export default class ColumnSettings extends PureComponent {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div role='group' aria-labelledby='notifications-reaction'>
|
||||
<span id='notifications-reaction' className='column-settings__section'><FormattedMessage id='notifications.column_settings.reaction' defaultMessage='Reactions:' /></span>
|
||||
|
||||
<div className='column-settings__pillbar'>
|
||||
<SettingToggle disabled={browserPermission === 'denied'} prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'reaction']} onChange={onChange} label={alertStr} />
|
||||
{showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'reaction']} onChange={this.onPushChange} label={pushStr} />}
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'reaction']} onChange={onChange} label={showStr} />
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'reaction']} onChange={onChange} label={soundStr} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div role='group' aria-labelledby='notifications-mention'>
|
||||
<span id='notifications-mention' className='column-settings__section'><FormattedMessage id='notifications.column_settings.mention' defaultMessage='Mentions:' /></span>
|
||||
|
||||
|
|
|
@ -46,8 +46,6 @@ import {
|
|||
unreblog,
|
||||
pin,
|
||||
unpin,
|
||||
addReaction,
|
||||
removeReaction,
|
||||
} from '../../actions/interactions';
|
||||
import { openModal } from '../../actions/modal';
|
||||
import { initMuteModal } from '../../actions/mutes';
|
||||
|
@ -267,19 +265,6 @@ class Status extends ImmutablePureComponent {
|
|||
}
|
||||
};
|
||||
|
||||
handleReactionAdd = (statusId, name, url) => {
|
||||
const { dispatch } = this.props;
|
||||
const { signedIn } = this.context.identity;
|
||||
|
||||
if (signedIn) {
|
||||
dispatch(addReaction(statusId, name, url));
|
||||
}
|
||||
};
|
||||
|
||||
handleReactionRemove = (statusId, name) => {
|
||||
this.props.dispatch(removeReaction(statusId, name));
|
||||
};
|
||||
|
||||
handlePin = (status) => {
|
||||
if (status.get('pinned')) {
|
||||
this.props.dispatch(unpin(status));
|
||||
|
@ -720,15 +705,12 @@ class Status extends ImmutablePureComponent {
|
|||
status={status}
|
||||
onOpenVideo={this.handleOpenVideo}
|
||||
onOpenMedia={this.handleOpenMedia}
|
||||
onReactionAdd={this.handleReactionAdd}
|
||||
onReactionRemove={this.handleReactionRemove}
|
||||
onToggleHidden={this.handleToggleHidden}
|
||||
onTranslate={this.handleTranslate}
|
||||
domain={domain}
|
||||
showMedia={this.state.showMedia}
|
||||
onToggleMediaVisibility={this.handleToggleMediaVisibility}
|
||||
pictureInPicture={pictureInPicture}
|
||||
emojiMap={this.props.emojiMap}
|
||||
/>
|
||||
|
||||
<ActionBar
|
||||
|
@ -736,7 +718,6 @@ class Status extends ImmutablePureComponent {
|
|||
status={status}
|
||||
onReply={this.handleReplyClick}
|
||||
onFavourite={this.handleFavouriteClick}
|
||||
onReactionAdd={this.handleReactionAdd}
|
||||
onReblog={this.handleReblogClick}
|
||||
onBookmark={this.handleBookmarkClick}
|
||||
onDelete={this.handleDeleteClick}
|
||||
|
|
|
@ -34,7 +34,6 @@ const initialState = ImmutableMap({
|
|||
follow: false,
|
||||
follow_request: false,
|
||||
favourite: false,
|
||||
reaction: false,
|
||||
reblog: false,
|
||||
mention: false,
|
||||
poll: false,
|
||||
|
@ -58,7 +57,6 @@ const initialState = ImmutableMap({
|
|||
follow_request: false,
|
||||
favourite: true,
|
||||
reblog: true,
|
||||
reaction: true,
|
||||
mention: true,
|
||||
poll: true,
|
||||
status: true,
|
||||
|
@ -72,7 +70,6 @@ const initialState = ImmutableMap({
|
|||
follow_request: false,
|
||||
favourite: true,
|
||||
reblog: true,
|
||||
reaction: true,
|
||||
mention: true,
|
||||
poll: true,
|
||||
status: true,
|
||||
|
|
1
app/javascript/material-icons/400-24px/colors-fill.svg
Normal file
1
app/javascript/material-icons/400-24px/colors-fill.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M346-140 100-386q-10-10-15-22t-5-25q0-13 5-25t15-22l230-229-106-106 62-65 400 400q10 10 14.5 22t4.5 25q0 13-4.5 25T686-386L440-140q-10 10-22 15t-25 5q-13 0-25-5t-22-15Zm47-506L179-432h428L393-646Zm399 526q-36 0-61-25.5T706-208q0-27 13.5-51t30.5-47l42-54 44 54q16 23 30 47t14 51q0 37-26 62.5T792-120Z"/></svg>
|
After Width: | Height: | Size: 405 B |
1
app/javascript/material-icons/400-24px/colors.svg
Normal file
1
app/javascript/material-icons/400-24px/colors.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M346-140 100-386q-10-10-15-22t-5-25q0-13 5-25t15-22l230-229-106-106 62-65 400 400q10 10 14.5 22t4.5 25q0 13-4.5 25T686-386L440-140q-10 10-22 15t-25 5q-13 0-25-5t-22-15Zm47-506L179-432h428L393-646Zm399 526q-36 0-61-25.5T706-208q0-27 13.5-51t30.5-47l42-54 44 54q16 23 30 47t14 51q0 37-26 62.5T792-120Z"/></svg>
|
After Width: | Height: | Size: 405 B |
1
app/javascript/material-icons/400-24px/undo-fill.svg
Normal file
1
app/javascript/material-icons/400-24px/undo-fill.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M280-200v-80h284q63 0 109.5-40T720-420q0-60-46.5-100T564-560H312l104 104-56 56-200-200 200-200 56 56-104 104h252q97 0 166.5 63T800-420q0 94-69.5 157T564-200H280Z"/></svg>
|
After Width: | Height: | Size: 267 B |
1
app/javascript/material-icons/400-24px/undo.svg
Normal file
1
app/javascript/material-icons/400-24px/undo.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M280-200v-80h284q63 0 109.5-40T720-420q0-60-46.5-100T564-560H312l104 104-56 56-200-200 200-200 56 56-104 104h252q97 0 166.5 63T800-420q0 94-69.5 157T564-200H280Z"/></svg>
|
After Width: | Height: | Size: 267 B |
|
@ -1136,7 +1136,6 @@ body > [data-popper-placement] {
|
|||
|
||||
.status__content,
|
||||
.status__action-bar,
|
||||
.reactions-bar,
|
||||
.media-gallery,
|
||||
.video-player,
|
||||
.audio-player,
|
||||
|
@ -1190,10 +1189,6 @@ body > [data-popper-placement] {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reactions-bar--empty {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.status__relative-time {
|
||||
|
@ -1335,16 +1330,6 @@ body > [data-popper-placement] {
|
|||
align-items: center;
|
||||
gap: 18px;
|
||||
margin-top: 16px;
|
||||
|
||||
& > .emoji-picker-dropdown > .emoji-button {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.status__action-bar-button {
|
||||
.fa-plus {
|
||||
padding-top: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.detailed-status__action-bar-dropdown {
|
||||
|
@ -4236,10 +4221,6 @@ a.status-card {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
.detailed-status__button .emoji-button {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.column-settings__outer {
|
||||
background: lighten($ui-base-color, 8%);
|
||||
padding: 15px;
|
||||
|
|
|
@ -4,14 +4,34 @@ module ApplicationExtension
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
include Redisable
|
||||
|
||||
has_many :created_users, class_name: 'User', foreign_key: 'created_by_application_id', inverse_of: :created_by_application
|
||||
|
||||
validates :name, length: { maximum: 60 }
|
||||
validates :website, url: true, length: { maximum: 2_000 }, if: :website?
|
||||
validates :redirect_uri, length: { maximum: 2_000 }
|
||||
|
||||
# The relationship used between Applications and AccessTokens is using
|
||||
# dependent: delete_all, which means the ActiveRecord callback in
|
||||
# AccessTokenExtension is not run, so instead we manually announce to
|
||||
# streaming that these tokens are being deleted.
|
||||
before_destroy :push_to_streaming_api, prepend: true
|
||||
end
|
||||
|
||||
def confirmation_redirect_uri
|
||||
redirect_uri.lines.first.strip
|
||||
end
|
||||
|
||||
def push_to_streaming_api
|
||||
# TODO: #28793 Combine into a single topic
|
||||
payload = Oj.dump(event: :kill)
|
||||
access_tokens.in_batches do |tokens|
|
||||
redis.pipelined do |pipeline|
|
||||
tokens.ids.each do |id|
|
||||
pipeline.publish("timeline:access_token:#{id}", payload)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,6 +32,7 @@ class StatusCacheHydrator
|
|||
payload[:bookmarked] = Bookmark.exists?(account_id: account_id, status_id: @status.id)
|
||||
payload[:pinned] = StatusPin.exists?(account_id: account_id, status_id: @status.id) if @status.account_id == account_id
|
||||
payload[:filtered] = mapped_applied_custom_filter(account_id, @status)
|
||||
payload[:reactions] = serialized_reactions(account_id)
|
||||
|
||||
if payload[:poll]
|
||||
payload[:poll][:voted] = @status.account_id == account_id
|
||||
|
@ -57,6 +58,7 @@ class StatusCacheHydrator
|
|||
payload[:reblog][:bookmarked] = Bookmark.exists?(account_id: account_id, status_id: @status.reblog_of_id)
|
||||
payload[:reblog][:pinned] = StatusPin.exists?(account_id: account_id, status_id: @status.reblog_of_id) if @status.reblog.account_id == account_id
|
||||
payload[:reblog][:filtered] = payload[:filtered]
|
||||
payload[:reblog][:reactions] = serialized_reactions(account_id)
|
||||
|
||||
if payload[:reblog][:poll]
|
||||
if @status.reblog.account_id == account_id
|
||||
|
@ -71,6 +73,7 @@ class StatusCacheHydrator
|
|||
|
||||
payload[:favourited] = payload[:reblog][:favourited]
|
||||
payload[:reblogged] = payload[:reblog][:reblogged]
|
||||
payload[:reactions] = payload[:reblog][:reactions]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -87,6 +90,16 @@ class StatusCacheHydrator
|
|||
).as_json
|
||||
end
|
||||
|
||||
def serialized_reactions(account_id)
|
||||
reactions = @status.reactions(account_id)
|
||||
ActiveModelSerializers::SerializableResource.new(
|
||||
reactions,
|
||||
each_serializer: REST::ReactionSerializer,
|
||||
scope: account_id, # terrible
|
||||
scope_name: :current_user
|
||||
).as_json
|
||||
end
|
||||
|
||||
def payload_application
|
||||
@status.application.present? ? serialized_status_application_json : nil
|
||||
end
|
||||
|
|
|
@ -25,7 +25,15 @@ module User::LdapAuthenticable
|
|||
resource = joins(:account).find_by(accounts: { username: safe_username })
|
||||
|
||||
if resource.blank?
|
||||
resource = new(email: attributes[Devise.ldap_mail.to_sym].first, agreement: true, account_attributes: { username: safe_username }, admin: false, external: true, confirmed_at: Time.now.utc)
|
||||
resource = new(
|
||||
email: attributes[Devise.ldap_mail.to_sym].first,
|
||||
agreement: true,
|
||||
account_attributes: {
|
||||
username: safe_username,
|
||||
},
|
||||
external: true,
|
||||
confirmed_at: Time.now.utc
|
||||
)
|
||||
resource.save!
|
||||
end
|
||||
|
||||
|
|
|
@ -19,17 +19,18 @@ module User::Omniauthable
|
|||
end
|
||||
|
||||
class_methods do
|
||||
def find_for_oauth(auth, signed_in_resource = nil)
|
||||
def find_for_omniauth(auth, signed_in_resource = nil)
|
||||
# EOLE-SSO Patch
|
||||
auth.uid = (auth.uid[0][:uid] || auth.uid[0][:user]) if auth.uid.is_a? Hashie::Array
|
||||
identity = Identity.find_for_oauth(auth)
|
||||
identity = Identity.find_for_omniauth(auth)
|
||||
|
||||
# If a signed_in_resource is provided it always overrides the existing user
|
||||
# to prevent the identity being locked with accidentally created accounts.
|
||||
# Note that this may leave zombie accounts (with no associated identity) which
|
||||
# can be cleaned up at a later date.
|
||||
user = signed_in_resource || identity.user
|
||||
user ||= create_for_oauth(auth)
|
||||
user ||= reattach_for_auth(auth)
|
||||
user ||= create_for_auth(auth)
|
||||
|
||||
if identity.user.nil?
|
||||
identity.user = user
|
||||
|
@ -39,19 +40,35 @@ module User::Omniauthable
|
|||
user
|
||||
end
|
||||
|
||||
def create_for_oauth(auth)
|
||||
# Check if the user exists with provided email. If no email was provided,
|
||||
private
|
||||
|
||||
def reattach_for_auth(auth)
|
||||
# If allowed, check if a user exists with the provided email address,
|
||||
# and return it if they does not have an associated identity with the
|
||||
# current authentication provider.
|
||||
|
||||
# This can be used to provide a choice of alternative auth providers
|
||||
# or provide smooth gradual transition between multiple auth providers,
|
||||
# but this is discouraged because any insecure provider will put *all*
|
||||
# local users at risk, regardless of which provider they registered with.
|
||||
|
||||
return unless ENV['ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH'] == 'true'
|
||||
|
||||
email, email_is_verified = email_from_auth(auth)
|
||||
return unless email_is_verified
|
||||
|
||||
user = User.find_by(email: email)
|
||||
return if user.nil? || Identity.exists?(provider: auth.provider, user_id: user.id)
|
||||
|
||||
user
|
||||
end
|
||||
|
||||
def create_for_auth(auth)
|
||||
# Create a user for the given auth params. If no email was provided,
|
||||
# we assign a temporary email and ask the user to verify it on
|
||||
# the next step via Auth::SetupController.show
|
||||
|
||||
strategy = Devise.omniauth_configs[auth.provider.to_sym].strategy
|
||||
assume_verified = strategy&.security&.assume_email_is_verified
|
||||
email_is_verified = auth.info.verified || auth.info.verified_email || auth.info.email_verified || assume_verified
|
||||
email = auth.info.verified_email || auth.info.email
|
||||
|
||||
user = User.find_by(email: email) if email_is_verified
|
||||
|
||||
return user unless user.nil?
|
||||
email, email_is_verified = email_from_auth(auth)
|
||||
|
||||
user = User.new(user_params_from_auth(email, auth))
|
||||
|
||||
|
@ -66,7 +83,14 @@ module User::Omniauthable
|
|||
user
|
||||
end
|
||||
|
||||
private
|
||||
def email_from_auth(auth)
|
||||
strategy = Devise.omniauth_configs[auth.provider.to_sym].strategy
|
||||
assume_verified = strategy&.security&.assume_email_is_verified
|
||||
email_is_verified = auth.info.verified || auth.info.verified_email || auth.info.email_verified || assume_verified
|
||||
email = auth.info.verified_email || auth.info.email
|
||||
|
||||
[email, email_is_verified]
|
||||
end
|
||||
|
||||
def user_params_from_auth(email, auth)
|
||||
{
|
||||
|
|
|
@ -32,7 +32,6 @@ module User::PamAuthenticable
|
|||
|
||||
self.email = "#{account.username}@#{find_pam_suffix}" if email.nil? && find_pam_suffix
|
||||
self.confirmed_at = Time.now.utc
|
||||
self.admin = false
|
||||
self.account = account
|
||||
self.external = true
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ class Identity < ApplicationRecord
|
|||
validates :uid, presence: true, uniqueness: { scope: :provider }
|
||||
validates :provider, presence: true
|
||||
|
||||
def self.find_for_oauth(auth)
|
||||
def self.find_for_omniauth(auth)
|
||||
find_or_create_by(uid: auth.uid, provider: auth.provider)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -283,10 +283,10 @@ class Status < ApplicationRecord
|
|||
@emojis = CustomEmoji.from_text(fields.join(' '), account.domain)
|
||||
end
|
||||
|
||||
def reactions(account = nil)
|
||||
def reactions(account_id = nil)
|
||||
grouped_ordered_status_reactions.select(
|
||||
[:name, :custom_emoji_id, 'COUNT(*) as count'].tap do |values|
|
||||
values << value_for_reaction_me_column(account)
|
||||
values << value_for_reaction_me_column(account_id)
|
||||
end
|
||||
).to_a.tap do |records|
|
||||
ActiveRecord::Associations::Preloader.new(records: records, associations: :custom_emoji).call
|
||||
|
@ -488,15 +488,15 @@ class Status < ApplicationRecord
|
|||
)
|
||||
end
|
||||
|
||||
def value_for_reaction_me_column(account)
|
||||
if account.nil?
|
||||
def value_for_reaction_me_column(account_id)
|
||||
if account_id.nil?
|
||||
'FALSE AS me'
|
||||
else
|
||||
<<~SQL.squish
|
||||
EXISTS(
|
||||
SELECT 1
|
||||
FROM status_reactions inner_reactions
|
||||
WHERE inner_reactions.account_id = #{account.id}
|
||||
WHERE inner_reactions.account_id = #{account_id}
|
||||
AND inner_reactions.status_id = status_reactions.status_id
|
||||
AND inner_reactions.name = status_reactions.name
|
||||
AND (
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class TagFeed < PublicFeed
|
||||
LIMIT_PER_MODE = 4
|
||||
LIMIT_PER_MODE = (ENV['MAX_FEED_HASHTAGS'] || 4).to_i
|
||||
|
||||
# @param [Tag] tag
|
||||
# @param [Account] account
|
||||
|
|
|
@ -51,6 +51,8 @@ class User < ApplicationRecord
|
|||
last_sign_in_ip
|
||||
skip_sign_in_token
|
||||
filtered_languages
|
||||
admin
|
||||
moderator
|
||||
)
|
||||
|
||||
include LanguagesHelper
|
||||
|
@ -342,6 +344,16 @@ class User < ApplicationRecord
|
|||
Doorkeeper::AccessToken.by_resource_owner(self).in_batches do |batch|
|
||||
batch.update_all(revoked_at: Time.now.utc)
|
||||
Web::PushSubscription.where(access_token_id: batch).delete_all
|
||||
|
||||
# Revoke each access token for the Streaming API, since `update_all``
|
||||
# doesn't trigger ActiveRecord Callbacks:
|
||||
# TODO: #28793 Combine into a single topic
|
||||
payload = Oj.dump(event: :kill)
|
||||
redis.pipelined do |pipeline|
|
||||
batch.ids.each do |id|
|
||||
pipeline.publish("timeline:access_token:#{id}", payload)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ class InitialStateSerializer < ActiveModel::Serializer
|
|||
|
||||
attributes :meta, :compose, :accounts,
|
||||
:media_attachments, :settings,
|
||||
:max_toot_chars, :poll_limits,
|
||||
:max_toot_chars, :max_feed_hashtags, :poll_limits,
|
||||
:languages, :max_reactions
|
||||
|
||||
attribute :critical_updates_pending, if: -> { object&.role&.can?(:view_devops) && SoftwareUpdate.check_enabled? }
|
||||
|
@ -21,6 +21,10 @@ class InitialStateSerializer < ActiveModel::Serializer
|
|||
StatusReactionValidator::LIMIT
|
||||
end
|
||||
|
||||
def max_feed_hashtags
|
||||
TagFeed::LIMIT_PER_MODE
|
||||
end
|
||||
|
||||
def poll_limits
|
||||
{
|
||||
min_options: PollValidator::MIN_OPTIONS,
|
||||
|
@ -34,8 +38,8 @@ class InitialStateSerializer < ActiveModel::Serializer
|
|||
def meta
|
||||
store = default_meta_store
|
||||
|
||||
if object.current_account
|
||||
store[:me] = object.current_account.id.to_s
|
||||
if object_account
|
||||
store[:me] = object_account.id.to_s
|
||||
store[:unfollow_modal] = object_account_user.setting_unfollow_modal
|
||||
store[:boost_modal] = object_account_user.setting_boost_modal
|
||||
store[:favourite_modal] = object_account_user.setting_favourite_modal
|
||||
|
@ -135,6 +139,10 @@ class InitialStateSerializer < ActiveModel::Serializer
|
|||
}
|
||||
end
|
||||
|
||||
def object_account
|
||||
object.current_account
|
||||
end
|
||||
|
||||
def object_account_user
|
||||
object.current_account.user
|
||||
end
|
||||
|
|
|
@ -158,7 +158,7 @@ class REST::StatusSerializer < ActiveModel::Serializer
|
|||
end
|
||||
|
||||
def reactions
|
||||
object.reactions(current_user&.account)
|
||||
object.reactions(current_user&.account&.id)
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -147,6 +147,7 @@ class DeleteAccountService < BaseService
|
|||
purge_polls!
|
||||
purge_generated_notifications!
|
||||
purge_favourites!
|
||||
purge_status_reactions!
|
||||
purge_bookmarks!
|
||||
purge_feeds!
|
||||
purge_other_associations!
|
||||
|
@ -193,6 +194,15 @@ class DeleteAccountService < BaseService
|
|||
end
|
||||
end
|
||||
|
||||
def purge_status_reactions!
|
||||
@account.status_reactions.in_batches do |status_reactions|
|
||||
ids = status_reactions.pluck(:status_id)
|
||||
Chewy.strategy.current.update(StatusesIndex, ids) if Chewy.enabled?
|
||||
Rails.cache.delete_multi(ids.map { |id| "statuses/#{id}" })
|
||||
status_reactions.delete_all
|
||||
end
|
||||
end
|
||||
|
||||
def purge_bookmarks!
|
||||
@account.bookmarks.in_batches do |bookmarks|
|
||||
Chewy.strategy.current.update(StatusesIndex, bookmarks.pluck(:status_id)) if Chewy.enabled?
|
||||
|
|
|
@ -44,7 +44,7 @@ class FetchResourceService < BaseService
|
|||
@response_code = response.code
|
||||
return nil if response.code != 200
|
||||
|
||||
if ['application/activity+json', 'application/ld+json'].include?(response.mime_type)
|
||||
if valid_activitypub_content_type?(response)
|
||||
body = response.body_with_limit
|
||||
json = body_to_json(body)
|
||||
|
||||
|
|
|
@ -8,9 +8,7 @@
|
|||
%td.email-inner-card-td.email-prose
|
||||
%p= t @resource.approved? ? 'devise.mailer.confirmation_instructions.explanation' : 'devise.mailer.confirmation_instructions.explanation_when_pending', host: site_hostname
|
||||
- if @resource.created_by_application
|
||||
= render 'application/mailer/button', text: t('settings.account_settings'), url: edit_user_registration_url
|
||||
= link_to confirmation_url(@resource, confirmation_token: @token, redirect_to_app: 'true') do
|
||||
%span= t 'devise.mailer.confirmation_instructions.action_with_app', app: @resource.created_by_application.name
|
||||
= render 'application/mailer/button', text: t('devise.mailer.confirmation_instructions.action_with_app', app: @resource.created_by_application.name), url: confirmation_url(@resource, confirmation_token: @token, redirect_to_app: 'true')
|
||||
- else
|
||||
= render 'application/mailer/button', text: t('devise.mailer.confirmation_instructions.action'), url: confirmation_url(@resource, confirmation_token: @token)
|
||||
%p= t 'devise.mailer.confirmation_instructions.extra_html', terms_path: about_more_url, policy_path: privacy_policy_url
|
||||
|
|
|
@ -21,9 +21,14 @@ Doorkeeper.configure do
|
|||
user unless user&.otp_required_for_login?
|
||||
end
|
||||
|
||||
# If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below.
|
||||
# Doorkeeper provides some administrative interfaces for managing OAuth
|
||||
# Applications, allowing creation, edit, and deletion of applications from the
|
||||
# server. At present, these administrative routes are not integrated into
|
||||
# Mastodon, and as such, we've disabled them by always return a 403 forbidden
|
||||
# response for them. This does not affect the ability for users to manage
|
||||
# their own OAuth Applications.
|
||||
admin_authenticator do
|
||||
current_user&.admin? || redirect_to(new_user_session_url)
|
||||
head 403
|
||||
end
|
||||
|
||||
# Authorization Code expiration time (default 10 minutes).
|
||||
|
|
|
@ -26,6 +26,7 @@ Sidekiq.configure_server do |config|
|
|||
'queue' => 'scheduler',
|
||||
},
|
||||
}
|
||||
SidekiqScheduler::Scheduler.instance.reload_schedule!
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ en:
|
|||
last_attempt: You have one more attempt before your account is locked.
|
||||
locked: Your account is locked.
|
||||
not_found_in_database: Invalid %{authentication_keys} or password.
|
||||
omniauth_user_creation_failure: Error creating an account for this identity.
|
||||
pending: Your account is still under review.
|
||||
timeout: Your session expired. Please login again to continue.
|
||||
unauthenticated: You need to login or sign up before continuing.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'sidekiq_unique_jobs/web'
|
||||
require 'sidekiq_unique_jobs/web' if ENV['ENABLE_SIDEKIQ_UNIQUE_JOBS_UI'] == true
|
||||
require 'sidekiq-scheduler/web'
|
||||
|
||||
class RedirectWithVary < ActionDispatch::Routing::PathRedirect
|
||||
|
|
|
@ -56,7 +56,7 @@ services:
|
|||
|
||||
web:
|
||||
build: .
|
||||
image: ghcr.io/mastodon/mastodon:v4.2.0
|
||||
image: ghcr.io/mastodon/mastodon:v4.2.7
|
||||
restart: always
|
||||
env_file: .env.production
|
||||
command: bundle exec puma -C config/puma.rb
|
||||
|
@ -77,7 +77,7 @@ services:
|
|||
|
||||
streaming:
|
||||
build: .
|
||||
image: ghcr.io/mastodon/mastodon:v4.2.0
|
||||
image: ghcr.io/mastodon/mastodon:v4.2.7
|
||||
restart: always
|
||||
env_file: .env.production
|
||||
command: node ./streaming
|
||||
|
@ -95,7 +95,7 @@ services:
|
|||
|
||||
sidekiq:
|
||||
build: .
|
||||
image: ghcr.io/mastodon/mastodon:v4.2.0
|
||||
image: ghcr.io/mastodon/mastodon:v4.2.7
|
||||
restart: always
|
||||
env_file: .env.production
|
||||
command: bundle exec sidekiq
|
||||
|
|
|
@ -17,7 +17,7 @@ module Mastodon
|
|||
end
|
||||
|
||||
def default_prerelease
|
||||
'alpha.1'
|
||||
'alpha.3'
|
||||
end
|
||||
|
||||
def prerelease
|
||||
|
@ -29,7 +29,7 @@ module Mastodon
|
|||
end
|
||||
|
||||
def catstodon_revision
|
||||
'1.0.15'
|
||||
'1.0.0'
|
||||
end
|
||||
|
||||
def nyastodon_revision
|
||||
|
|
11
lib/tasks/sidekiq_unique_jobs.rake
Normal file
11
lib/tasks/sidekiq_unique_jobs.rake
Normal file
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
namespace :sidekiq_unique_jobs do
|
||||
task delete_all_locks: :environment do
|
||||
digests = SidekiqUniqueJobs::Digests.new
|
||||
digests.delete_by_pattern('*', count: digests.count)
|
||||
|
||||
expiring_digests = SidekiqUniqueJobs::ExpiringDigests.new
|
||||
expiring_digests.delete_by_pattern('*', count: expiring_digests.count)
|
||||
end
|
||||
end
|
|
@ -56,15 +56,15 @@ describe JsonLdHelper do
|
|||
describe '#fetch_resource' do
|
||||
context 'when the second argument is false' do
|
||||
it 'returns resource even if the retrieved ID and the given URI does not match' do
|
||||
stub_request(:get, 'https://bob.test/').to_return body: '{"id": "https://alice.test/"}'
|
||||
stub_request(:get, 'https://alice.test/').to_return body: '{"id": "https://alice.test/"}'
|
||||
stub_request(:get, 'https://bob.test/').to_return(body: '{"id": "https://alice.test/"}', headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, 'https://alice.test/').to_return(body: '{"id": "https://alice.test/"}', headers: { 'Content-Type': 'application/activity+json' })
|
||||
|
||||
expect(fetch_resource('https://bob.test/', false)).to eq({ 'id' => 'https://alice.test/' })
|
||||
end
|
||||
|
||||
it 'returns nil if the object identified by the given URI and the object identified by the retrieved ID does not match' do
|
||||
stub_request(:get, 'https://mallory.test/').to_return body: '{"id": "https://marvin.test/"}'
|
||||
stub_request(:get, 'https://marvin.test/').to_return body: '{"id": "https://alice.test/"}'
|
||||
stub_request(:get, 'https://mallory.test/').to_return(body: '{"id": "https://marvin.test/"}', headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, 'https://marvin.test/').to_return(body: '{"id": "https://alice.test/"}', headers: { 'Content-Type': 'application/activity+json' })
|
||||
|
||||
expect(fetch_resource('https://mallory.test/', false)).to be_nil
|
||||
end
|
||||
|
@ -72,7 +72,7 @@ describe JsonLdHelper do
|
|||
|
||||
context 'when the second argument is true' do
|
||||
it 'returns nil if the retrieved ID and the given URI does not match' do
|
||||
stub_request(:get, 'https://mallory.test/').to_return body: '{"id": "https://alice.test/"}'
|
||||
stub_request(:get, 'https://mallory.test/').to_return(body: '{"id": "https://alice.test/"}', headers: { 'Content-Type': 'application/activity+json' })
|
||||
expect(fetch_resource('https://mallory.test/', true)).to be_nil
|
||||
end
|
||||
end
|
||||
|
@ -80,12 +80,12 @@ describe JsonLdHelper do
|
|||
|
||||
describe '#fetch_resource_without_id_validation' do
|
||||
it 'returns nil if the status code is not 200' do
|
||||
stub_request(:get, 'https://host.test/').to_return status: 400, body: '{}'
|
||||
stub_request(:get, 'https://host.test/').to_return(status: 400, body: '{}', headers: { 'Content-Type': 'application/activity+json' })
|
||||
expect(fetch_resource_without_id_validation('https://host.test/')).to be_nil
|
||||
end
|
||||
|
||||
it 'returns hash' do
|
||||
stub_request(:get, 'https://host.test/').to_return status: 200, body: '{}'
|
||||
stub_request(:get, 'https://host.test/').to_return(status: 200, body: '{}', headers: { 'Content-Type': 'application/activity+json' })
|
||||
expect(fetch_resource_without_id_validation('https://host.test/')).to eq({})
|
||||
end
|
||||
end
|
||||
|
|
|
@ -35,7 +35,7 @@ RSpec.describe ActivityPub::Activity::Announce do
|
|||
context 'when sender is followed by a local account' do
|
||||
before do
|
||||
Fabricate(:account).follow!(sender)
|
||||
stub_request(:get, 'https://example.com/actor/hello-world').to_return(body: Oj.dump(unknown_object_json))
|
||||
stub_request(:get, 'https://example.com/actor/hello-world').to_return(body: Oj.dump(unknown_object_json), headers: { 'Content-Type': 'application/activity+json' })
|
||||
subject.perform
|
||||
end
|
||||
|
||||
|
@ -120,7 +120,7 @@ RSpec.describe ActivityPub::Activity::Announce do
|
|||
let(:object_json) { 'https://example.com/actor/hello-world' }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/actor/hello-world').to_return(body: Oj.dump(unknown_object_json))
|
||||
stub_request(:get, 'https://example.com/actor/hello-world').to_return(body: Oj.dump(unknown_object_json), headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
context 'when the relay is enabled' do
|
||||
|
|
|
@ -3,19 +3,19 @@
|
|||
require 'rails_helper'
|
||||
|
||||
RSpec.describe Identity do
|
||||
describe '.find_for_oauth' do
|
||||
describe '.find_for_omniauth' do
|
||||
let(:auth) { Fabricate(:identity, user: Fabricate(:user)) }
|
||||
|
||||
it 'calls .find_or_create_by' do
|
||||
allow(described_class).to receive(:find_or_create_by)
|
||||
|
||||
described_class.find_for_oauth(auth)
|
||||
described_class.find_for_omniauth(auth)
|
||||
|
||||
expect(described_class).to have_received(:find_or_create_by).with(uid: auth.uid, provider: auth.provider)
|
||||
end
|
||||
|
||||
it 'returns an instance of Identity' do
|
||||
expect(described_class.find_for_oauth(auth)).to be_instance_of described_class
|
||||
expect(described_class.find_for_omniauth(auth)).to be_instance_of described_class
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -438,7 +438,10 @@ RSpec.describe User do
|
|||
let!(:access_token) { Fabricate(:access_token, resource_owner_id: user.id) }
|
||||
let!(:web_push_subscription) { Fabricate(:web_push_subscription, access_token: access_token) }
|
||||
|
||||
let(:redis_pipeline_stub) { instance_double(Redis::Namespace, publish: nil) }
|
||||
|
||||
before do
|
||||
allow(redis).to receive(:pipelined).and_yield(redis_pipeline_stub)
|
||||
user.reset_password!
|
||||
end
|
||||
|
||||
|
@ -455,6 +458,10 @@ RSpec.describe User do
|
|||
expect(Doorkeeper::AccessToken.active_for(user).count).to eq 0
|
||||
end
|
||||
|
||||
it 'revokes streaming access for all access tokens' do
|
||||
expect(redis_pipeline_stub).to have_received(:publish).with("timeline:access_token:#{access_token.id}", Oj.dump(event: :kill)).once
|
||||
end
|
||||
|
||||
it 'removes push subscriptions' do
|
||||
expect(Web::PushSubscription.where(user: user).or(Web::PushSubscription.where(access_token: access_token)).count).to eq 0
|
||||
expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
||||
|
|
83
spec/requests/disabled_oauth_endpoints_spec.rb
Normal file
83
spec/requests/disabled_oauth_endpoints_spec.rb
Normal file
|
@ -0,0 +1,83 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'Disabled OAuth routes' do
|
||||
# These routes are disabled via the doorkeeper configuration for
|
||||
# `admin_authenticator`, as these routes should only be accessible by server
|
||||
# administrators. For now, these routes are not properly designed and
|
||||
# integrated into Mastodon, so we're disabling them completely
|
||||
describe 'GET /oauth/applications' do
|
||||
it 'returns 403 forbidden' do
|
||||
get oauth_applications_path
|
||||
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST /oauth/applications' do
|
||||
it 'returns 403 forbidden' do
|
||||
post oauth_applications_path
|
||||
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /oauth/applications/new' do
|
||||
it 'returns 403 forbidden' do
|
||||
get new_oauth_application_path
|
||||
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /oauth/applications/:id' do
|
||||
let(:application) { Fabricate(:application, scopes: 'read') }
|
||||
|
||||
it 'returns 403 forbidden' do
|
||||
get oauth_application_path(application)
|
||||
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PATCH /oauth/applications/:id' do
|
||||
let(:application) { Fabricate(:application, scopes: 'read') }
|
||||
|
||||
it 'returns 403 forbidden' do
|
||||
patch oauth_application_path(application)
|
||||
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'PUT /oauth/applications/:id' do
|
||||
let(:application) { Fabricate(:application, scopes: 'read') }
|
||||
|
||||
it 'returns 403 forbidden' do
|
||||
put oauth_application_path(application)
|
||||
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'DELETE /oauth/applications/:id' do
|
||||
let(:application) { Fabricate(:application, scopes: 'read') }
|
||||
|
||||
it 'returns 403 forbidden' do
|
||||
delete oauth_application_path(application)
|
||||
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET /oauth/applications/:id/edit' do
|
||||
let(:application) { Fabricate(:application, scopes: 'read') }
|
||||
|
||||
it 'returns 403 forbidden' do
|
||||
get edit_oauth_application_path(application)
|
||||
|
||||
expect(response).to have_http_status(403)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -39,6 +39,13 @@ describe 'OmniAuth callbacks' do
|
|||
Fabricate(:user, email: 'user@host.example')
|
||||
end
|
||||
|
||||
context 'when ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH is set to true' do
|
||||
around do |example|
|
||||
ClimateControl.modify ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH: 'true' do
|
||||
example.run
|
||||
end
|
||||
end
|
||||
|
||||
it 'matches the existing user, creates an identity, and redirects to root path' do
|
||||
expect { subject }
|
||||
.to not_change(User, :count)
|
||||
|
@ -52,6 +59,18 @@ describe 'OmniAuth callbacks' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH is not set to true' do
|
||||
it 'does not match the existing user or create an identity, and redirects to login page' do
|
||||
expect { subject }
|
||||
.to not_change(User, :count)
|
||||
.and not_change(Identity, :count)
|
||||
.and not_change(LoginActivity, :count)
|
||||
|
||||
expect(response).to redirect_to(new_user_session_url)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a matching user and a matching identity' do
|
||||
before do
|
||||
user = Fabricate(:user, email: 'user@host.example')
|
||||
|
@ -96,7 +115,7 @@ describe 'OmniAuth callbacks' do
|
|||
|
||||
context 'when a user cannot be built' do
|
||||
before do
|
||||
allow(User).to receive(:find_for_oauth).and_return(User.new)
|
||||
allow(User).to receive(:find_for_omniauth).and_return(User.new)
|
||||
end
|
||||
|
||||
it 'redirects to the new user signup page' do
|
||||
|
|
|
@ -72,11 +72,11 @@ RSpec.describe ActivityPub::FetchFeaturedCollectionService, type: :service do
|
|||
|
||||
shared_examples 'sets pinned posts' do
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/account/pinned/known').to_return(status: 200, body: Oj.dump(status_json_pinned_known))
|
||||
stub_request(:get, 'https://example.com/account/pinned/unknown-inlined').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_inlined))
|
||||
stub_request(:get, 'https://example.com/account/pinned/known').to_return(status: 200, body: Oj.dump(status_json_pinned_known), headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, 'https://example.com/account/pinned/unknown-inlined').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_inlined), headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, 'https://example.com/account/pinned/unknown-unreachable').to_return(status: 404)
|
||||
stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_reachable))
|
||||
stub_request(:get, 'https://example.com/account/collections/featured').to_return(status: 200, body: Oj.dump(featured_with_null))
|
||||
stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_reachable), headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, 'https://example.com/account/collections/featured').to_return(status: 200, body: Oj.dump(featured_with_null), headers: { 'Content-Type': 'application/activity+json' })
|
||||
|
||||
subject.call(actor, note: true, hashtag: false)
|
||||
end
|
||||
|
@ -94,7 +94,7 @@ RSpec.describe ActivityPub::FetchFeaturedCollectionService, type: :service do
|
|||
describe '#call' do
|
||||
context 'when the endpoint is a Collection' do
|
||||
before do
|
||||
stub_request(:get, actor.featured_collection_url).to_return(status: 200, body: Oj.dump(payload))
|
||||
stub_request(:get, actor.featured_collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it_behaves_like 'sets pinned posts'
|
||||
|
@ -111,7 +111,7 @@ RSpec.describe ActivityPub::FetchFeaturedCollectionService, type: :service do
|
|||
end
|
||||
|
||||
before do
|
||||
stub_request(:get, actor.featured_collection_url).to_return(status: 200, body: Oj.dump(payload))
|
||||
stub_request(:get, actor.featured_collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it_behaves_like 'sets pinned posts'
|
||||
|
@ -120,7 +120,7 @@ RSpec.describe ActivityPub::FetchFeaturedCollectionService, type: :service do
|
|||
let(:items) { 'https://example.com/account/pinned/unknown-reachable' }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_reachable))
|
||||
stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_reachable), headers: { 'Content-Type': 'application/activity+json' })
|
||||
subject.call(actor, note: true, hashtag: false)
|
||||
end
|
||||
|
||||
|
@ -147,7 +147,7 @@ RSpec.describe ActivityPub::FetchFeaturedCollectionService, type: :service do
|
|||
end
|
||||
|
||||
before do
|
||||
stub_request(:get, actor.featured_collection_url).to_return(status: 200, body: Oj.dump(payload))
|
||||
stub_request(:get, actor.featured_collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it_behaves_like 'sets pinned posts'
|
||||
|
@ -156,7 +156,7 @@ RSpec.describe ActivityPub::FetchFeaturedCollectionService, type: :service do
|
|||
let(:items) { 'https://example.com/account/pinned/unknown-reachable' }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_reachable))
|
||||
stub_request(:get, 'https://example.com/account/pinned/unknown-reachable').to_return(status: 200, body: Oj.dump(status_json_pinned_unknown_reachable), headers: { 'Content-Type': 'application/activity+json' })
|
||||
subject.call(actor, note: true, hashtag: false)
|
||||
end
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ RSpec.describe ActivityPub::FetchFeaturedTagsCollectionService, type: :service d
|
|||
describe '#call' do
|
||||
context 'when the endpoint is a Collection' do
|
||||
before do
|
||||
stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload))
|
||||
stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it_behaves_like 'sets featured tags'
|
||||
|
@ -46,7 +46,7 @@ RSpec.describe ActivityPub::FetchFeaturedTagsCollectionService, type: :service d
|
|||
|
||||
context 'when the account already has featured tags' do
|
||||
before do
|
||||
stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload))
|
||||
stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
|
||||
|
||||
actor.featured_tags.create!(name: 'FoO')
|
||||
actor.featured_tags.create!(name: 'baz')
|
||||
|
@ -67,7 +67,7 @@ RSpec.describe ActivityPub::FetchFeaturedTagsCollectionService, type: :service d
|
|||
end
|
||||
|
||||
before do
|
||||
stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload))
|
||||
stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it_behaves_like 'sets featured tags'
|
||||
|
@ -88,7 +88,7 @@ RSpec.describe ActivityPub::FetchFeaturedTagsCollectionService, type: :service d
|
|||
end
|
||||
|
||||
before do
|
||||
stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload))
|
||||
stub_request(:get, collection_url).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it_behaves_like 'sets featured tags'
|
||||
|
|
|
@ -44,7 +44,7 @@ RSpec.describe ActivityPub::FetchRemoteAccountService, type: :service do
|
|||
before do
|
||||
actor[:inbox] = nil
|
||||
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
||||
|
@ -67,7 +67,7 @@ RSpec.describe ActivityPub::FetchRemoteAccountService, type: :service do
|
|||
let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice' }] } }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
||||
|
@ -93,7 +93,7 @@ RSpec.describe ActivityPub::FetchRemoteAccountService, type: :service do
|
|||
let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/alice' }] } }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
@ -125,7 +125,7 @@ RSpec.describe ActivityPub::FetchRemoteAccountService, type: :service do
|
|||
let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/bob' }] } }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
||||
|
@ -148,7 +148,7 @@ RSpec.describe ActivityPub::FetchRemoteAccountService, type: :service do
|
|||
let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/bob' }] } }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
|
|
@ -44,7 +44,7 @@ RSpec.describe ActivityPub::FetchRemoteActorService, type: :service do
|
|||
before do
|
||||
actor[:inbox] = nil
|
||||
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
||||
|
@ -67,7 +67,7 @@ RSpec.describe ActivityPub::FetchRemoteActorService, type: :service do
|
|||
let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice' }] } }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
||||
|
@ -93,7 +93,7 @@ RSpec.describe ActivityPub::FetchRemoteActorService, type: :service do
|
|||
let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/alice' }] } }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
@ -125,7 +125,7 @@ RSpec.describe ActivityPub::FetchRemoteActorService, type: :service do
|
|||
let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/bob' }] } }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
||||
|
@ -148,7 +148,7 @@ RSpec.describe ActivityPub::FetchRemoteActorService, type: :service do
|
|||
let!(:webfinger) { { subject: 'acct:alice@iscool.af', links: [{ rel: 'self', href: 'https://example.com/bob' }] } }
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
stub_request(:get, 'https://iscool.af/.well-known/webfinger?resource=acct:alice@iscool.af').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
|
|
@ -50,7 +50,7 @@ RSpec.describe ActivityPub::FetchRemoteKeyService, type: :service do
|
|||
end
|
||||
|
||||
before do
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor))
|
||||
stub_request(:get, 'https://example.com/alice').to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' })
|
||||
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
|
||||
end
|
||||
|
||||
|
@ -59,7 +59,7 @@ RSpec.describe ActivityPub::FetchRemoteKeyService, type: :service do
|
|||
|
||||
context 'when the key is a sub-object from the actor' do
|
||||
before do
|
||||
stub_request(:get, public_key_id).to_return(body: Oj.dump(actor))
|
||||
stub_request(:get, public_key_id).to_return(body: Oj.dump(actor), headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it 'returns the expected account' do
|
||||
|
@ -71,7 +71,7 @@ RSpec.describe ActivityPub::FetchRemoteKeyService, type: :service do
|
|||
let(:public_key_id) { 'https://example.com/alice-public-key.json' }
|
||||
|
||||
before do
|
||||
stub_request(:get, public_key_id).to_return(body: Oj.dump(key_json.merge({ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'] })))
|
||||
stub_request(:get, public_key_id).to_return(body: Oj.dump(key_json.merge({ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'] })), headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it 'returns the expected account' do
|
||||
|
@ -84,7 +84,7 @@ RSpec.describe ActivityPub::FetchRemoteKeyService, type: :service do
|
|||
let(:actor_public_key) { 'https://example.com/alice-public-key.json' }
|
||||
|
||||
before do
|
||||
stub_request(:get, public_key_id).to_return(body: Oj.dump(key_json.merge({ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'] })))
|
||||
stub_request(:get, public_key_id).to_return(body: Oj.dump(key_json.merge({ '@context': ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1'] })), headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it 'returns the nil' do
|
||||
|
|
|
@ -58,7 +58,7 @@ RSpec.describe ActivityPub::FetchRepliesService, type: :service do
|
|||
|
||||
context 'when passing the URL to the collection' do
|
||||
before do
|
||||
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload))
|
||||
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it 'spawns workers for up to 5 replies on the same server' do
|
||||
|
@ -93,7 +93,7 @@ RSpec.describe ActivityPub::FetchRepliesService, type: :service do
|
|||
|
||||
context 'when passing the URL to the collection' do
|
||||
before do
|
||||
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload))
|
||||
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it 'spawns workers for up to 5 replies on the same server' do
|
||||
|
@ -132,7 +132,7 @@ RSpec.describe ActivityPub::FetchRepliesService, type: :service do
|
|||
|
||||
context 'when passing the URL to the collection' do
|
||||
before do
|
||||
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload))
|
||||
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it 'spawns workers for up to 5 replies on the same server' do
|
||||
|
|
|
@ -60,7 +60,7 @@ RSpec.describe ActivityPub::SynchronizeFollowersService, type: :service do
|
|||
describe '#call' do
|
||||
context 'when the endpoint is a Collection of actor URIs' do
|
||||
before do
|
||||
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload))
|
||||
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it_behaves_like 'synchronizes followers'
|
||||
|
@ -77,7 +77,7 @@ RSpec.describe ActivityPub::SynchronizeFollowersService, type: :service do
|
|||
end
|
||||
|
||||
before do
|
||||
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload))
|
||||
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it_behaves_like 'synchronizes followers'
|
||||
|
@ -98,7 +98,7 @@ RSpec.describe ActivityPub::SynchronizeFollowersService, type: :service do
|
|||
end
|
||||
|
||||
before do
|
||||
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload))
|
||||
stub_request(:get, collection_uri).to_return(status: 200, body: Oj.dump(payload), headers: { 'Content-Type': 'application/activity+json' })
|
||||
end
|
||||
|
||||
it_behaves_like 'synchronizes followers'
|
||||
|
|
|
@ -21,7 +21,7 @@ describe ActivityPub::FetchRepliesWorker do
|
|||
|
||||
describe 'perform' do
|
||||
it 'performs a request if the collection URI is from the same host' do
|
||||
stub_request(:get, 'https://example.com/statuses_replies/1').to_return(status: 200, body: json)
|
||||
stub_request(:get, 'https://example.com/statuses_replies/1').to_return(status: 200, body: json, headers: { 'Content-Type': 'application/activity+json' })
|
||||
subject.perform(status.id, 'https://example.com/statuses_replies/1')
|
||||
expect(a_request(:get, 'https://example.com/statuses_replies/1')).to have_been_made.once
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue