commit
37965ac8d9
3
Gemfile
3
Gemfile
|
@ -16,7 +16,6 @@ gem 'dotenv-rails', '~> 2.2', '< 2.3'
|
||||||
|
|
||||||
gem 'aws-sdk-s3', '~> 1.9', require: false
|
gem 'aws-sdk-s3', '~> 1.9', require: false
|
||||||
gem 'fog-core', '~> 1.45'
|
gem 'fog-core', '~> 1.45'
|
||||||
gem 'fog-local', '~> 0.5', require: false
|
|
||||||
gem 'fog-openstack', '~> 0.1', require: false
|
gem 'fog-openstack', '~> 0.1', require: false
|
||||||
gem 'paperclip', '~> 6.0'
|
gem 'paperclip', '~> 6.0'
|
||||||
gem 'paperclip-av-transcoder', '~> 0.6'
|
gem 'paperclip-av-transcoder', '~> 0.6'
|
||||||
|
@ -42,7 +41,7 @@ gem 'omniauth-cas', '~> 1.1'
|
||||||
gem 'omniauth-saml', '~> 1.10'
|
gem 'omniauth-saml', '~> 1.10'
|
||||||
gem 'omniauth', '~> 1.2'
|
gem 'omniauth', '~> 1.2'
|
||||||
|
|
||||||
gem 'doorkeeper', '~> 4.2', '< 4.3'
|
gem 'doorkeeper', '~> 4.4'
|
||||||
gem 'fast_blank', '~> 1.0'
|
gem 'fast_blank', '~> 1.0'
|
||||||
gem 'fastimage'
|
gem 'fastimage'
|
||||||
gem 'goldfinger', '~> 2.1'
|
gem 'goldfinger', '~> 2.1'
|
||||||
|
|
|
@ -181,7 +181,7 @@ GEM
|
||||||
docile (1.3.0)
|
docile (1.3.0)
|
||||||
domain_name (0.5.20180417)
|
domain_name (0.5.20180417)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
doorkeeper (4.2.6)
|
doorkeeper (4.4.2)
|
||||||
railties (>= 4.2)
|
railties (>= 4.2)
|
||||||
dotenv (2.2.2)
|
dotenv (2.2.2)
|
||||||
dotenv-rails (2.2.2)
|
dotenv-rails (2.2.2)
|
||||||
|
@ -220,8 +220,6 @@ GEM
|
||||||
fog-json (1.0.2)
|
fog-json (1.0.2)
|
||||||
fog-core (~> 1.0)
|
fog-core (~> 1.0)
|
||||||
multi_json (~> 1.10)
|
multi_json (~> 1.10)
|
||||||
fog-local (0.5.0)
|
|
||||||
fog-core (>= 1.27, < 3.0)
|
|
||||||
fog-openstack (0.1.25)
|
fog-openstack (0.1.25)
|
||||||
fog-core (~> 1.40)
|
fog-core (~> 1.40)
|
||||||
fog-json (>= 1.0)
|
fog-json (>= 1.0)
|
||||||
|
@ -674,14 +672,13 @@ DEPENDENCIES
|
||||||
devise (~> 4.4)
|
devise (~> 4.4)
|
||||||
devise-two-factor (~> 3.0)
|
devise-two-factor (~> 3.0)
|
||||||
devise_pam_authenticatable2 (~> 9.1)
|
devise_pam_authenticatable2 (~> 9.1)
|
||||||
doorkeeper (~> 4.2, < 4.3)
|
doorkeeper (~> 4.4)
|
||||||
dotenv-rails (~> 2.2, < 2.3)
|
dotenv-rails (~> 2.2, < 2.3)
|
||||||
fabrication (~> 2.20)
|
fabrication (~> 2.20)
|
||||||
faker (~> 1.8)
|
faker (~> 1.8)
|
||||||
fast_blank (~> 1.0)
|
fast_blank (~> 1.0)
|
||||||
fastimage
|
fastimage
|
||||||
fog-core (~> 1.45)
|
fog-core (~> 1.45)
|
||||||
fog-local (~> 0.5)
|
|
||||||
fog-openstack (~> 0.1)
|
fog-openstack (~> 0.1)
|
||||||
fuubar (~> 2.2)
|
fuubar (~> 2.2)
|
||||||
goldfinger (~> 2.1)
|
goldfinger (~> 2.1)
|
||||||
|
|
|
@ -43,7 +43,7 @@ class AccountsController < ApplicationController
|
||||||
format.json do
|
format.json do
|
||||||
skip_session!
|
skip_session!
|
||||||
|
|
||||||
render_cached_json(['activitypub', 'actor', @account.cache_key], 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
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Api::V1::EndorsementsController < Api::BaseController
|
||||||
|
before_action -> { doorkeeper_authorize! :read, :'read:accounts' }
|
||||||
|
before_action :require_user!
|
||||||
|
after_action :insert_pagination_headers
|
||||||
|
|
||||||
|
respond_to :json
|
||||||
|
|
||||||
|
def index
|
||||||
|
@accounts = load_accounts
|
||||||
|
render json: @accounts, each_serializer: REST::AccountSerializer
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def load_accounts
|
||||||
|
if unlimited?
|
||||||
|
endorsed_accounts.all
|
||||||
|
else
|
||||||
|
endorsed_accounts.paginate_by_max_id(
|
||||||
|
limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||||
|
params[:max_id],
|
||||||
|
params[:since_id]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def endorsed_accounts
|
||||||
|
current_account.endorsed_accounts
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_pagination_headers
|
||||||
|
set_pagination_headers(next_path, prev_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_path
|
||||||
|
return if unlimited?
|
||||||
|
|
||||||
|
if records_continue?
|
||||||
|
api_v1_endorsements_url pagination_params(max_id: pagination_max_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def prev_path
|
||||||
|
return if unlimited?
|
||||||
|
|
||||||
|
unless @accounts.empty?
|
||||||
|
api_v1_endorsements_url pagination_params(since_id: pagination_since_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_max_id
|
||||||
|
@accounts.last.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_since_id
|
||||||
|
@accounts.first.id
|
||||||
|
end
|
||||||
|
|
||||||
|
def records_continue?
|
||||||
|
@accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_params(core_params)
|
||||||
|
params.slice(:limit).permit(:limit).merge(core_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def unlimited?
|
||||||
|
params[:limit] == '0'
|
||||||
|
end
|
||||||
|
end
|
|
@ -17,8 +17,7 @@ class Api::V1::StatusesController < Api::BaseController
|
||||||
CONTEXT_LIMIT = 4_096
|
CONTEXT_LIMIT = 4_096
|
||||||
|
|
||||||
def show
|
def show
|
||||||
cached = Rails.cache.read(@status.cache_key)
|
@status = cache_collection([@status], Status).first
|
||||||
@status = cached unless cached.nil?
|
|
||||||
render json: @status, serializer: REST::StatusSerializer
|
render json: @status, serializer: REST::StatusSerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -178,12 +178,8 @@ class ApplicationController < ActionController::Base
|
||||||
return raw unless klass.respond_to?(:with_includes)
|
return raw unless klass.respond_to?(:with_includes)
|
||||||
|
|
||||||
raw = raw.cache_ids.to_a if raw.is_a?(ActiveRecord::Relation)
|
raw = raw.cache_ids.to_a if raw.is_a?(ActiveRecord::Relation)
|
||||||
uncached_ids = []
|
cached_keys_with_value = Rails.cache.read_multi(*raw).transform_keys(&:id)
|
||||||
cached_keys_with_value = Rails.cache.read_multi(*raw.map(&:cache_key))
|
uncached_ids = raw.map(&:id) - cached_keys_with_value.keys
|
||||||
|
|
||||||
raw.each do |item|
|
|
||||||
uncached_ids << item.id unless cached_keys_with_value.key?(item.cache_key)
|
|
||||||
end
|
|
||||||
|
|
||||||
klass.reload_stale_associations!(cached_keys_with_value.values) if klass.respond_to?(:reload_stale_associations!)
|
klass.reload_stale_associations!(cached_keys_with_value.values) if klass.respond_to?(:reload_stale_associations!)
|
||||||
|
|
||||||
|
@ -191,11 +187,11 @@ class ApplicationController < ActionController::Base
|
||||||
uncached = klass.where(id: uncached_ids).with_includes.map { |item| [item.id, item] }.to_h
|
uncached = klass.where(id: uncached_ids).with_includes.map { |item| [item.id, item] }.to_h
|
||||||
|
|
||||||
uncached.each_value do |item|
|
uncached.each_value do |item|
|
||||||
Rails.cache.write(item.cache_key, item)
|
Rails.cache.write(item, item)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
raw.map { |item| cached_keys_with_value[item.cache_key] || uncached[item.id] }.compact
|
raw.map { |item| cached_keys_with_value[item.id] || uncached[item.id] }.compact
|
||||||
end
|
end
|
||||||
|
|
||||||
def respond_with_error(code)
|
def respond_with_error(code)
|
||||||
|
@ -211,7 +207,6 @@ class ApplicationController < ActionController::Base
|
||||||
|
|
||||||
def render_cached_json(cache_key, **options)
|
def render_cached_json(cache_key, **options)
|
||||||
options[:expires_in] ||= 3.minutes
|
options[:expires_in] ||= 3.minutes
|
||||||
cache_key = cache_key.join(':') if cache_key.is_a?(Enumerable)
|
|
||||||
cache_public = options.key?(:public) ? options.delete(:public) : true
|
cache_public = options.key?(:public) ? options.delete(:public) : true
|
||||||
content_type = options.delete(:content_type) || 'application/json'
|
content_type = options.delete(:content_type) || 'application/json'
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ class EmojisController < ApplicationController
|
||||||
format.json do
|
format.json do
|
||||||
skip_session!
|
skip_session!
|
||||||
|
|
||||||
render_cached_json(['activitypub', 'emoji', @emoji.cache_key], 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
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,7 +34,7 @@ class StatusesController < ApplicationController
|
||||||
format.json do
|
format.json do
|
||||||
skip_session! unless @stream_entry.hidden?
|
skip_session! unless @stream_entry.hidden?
|
||||||
|
|
||||||
render_cached_json(['activitypub', 'note', @status.cache_key], 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
|
||||||
end
|
end
|
||||||
|
@ -44,7 +44,7 @@ class StatusesController < ApplicationController
|
||||||
def activity
|
def activity
|
||||||
skip_session!
|
skip_session!
|
||||||
|
|
||||||
render_cached_json(['activitypub', 'activity', @status.cache_key], 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
|
||||||
end
|
end
|
||||||
|
|
|
@ -55,7 +55,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!params.max_id && timeline.get('items', ImmutableList()).size > 0) {
|
if (!params.max_id && !params.pinned && timeline.get('items', ImmutableList()).size > 0) {
|
||||||
params.since_id = timeline.getIn(['items', 0]);
|
params.since_id = timeline.getIn(['items', 0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -137,7 +137,7 @@ class DropdownMenu extends React.PureComponent {
|
||||||
// It should not be transformed when mounting because the resulting
|
// It should not be transformed when mounting because the resulting
|
||||||
// size will be used to determine the coordinate of the menu by
|
// size will be used to determine the coordinate of the menu by
|
||||||
// react-overlays
|
// react-overlays
|
||||||
<div className='dropdown-menu' style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}>
|
<div className={`dropdown-menu ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} ref={this.setRef}>
|
||||||
<div className={`dropdown-menu__arrow ${placement}`} style={{ left: arrowOffsetLeft, top: arrowOffsetTop }} />
|
<div className={`dropdown-menu__arrow ${placement}`} style={{ left: arrowOffsetLeft, top: arrowOffsetTop }} />
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
|
|
|
@ -28,6 +28,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
|
||||||
style: PropTypes.object,
|
style: PropTypes.object,
|
||||||
items: PropTypes.array.isRequired,
|
items: PropTypes.array.isRequired,
|
||||||
value: PropTypes.string.isRequired,
|
value: PropTypes.string.isRequired,
|
||||||
|
placement: PropTypes.string.isRequired,
|
||||||
onClose: PropTypes.func.isRequired,
|
onClose: PropTypes.func.isRequired,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
@ -119,7 +120,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { mounted } = this.state;
|
const { mounted } = this.state;
|
||||||
const { style, items, value } = this.props;
|
const { style, items, placement, value } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<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 }) }}>
|
||||||
|
@ -127,7 +128,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
|
||||||
// It should not be transformed when mounting because the resulting
|
// It should not be transformed when mounting because the resulting
|
||||||
// size will be used to determine the coordinate of the menu by
|
// size will be used to determine the coordinate of the menu by
|
||||||
// react-overlays
|
// react-overlays
|
||||||
<div className='privacy-dropdown__dropdown' style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} role='listbox' ref={this.setRef}>
|
<div className={`privacy-dropdown__dropdown ${placement}`} style={{ ...style, opacity: opacity, transform: mounted ? `scale(${scaleX}, ${scaleY})` : null }} role='listbox' ref={this.setRef}>
|
||||||
{items.map(item => (
|
{items.map(item => (
|
||||||
<div role='option' tabIndex='0' key={item.value} data-index={item.value} onKeyDown={this.handleKeyDown} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? this.setFocusRef : null}>
|
<div role='option' tabIndex='0' key={item.value} data-index={item.value} onKeyDown={this.handleKeyDown} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? this.setFocusRef : null}>
|
||||||
<div className='privacy-dropdown__option__icon'>
|
<div className='privacy-dropdown__option__icon'>
|
||||||
|
@ -226,7 +227,7 @@ export default class PrivacyDropdown extends React.PureComponent {
|
||||||
const valueOption = this.options.find(item => item.value === value);
|
const valueOption = this.options.find(item => item.value === value);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames('privacy-dropdown', { active: open })} onKeyDown={this.handleKeyDown}>
|
<div className={classNames('privacy-dropdown', placement, { active: open })} onKeyDown={this.handleKeyDown}>
|
||||||
<div className={classNames('privacy-dropdown__value', { active: this.options.indexOf(valueOption) === 0 })}>
|
<div className={classNames('privacy-dropdown__value', { active: this.options.indexOf(valueOption) === 0 })}>
|
||||||
<IconButton
|
<IconButton
|
||||||
className='privacy-dropdown__value-icon'
|
className='privacy-dropdown__value-icon'
|
||||||
|
@ -247,6 +248,7 @@ export default class PrivacyDropdown extends React.PureComponent {
|
||||||
value={value}
|
value={value}
|
||||||
onClose={this.handleClose}
|
onClose={this.handleClose}
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
|
placement={placement}
|
||||||
/>
|
/>
|
||||||
</Overlay>
|
</Overlay>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -89,6 +89,7 @@ const keyMap = {
|
||||||
goToProfile: 'g u',
|
goToProfile: 'g u',
|
||||||
goToBlocked: 'g b',
|
goToBlocked: 'g b',
|
||||||
goToMuted: 'g m',
|
goToMuted: 'g m',
|
||||||
|
goToRequests: 'g r',
|
||||||
toggleHidden: 'x',
|
toggleHidden: 'x',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -427,6 +428,10 @@ export default class UI extends React.PureComponent {
|
||||||
this.context.router.history.push('/mutes');
|
this.context.router.history.push('/mutes');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleHotkeyGoToRequests = () => {
|
||||||
|
this.context.router.history.push('/follow_requests');
|
||||||
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { draggingOver } = this.state;
|
const { draggingOver } = this.state;
|
||||||
const { children, isComposing, location, dropdownMenuIsOpen } = this.props;
|
const { children, isComposing, location, dropdownMenuIsOpen } = this.props;
|
||||||
|
@ -449,6 +454,7 @@ export default class UI extends React.PureComponent {
|
||||||
goToProfile: this.handleHotkeyGoToProfile,
|
goToProfile: this.handleHotkeyGoToProfile,
|
||||||
goToBlocked: this.handleHotkeyGoToBlocked,
|
goToBlocked: this.handleHotkeyGoToBlocked,
|
||||||
goToMuted: this.handleHotkeyGoToMuted,
|
goToMuted: this.handleHotkeyGoToMuted,
|
||||||
|
goToRequests: this.handleHotkeyGoToRequests,
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { List as ImmutableList } from 'immutable';
|
import { List as ImmutableList } from 'immutable';
|
||||||
|
import { me } from '../initial_state';
|
||||||
|
|
||||||
const getAccountBase = (state, id) => state.getIn(['accounts', id], null);
|
const getAccountBase = (state, id) => state.getIn(['accounts', id], null);
|
||||||
const getAccountCounters = (state, id) => state.getIn(['accounts_counters', id], null);
|
const getAccountCounters = (state, id) => state.getIn(['accounts_counters', id], null);
|
||||||
|
@ -83,7 +84,7 @@ export const makeGetStatus = () => {
|
||||||
statusReblog = null;
|
statusReblog = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const regex = regexFromFilters(filters);
|
const regex = (accountReblog || accountBase).get('id') !== me && regexFromFilters(filters);
|
||||||
const filtered = regex && regex.test(statusBase.get('reblog') ? statusReblog.get('search_index') : statusBase.get('search_index'));
|
const filtered = regex && regex.test(statusBase.get('reblog') ? statusReblog.get('search_index') : statusBase.get('search_index'));
|
||||||
|
|
||||||
return statusBase.withMutations(map => {
|
return statusBase.withMutations(map => {
|
||||||
|
|
|
@ -230,7 +230,6 @@
|
||||||
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
transform-origin: 50% 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.invisible {
|
.invisible {
|
||||||
|
@ -1634,6 +1633,22 @@ a.account__display-name {
|
||||||
ul {
|
ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.left {
|
||||||
|
transform-origin: 100% 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.top {
|
||||||
|
transform-origin: 50% 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.bottom {
|
||||||
|
transform-origin: 50% 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.right {
|
||||||
|
transform-origin: 0 50%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-menu__arrow {
|
.dropdown-menu__arrow {
|
||||||
|
@ -3300,7 +3315,14 @@ a.status-card {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
margin-left: 40px;
|
margin-left: 40px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transform-origin: 50% 0;
|
|
||||||
|
&.top {
|
||||||
|
transform-origin: 50% 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.bottom {
|
||||||
|
transform-origin: 50% 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.privacy-dropdown__option {
|
.privacy-dropdown__option {
|
||||||
|
@ -3372,6 +3394,10 @@ a.status-card {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.top .privacy-dropdown__value {
|
||||||
|
border-radius: 0 0 4px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.privacy-dropdown__dropdown {
|
.privacy-dropdown__dropdown {
|
||||||
display: block;
|
display: block;
|
||||||
box-shadow: 2px 4px 6px rgba($base-shadow-color, 0.1);
|
box-shadow: 2px 4px 6px rgba($base-shadow-color, 0.1);
|
||||||
|
@ -4176,6 +4202,10 @@ a.status-card {
|
||||||
color: $highlight-text-color;
|
color: $highlight-text-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status__content p {
|
||||||
|
color: $inverted-text-color;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 480px) {
|
@media screen and (max-width: 480px) {
|
||||||
max-height: 10vh;
|
max-height: 10vh;
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,9 +154,8 @@ code {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
li {
|
ul {
|
||||||
float: left;
|
columns: 2;
|
||||||
width: 50%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
class AccountPin < ApplicationRecord
|
class AccountPin < ApplicationRecord
|
||||||
|
include Paginable
|
||||||
include RelationshipCacheable
|
include RelationshipCacheable
|
||||||
|
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
|
|
|
@ -23,7 +23,7 @@ class Form::StatusBatch
|
||||||
media_attached_status_ids = MediaAttachment.where(status_id: status_ids).pluck(:status_id)
|
media_attached_status_ids = MediaAttachment.where(status_id: status_ids).pluck(:status_id)
|
||||||
|
|
||||||
ApplicationRecord.transaction do
|
ApplicationRecord.transaction do
|
||||||
Status.where(id: media_attached_status_ids).find_each do |status|
|
Status.where(id: media_attached_status_ids).reorder(nil).find_each do |status|
|
||||||
status.update!(sensitive: sensitive)
|
status.update!(sensitive: sensitive)
|
||||||
log_action :update, status
|
log_action :update, status
|
||||||
end
|
end
|
||||||
|
@ -35,7 +35,7 @@ class Form::StatusBatch
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_statuses
|
def delete_statuses
|
||||||
Status.where(id: status_ids).find_each do |status|
|
Status.where(id: status_ids).reorder(nil).find_each do |status|
|
||||||
RemovalWorker.perform_async(status.id)
|
RemovalWorker.perform_async(status.id)
|
||||||
log_action :destroy, status
|
log_action :destroy, status
|
||||||
end
|
end
|
||||||
|
|
|
@ -39,8 +39,6 @@ class Notification < ApplicationRecord
|
||||||
validates :account_id, uniqueness: { scope: [:activity_type, :activity_id] }
|
validates :account_id, uniqueness: { scope: [:activity_type, :activity_id] }
|
||||||
validates :activity_type, inclusion: { in: TYPE_CLASS_MAP.values }
|
validates :activity_type, inclusion: { in: TYPE_CLASS_MAP.values }
|
||||||
|
|
||||||
scope :cache_ids, -> { select(:id, :updated_at, :activity_type, :activity_id) }
|
|
||||||
|
|
||||||
scope :browserable, ->(exclude_types = []) {
|
scope :browserable, ->(exclude_types = []) {
|
||||||
types = TYPE_CLASS_MAP.values - activity_types_from_types(exclude_types + [:follow_request])
|
types = TYPE_CLASS_MAP.values - activity_types_from_types(exclude_types + [:follow_request])
|
||||||
where(activity_type: types)
|
where(activity_type: types)
|
||||||
|
@ -68,6 +66,10 @@ class Notification < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
def cache_ids
|
||||||
|
select(:id, :updated_at, :activity_type, :activity_id)
|
||||||
|
end
|
||||||
|
|
||||||
def reload_stale_associations!(cached_items)
|
def reload_stale_associations!(cached_items)
|
||||||
account_ids = (cached_items.map(&:from_account_id) + cached_items.map { |item| item.target_status&.account_id }.compact).uniq
|
account_ids = (cached_items.map(&:from_account_id) + cached_items.map { |item| item.target_status&.account_id }.compact).uniq
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,6 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
class Status < ApplicationRecord
|
class Status < ApplicationRecord
|
||||||
self.cache_versioning = false
|
|
||||||
|
|
||||||
include Paginable
|
include Paginable
|
||||||
include Streamable
|
include Streamable
|
||||||
include Cacheable
|
include Cacheable
|
||||||
|
|
|
@ -15,13 +15,13 @@ class AfterBlockDomainFromAccountService < BaseService
|
||||||
private
|
private
|
||||||
|
|
||||||
def reject_existing_followers!
|
def reject_existing_followers!
|
||||||
@account.passive_relationships.where(account: Account.where(domain: @domain)).includes(:account).find_each do |follow|
|
@account.passive_relationships.where(account: Account.where(domain: @domain)).includes(:account).reorder(nil).find_each do |follow|
|
||||||
reject_follow!(follow)
|
reject_follow!(follow)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def reject_pending_follow_requests!
|
def reject_pending_follow_requests!
|
||||||
FollowRequest.where(target_account: @account).where(account: Account.where(domain: @domain)).includes(:account).find_each do |follow_request|
|
FollowRequest.where(target_account: @account).where(account: Account.where(domain: @domain)).includes(:account).reorder(nil).find_each do |follow_request|
|
||||||
reject_follow!(follow_request)
|
reject_follow!(follow_request)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,7 +18,7 @@ class BackupService < BaseService
|
||||||
def build_json!
|
def build_json!
|
||||||
@collection = serialize(collection_presenter, ActivityPub::CollectionSerializer)
|
@collection = serialize(collection_presenter, ActivityPub::CollectionSerializer)
|
||||||
|
|
||||||
account.statuses.with_includes.find_in_batches do |statuses|
|
account.statuses.with_includes.reorder(nil).find_in_batches do |statuses|
|
||||||
statuses.each do |status|
|
statuses.each do |status|
|
||||||
item = serialize(status, ActivityPub::ActivitySerializer)
|
item = serialize(status, ActivityPub::ActivitySerializer)
|
||||||
item.delete(:'@context')
|
item.delete(:'@context')
|
||||||
|
@ -60,7 +60,7 @@ class BackupService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def dump_media_attachments!(tar)
|
def dump_media_attachments!(tar)
|
||||||
MediaAttachment.attached.where(account: account).find_in_batches do |media_attachments|
|
MediaAttachment.attached.where(account: account).reorder(nil).find_in_batches do |media_attachments|
|
||||||
media_attachments.each do |m|
|
media_attachments.each do |m|
|
||||||
download_to_tar(tar, m.file, m.file.path)
|
download_to_tar(tar, m.file, m.file.path)
|
||||||
end
|
end
|
||||||
|
|
|
@ -43,14 +43,14 @@ class BlockDomainService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def suspend_accounts!
|
def suspend_accounts!
|
||||||
blocked_domain_accounts.where(suspended: false).find_each do |account|
|
blocked_domain_accounts.where(suspended: false).reorder(nil).find_each do |account|
|
||||||
UnsubscribeService.new.call(account) if account.subscribed?
|
UnsubscribeService.new.call(account) if account.subscribed?
|
||||||
SuspendAccountService.new.call(account)
|
SuspendAccountService.new.call(account)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear_account_images!
|
def clear_account_images!
|
||||||
blocked_domain_accounts.find_each do |account|
|
blocked_domain_accounts.reorder(nil).find_each do |account|
|
||||||
account.avatar.destroy if account.avatar.exists?
|
account.avatar.destroy if account.avatar.exists?
|
||||||
account.header.destroy if account.header.exists?
|
account.header.destroy if account.header.exists?
|
||||||
account.save
|
account.save
|
||||||
|
@ -58,7 +58,7 @@ class BlockDomainService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear_account_attachments!
|
def clear_account_attachments!
|
||||||
media_from_blocked_domain.find_each do |attachment|
|
media_from_blocked_domain.reorder(nil).find_each do |attachment|
|
||||||
@affected_status_ids << attachment.status_id if attachment.status_id.present?
|
@affected_status_ids << attachment.status_id if attachment.status_id.present?
|
||||||
|
|
||||||
attachment.file.destroy if attachment.file.exists?
|
attachment.file.destroy if attachment.file.exists?
|
||||||
|
|
|
@ -43,13 +43,13 @@ class RemoveStatusService < BaseService
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_from_followers
|
def remove_from_followers
|
||||||
@account.followers_for_local_distribution.find_each do |follower|
|
@account.followers_for_local_distribution.reorder(nil).find_each do |follower|
|
||||||
FeedManager.instance.unpush_from_home(follower, @status)
|
FeedManager.instance.unpush_from_home(follower, @status)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_from_lists
|
def remove_from_lists
|
||||||
@account.lists_for_local_distribution.select(:id, :account_id).find_each do |list|
|
@account.lists_for_local_distribution.select(:id, :account_id).reorder(nil).find_each do |list|
|
||||||
FeedManager.instance.unpush_from_list(list, @status)
|
FeedManager.instance.unpush_from_list(list, @status)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,9 +23,7 @@ class SuspendAccountService < BaseService
|
||||||
|
|
||||||
def purge_content!
|
def purge_content!
|
||||||
if @account.local?
|
if @account.local?
|
||||||
ActivityPub::RawDistributionWorker.perform_async(delete_actor_json, @account.id)
|
ActivityPub::DeliveryWorker.push_bulk(delivery_inboxes) do |inbox_url|
|
||||||
|
|
||||||
ActivityPub::DeliveryWorker.push_bulk(Relay.enabled.pluck(:inbox_url)) do |inbox_url|
|
|
||||||
[delete_actor_json, @account.id, inbox_url]
|
[delete_actor_json, @account.id, inbox_url]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -75,4 +73,8 @@ class SuspendAccountService < BaseService
|
||||||
|
|
||||||
@delete_actor_json = Oj.dump(ActivityPub::LinkedDataSignature.new(payload).sign!(@account))
|
@delete_actor_json = Oj.dump(ActivityPub::LinkedDataSignature.new(payload).sign!(@account))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def delivery_inboxes
|
||||||
|
Account.inboxes + Relay.enabled.pluck(:inbox_url)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ class Maintenance::UncacheMediaWorker
|
||||||
def perform(media_attachment_id)
|
def perform(media_attachment_id)
|
||||||
media = MediaAttachment.find(media_attachment_id)
|
media = MediaAttachment.find(media_attachment_id)
|
||||||
|
|
||||||
return unless media.file.exists?
|
return if media.file.blank?
|
||||||
|
|
||||||
media.file.destroy
|
media.file.destroy
|
||||||
media.save
|
media.save
|
||||||
|
|
|
@ -9,7 +9,7 @@ class RefollowWorker
|
||||||
target_account = Account.find(target_account_id)
|
target_account = Account.find(target_account_id)
|
||||||
return unless target_account.protocol == :activitypub
|
return unless target_account.protocol == :activitypub
|
||||||
|
|
||||||
target_account.followers.where(domain: nil).find_each do |follower|
|
target_account.followers.where(domain: nil).reorder(nil).find_each do |follower|
|
||||||
# Locally unfollow remote account
|
# Locally unfollow remote account
|
||||||
follower.unfollow!(target_account)
|
follower.unfollow!(target_account)
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,10 @@
|
||||||
class Scheduler::BackupCleanupScheduler
|
class Scheduler::BackupCleanupScheduler
|
||||||
include Sidekiq::Worker
|
include Sidekiq::Worker
|
||||||
|
|
||||||
|
sidekiq_options unique: :until_executed
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
old_backups.find_each(&:destroy!)
|
old_backups.reorder(nil).find_each(&:destroy!)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
class Scheduler::DoorkeeperCleanupScheduler
|
class Scheduler::DoorkeeperCleanupScheduler
|
||||||
include Sidekiq::Worker
|
include Sidekiq::Worker
|
||||||
|
|
||||||
|
sidekiq_options unique: :until_executed
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
Doorkeeper::AccessToken.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all
|
Doorkeeper::AccessToken.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all
|
||||||
Doorkeeper::AccessGrant.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all
|
Doorkeeper::AccessGrant.where('revoked_at IS NOT NULL').where('revoked_at < NOW()').delete_all
|
||||||
|
|
|
@ -3,8 +3,10 @@
|
||||||
class Scheduler::EmailScheduler
|
class Scheduler::EmailScheduler
|
||||||
include Sidekiq::Worker
|
include Sidekiq::Worker
|
||||||
|
|
||||||
|
sidekiq_options unique: :until_executed
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
eligible_users.find_each do |user|
|
eligible_users.reorder(nil).find_each do |user|
|
||||||
next unless user.allows_digest_emails?
|
next unless user.allows_digest_emails?
|
||||||
DigestMailerWorker.perform_async(user.id)
|
DigestMailerWorker.perform_async(user.id)
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
class Scheduler::FeedCleanupScheduler
|
class Scheduler::FeedCleanupScheduler
|
||||||
include Sidekiq::Worker
|
include Sidekiq::Worker
|
||||||
|
|
||||||
|
sidekiq_options unique: :until_executed
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
clean_home_feeds!
|
clean_home_feeds!
|
||||||
clean_list_feeds!
|
clean_list_feeds!
|
||||||
|
|
|
@ -5,6 +5,8 @@ class Scheduler::IpCleanupScheduler
|
||||||
|
|
||||||
RETENTION_PERIOD = 1.year
|
RETENTION_PERIOD = 1.year
|
||||||
|
|
||||||
|
sidekiq_options unique: :until_executed
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
time_ago = RETENTION_PERIOD.ago
|
time_ago = RETENTION_PERIOD.ago
|
||||||
SessionActivation.where('updated_at < ?', time_ago).destroy_all
|
SessionActivation.where('updated_at < ?', time_ago).destroy_all
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
class Scheduler::MediaCleanupScheduler
|
class Scheduler::MediaCleanupScheduler
|
||||||
include Sidekiq::Worker
|
include Sidekiq::Worker
|
||||||
|
|
||||||
|
sidekiq_options unique: :until_executed
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
unattached_media.find_each(&:destroy)
|
unattached_media.find_each(&:destroy)
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
class Scheduler::SubscriptionsCleanupScheduler
|
class Scheduler::SubscriptionsCleanupScheduler
|
||||||
include Sidekiq::Worker
|
include Sidekiq::Worker
|
||||||
|
|
||||||
|
sidekiq_options unique: :until_executed
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
Subscription.expired.in_batches.delete_all
|
Subscription.expired.in_batches.delete_all
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
class Scheduler::SubscriptionsScheduler
|
class Scheduler::SubscriptionsScheduler
|
||||||
include Sidekiq::Worker
|
include Sidekiq::Worker
|
||||||
|
|
||||||
|
sidekiq_options unique: :until_executed
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
Pubsubhubbub::SubscribeWorker.push_bulk(expiring_accounts.pluck(:id))
|
Pubsubhubbub::SubscribeWorker.push_bulk(expiring_accounts.pluck(:id))
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,8 +3,10 @@
|
||||||
class Scheduler::UserCleanupScheduler
|
class Scheduler::UserCleanupScheduler
|
||||||
include Sidekiq::Worker
|
include Sidekiq::Worker
|
||||||
|
|
||||||
|
sidekiq_options unique: :until_executed
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
User.where('confirmed_at is NULL AND confirmation_sent_at <= ?', 2.days.ago).find_in_batches do |batch|
|
User.where('confirmed_at is NULL AND confirmation_sent_at <= ?', 2.days.ago).reorder(nil).find_in_batches do |batch|
|
||||||
Account.where(id: batch.map(&:account_id)).delete_all
|
Account.where(id: batch.map(&:account_id)).delete_all
|
||||||
User.where(id: batch.map(&:id)).delete_all
|
User.where(id: batch.map(&:id)).delete_all
|
||||||
end
|
end
|
||||||
|
|
|
@ -74,14 +74,10 @@ elsif ENV['SWIFT_ENABLED'] == 'true'
|
||||||
fog_public: true
|
fog_public: true
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
require 'fog/local'
|
|
||||||
|
|
||||||
Paperclip::Attachment.default_options.merge!(
|
Paperclip::Attachment.default_options.merge!(
|
||||||
fog_credentials: {
|
storage: :filesystem,
|
||||||
provider: 'Local',
|
use_timestamp: true,
|
||||||
local_root: ENV.fetch('PAPERCLIP_ROOT_PATH') { Rails.root.join('public', 'system') },
|
path: (ENV['PAPERCLIP_ROOT_PATH'] || ':rails_root/public/system') + '/:class/:attachment/:id_partition/:style/:filename',
|
||||||
},
|
url: (ENV['PAPERCLIP_ROOT_URL'] || '/system') + '/:class/:attachment/:id_partition/:style/:filename',
|
||||||
fog_directory: '',
|
|
||||||
fog_host: ENV.fetch('PAPERCLIP_ROOT_URL') { '/system' }
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
|
@ -725,7 +725,7 @@ fi:
|
||||||
tip_local_timeline: Paikallinen aikajana näyttää instanssin %{instance} käyttäjien julkaisut. He ovat naapureitasi!
|
tip_local_timeline: Paikallinen aikajana näyttää instanssin %{instance} käyttäjien julkaisut. He ovat naapureitasi!
|
||||||
tip_mobile_webapp: Jos voit lisätä Mastodonin mobiiliselaimen kautta aloitusnäytöllesi, voit vastaanottaa push-ilmoituksia. Toiminta vastaa monin tavoin tavanomaista sovellusta!
|
tip_mobile_webapp: Jos voit lisätä Mastodonin mobiiliselaimen kautta aloitusnäytöllesi, voit vastaanottaa push-ilmoituksia. Toiminta vastaa monin tavoin tavanomaista sovellusta!
|
||||||
tips: Vinkkejä
|
tips: Vinkkejä
|
||||||
title: Tervetuloa mukaan, %name}!
|
title: Tervetuloa mukaan, %{name}!
|
||||||
users:
|
users:
|
||||||
invalid_email: Virheellinen sähköpostiosoite
|
invalid_email: Virheellinen sähköpostiosoite
|
||||||
invalid_otp_token: Virheellinen kaksivaiheisen todentamisen koodi
|
invalid_otp_token: Virheellinen kaksivaiheisen todentamisen koodi
|
||||||
|
|
|
@ -6,6 +6,7 @@ pl:
|
||||||
about_this: O tej instancji
|
about_this: O tej instancji
|
||||||
administered_by: 'Administrowana przez:'
|
administered_by: 'Administrowana przez:'
|
||||||
api: API
|
api: API
|
||||||
|
apps: Aplikacje
|
||||||
closed_registrations: Rejestracja na tej instancji jest obecnie zamknięta. Możesz jednak zarejestrować się na innej instancji, uzyskując dostęp do tej samej sieci.
|
closed_registrations: Rejestracja na tej instancji jest obecnie zamknięta. Możesz jednak zarejestrować się na innej instancji, uzyskując dostęp do tej samej sieci.
|
||||||
contact: Kontakt
|
contact: Kontakt
|
||||||
contact_missing: Nie ustawiono
|
contact_missing: Nie ustawiono
|
||||||
|
@ -281,6 +282,7 @@ pl:
|
||||||
search: Szukaj
|
search: Szukaj
|
||||||
title: Znane instancje
|
title: Znane instancje
|
||||||
invites:
|
invites:
|
||||||
|
deactivate_all: Unieważnij wszystkie
|
||||||
filter:
|
filter:
|
||||||
all: Wszystkie
|
all: Wszystkie
|
||||||
available: Dostępne
|
available: Dostępne
|
||||||
|
@ -663,11 +665,14 @@ pl:
|
||||||
publishing: Publikowanie
|
publishing: Publikowanie
|
||||||
web: Sieć
|
web: Sieć
|
||||||
remote_follow:
|
remote_follow:
|
||||||
acct: Podaj swój adres (nazwa@domena), z którego chcesz śledzić
|
acct: Podaj swój adres (nazwa@domena), z którego chcesz wykonać działanie
|
||||||
missing_resource: Nie udało się znaleźć adresu przekierowania z Twojej domeny
|
missing_resource: Nie udało się znaleźć adresu przekierowania z Twojej domeny
|
||||||
no_account_html: Nie masz konta? Możesz <a href='%{sign_up_path}' target='_blank'>zarejestrować się tutaj</a>
|
no_account_html: Nie masz konta? Możesz <a href='%{sign_up_path}' target='_blank'>zarejestrować się tutaj</a>
|
||||||
proceed: Śledź
|
proceed: Śledź
|
||||||
prompt: 'Zamierzasz śledzić:'
|
prompt: 'Zamierzasz śledzić:'
|
||||||
|
remote_interaction:
|
||||||
|
proceed: Przejdź do interakcji
|
||||||
|
prompt: 'Chcesz dokonać interakcji z tym wpisem:'
|
||||||
remote_unfollow:
|
remote_unfollow:
|
||||||
error: Błąd
|
error: Błąd
|
||||||
title: Tytuł
|
title: Tytuł
|
||||||
|
@ -756,6 +761,7 @@ pl:
|
||||||
private: Nie możesz przypiąć niepublicznego wpisu
|
private: Nie możesz przypiąć niepublicznego wpisu
|
||||||
reblog: Nie możesz przypiąć podbicia wpisu
|
reblog: Nie możesz przypiąć podbicia wpisu
|
||||||
show_more: Pokaż więcej
|
show_more: Pokaż więcej
|
||||||
|
sign_in_to_participate: Zaloguj się, aby udzielić się w tej konwersacji
|
||||||
title: '%{name}: "%{quote}"'
|
title: '%{name}: "%{quote}"'
|
||||||
visibilities:
|
visibilities:
|
||||||
private: Tylko dla śledzących
|
private: Tylko dla śledzących
|
||||||
|
|
|
@ -267,18 +267,19 @@ Rails.application.routes.draw do
|
||||||
|
|
||||||
get '/search', to: 'search#index', as: :search
|
get '/search', to: 'search#index', as: :search
|
||||||
|
|
||||||
resources :follows, only: [:create]
|
resources :follows, only: [:create]
|
||||||
resources :media, only: [:create, :update]
|
resources :media, only: [:create, :update]
|
||||||
resources :blocks, only: [:index]
|
resources :blocks, only: [:index]
|
||||||
resources :mutes, only: [:index] do
|
resources :mutes, only: [:index] do
|
||||||
collection do
|
collection do
|
||||||
get 'details'
|
get 'details'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
resources :favourites, only: [:index]
|
resources :favourites, only: [:index]
|
||||||
resources :bookmarks, only: [:index]
|
resources :bookmarks, only: [:index]
|
||||||
resources :reports, only: [:index, :create]
|
resources :reports, only: [:index, :create]
|
||||||
resources :filters, only: [:index, :create, :show, :update, :destroy]
|
resources :filters, only: [:index, :create, :show, :update, :destroy]
|
||||||
|
resources :endorsements, only: [:index]
|
||||||
|
|
||||||
namespace :apps do
|
namespace :apps do
|
||||||
get :verify_credentials, to: 'credentials#show'
|
get :verify_credentials, to: 'credentials#show'
|
||||||
|
|
|
@ -503,7 +503,7 @@ namespace :mastodon do
|
||||||
desc 'Remove media attachments attributed to silenced accounts'
|
desc 'Remove media attachments attributed to silenced accounts'
|
||||||
task remove_silenced: :environment do
|
task remove_silenced: :environment do
|
||||||
nb_media_attachments = 0
|
nb_media_attachments = 0
|
||||||
MediaAttachment.where(account: Account.silenced).select(:id).find_in_batches do |media_attachments|
|
MediaAttachment.where(account: Account.silenced).select(:id).reorder(nil).find_in_batches do |media_attachments|
|
||||||
nb_media_attachments += media_attachments.length
|
nb_media_attachments += media_attachments.length
|
||||||
Maintenance::DestroyMediaWorker.push_bulk(media_attachments.map(&:id))
|
Maintenance::DestroyMediaWorker.push_bulk(media_attachments.map(&:id))
|
||||||
end
|
end
|
||||||
|
@ -515,7 +515,7 @@ namespace :mastodon do
|
||||||
time_ago = ENV.fetch('NUM_DAYS') { 7 }.to_i.days.ago
|
time_ago = ENV.fetch('NUM_DAYS') { 7 }.to_i.days.ago
|
||||||
nb_media_attachments = 0
|
nb_media_attachments = 0
|
||||||
|
|
||||||
MediaAttachment.where.not(remote_url: '').where.not(file_file_name: nil).where('created_at < ?', time_ago).select(:id).find_in_batches do |media_attachments|
|
MediaAttachment.where.not(remote_url: '').where.not(file_file_name: nil).where('created_at < ?', time_ago).select(:id).reorder(nil).find_in_batches do |media_attachments|
|
||||||
nb_media_attachments += media_attachments.length
|
nb_media_attachments += media_attachments.length
|
||||||
Maintenance::UncacheMediaWorker.push_bulk(media_attachments.map(&:id))
|
Maintenance::UncacheMediaWorker.push_bulk(media_attachments.map(&:id))
|
||||||
end
|
end
|
||||||
|
@ -535,7 +535,7 @@ namespace :mastodon do
|
||||||
accounts = accounts.where(domain: ENV['DOMAIN']) if ENV['DOMAIN'].present?
|
accounts = accounts.where(domain: ENV['DOMAIN']) if ENV['DOMAIN'].present?
|
||||||
nb_accounts = 0
|
nb_accounts = 0
|
||||||
|
|
||||||
accounts.select(:id).find_in_batches do |accounts_batch|
|
accounts.select(:id).reorder(nil).find_in_batches do |accounts_batch|
|
||||||
nb_accounts += accounts_batch.length
|
nb_accounts += accounts_batch.length
|
||||||
Maintenance::RedownloadAccountMediaWorker.push_bulk(accounts_batch.map(&:id))
|
Maintenance::RedownloadAccountMediaWorker.push_bulk(accounts_batch.map(&:id))
|
||||||
end
|
end
|
||||||
|
@ -570,7 +570,7 @@ namespace :mastodon do
|
||||||
|
|
||||||
desc 'Generates home timelines for users who logged in in the past two weeks'
|
desc 'Generates home timelines for users who logged in in the past two weeks'
|
||||||
task build: :environment do
|
task build: :environment do
|
||||||
User.active.select(:id, :account_id).find_in_batches do |users|
|
User.active.select(:id, :account_id).reorder(nil).find_in_batches do |users|
|
||||||
RegenerationWorker.push_bulk(users.map(&:account_id))
|
RegenerationWorker.push_bulk(users.map(&:account_id))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,11 @@ require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe SuspendAccountService, type: :service do
|
RSpec.describe SuspendAccountService, type: :service do
|
||||||
describe '#call' do
|
describe '#call' do
|
||||||
|
before do
|
||||||
|
stub_request(:post, "https://alice.com/inbox").to_return(status: 201)
|
||||||
|
stub_request(:post, "https://bob.com/inbox").to_return(status: 201)
|
||||||
|
end
|
||||||
|
|
||||||
subject do
|
subject do
|
||||||
-> { described_class.new.call(account) }
|
-> { described_class.new.call(account) }
|
||||||
end
|
end
|
||||||
|
@ -14,6 +19,8 @@ RSpec.describe SuspendAccountService, type: :service do
|
||||||
let!(:active_relationship) { Fabricate(:follow, account: account) }
|
let!(:active_relationship) { Fabricate(:follow, account: account) }
|
||||||
let!(:passive_relationship) { Fabricate(:follow, target_account: account) }
|
let!(:passive_relationship) { Fabricate(:follow, target_account: account) }
|
||||||
let!(:subscription) { Fabricate(:subscription, account: account) }
|
let!(:subscription) { Fabricate(:subscription, account: account) }
|
||||||
|
let!(:remote_alice) { Fabricate(:account, inbox_url: 'https://alice.com/inbox', protocol: :activitypub) }
|
||||||
|
let!(:remote_bob) { Fabricate(:account, inbox_url: 'https://bob.com/inbox', protocol: :activitypub) }
|
||||||
|
|
||||||
it 'deletes associated records' do
|
it 'deletes associated records' do
|
||||||
is_expected.to change {
|
is_expected.to change {
|
||||||
|
@ -29,5 +36,11 @@ RSpec.describe SuspendAccountService, type: :service do
|
||||||
].map(&:count)
|
].map(&:count)
|
||||||
}.from([1, 1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0])
|
}.from([1, 1, 1, 1, 1, 1, 1, 1]).to([0, 0, 0, 0, 0, 0, 0, 0])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'sends a delete actor activity to all known inboxes' do
|
||||||
|
subject.call
|
||||||
|
expect(a_request(:post, "https://alice.com/inbox")).to have_been_made.once
|
||||||
|
expect(a_request(:post, "https://bob.com/inbox")).to have_been_made.once
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue