forked from treehouse/mastodon
Merge branch 'master' into glitch-soc/merge-upstream
Conflicts: - app/controllers/statuses_controller.rb - app/controllers/stream_entries_controller.rbsignup-info-prompt
commit
34b8346e7f
|
@ -1,3 +1,6 @@
|
||||||
|
require:
|
||||||
|
- rubocop-rails
|
||||||
|
|
||||||
AllCops:
|
AllCops:
|
||||||
TargetRubyVersion: 2.3
|
TargetRubyVersion: 2.3
|
||||||
Exclude:
|
Exclude:
|
||||||
|
@ -82,6 +85,9 @@ Rails/Exit:
|
||||||
- 'lib/mastodon/*'
|
- 'lib/mastodon/*'
|
||||||
- 'lib/cli.rb'
|
- 'lib/cli.rb'
|
||||||
|
|
||||||
|
Rails/HelperInstanceVariable:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
Style/ClassAndModuleChildren:
|
Style/ClassAndModuleChildren:
|
||||||
Enabled: false
|
Enabled: false
|
||||||
|
|
||||||
|
|
281
.sass-lint.yml
281
.sass-lint.yml
|
@ -4,261 +4,34 @@
|
||||||
files:
|
files:
|
||||||
include: app/javascript/styles/**/*.scss
|
include: app/javascript/styles/**/*.scss
|
||||||
ignore:
|
ignore:
|
||||||
- app/javascript/styles/reset.scss
|
- app/javascript/styles/mastodon/reset.scss
|
||||||
|
|
||||||
linters:
|
rules:
|
||||||
# Reports when you use improper spacing around ! (the "bang") in !default,
|
# Disallows
|
||||||
# !global, !important, and !optional flags.
|
no-color-literals: 0
|
||||||
BangFormat:
|
no-css-comments: 0
|
||||||
enabled: false
|
no-duplicate-properties: 0
|
||||||
|
no-ids: 0
|
||||||
|
no-important: 0
|
||||||
|
no-mergeable-selectors: 0
|
||||||
|
no-misspelled-properties: 0
|
||||||
|
no-qualifying-elements: 0
|
||||||
|
no-transition-all: 0
|
||||||
|
no-vendor-prefixes: 0
|
||||||
|
|
||||||
# Whether or not to prefer `border: 0` over `border: none`.
|
# Nesting
|
||||||
BorderZero:
|
force-element-nesting: 0
|
||||||
enabled: false
|
force-attribute-nesting: 0
|
||||||
|
force-pseudo-nesting: 0
|
||||||
|
|
||||||
# Reports when you define a rule set using a selector with chained classes
|
# Name Formats
|
||||||
# (a.k.a. adjoining classes).
|
class-name-format: 0
|
||||||
ChainedClasses:
|
leading-zero: 0
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Prefer hexadecimal color codes over color keywords.
|
# Style Guide
|
||||||
# (e.g. `color: green` is a color keyword)
|
attribute-quotes: 0
|
||||||
ColorKeyword:
|
hex-length: 0
|
||||||
enabled: false
|
indentation: 0
|
||||||
|
nesting-depth: 0
|
||||||
# Prefer color literals (keywords or hexadecimal codes) to be used only in
|
property-sort-order: 0
|
||||||
# variable declarations. They should be referred to via variables everywhere
|
quotes: 0
|
||||||
# else.
|
|
||||||
ColorVariable:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Which form of comments to prefer in CSS.
|
|
||||||
Comment:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Reports @debug statements (which you probably left behind accidentally).
|
|
||||||
DebugStatement:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Rule sets should be ordered as follows:
|
|
||||||
# - @extend declarations
|
|
||||||
# - @include declarations without inner @content
|
|
||||||
# - properties, @include declarations with inner @content
|
|
||||||
# - nested rule sets.
|
|
||||||
DeclarationOrder:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# `scss-lint:disable` control comments should be preceded by a comment
|
|
||||||
# explaining why these linters are being disabled for this file.
|
|
||||||
# See https://github.com/brigade/scss-lint#disabling-linters-via-source for
|
|
||||||
# more information.
|
|
||||||
DisableLinterReason:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Reports when you define the same property twice in a single rule set.
|
|
||||||
DuplicateProperty:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Separate rule, function, and mixin declarations with empty lines.
|
|
||||||
EmptyLineBetweenBlocks:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Reports when you have an empty rule set.
|
|
||||||
EmptyRule:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Reports when you have an @extend directive.
|
|
||||||
ExtendDirective:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Files should always have a final newline. This results in better diffs
|
|
||||||
# when adding lines to the file, since SCM systems such as git won't
|
|
||||||
# think that you touched the last line.
|
|
||||||
FinalNewline:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# HEX colors should use three-character values where possible.
|
|
||||||
HexLength:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# HEX color values should use lower-case colors to differentiate between
|
|
||||||
# letters and numbers, e.g. `#E3E3E3` vs. `#e3e3e3`.
|
|
||||||
HexNotation:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Avoid using ID selectors.
|
|
||||||
IdSelector:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# The basenames of @imported SCSS partials should not begin with an
|
|
||||||
# underscore and should not include the filename extension.
|
|
||||||
ImportPath:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Avoid using !important in properties. It is usually indicative of a
|
|
||||||
# misunderstanding of CSS specificity and can lead to brittle code.
|
|
||||||
ImportantRule:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Indentation should always be done in increments of 2 spaces.
|
|
||||||
Indentation:
|
|
||||||
enabled: true
|
|
||||||
width: 2
|
|
||||||
|
|
||||||
# Don't write leading zeros for numeric values with a decimal point.
|
|
||||||
LeadingZero:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Reports when you define the same selector twice in a single sheet.
|
|
||||||
MergeableSelector:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Functions, mixins, variables, and placeholders should be declared
|
|
||||||
# with all lowercase letters and hyphens instead of underscores.
|
|
||||||
NameFormat:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Avoid nesting selectors too deeply.
|
|
||||||
NestingDepth:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Always use placeholder selectors in @extend.
|
|
||||||
PlaceholderInExtend:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Sort properties in a strict order.
|
|
||||||
PropertySortOrder:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Reports when you use an unknown or disabled CSS property
|
|
||||||
# (ignoring vendor-prefixed properties).
|
|
||||||
PropertySpelling:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Configure which units are allowed for property values.
|
|
||||||
PropertyUnits:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Pseudo-elements, like ::before, and ::first-letter, should be declared
|
|
||||||
# with two colons. Pseudo-classes, like :hover and :first-child, should
|
|
||||||
# be declared with one colon.
|
|
||||||
PseudoElement:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Avoid qualifying elements in selectors (also known as "tag-qualifying").
|
|
||||||
QualifyingElement:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Don't write selectors with a depth of applicability greater than 3.
|
|
||||||
SelectorDepth:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Selectors should always use hyphenated-lowercase, rather than camelCase or
|
|
||||||
# snake_case.
|
|
||||||
SelectorFormat:
|
|
||||||
enabled: false
|
|
||||||
convention: hyphenated_lowercase
|
|
||||||
|
|
||||||
# Prefer the shortest shorthand form possible for properties that support it.
|
|
||||||
Shorthand:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Each property should have its own line, except in the special case of
|
|
||||||
# single line rulesets.
|
|
||||||
SingleLinePerProperty:
|
|
||||||
enabled: true
|
|
||||||
allow_single_line_rule_sets: true
|
|
||||||
|
|
||||||
# Split selectors onto separate lines after each comma, and have each
|
|
||||||
# individual selector occupy a single line.
|
|
||||||
SingleLinePerSelector:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Commas in lists should be followed by a space.
|
|
||||||
SpaceAfterComma:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Properties should be formatted with a single space separating the colon
|
|
||||||
# from the property's value.
|
|
||||||
SpaceAfterPropertyColon:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Properties should be formatted with no space between the name and the
|
|
||||||
# colon.
|
|
||||||
SpaceAfterPropertyName:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Variables should be formatted with a single space separating the colon
|
|
||||||
# from the variable's value.
|
|
||||||
SpaceAfterVariableColon:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Variables should be formatted with no space between the name and the
|
|
||||||
# colon.
|
|
||||||
SpaceAfterVariableName:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Operators should be formatted with a single space on both sides of an
|
|
||||||
# infix operator.
|
|
||||||
SpaceAroundOperator:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Opening braces should be preceded by a single space.
|
|
||||||
SpaceBeforeBrace:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Parentheses should not be padded with spaces.
|
|
||||||
SpaceBetweenParens:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Enforces that string literals should be written with a consistent form
|
|
||||||
# of quotes (single or double).
|
|
||||||
StringQuotes:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Property values, @extend, @include, and @import directives, and variable
|
|
||||||
# declarations should always end with a semicolon.
|
|
||||||
TrailingSemicolon:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Reports lines containing trailing whitespace.
|
|
||||||
TrailingWhitespace:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Don't write trailing zeros for numeric values with a decimal point.
|
|
||||||
TrailingZero:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Don't use the `all` keyword to specify transition properties.
|
|
||||||
TransitionAll:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Numeric values should not contain unnecessary fractional portions.
|
|
||||||
UnnecessaryMantissa:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Do not use parent selector references (&) when they would otherwise
|
|
||||||
# be unnecessary.
|
|
||||||
UnnecessaryParentReference:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# URLs should be valid and not contain protocols or domain names.
|
|
||||||
UrlFormat:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# URLs should always be enclosed within quotes.
|
|
||||||
UrlQuotes:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
# Properties, like color and font, are easier to read and maintain
|
|
||||||
# when defined using variables rather than literals.
|
|
||||||
VariableForProperty:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Avoid vendor prefixes. Or rather: don't write them yourself.
|
|
||||||
VendorPrefix:
|
|
||||||
enabled: false
|
|
||||||
|
|
||||||
# Omit length units on zero values, e.g. `0px` vs. `0`.
|
|
||||||
ZeroUnit:
|
|
||||||
enabled: true
|
|
||||||
|
|
1
Gemfile
1
Gemfile
|
@ -132,6 +132,7 @@ group :development do
|
||||||
gem 'letter_opener_web', '~> 1.3'
|
gem 'letter_opener_web', '~> 1.3'
|
||||||
gem 'memory_profiler'
|
gem 'memory_profiler'
|
||||||
gem 'rubocop', '~> 0.71', require: false
|
gem 'rubocop', '~> 0.71', require: false
|
||||||
|
gem 'rubocop-rails', '~> 2.0', require: false
|
||||||
gem 'brakeman', '~> 4.5', require: false
|
gem 'brakeman', '~> 4.5', require: false
|
||||||
gem 'bundler-audit', '~> 0.6', require: false
|
gem 'bundler-audit', '~> 0.6', require: false
|
||||||
|
|
||||||
|
|
|
@ -534,6 +534,9 @@ GEM
|
||||||
rainbow (>= 2.2.2, < 4.0)
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
ruby-progressbar (~> 1.7)
|
ruby-progressbar (~> 1.7)
|
||||||
unicode-display_width (>= 1.4.0, < 1.7)
|
unicode-display_width (>= 1.4.0, < 1.7)
|
||||||
|
rubocop-rails (2.0.0)
|
||||||
|
rack (>= 2.0)
|
||||||
|
rubocop (>= 0.70.0)
|
||||||
ruby-progressbar (1.10.1)
|
ruby-progressbar (1.10.1)
|
||||||
ruby-saml (1.9.0)
|
ruby-saml (1.9.0)
|
||||||
nokogiri (>= 1.5.10)
|
nokogiri (>= 1.5.10)
|
||||||
|
@ -740,6 +743,7 @@ DEPENDENCIES
|
||||||
rspec-rails (~> 3.8)
|
rspec-rails (~> 3.8)
|
||||||
rspec-sidekiq (~> 3.0)
|
rspec-sidekiq (~> 3.0)
|
||||||
rubocop (~> 0.71)
|
rubocop (~> 0.71)
|
||||||
|
rubocop-rails (~> 2.0)
|
||||||
sanitize (~> 5.0)
|
sanitize (~> 5.0)
|
||||||
sidekiq (~> 5.2)
|
sidekiq (~> 5.2)
|
||||||
sidekiq-bulk (~> 0.2.0)
|
sidekiq-bulk (~> 0.2.0)
|
||||||
|
|
|
@ -47,8 +47,6 @@ class AccountsController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
format.json do
|
format.json do
|
||||||
mark_cacheable!
|
|
||||||
|
|
||||||
render_cached_json(['activitypub', 'actor', @account], content_type: 'application/activity+json') do
|
render_cached_json(['activitypub', 'actor', @account], content_type: 'application/activity+json') do
|
||||||
ActiveModelSerializers::SerializableResource.new(@account, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter)
|
ActiveModelSerializers::SerializableResource.new(@account, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter)
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,8 +9,6 @@ class ActivityPub::CollectionsController < Api::BaseController
|
||||||
before_action :set_cache_headers
|
before_action :set_cache_headers
|
||||||
|
|
||||||
def show
|
def show
|
||||||
skip_session!
|
|
||||||
|
|
||||||
render_cached_json(['activitypub', 'collection', @account, params[:id]], content_type: 'application/activity+json') do
|
render_cached_json(['activitypub', 'collection', @account, params[:id]], content_type: 'application/activity+json') do
|
||||||
ActiveModelSerializers::SerializableResource.new(
|
ActiveModelSerializers::SerializableResource.new(
|
||||||
collection_presenter,
|
collection_presenter,
|
||||||
|
|
|
@ -10,10 +10,7 @@ class ActivityPub::OutboxesController < Api::BaseController
|
||||||
before_action :set_cache_headers
|
before_action :set_cache_headers
|
||||||
|
|
||||||
def show
|
def show
|
||||||
unless page_requested?
|
expires_in 1.minute, public: true unless page_requested?
|
||||||
skip_session!
|
|
||||||
expires_in 1.minute, public: true
|
|
||||||
end
|
|
||||||
|
|
||||||
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
render json: outbox_presenter, serializer: ActivityPub::OutboxSerializer, adapter: ActivityPub::Adapter, content_type: 'application/activity+json'
|
||||||
end
|
end
|
||||||
|
|
|
@ -48,13 +48,13 @@ module Admin
|
||||||
def approve
|
def approve
|
||||||
authorize @account.user, :approve?
|
authorize @account.user, :approve?
|
||||||
@account.user.approve!
|
@account.user.approve!
|
||||||
redirect_to admin_accounts_path(pending: '1')
|
redirect_to admin_pending_accounts_path
|
||||||
end
|
end
|
||||||
|
|
||||||
def reject
|
def reject
|
||||||
authorize @account.user, :reject?
|
authorize @account.user, :reject?
|
||||||
SuspendAccountService.new.call(@account, including_user: true, destroy: true, skip_distribution: true)
|
SuspendAccountService.new.call(@account, including_user: true, destroy: true, skip_distribution: true)
|
||||||
redirect_to admin_accounts_path(pending: '1')
|
redirect_to admin_pending_accounts_path
|
||||||
end
|
end
|
||||||
|
|
||||||
def unsilence
|
def unsilence
|
||||||
|
|
|
@ -228,11 +228,6 @@ class ApplicationController < ActionController::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def mark_cacheable!
|
def mark_cacheable!
|
||||||
skip_session!
|
|
||||||
expires_in 0, public: true
|
expires_in 0, public: true
|
||||||
end
|
end
|
||||||
|
|
||||||
def skip_session!
|
|
||||||
request.session_options[:skip] = true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -70,7 +70,6 @@ module AccountControllerConcern
|
||||||
|
|
||||||
def check_account_suspension
|
def check_account_suspension
|
||||||
if @account.suspended?
|
if @account.suspended?
|
||||||
skip_session!
|
|
||||||
expires_in(3.minutes, public: true)
|
expires_in(3.minutes, public: true)
|
||||||
gone
|
gone
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,7 +4,6 @@ class CustomCssController < ApplicationController
|
||||||
before_action :set_cache_headers
|
before_action :set_cache_headers
|
||||||
|
|
||||||
def show
|
def show
|
||||||
skip_session!
|
|
||||||
render plain: Setting.custom_css || '', content_type: 'text/css'
|
render plain: Setting.custom_css || '', content_type: 'text/css'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,8 +7,6 @@ class EmojisController < ApplicationController
|
||||||
def show
|
def show
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.json do
|
format.json do
|
||||||
skip_session!
|
|
||||||
|
|
||||||
render_cached_json(['activitypub', 'emoji', @emoji], content_type: 'application/activity+json') do
|
render_cached_json(['activitypub', 'emoji', @emoji], content_type: 'application/activity+json') do
|
||||||
ActiveModelSerializers::SerializableResource.new(@emoji, serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter)
|
ActiveModelSerializers::SerializableResource.new(@emoji, serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter)
|
||||||
end
|
end
|
||||||
|
|
|
@ -20,10 +20,7 @@ class FollowerAccountsController < ApplicationController
|
||||||
format.json do
|
format.json do
|
||||||
raise Mastodon::NotPermittedError if params[:page].present? && @account.user_hides_network?
|
raise Mastodon::NotPermittedError if params[:page].present? && @account.user_hides_network?
|
||||||
|
|
||||||
if params[:page].blank?
|
expires_in 3.minutes, public: true if params[:page].blank?
|
||||||
skip_session!
|
|
||||||
expires_in 3.minutes, public: true
|
|
||||||
end
|
|
||||||
|
|
||||||
render json: collection_presenter,
|
render json: collection_presenter,
|
||||||
serializer: ActivityPub::CollectionSerializer,
|
serializer: ActivityPub::CollectionSerializer,
|
||||||
|
|
|
@ -20,10 +20,7 @@ class FollowingAccountsController < ApplicationController
|
||||||
format.json do
|
format.json do
|
||||||
raise Mastodon::NotPermittedError if params[:page].present? && @account.user_hides_network?
|
raise Mastodon::NotPermittedError if params[:page].present? && @account.user_hides_network?
|
||||||
|
|
||||||
if params[:page].blank?
|
expires_in 3.minutes, public: true if params[:page].blank?
|
||||||
skip_session!
|
|
||||||
expires_in 3.minutes, public: true
|
|
||||||
end
|
|
||||||
|
|
||||||
render json: collection_presenter,
|
render json: collection_presenter,
|
||||||
serializer: ActivityPub::CollectionSerializer,
|
serializer: ActivityPub::CollectionSerializer,
|
||||||
|
|
|
@ -29,10 +29,7 @@ class StatusesController < ApplicationController
|
||||||
format.html do
|
format.html do
|
||||||
use_pack 'public'
|
use_pack 'public'
|
||||||
|
|
||||||
unless user_signed_in?
|
expires_in 10.seconds, public: true if current_account.nil?
|
||||||
skip_session!
|
|
||||||
expires_in 10.seconds, public: true
|
|
||||||
end
|
|
||||||
|
|
||||||
@body_classes = 'with-modals'
|
@body_classes = 'with-modals'
|
||||||
|
|
||||||
|
@ -43,8 +40,6 @@ class StatusesController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
format.json do
|
format.json do
|
||||||
mark_cacheable! unless @stream_entry.hidden?
|
|
||||||
|
|
||||||
render_cached_json(['activitypub', 'note', @status], content_type: 'application/activity+json', public: !@stream_entry.hidden?) do
|
render_cached_json(['activitypub', 'note', @status], content_type: 'application/activity+json', public: !@stream_entry.hidden?) do
|
||||||
ActiveModelSerializers::SerializableResource.new(@status, serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter)
|
ActiveModelSerializers::SerializableResource.new(@status, serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter)
|
||||||
end
|
end
|
||||||
|
@ -53,8 +48,6 @@ class StatusesController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def activity
|
def activity
|
||||||
skip_session!
|
|
||||||
|
|
||||||
render_cached_json(['activitypub', 'activity', @status], content_type: 'application/activity+json', public: !@stream_entry.hidden?) do
|
render_cached_json(['activitypub', 'activity', @status], content_type: 'application/activity+json', public: !@stream_entry.hidden?) do
|
||||||
ActiveModelSerializers::SerializableResource.new(@status, serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter)
|
ActiveModelSerializers::SerializableResource.new(@status, serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter)
|
||||||
end
|
end
|
||||||
|
@ -64,7 +57,6 @@ class StatusesController < ApplicationController
|
||||||
use_pack 'embed'
|
use_pack 'embed'
|
||||||
raise ActiveRecord::RecordNotFound if @status.hidden?
|
raise ActiveRecord::RecordNotFound if @status.hidden?
|
||||||
|
|
||||||
skip_session!
|
|
||||||
expires_in 180, public: true
|
expires_in 180, public: true
|
||||||
response.headers['X-Frame-Options'] = 'ALLOWALL'
|
response.headers['X-Frame-Options'] = 'ALLOWALL'
|
||||||
@autoplay = ActiveModel::Type::Boolean.new.cast(params[:autoplay])
|
@autoplay = ActiveModel::Type::Boolean.new.cast(params[:autoplay])
|
||||||
|
@ -73,8 +65,6 @@ class StatusesController < ApplicationController
|
||||||
end
|
end
|
||||||
|
|
||||||
def replies
|
def replies
|
||||||
skip_session!
|
|
||||||
|
|
||||||
render json: replies_collection_presenter,
|
render json: replies_collection_presenter,
|
||||||
serializer: ActivityPub::CollectionSerializer,
|
serializer: ActivityPub::CollectionSerializer,
|
||||||
adapter: ActivityPub::Adapter,
|
adapter: ActivityPub::Adapter,
|
||||||
|
|
|
@ -17,19 +17,13 @@ class StreamEntriesController < ApplicationController
|
||||||
format.html do
|
format.html do
|
||||||
use_pack 'public'
|
use_pack 'public'
|
||||||
|
|
||||||
unless user_signed_in?
|
expires_in 5.minutes, public: true unless @stream_entry.hidden?
|
||||||
skip_session!
|
|
||||||
expires_in 5.minutes, public: true
|
|
||||||
end
|
|
||||||
|
|
||||||
redirect_to short_account_status_url(params[:account_username], @stream_entry.activity) if @type == 'status'
|
redirect_to short_account_status_url(params[:account_username], @stream_entry.activity)
|
||||||
end
|
end
|
||||||
|
|
||||||
format.atom do
|
format.atom do
|
||||||
unless @stream_entry.hidden?
|
expires_in 3.minutes, public: true unless @stream_entry.hidden?
|
||||||
skip_session!
|
|
||||||
expires_in 3.minutes, public: true
|
|
||||||
end
|
|
||||||
|
|
||||||
render xml: OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.entry(@stream_entry, true))
|
render xml: OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.entry(@stream_entry, true))
|
||||||
end
|
end
|
||||||
|
@ -57,7 +51,7 @@ class StreamEntriesController < ApplicationController
|
||||||
|
|
||||||
def set_stream_entry
|
def set_stream_entry
|
||||||
@stream_entry = @account.stream_entries.where(activity_type: 'Status').find(params[:id])
|
@stream_entry = @account.stream_entries.where(activity_type: 'Status').find(params[:id])
|
||||||
@type = @stream_entry.activity_type.downcase
|
@type = 'status'
|
||||||
|
|
||||||
raise ActiveRecord::RecordNotFound if @stream_entry.activity.nil?
|
raise ActiveRecord::RecordNotFound if @stream_entry.activity.nil?
|
||||||
authorize @stream_entry.activity, :show? if @stream_entry.hidden? || @stream_entry.local_only?
|
authorize @stream_entry.activity, :show? if @stream_entry.hidden? || @stream_entry.local_only?
|
||||||
|
|
|
@ -138,8 +138,11 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||||
this.setState({ suggestionsHidden: true, focused: false });
|
this.setState({ suggestionsHidden: true, focused: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
onFocus = () => {
|
onFocus = (e) => {
|
||||||
this.setState({ focused: true });
|
this.setState({ focused: true });
|
||||||
|
if (this.props.onFocus) {
|
||||||
|
this.props.onFocus(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onSuggestionClick = (e) => {
|
onSuggestionClick = (e) => {
|
||||||
|
|
|
@ -34,6 +34,10 @@ const messages = defineMessages({
|
||||||
export default @injectIntl
|
export default @injectIntl
|
||||||
class ComposeForm extends ImmutablePureComponent {
|
class ComposeForm extends ImmutablePureComponent {
|
||||||
|
|
||||||
|
setRef = c => {
|
||||||
|
this.composeForm = c;
|
||||||
|
};
|
||||||
|
|
||||||
static contextTypes = {
|
static contextTypes = {
|
||||||
router: PropTypes.object,
|
router: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
@ -115,6 +119,10 @@ class ComposeForm extends ImmutablePureComponent {
|
||||||
this.props.onChangeSpoilerText(e.target.value);
|
this.props.onChangeSpoilerText(e.target.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleFocus = () => {
|
||||||
|
this.composeForm.scrollIntoView();
|
||||||
|
}
|
||||||
|
|
||||||
componentDidUpdate (prevProps) {
|
componentDidUpdate (prevProps) {
|
||||||
// This statement does several things:
|
// This statement does several things:
|
||||||
// - If we're beginning a reply, and,
|
// - If we're beginning a reply, and,
|
||||||
|
@ -178,7 +186,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='compose-form'>
|
<div className='compose-form' ref={this.setRef}>
|
||||||
<WarningContainer />
|
<WarningContainer />
|
||||||
|
|
||||||
<ReplyIndicatorContainer />
|
<ReplyIndicatorContainer />
|
||||||
|
@ -201,7 +209,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='emoji-picker-wrapper'>
|
<div className={`emoji-picker-wrapper ${this.props.showSearch ? 'emoji-picker-wrapper--hidden' : ''}`}>
|
||||||
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
|
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -212,6 +220,7 @@ class ComposeForm extends ImmutablePureComponent {
|
||||||
value={this.props.text}
|
value={this.props.text}
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
suggestions={this.props.suggestions}
|
suggestions={this.props.suggestions}
|
||||||
|
onFocus={this.handleFocus}
|
||||||
onKeyDown={this.handleKeyDown}
|
onKeyDown={this.handleKeyDown}
|
||||||
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
|
||||||
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
|
||||||
|
|
|
@ -21,7 +21,7 @@ class SearchPopout extends React.PureComponent {
|
||||||
const { style } = this.props;
|
const { style } = this.props;
|
||||||
const extraInformation = searchEnabled ? <FormattedMessage id='search_popout.tips.full_text' defaultMessage='Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.' /> : <FormattedMessage id='search_popout.tips.text' defaultMessage='Simple text returns matching display names, usernames and hashtags' />;
|
const extraInformation = searchEnabled ? <FormattedMessage id='search_popout.tips.full_text' defaultMessage='Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.' /> : <FormattedMessage id='search_popout.tips.text' defaultMessage='Simple text returns matching display names, usernames and hashtags' />;
|
||||||
return (
|
return (
|
||||||
<div style={{ ...style, position: 'absolute', width: 285 }}>
|
<div style={{ ...style, position: 'absolute', width: 285, zIndex: 2 }}>
|
||||||
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
<Motion defaultStyle={{ opacity: 0, scaleX: 0.85, scaleY: 0.75 }} style={{ opacity: spring(1, { damping: 35, stiffness: 400 }), scaleX: spring(1, { damping: 35, stiffness: 400 }), scaleY: spring(1, { damping: 35, stiffness: 400 }) }}>
|
||||||
{({ opacity, scaleX, scaleY }) => (
|
{({ opacity, scaleX, scaleY }) => (
|
||||||
<div className='search-popout' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
|
<div className='search-popout' style={{ opacity: opacity, transform: `scale(${scaleX}, ${scaleY})` }}>
|
||||||
|
|
|
@ -75,6 +75,23 @@ class ListTimeline extends React.PureComponent {
|
||||||
this.disconnect = dispatch(connectListStream(id));
|
this.disconnect = dispatch(connectListStream(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps (nextProps) {
|
||||||
|
const { dispatch } = this.props;
|
||||||
|
const { id } = nextProps.params;
|
||||||
|
|
||||||
|
if (id !== this.props.params.id) {
|
||||||
|
if (this.disconnect) {
|
||||||
|
this.disconnect();
|
||||||
|
this.disconnect = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(fetchList(id));
|
||||||
|
dispatch(expandListTimeline(id));
|
||||||
|
|
||||||
|
this.disconnect = dispatch(connectListStream(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentWillUnmount () {
|
componentWillUnmount () {
|
||||||
if (this.disconnect) {
|
if (this.disconnect) {
|
||||||
this.disconnect();
|
this.disconnect();
|
||||||
|
|
|
@ -338,6 +338,7 @@ export default function compose(state = initialState, action) {
|
||||||
map.set('focusDate', new Date());
|
map.set('focusDate', new Date());
|
||||||
map.set('caretPosition', null);
|
map.set('caretPosition', null);
|
||||||
map.set('idempotencyKey', uuid());
|
map.set('idempotencyKey', uuid());
|
||||||
|
map.set('sensitive', action.status.get('sensitive'));
|
||||||
|
|
||||||
if (action.status.get('spoiler_text').length > 0) {
|
if (action.status.get('spoiler_text').length > 0) {
|
||||||
map.set('spoiler', true);
|
map.set('spoiler', true);
|
||||||
|
|
|
@ -35,14 +35,12 @@ const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, is
|
||||||
|
|
||||||
if (!next && !isLoadingRecent) mMap.set('hasMore', false);
|
if (!next && !isLoadingRecent) mMap.set('hasMore', false);
|
||||||
|
|
||||||
if (!statuses.isEmpty()) {
|
if (timeline.endsWith(':pinned')) {
|
||||||
|
mMap.set('items', statuses.map(status => status.get('id')));
|
||||||
|
} else if (!statuses.isEmpty()) {
|
||||||
mMap.update('items', ImmutableList(), oldIds => {
|
mMap.update('items', ImmutableList(), oldIds => {
|
||||||
const newIds = statuses.map(status => status.get('id'));
|
const newIds = statuses.map(status => status.get('id'));
|
||||||
|
|
||||||
if (timeline.indexOf(':pinned') !== -1) {
|
|
||||||
return newIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
const lastIndex = oldIds.findLastIndex(id => id !== null && compareId(id, newIds.last()) >= 0) + 1;
|
const lastIndex = oldIds.findLastIndex(id => id !== null && compareId(id, newIds.last()) >= 0) + 1;
|
||||||
const firstIndex = oldIds.take(lastIndex).findLastIndex(id => id !== null && compareId(id, newIds.first()) > 0);
|
const firstIndex = oldIds.take(lastIndex).findLastIndex(id => id !== null && compareId(id, newIds.first()) > 0);
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
&-description {
|
&-description {
|
||||||
input {
|
input {
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
opacity: 1.0;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,5 +20,5 @@ $highlight-text-color: $classic-highlight-color !default;
|
||||||
$action-button-color: #8d9ac2;
|
$action-button-color: #8d9ac2;
|
||||||
|
|
||||||
$inverted-text-color: $black !default;
|
$inverted-text-color: $black !default;
|
||||||
$lighter-text-color: darken($ui-base-color,6%) !default;
|
$lighter-text-color: darken($ui-base-color, 6%) !default;
|
||||||
$light-text-color: darken($ui-primary-color, 40%) !default;
|
$light-text-color: darken($ui-primary-color, 40%) !default;
|
||||||
|
|
|
@ -279,6 +279,8 @@ h5 {
|
||||||
}
|
}
|
||||||
|
|
||||||
.hero-with-button {
|
.hero-with-button {
|
||||||
|
padding-bottom: 16px;
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
@ -286,8 +288,6 @@ h5 {
|
||||||
p.lead {
|
p.lead {
|
||||||
margin-bottom: 32px;
|
margin-bottom: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
padding-bottom: 16px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
@mixin avatar-radius() {
|
@mixin avatar-radius {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: transparent no-repeat;
|
background: transparent no-repeat;
|
||||||
background-position: 50%;
|
background-position: 50%;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin avatar-size($size:48px) {
|
@mixin avatar-size($size: 48px) {
|
||||||
width: $size;
|
width: $size;
|
||||||
height: $size;
|
height: $size;
|
||||||
background-size: $size $size;
|
background-size: $size $size;
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin search-input() {
|
@mixin search-input {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: none;
|
border: 0;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
background: $ui-base-color;
|
background: $ui-base-color;
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@mixin search-popout() {
|
@mixin search-popout {
|
||||||
background: $simple-background-color;
|
background: $simple-background-color;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 10px 14px;
|
padding: 10px 14px;
|
||||||
|
|
|
@ -171,7 +171,7 @@ $content-width: 840px;
|
||||||
text-transform: none;
|
text-transform: none;
|
||||||
padding-bottom: 0;
|
padding-bottom: 0;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
border-bottom: none;
|
border-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > p {
|
& > p {
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
@if type-of($color) == 'color' {
|
@if type-of($color) == 'color' {
|
||||||
$color: str-slice(ie-hex-str($color), 4);
|
$color: str-slice(ie-hex-str($color), 4);
|
||||||
}
|
}
|
||||||
@return '%23' + unquote($color)
|
|
||||||
|
@return '%23' + unquote($color);
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
@ -15,7 +16,7 @@ body {
|
||||||
text-rendering: optimizelegibility;
|
text-rendering: optimizelegibility;
|
||||||
font-feature-settings: "kern";
|
font-feature-settings: "kern";
|
||||||
text-size-adjust: none;
|
text-size-adjust: none;
|
||||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
|
||||||
&.system-font {
|
&.system-font {
|
||||||
|
|
|
@ -128,7 +128,7 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
color: $action-button-color;
|
color: $action-button-color;
|
||||||
border: none;
|
border: 0;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: color 100ms ease-in;
|
transition: color 100ms ease-in;
|
||||||
|
@ -196,7 +196,7 @@
|
||||||
|
|
||||||
.text-icon-button {
|
.text-icon-button {
|
||||||
color: $lighter-text-color;
|
color: $lighter-text-color;
|
||||||
border: none;
|
border: 0;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
@ -353,12 +353,12 @@
|
||||||
.spoiler-input {
|
.spoiler-input {
|
||||||
height: 0;
|
height: 0;
|
||||||
transform-origin: bottom;
|
transform-origin: bottom;
|
||||||
opacity: 0.0;
|
opacity: 0;
|
||||||
|
|
||||||
&.spoiler-input--visible {
|
&.spoiler-input--visible {
|
||||||
height: 36px;
|
height: 36px;
|
||||||
margin-bottom: 11px;
|
margin-bottom: 11px;
|
||||||
opacity: 1.0;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,12 +408,20 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.emoji-picker-wrapper,
|
|
||||||
.autosuggest-textarea__suggestions-wrapper {
|
.autosuggest-textarea__suggestions-wrapper {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.emoji-picker-wrapper {
|
||||||
|
position: relative;
|
||||||
|
height: 0;
|
||||||
|
|
||||||
|
&.emoji-picker-wrapper--hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.autosuggest-textarea__suggestions {
|
.autosuggest-textarea__suggestions {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -1185,7 +1193,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.account__avatar {
|
.account__avatar {
|
||||||
@include avatar-radius();
|
@include avatar-radius;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&-inline {
|
&-inline {
|
||||||
|
@ -1195,11 +1203,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&-composite {
|
&-composite {
|
||||||
@include avatar-radius();
|
@include avatar-radius;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
@include avatar-radius();
|
@include avatar-radius;
|
||||||
float: left;
|
float: left;
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
@ -1215,12 +1223,12 @@ a .account__avatar {
|
||||||
@include avatar-size(48px);
|
@include avatar-size(48px);
|
||||||
|
|
||||||
&-base {
|
&-base {
|
||||||
@include avatar-radius();
|
@include avatar-radius;
|
||||||
@include avatar-size(36px);
|
@include avatar-size(36px);
|
||||||
}
|
}
|
||||||
|
|
||||||
&-overlay {
|
&-overlay {
|
||||||
@include avatar-radius();
|
@include avatar-radius;
|
||||||
@include avatar-size(24px);
|
@include avatar-size(24px);
|
||||||
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -1598,13 +1606,13 @@ a.account__display-name {
|
||||||
.icon-button.close {
|
.icon-button.close {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transform: scale(0.0, 1.0) translate(-100%, 0);
|
transform: scale(0, 1) translate(-100%, 0);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.compose__action-bar .icon-button {
|
.compose__action-bar .icon-button {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
transform: scale(1.0, 1.0) translate(0, 0);
|
transform: scale(1, 1) translate(0, 0);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2071,6 +2079,10 @@ a.account__display-name {
|
||||||
|
|
||||||
.account {
|
.account {
|
||||||
padding: 15px 10px;
|
padding: 15px 10px;
|
||||||
|
|
||||||
|
&__header__bio {
|
||||||
|
margin: 0 -10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification {
|
.notification {
|
||||||
|
@ -2699,7 +2711,7 @@ a.account__display-name {
|
||||||
.setting-text {
|
.setting-text {
|
||||||
color: $darker-text-color;
|
color: $darker-text-color;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
border: none;
|
border: 0;
|
||||||
border-bottom: 2px solid $ui-primary-color;
|
border-bottom: 2px solid $ui-primary-color;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -3037,7 +3049,7 @@ a.status-card.compact:hover {
|
||||||
|
|
||||||
& > button {
|
& > button {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
border: none;
|
border: 0;
|
||||||
padding: 15px 0 15px 15px;
|
padding: 15px 0 15px 15px;
|
||||||
color: inherit;
|
color: inherit;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
@ -3202,11 +3214,11 @@ a.status-card.compact:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-reduce-motion .loading-indicator span {
|
.no-reduce-motion .loading-indicator span {
|
||||||
animation: loader-label 1.15s infinite cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
animation: loader-label 1.15s infinite cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-reduce-motion .loading-indicator__figure {
|
.no-reduce-motion .loading-indicator__figure {
|
||||||
animation: loader-figure 1.15s infinite cubic-bezier(0.215, 0.610, 0.355, 1.000);
|
animation: loader-figure 1.15s infinite cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes loader-figure {
|
@keyframes loader-figure {
|
||||||
|
@ -3373,7 +3385,7 @@ a.status-card.compact:hover {
|
||||||
|
|
||||||
.column-select {
|
.column-select {
|
||||||
&__control {
|
&__control {
|
||||||
@include search-input();
|
@include search-input;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__placeholder {
|
&__placeholder {
|
||||||
|
@ -3424,7 +3436,7 @@ a.status-card.compact:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
&__menu {
|
&__menu {
|
||||||
@include search-popout();
|
@include search-popout;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: $ui-secondary-color;
|
background: $ui-secondary-color;
|
||||||
}
|
}
|
||||||
|
@ -3585,7 +3597,7 @@ a.status-card.compact:hover {
|
||||||
|
|
||||||
.no-reduce-motion .shake-bottom {
|
.no-reduce-motion .shake-bottom {
|
||||||
transform-origin: 50% 100%;
|
transform-origin: 50% 100%;
|
||||||
animation: shake-bottom 0.8s cubic-bezier(0.455, 0.030, 0.515, 0.955) 2s 2 both;
|
animation: shake-bottom 0.8s cubic-bezier(0.455, 0.03, 0.515, 0.955) 2s 2 both;
|
||||||
}
|
}
|
||||||
|
|
||||||
.emoji-picker-dropdown__menu {
|
.emoji-picker-dropdown__menu {
|
||||||
|
@ -3880,10 +3892,11 @@ a.status-card.compact:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
.search__input {
|
.search__input {
|
||||||
|
@include search-input;
|
||||||
|
|
||||||
display: block;
|
display: block;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
padding-right: 30px;
|
padding-right: 30px;
|
||||||
@include search-input();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.search__icon {
|
.search__icon {
|
||||||
|
@ -4491,14 +4504,14 @@ a.status-card.compact:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
.actions-modal {
|
.actions-modal {
|
||||||
|
max-height: 80vh;
|
||||||
|
max-width: 80vw;
|
||||||
|
|
||||||
.status {
|
.status {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
max-height: 300px;
|
max-height: 300px;
|
||||||
}
|
}
|
||||||
|
|
||||||
max-height: 80vh;
|
|
||||||
max-width: 80vw;
|
|
||||||
|
|
||||||
.actions-modal__item-label {
|
.actions-modal__item-label {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
@ -4713,7 +4726,7 @@ a.status-card.compact:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
.media-gallery__item {
|
.media-gallery__item {
|
||||||
border: none;
|
border: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: block;
|
display: block;
|
||||||
float: left;
|
float: left;
|
||||||
|
@ -5173,7 +5186,7 @@ a.status-card.compact:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
.account-gallery__item {
|
.account-gallery__item {
|
||||||
border: none;
|
border: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: block;
|
display: block;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -5247,7 +5260,7 @@ a.status-card.compact:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-popout {
|
.search-popout {
|
||||||
@include search-popout();
|
@include search-popout;
|
||||||
}
|
}
|
||||||
|
|
||||||
noscript {
|
noscript {
|
||||||
|
@ -5349,14 +5362,14 @@ noscript {
|
||||||
.icon-button.close {
|
.icon-button.close {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: scale(1.0, 1.0) translate(0, 0);
|
transform: scale(1, 1) translate(0, 0);
|
||||||
bottom: 5px;
|
bottom: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.compose__action-bar .icon-button {
|
.compose__action-bar .icon-button {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scale(0.0, 1.0) translate(100%, 0);
|
transform: scale(0, 1) translate(100%, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5386,7 +5399,7 @@ noscript {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: none;
|
border: 0;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
font-family: $font-monospace, monospace;
|
font-family: $font-monospace, monospace;
|
||||||
background: $ui-base-color;
|
background: $ui-base-color;
|
||||||
|
|
|
@ -121,7 +121,7 @@
|
||||||
grid-auto-rows: max-content;
|
grid-auto-rows: max-content;
|
||||||
|
|
||||||
.column-0 {
|
.column-0 {
|
||||||
grid-column: 1/3;
|
grid-column: 1 / 3;
|
||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.column-3 {
|
.column-3 {
|
||||||
grid-column: 1/3;
|
grid-column: 1 / 3;
|
||||||
grid-row: 3;
|
grid-row: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
.emoji-mart {
|
.emoji-mart {
|
||||||
|
font-size: 13px;
|
||||||
|
display: inline-block;
|
||||||
|
color: $inverted-text-color;
|
||||||
|
|
||||||
&,
|
&,
|
||||||
* {
|
* {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
line-height: 1.15;
|
line-height: 1.15;
|
||||||
}
|
}
|
||||||
|
|
||||||
font-size: 13px;
|
|
||||||
display: inline-block;
|
|
||||||
color: $inverted-text-color;
|
|
||||||
|
|
||||||
.emoji-mart-emoji {
|
.emoji-mart-emoji {
|
||||||
padding: 6px;
|
padding: 6px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -553,7 +553,7 @@ code {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: none;
|
border: 0;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
font-family: $font-monospace, monospace;
|
font-family: $font-monospace, monospace;
|
||||||
background: $ui-base-color;
|
background: $ui-base-color;
|
||||||
|
|
|
@ -47,7 +47,6 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: $inverted-text-color;
|
color: $inverted-text-color;
|
||||||
display: block;
|
|
||||||
outline: 0;
|
outline: 0;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
background: $simple-background-color;
|
background: $simple-background-color;
|
||||||
|
|
|
@ -180,7 +180,6 @@ body.rtl {
|
||||||
}
|
}
|
||||||
|
|
||||||
.fa-ul {
|
.fa-ul {
|
||||||
margin-left: 0;
|
|
||||||
margin-left: 2.14285714em;
|
margin-left: 2.14285714em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -143,7 +143,7 @@ class ActivityPub::Activity
|
||||||
|
|
||||||
# If the boosted toot is embedded and it is a self-boost, handle it like a Create
|
# If the boosted toot is embedded and it is a self-boost, handle it like a Create
|
||||||
unless unsupported_object_type?
|
unless unsupported_object_type?
|
||||||
actor_id = value_or_id(first_of_value(@object['attributedTo'])) || @account.uri
|
actor_id = value_or_id(first_of_value(@object['attributedTo']))
|
||||||
|
|
||||||
if actor_id == @account.uri
|
if actor_id == @account.uri
|
||||||
return ActivityPub::Activity.factory({ 'type' => 'Create', 'actor' => actor_id, 'object' => @object }, @account).perform
|
return ActivityPub::Activity.factory({ 'type' => 'Create', 'actor' => actor_id, 'object' => @object }, @account).perform
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ActivityPub::Activity::Follow < ActivityPub::Activity
|
class ActivityPub::Activity::Follow < ActivityPub::Activity
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
target_account = account_from_uri(object_uri)
|
target_account = account_from_uri(object_uri)
|
||||||
|
|
||||||
|
@ -28,7 +30,7 @@ class ActivityPub::Activity::Follow < ActivityPub::Activity
|
||||||
end
|
end
|
||||||
|
|
||||||
def reject_follow_request!(target_account)
|
def reject_follow_request!(target_account)
|
||||||
json = ActiveModelSerializers::SerializableResource.new(FollowRequest.new(account: @account, target_account: target_account, uri: @json['id']), serializer: ActivityPub::RejectFollowSerializer, adapter: ActivityPub::Adapter).to_json
|
json = Oj.dump(serialize_payload(FollowRequest.new(account: @account, target_account: target_account, uri: @json['id']), ActivityPub::RejectFollowSerializer))
|
||||||
ActivityPub::DeliveryWorker.perform_async(json, target_account.id, @account.inbox_url)
|
ActivityPub::DeliveryWorker.perform_async(json, target_account.id, @account.inbox_url)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -208,6 +208,10 @@ class Account < ApplicationRecord
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sign?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
def keypair
|
def keypair
|
||||||
@keypair ||= OpenSSL::PKey::RSA.new(private_key || public_key)
|
@keypair ||= OpenSSL::PKey::RSA.new(private_key || public_key)
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
class Form::AccountBatch
|
class Form::AccountBatch
|
||||||
include ActiveModel::Model
|
include ActiveModel::Model
|
||||||
include Authorization
|
include Authorization
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
attr_accessor :account_ids, :action, :current_account
|
attr_accessor :account_ids, :action, :current_account
|
||||||
|
|
||||||
|
@ -54,13 +55,7 @@ class Form::AccountBatch
|
||||||
|
|
||||||
return unless follow.account.activitypub?
|
return unless follow.account.activitypub?
|
||||||
|
|
||||||
json = ActiveModelSerializers::SerializableResource.new(
|
ActivityPub::DeliveryWorker.perform_async(Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)), current_account.id, follow.account.inbox_url)
|
||||||
follow,
|
|
||||||
serializer: ActivityPub::RejectFollowSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).to_json
|
|
||||||
|
|
||||||
ActivityPub::DeliveryWorker.perform_async(json, current_account.id, follow.account.inbox_url)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def approve!
|
def approve!
|
||||||
|
|
|
@ -211,6 +211,8 @@ class Status < ApplicationRecord
|
||||||
public_visibility? || unlisted_visibility?
|
public_visibility? || unlisted_visibility?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias sign? distributable?
|
||||||
|
|
||||||
def with_media?
|
def with_media?
|
||||||
media_attachments.any?
|
media_attachments.any?
|
||||||
end
|
end
|
||||||
|
@ -529,7 +531,7 @@ class Status < ApplicationRecord
|
||||||
return if direct_visibility?
|
return if direct_visibility?
|
||||||
|
|
||||||
account&.increment_count!(:statuses_count)
|
account&.increment_count!(:statuses_count)
|
||||||
reblog&.increment_count!(:reblogs_count) if reblog? && (public_visibility? || unlisted_visibility?)
|
reblog&.increment_count!(:reblogs_count) if reblog?
|
||||||
thread&.increment_count!(:replies_count) if in_reply_to_id.present? && (public_visibility? || unlisted_visibility?)
|
thread&.increment_count!(:replies_count) if in_reply_to_id.present? && (public_visibility? || unlisted_visibility?)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -537,7 +539,7 @@ class Status < ApplicationRecord
|
||||||
return if direct_visibility? || marked_for_mass_destruction?
|
return if direct_visibility? || marked_for_mass_destruction?
|
||||||
|
|
||||||
account&.decrement_count!(:statuses_count)
|
account&.decrement_count!(:statuses_count)
|
||||||
reblog&.decrement_count!(:reblogs_count) if reblog? && (public_visibility? || unlisted_visibility?)
|
reblog&.decrement_count!(:reblogs_count) if reblog?
|
||||||
thread&.decrement_count!(:replies_count) if in_reply_to_id.present? && (public_visibility? || unlisted_visibility?)
|
thread&.decrement_count!(:replies_count) if in_reply_to_id.present? && (public_visibility? || unlisted_visibility?)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class AfterBlockDomainFromAccountService < BaseService
|
class AfterBlockDomainFromAccountService < BaseService
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
# This service does not create an AccountDomainBlock record,
|
# This service does not create an AccountDomainBlock record,
|
||||||
# it's meant to be called after such a record has been created
|
# it's meant to be called after such a record has been created
|
||||||
# synchronously, to "clean up"
|
# synchronously, to "clean up"
|
||||||
|
@ -31,12 +33,6 @@ class AfterBlockDomainFromAccountService < BaseService
|
||||||
|
|
||||||
return unless follow.account.activitypub?
|
return unless follow.account.activitypub?
|
||||||
|
|
||||||
json = ActiveModelSerializers::SerializableResource.new(
|
ActivityPub::DeliveryWorker.perform_async(Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer)), @account.id, follow.account.inbox_url)
|
||||||
follow,
|
|
||||||
serializer: ActivityPub::RejectFollowSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).to_json
|
|
||||||
|
|
||||||
ActivityPub::DeliveryWorker.perform_async(json, @account.id, follow.account.inbox_url)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class AuthorizeFollowService < BaseService
|
class AuthorizeFollowService < BaseService
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
def call(source_account, target_account, **options)
|
def call(source_account, target_account, **options)
|
||||||
if options[:skip_follow_request]
|
if options[:skip_follow_request]
|
||||||
follow_request = FollowRequest.new(account: source_account, target_account: target_account, uri: options[:follow_request_uri])
|
follow_request = FollowRequest.new(account: source_account, target_account: target_account, uri: options[:follow_request_uri])
|
||||||
|
@ -24,11 +26,7 @@ class AuthorizeFollowService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_json(follow_request)
|
def build_json(follow_request)
|
||||||
ActiveModelSerializers::SerializableResource.new(
|
Oj.dump(serialize_payload(follow_request, ActivityPub::AcceptFollowSerializer))
|
||||||
follow_request,
|
|
||||||
serializer: ActivityPub::AcceptFollowSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_xml(follow_request)
|
def build_xml(follow_request)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class BlockService < BaseService
|
class BlockService < BaseService
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
def call(account, target_account)
|
def call(account, target_account)
|
||||||
return if account.id == target_account.id
|
return if account.id == target_account.id
|
||||||
|
|
||||||
|
@ -26,11 +28,7 @@ class BlockService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_json(block)
|
def build_json(block)
|
||||||
ActiveModelSerializers::SerializableResource.new(
|
Oj.dump(serialize_payload(block, ActivityPub::BlockSerializer))
|
||||||
block,
|
|
||||||
serializer: ActivityPub::BlockSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_xml(block)
|
def build_xml(block)
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Payloadable
|
||||||
|
def serialize_payload(record, serializer, options = {})
|
||||||
|
signer = options.delete(:signer)
|
||||||
|
sign_with = options.delete(:sign_with)
|
||||||
|
payload = ActiveModelSerializers::SerializableResource.new(record, options.merge(serializer: serializer, adapter: ActivityPub::Adapter)).as_json
|
||||||
|
|
||||||
|
if (record.respond_to?(:sign?) && record.sign?) && signer && signing_enabled?
|
||||||
|
ActivityPub::LinkedDataSignature.new(payload).sign!(signer, sign_with: sign_with)
|
||||||
|
else
|
||||||
|
payload
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def signing_enabled?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class FavouriteService < BaseService
|
class FavouriteService < BaseService
|
||||||
include Authorization
|
include Authorization
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
# Favourite a status and notify remote user
|
# Favourite a status and notify remote user
|
||||||
# @param [Account] account
|
# @param [Account] account
|
||||||
|
@ -43,11 +44,7 @@ class FavouriteService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_json(favourite)
|
def build_json(favourite)
|
||||||
Oj.dump(ActivityPub::LinkedDataSignature.new(ActiveModelSerializers::SerializableResource.new(
|
Oj.dump(serialize_payload(favourite, ActivityPub::LikeSerializer))
|
||||||
favourite,
|
|
||||||
serializer: ActivityPub::LikeSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).as_json).sign!(favourite.account))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_xml(favourite)
|
def build_xml(favourite)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class FollowService < BaseService
|
class FollowService < BaseService
|
||||||
include Redisable
|
include Redisable
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
# Follow a remote user, notify remote user about the follow
|
# Follow a remote user, notify remote user about the follow
|
||||||
# @param [Account] source_account From which to follow
|
# @param [Account] source_account From which to follow
|
||||||
|
@ -78,10 +79,6 @@ class FollowService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_json(follow_request)
|
def build_json(follow_request)
|
||||||
ActiveModelSerializers::SerializableResource.new(
|
Oj.dump(serialize_payload(follow_request, ActivityPub::FollowSerializer))
|
||||||
follow_request,
|
|
||||||
serializer: ActivityPub::FollowSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).to_json
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class ProcessMentionsService < BaseService
|
class ProcessMentionsService < BaseService
|
||||||
include StreamEntryRenderer
|
include StreamEntryRenderer
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
# Scan status for mentions and fetch remote mentioned users, create
|
# Scan status for mentions and fetch remote mentioned users, create
|
||||||
# local mention pointers, send Salmon notifications to mentioned
|
# local mention pointers, send Salmon notifications to mentioned
|
||||||
|
@ -61,12 +62,7 @@ class ProcessMentionsService < BaseService
|
||||||
|
|
||||||
def activitypub_json
|
def activitypub_json
|
||||||
return @activitypub_json if defined?(@activitypub_json)
|
return @activitypub_json if defined?(@activitypub_json)
|
||||||
payload = ActiveModelSerializers::SerializableResource.new(
|
@activitypub_json = Oj.dump(serialize_payload(@status, ActivityPub::ActivitySerializer, signer: @status.account))
|
||||||
@status,
|
|
||||||
serializer: ActivityPub::ActivitySerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).as_json
|
|
||||||
@activitypub_json = Oj.dump(@status.distributable? ? ActivityPub::LinkedDataSignature.new(payload).sign!(@status.account) : payload)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def resolve_account_service
|
def resolve_account_service
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
class ReblogService < BaseService
|
class ReblogService < BaseService
|
||||||
include Authorization
|
include Authorization
|
||||||
include StreamEntryRenderer
|
include StreamEntryRenderer
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
# Reblog a status and notify its remote author
|
# Reblog a status and notify its remote author
|
||||||
# @param [Account] account Account to reblog from
|
# @param [Account] account Account to reblog from
|
||||||
|
@ -56,10 +57,6 @@ class ReblogService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_json(reblog)
|
def build_json(reblog)
|
||||||
Oj.dump(ActivityPub::LinkedDataSignature.new(ActiveModelSerializers::SerializableResource.new(
|
Oj.dump(serialize_payload(reblog, ActivityPub::ActivitySerializer, signer: reblog.account))
|
||||||
reblog,
|
|
||||||
serializer: ActivityPub::ActivitySerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).as_json).sign!(reblog.account))
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class RejectFollowService < BaseService
|
class RejectFollowService < BaseService
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
def call(source_account, target_account)
|
def call(source_account, target_account)
|
||||||
follow_request = FollowRequest.find_by!(account: source_account, target_account: target_account)
|
follow_request = FollowRequest.find_by!(account: source_account, target_account: target_account)
|
||||||
follow_request.reject!
|
follow_request.reject!
|
||||||
|
@ -19,11 +21,7 @@ class RejectFollowService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_json(follow_request)
|
def build_json(follow_request)
|
||||||
ActiveModelSerializers::SerializableResource.new(
|
Oj.dump(serialize_payload(follow_request, ActivityPub::RejectFollowSerializer))
|
||||||
follow_request,
|
|
||||||
serializer: ActivityPub::RejectFollowSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_xml(follow_request)
|
def build_xml(follow_request)
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
class RemoveStatusService < BaseService
|
class RemoveStatusService < BaseService
|
||||||
include StreamEntryRenderer
|
include StreamEntryRenderer
|
||||||
include Redisable
|
include Redisable
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
def call(status, **options)
|
def call(status, **options)
|
||||||
@payload = Oj.dump(event: :delete, payload: status.id.to_s)
|
@payload = Oj.dump(event: :delete, payload: status.id.to_s)
|
||||||
|
@ -116,15 +117,7 @@ class RemoveStatusService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def signed_activity_json
|
def signed_activity_json
|
||||||
@signed_activity_json ||= Oj.dump(ActivityPub::LinkedDataSignature.new(activity_json).sign!(@account))
|
@signed_activity_json ||= Oj.dump(serialize_payload(@status, @status.reblog? ? ActivityPub::UndoAnnounceSerializer : ActivityPub::DeleteSerializer, signer: @account))
|
||||||
end
|
|
||||||
|
|
||||||
def activity_json
|
|
||||||
@activity_json ||= ActiveModelSerializers::SerializableResource.new(
|
|
||||||
@status,
|
|
||||||
serializer: @status.reblog? ? ActivityPub::UndoAnnounceSerializer : ActivityPub::DeleteSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).as_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_reblogs
|
def remove_reblogs
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ReportService < BaseService
|
class ReportService < BaseService
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
def call(source_account, target_account, options = {})
|
def call(source_account, target_account, options = {})
|
||||||
@source_account = source_account
|
@source_account = source_account
|
||||||
@target_account = target_account
|
@target_account = target_account
|
||||||
|
@ -44,12 +46,7 @@ class ReportService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def payload
|
def payload
|
||||||
Oj.dump(ActiveModelSerializers::SerializableResource.new(
|
Oj.dump(serialize_payload(@report, ActivityPub::FlagSerializer, account: some_local_account))
|
||||||
@report,
|
|
||||||
serializer: ActivityPub::FlagSerializer,
|
|
||||||
adapter: ActivityPub::Adapter,
|
|
||||||
account: some_local_account
|
|
||||||
).as_json)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def some_local_account
|
def some_local_account
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class SuspendAccountService < BaseService
|
class SuspendAccountService < BaseService
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
ASSOCIATIONS_ON_SUSPEND = %w(
|
ASSOCIATIONS_ON_SUSPEND = %w(
|
||||||
account_pins
|
account_pins
|
||||||
active_relationships
|
active_relationships
|
||||||
|
@ -118,23 +120,11 @@ class SuspendAccountService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_actor_json
|
def delete_actor_json
|
||||||
return @delete_actor_json if defined?(@delete_actor_json)
|
@delete_actor_json ||= Oj.dump(serialize_payload(@account, ActivityPub::DeleteActorSerializer, signer: @account))
|
||||||
|
|
||||||
payload = ActiveModelSerializers::SerializableResource.new(
|
|
||||||
@account,
|
|
||||||
serializer: ActivityPub::DeleteActorSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).as_json
|
|
||||||
|
|
||||||
@delete_actor_json = Oj.dump(ActivityPub::LinkedDataSignature.new(payload).sign!(@account))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_reject_json(follow)
|
def build_reject_json(follow)
|
||||||
ActiveModelSerializers::SerializableResource.new(
|
Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer))
|
||||||
follow,
|
|
||||||
serializer: ActivityPub::RejectFollowSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def delivery_inboxes
|
def delivery_inboxes
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class UnblockService < BaseService
|
class UnblockService < BaseService
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
def call(account, target_account)
|
def call(account, target_account)
|
||||||
return unless account.blocking?(target_account)
|
return unless account.blocking?(target_account)
|
||||||
|
|
||||||
|
@ -20,11 +22,7 @@ class UnblockService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_json(unblock)
|
def build_json(unblock)
|
||||||
ActiveModelSerializers::SerializableResource.new(
|
Oj.dump(serialize_payload(unblock, ActivityPub::UndoBlockSerializer))
|
||||||
unblock,
|
|
||||||
serializer: ActivityPub::UndoBlockSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_xml(block)
|
def build_xml(block)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class UnfavouriteService < BaseService
|
class UnfavouriteService < BaseService
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
def call(account, status)
|
def call(account, status)
|
||||||
favourite = Favourite.find_by!(account: account, status: status)
|
favourite = Favourite.find_by!(account: account, status: status)
|
||||||
favourite.destroy!
|
favourite.destroy!
|
||||||
|
@ -21,11 +23,7 @@ class UnfavouriteService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_json(favourite)
|
def build_json(favourite)
|
||||||
Oj.dump(ActivityPub::LinkedDataSignature.new(ActiveModelSerializers::SerializableResource.new(
|
Oj.dump(serialize_payload(favourite, ActivityPub::UndoLikeSerializer))
|
||||||
favourite,
|
|
||||||
serializer: ActivityPub::UndoLikeSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).as_json).sign!(favourite.account))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_xml(favourite)
|
def build_xml(favourite)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class UnfollowService < BaseService
|
class UnfollowService < BaseService
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
# Unfollow and notify the remote user
|
# Unfollow and notify the remote user
|
||||||
# @param [Account] source_account Where to unfollow from
|
# @param [Account] source_account Where to unfollow from
|
||||||
# @param [Account] target_account Which to unfollow
|
# @param [Account] target_account Which to unfollow
|
||||||
|
@ -50,19 +52,11 @@ class UnfollowService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_json(follow)
|
def build_json(follow)
|
||||||
ActiveModelSerializers::SerializableResource.new(
|
Oj.dump(serialize_payload(follow, ActivityPub::UndoFollowSerializer))
|
||||||
follow,
|
|
||||||
serializer: ActivityPub::UndoFollowSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_reject_json(follow)
|
def build_reject_json(follow)
|
||||||
ActiveModelSerializers::SerializableResource.new(
|
Oj.dump(serialize_payload(follow, ActivityPub::RejectFollowSerializer))
|
||||||
follow,
|
|
||||||
serializer: ActivityPub::RejectFollowSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).to_json
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_xml(follow)
|
def build_xml(follow)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class VoteService < BaseService
|
class VoteService < BaseService
|
||||||
include Authorization
|
include Authorization
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
def call(account, poll, choices)
|
def call(account, poll, choices)
|
||||||
authorize_with account, poll, :vote?
|
authorize_with account, poll, :vote?
|
||||||
|
@ -50,10 +51,6 @@ class VoteService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def build_json(vote)
|
def build_json(vote)
|
||||||
ActiveModelSerializers::SerializableResource.new(
|
Oj.dump(serialize_payload(vote, ActivityPub::VoteSerializer))
|
||||||
vote,
|
|
||||||
serializer: ActivityPub::VoteSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).to_json
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
= "(@#{account.username})"
|
= "(@#{account.username})"
|
||||||
%br/
|
%br/
|
||||||
= account.user_current_sign_in_ip
|
= account.user_current_sign_in_ip
|
||||||
|
•
|
||||||
|
= t 'admin.accounts.time_in_queue', time: time_ago_in_words(account.user&.created_at)
|
||||||
|
|
||||||
- if account.user&.invite_request&.text&.present?
|
- if account.user&.invite_request&.text&.present?
|
||||||
.pending-account__body
|
.pending-account__body
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class ActivityPub::DistributePollUpdateWorker
|
class ActivityPub::DistributePollUpdateWorker
|
||||||
include Sidekiq::Worker
|
include Sidekiq::Worker
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
sidekiq_options queue: 'push', unique: :until_executed, retry: 0
|
sidekiq_options queue: 'push', unique: :until_executed, retry: 0
|
||||||
|
|
||||||
|
@ -41,20 +42,8 @@ class ActivityPub::DistributePollUpdateWorker
|
||||||
@inboxes
|
@inboxes
|
||||||
end
|
end
|
||||||
|
|
||||||
def signed_payload
|
|
||||||
Oj.dump(ActivityPub::LinkedDataSignature.new(unsigned_payload).sign!(@account))
|
|
||||||
end
|
|
||||||
|
|
||||||
def unsigned_payload
|
|
||||||
ActiveModelSerializers::SerializableResource.new(
|
|
||||||
@status,
|
|
||||||
serializer: ActivityPub::UpdatePollSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).as_json
|
|
||||||
end
|
|
||||||
|
|
||||||
def payload
|
def payload
|
||||||
@payload ||= @status.distributable? ? signed_payload : Oj.dump(unsigned_payload)
|
@payload ||= Oj.dump(serialize_payload(@status, ActivityPub::UpdatePollSerializer, signer: @account))
|
||||||
end
|
end
|
||||||
|
|
||||||
def relay!
|
def relay!
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class ActivityPub::DistributionWorker
|
class ActivityPub::DistributionWorker
|
||||||
include Sidekiq::Worker
|
include Sidekiq::Worker
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
sidekiq_options queue: 'push'
|
sidekiq_options queue: 'push'
|
||||||
|
|
||||||
|
@ -41,20 +42,8 @@ class ActivityPub::DistributionWorker
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def signed_payload
|
|
||||||
Oj.dump(ActivityPub::LinkedDataSignature.new(unsigned_payload).sign!(@account))
|
|
||||||
end
|
|
||||||
|
|
||||||
def unsigned_payload
|
|
||||||
ActiveModelSerializers::SerializableResource.new(
|
|
||||||
@status,
|
|
||||||
serializer: ActivityPub::ActivitySerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).as_json
|
|
||||||
end
|
|
||||||
|
|
||||||
def payload
|
def payload
|
||||||
@payload ||= @status.distributable? ? signed_payload : Oj.dump(unsigned_payload)
|
@payload ||= Oj.dump(serialize_payload(@status, ActivityPub::ActivitySerializer, signer: @account))
|
||||||
end
|
end
|
||||||
|
|
||||||
def relay!
|
def relay!
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
class ActivityPub::ReplyDistributionWorker
|
class ActivityPub::ReplyDistributionWorker
|
||||||
include Sidekiq::Worker
|
include Sidekiq::Worker
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
sidekiq_options queue: 'push'
|
sidekiq_options queue: 'push'
|
||||||
|
|
||||||
|
@ -27,19 +28,7 @@ class ActivityPub::ReplyDistributionWorker
|
||||||
@inboxes ||= @account.followers.inboxes
|
@inboxes ||= @account.followers.inboxes
|
||||||
end
|
end
|
||||||
|
|
||||||
def signed_payload
|
|
||||||
Oj.dump(ActivityPub::LinkedDataSignature.new(unsigned_payload).sign!(@status.account))
|
|
||||||
end
|
|
||||||
|
|
||||||
def unsigned_payload
|
|
||||||
ActiveModelSerializers::SerializableResource.new(
|
|
||||||
@status,
|
|
||||||
serializer: ActivityPub::ActivitySerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).as_json
|
|
||||||
end
|
|
||||||
|
|
||||||
def payload
|
def payload
|
||||||
@payload ||= @status.distributable? ? signed_payload : Oj.dump(unsigned_payload)
|
@payload ||= Oj.dump(serialize_payload(@status, ActivityPub::ActivitySerializer, signer: @status.account))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class ActivityPub::UpdateDistributionWorker
|
class ActivityPub::UpdateDistributionWorker
|
||||||
include Sidekiq::Worker
|
include Sidekiq::Worker
|
||||||
|
include Payloadable
|
||||||
|
|
||||||
sidekiq_options queue: 'push'
|
sidekiq_options queue: 'push'
|
||||||
|
|
||||||
|
@ -27,14 +28,6 @@ class ActivityPub::UpdateDistributionWorker
|
||||||
end
|
end
|
||||||
|
|
||||||
def signed_payload
|
def signed_payload
|
||||||
@signed_payload ||= Oj.dump(ActivityPub::LinkedDataSignature.new(payload).sign!(@account, sign_with: @options[:sign_with]))
|
@signed_payload ||= Oj.dump(serialize_payload(@account, ActivityPub::UpdateSerializer, signer: @account, sign_with: @options[:sign_with]))
|
||||||
end
|
|
||||||
|
|
||||||
def payload
|
|
||||||
@payload ||= ActiveModelSerializers::SerializableResource.new(
|
|
||||||
@account,
|
|
||||||
serializer: ActivityPub::UpdateSerializer,
|
|
||||||
adapter: ActivityPub::Adapter
|
|
||||||
).as_json
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -174,6 +174,7 @@ en:
|
||||||
statuses: Statuses
|
statuses: Statuses
|
||||||
subscribe: Subscribe
|
subscribe: Subscribe
|
||||||
suspended: Suspended
|
suspended: Suspended
|
||||||
|
time_in_queue: Waiting in queue %{time}
|
||||||
title: Accounts
|
title: Accounts
|
||||||
unconfirmed_email: Unconfirmed email
|
unconfirmed_email: Unconfirmed email
|
||||||
undo_silenced: Undo silence
|
undo_silenced: Undo silence
|
||||||
|
|
|
@ -10,9 +10,10 @@
|
||||||
"build:production": "cross-env RAILS_ENV=production NODE_ENV=production ./bin/webpack",
|
"build:production": "cross-env RAILS_ENV=production NODE_ENV=production ./bin/webpack",
|
||||||
"manage:translations": "node ./config/webpack/translationRunner.js",
|
"manage:translations": "node ./config/webpack/translationRunner.js",
|
||||||
"start": "node ./streaming/index.js",
|
"start": "node ./streaming/index.js",
|
||||||
"test": "${npm_execpath} run test:lint && ${npm_execpath} run test:jest",
|
"test": "${npm_execpath} run test:lint:js && ${npm_execpath} run test:jest",
|
||||||
"test:lint": "eslint --ext=js .",
|
"test:lint": "${npm_execpath} run test:lint:js && ${npm_execpath} run test:lint:sass",
|
||||||
"test:lint:sass": "sass-lint .",
|
"test:lint:js": "eslint --ext=js .",
|
||||||
|
"test:lint:sass": "sass-lint -v",
|
||||||
"test:jest": "cross-env NODE_ENV=test jest --coverage"
|
"test:jest": "cross-env NODE_ENV=test jest --coverage"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
|
@ -58,21 +58,6 @@ RSpec.describe ActivityPub::Activity::Announce do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'self-boost of a previously unknown status with missing attributedTo' do
|
|
||||||
let(:object_json) do
|
|
||||||
{
|
|
||||||
id: 'https://example.com/actor#bar',
|
|
||||||
type: 'Note',
|
|
||||||
content: 'Lorem ipsum',
|
|
||||||
to: 'http://example.com/followers',
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'creates a reblog by sender of status' do
|
|
||||||
expect(sender.reblogged?(sender.statuses.first)).to be true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'self-boost of a previously unknown status with correct attributedTo' do
|
context 'self-boost of a previously unknown status with correct attributedTo' do
|
||||||
let(:object_json) do
|
let(:object_json) do
|
||||||
{
|
{
|
||||||
|
@ -122,6 +107,7 @@ RSpec.describe ActivityPub::Activity::Announce do
|
||||||
type: 'Note',
|
type: 'Note',
|
||||||
content: 'Lorem ipsum',
|
content: 'Lorem ipsum',
|
||||||
to: 'http://example.com/followers',
|
to: 'http://example.com/followers',
|
||||||
|
attributedTo: 'https://example.com/actor',
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -141,6 +127,7 @@ RSpec.describe ActivityPub::Activity::Announce do
|
||||||
type: 'Note',
|
type: 'Note',
|
||||||
content: 'Lorem ipsum',
|
content: 'Lorem ipsum',
|
||||||
to: 'http://example.com/followers',
|
to: 'http://example.com/followers',
|
||||||
|
attributedTo: 'https://example.com/actor',
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -161,6 +148,7 @@ RSpec.describe ActivityPub::Activity::Announce do
|
||||||
type: 'Note',
|
type: 'Note',
|
||||||
content: 'Lorem ipsum',
|
content: 'Lorem ipsum',
|
||||||
to: 'http://example.com/followers',
|
to: 'http://example.com/followers',
|
||||||
|
attributedTo: 'https://example.com/actor',
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue