Merge branch 'master' into glitch-soc/merge-upstream

Conflicts:
- `app/javascript/mastodon/actions/compose.js`:
  Not a “real” conflict, but change too close to a change we made to
  fix the vanilla WebUI locally pushing authored local-only toots in the
  public TL view.
rebase/4.0.0rc2
Thibaut Girka 2020-07-22 13:38:17 +02:00
commit b9f351d845
90 changed files with 414 additions and 144 deletions

View File

@ -1,4 +1,3 @@
https://github.com/heroku/heroku-buildpack-apt https://github.com/heroku/heroku-buildpack-apt
https://github.com/Scalingo/ffmpeg-buildpack https://github.com/Scalingo/ffmpeg-buildpack
https://github.com/Scalingo/nodejs-buildpack
https://github.com/Scalingo/ruby-buildpack https://github.com/Scalingo/ruby-buildpack

View File

@ -72,11 +72,12 @@ aliases:
- run: - run:
name: Set bundler settings name: Set bundler settings
command: | command: |
bundle config clean 'true' bundle config --local clean 'true'
bundle config deployment 'true' bundle config --local deployment 'true'
bundle config with 'pam_authentication' bundle config --local with 'pam_authentication'
bundle config without 'development production' bundle config --local without 'development production'
bundle config frozen 'true' bundle config --local frozen 'true'
bundle config --local path $BUNDLE_PATH
- run: - run:
name: Install bundler dependencies name: Install bundler dependencies
command: bundle check || (bundle install && bundle clean) command: bundle check || (bundle install && bundle clean)

View File

@ -88,9 +88,6 @@
{ {
"url": "https://github.com/heroku/heroku-buildpack-apt" "url": "https://github.com/heroku/heroku-buildpack-apt"
}, },
{
"url": "heroku/nodejs"
},
{ {
"url": "heroku/ruby" "url": "heroku/ruby"
} }

View File

@ -58,6 +58,7 @@ class Api::V1::StatusesController < Api::BaseController
@status.discard @status.discard
RemovalWorker.perform_async(@status.id, redraft: true) RemovalWorker.perform_async(@status.id, redraft: true)
@status.account.statuses_count = @status.account.statuses_count - 1
render json: @status, serializer: REST::StatusSerializer, source_requested: true render json: @status, serializer: REST::StatusSerializer, source_requested: true
end end

View File

@ -56,7 +56,7 @@ class ApplicationController < ActionController::Base
end end
def store_current_location def store_current_location
store_location_for(:user, request.url) unless request.format == :json store_location_for(:user, request.url) unless [:json, :rss].include?(request.format&.to_sym)
end end
def require_admin! def require_admin!

View File

@ -163,7 +163,6 @@ export function submitCompose(routerHistory) {
// To make the app more responsive, immediately push the status // To make the app more responsive, immediately push the status
// into the columns // into the columns
const insertIfOnline = timelineId => { const insertIfOnline = timelineId => {
const timeline = getState().getIn(['timelines', timelineId]); const timeline = getState().getIn(['timelines', timelineId]);
@ -181,6 +180,7 @@ export function submitCompose(routerHistory) {
if (!response.data.local_only) { if (!response.data.local_only) {
insertIfOnline('public'); insertIfOnline('public');
} }
insertIfOnline(`account:${response.data.account.id}`);
} }
}).catch(function (error) { }).catch(function (error) {
dispatch(submitComposeFail(error)); dispatch(submitComposeFail(error));

View File

@ -3,7 +3,7 @@ import openDB from '../storage/db';
import { evictStatus } from '../storage/modifier'; import { evictStatus } from '../storage/modifier';
import { deleteFromTimelines } from './timelines'; import { deleteFromTimelines } from './timelines';
import { importFetchedStatus, importFetchedStatuses, importAccount, importStatus } from './importer'; import { importFetchedStatus, importFetchedStatuses, importAccount, importStatus, importFetchedAccount } from './importer';
import { ensureComposeIsVisible } from './compose'; import { ensureComposeIsVisible } from './compose';
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST'; export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
@ -155,6 +155,7 @@ export function deleteStatus(id, routerHistory, withRedraft = false) {
evictStatus(id); evictStatus(id);
dispatch(deleteStatusSuccess(id)); dispatch(deleteStatusSuccess(id));
dispatch(deleteFromTimelines(id)); dispatch(deleteFromTimelines(id));
dispatch(importFetchedAccount(response.data.account));
if (withRedraft) { if (withRedraft) {
dispatch(redraft(status, response.data.text)); dispatch(redraft(status, response.data.text));

View File

@ -71,9 +71,10 @@ const refreshHomeTimelineAndNotification = (dispatch, done) => {
dispatch(fetchAnnouncements(done)))))); dispatch(fetchAnnouncements(done))))));
}; };
export const connectUserStream = () => connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification); export const connectUserStream = () => connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification);
export const connectCommunityStream = ({ onlyMedia } = {}) => connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`); export const connectUserTimelineStream = (accountId) => connectTimelineStream(`account:${accountId}`, 'user');
export const connectPublicStream = ({ onlyMedia, onlyRemote } = {}) => connectTimelineStream(`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, `public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`); export const connectCommunityStream = ({ onlyMedia } = {}) => connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`);
export const connectHashtagStream = (id, tag, local, accept) => connectTimelineStream(`hashtag:${id}${local ? ':local' : ''}`, `hashtag${local ? ':local' : ''}&tag=${tag}`, null, accept); export const connectPublicStream = ({ onlyMedia, onlyRemote } = {}) => connectTimelineStream(`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`, `public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`);
export const connectDirectStream = () => connectTimelineStream('direct', 'direct'); export const connectHashtagStream = (id, tag, local, accept) => connectTimelineStream(`hashtag:${id}${local ? ':local' : ''}`, `hashtag${local ? ':local' : ''}&tag=${tag}`, null, accept);
export const connectListStream = id => connectTimelineStream(`list:${id}`, `list&list=${id}`); export const connectDirectStream = () => connectTimelineStream('direct', 'direct');
export const connectListStream = id => connectTimelineStream(`list:${id}`, `list&list=${id}`);

View File

@ -37,7 +37,7 @@ export function counterRenderer(counterType, isBold = true) {
return (displayNumber, pluralReady) => ( return (displayNumber, pluralReady) => (
<FormattedMessage <FormattedMessage
id='account.following_counter' id='account.following_counter'
defaultMessage='{count, plural, other {{counter} Following}}' defaultMessage='{count, plural, one {{counter} Following} other {{counter} Following}}'
values={{ values={{
count: pluralReady, count: pluralReady,
counter: renderCounter(displayNumber), counter: renderCounter(displayNumber),

View File

@ -20,7 +20,7 @@ const mapStateToProps = (state, { scrollKey }) => {
}; };
}; };
export default @connect(mapStateToProps) export default @connect(mapStateToProps, null, null, { forwardRef: true })
class ScrollableList extends PureComponent { class ScrollableList extends PureComponent {
static contextTypes = { static contextTypes = {

View File

@ -15,6 +15,8 @@ import { FormattedMessage } from 'react-intl';
import { fetchAccountIdentityProofs } from '../../actions/identity_proofs'; import { fetchAccountIdentityProofs } from '../../actions/identity_proofs';
import MissingIndicator from 'mastodon/components/missing_indicator'; import MissingIndicator from 'mastodon/components/missing_indicator';
import TimelineHint from 'mastodon/components/timeline_hint'; import TimelineHint from 'mastodon/components/timeline_hint';
import { me } from 'mastodon/initial_state';
import { connectUserTimelineStream } from '../../actions/streaming';
const emptyList = ImmutableList(); const emptyList = ImmutableList();
@ -73,6 +75,12 @@ class AccountTimeline extends ImmutablePureComponent {
this.props.dispatch(expandAccountTimeline(accountId, { withReplies })); this.props.dispatch(expandAccountTimeline(accountId, { withReplies }));
} }
componentDidMount () {
if (this.props.params.accountId === me) {
this.disconnect = this.props.dispatch(connectUserTimelineStream(me));
}
}
componentWillReceiveProps (nextProps) { componentWillReceiveProps (nextProps) {
if ((nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) || nextProps.withReplies !== this.props.withReplies) { if ((nextProps.params.accountId !== this.props.params.accountId && nextProps.params.accountId) || nextProps.withReplies !== this.props.withReplies) {
this.props.dispatch(fetchAccount(nextProps.params.accountId)); this.props.dispatch(fetchAccount(nextProps.params.accountId));
@ -86,6 +94,13 @@ class AccountTimeline extends ImmutablePureComponent {
} }
} }
componentWillUnmount () {
if (this.disconnect) {
this.disconnect();
this.disconnect = null;
}
}
handleLoadMore = maxId => { handleLoadMore = maxId => {
this.props.dispatch(expandAccountTimeline(this.props.params.accountId, { maxId, withReplies: this.props.withReplies })); this.props.dispatch(expandAccountTimeline(this.props.params.accountId, { maxId, withReplies: this.props.withReplies }));
} }

View File

@ -16,7 +16,7 @@
"account.followers": "مُتابِعون", "account.followers": "مُتابِعون",
"account.followers.empty": "لا أحد يتبع هذا الحساب بعد.", "account.followers.empty": "لا أحد يتبع هذا الحساب بعد.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "هذا الحساب لا يتبع أحدًا بعد.", "account.follows.empty": "هذا الحساب لا يتبع أحدًا بعد.",
"account.follows_you": "يتابعك", "account.follows_you": "يتابعك",
"account.hide_reblogs": "إخفاء ترقيات @{name}", "account.hide_reblogs": "إخفاء ترقيات @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Siguidores", "account.followers": "Siguidores",
"account.followers.empty": "Naide sigue a esti usuariu entá.", "account.followers.empty": "Naide sigue a esti usuariu entá.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Esti usuariu entá nun sigue a naide.", "account.follows.empty": "Esti usuariu entá nun sigue a naide.",
"account.follows_you": "Síguete", "account.follows_you": "Síguete",
"account.hide_reblogs": "Anubrir les comparticiones de @{name}", "account.hide_reblogs": "Anubrir les comparticiones de @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Последователи", "account.followers": "Последователи",
"account.followers.empty": "Все още никой не следва този потребител.", "account.followers.empty": "Все още никой не следва този потребител.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Този потребител все още не следва никого.", "account.follows.empty": "Този потребител все още не следва никого.",
"account.follows_you": "Твой последовател", "account.follows_you": "Твой последовател",
"account.hide_reblogs": "Hide boosts from @{name}", "account.hide_reblogs": "Hide boosts from @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "অনুসরণকারী", "account.followers": "অনুসরণকারী",
"account.followers.empty": "এই সদস্যকে এখনো কেউ অনুসরণ করে না।.", "account.followers.empty": "এই সদস্যকে এখনো কেউ অনুসরণ করে না।.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "এই সদস্য কাওকে এখনো অনুসরণ করেন না.", "account.follows.empty": "এই সদস্য কাওকে এখনো অনুসরণ করেন না.",
"account.follows_you": "আপনাকে অনুসরণ করে", "account.follows_you": "আপনাকে অনুসরণ করে",
"account.hide_reblogs": "@{name}'র সমর্থনগুলি লুকিয়ে ফেলুন", "account.hide_reblogs": "@{name}'র সমর্থনগুলি লুকিয়ে ফেলুন",

View File

@ -16,7 +16,7 @@
"account.followers": "Sledující", "account.followers": "Sledující",
"account.followers.empty": "Tohoto uživatele ještě nikdo nesleduje.", "account.followers.empty": "Tohoto uživatele ještě nikdo nesleduje.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Tento uživatel ještě nikoho nesleduje.", "account.follows.empty": "Tento uživatel ještě nikoho nesleduje.",
"account.follows_you": "Sleduje vás", "account.follows_you": "Sleduje vás",
"account.hide_reblogs": "Skrýt boosty od uživatele @{name}", "account.hide_reblogs": "Skrýt boosty od uživatele @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Dilynwyr", "account.followers": "Dilynwyr",
"account.followers.empty": "Nid oes neb yn dilyn y defnyddiwr hwn eto.", "account.followers.empty": "Nid oes neb yn dilyn y defnyddiwr hwn eto.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Nid yw'r defnyddiwr hwn yn dilyn unrhyw un eto.", "account.follows.empty": "Nid yw'r defnyddiwr hwn yn dilyn unrhyw un eto.",
"account.follows_you": "Yn eich dilyn chi", "account.follows_you": "Yn eich dilyn chi",
"account.hide_reblogs": "Cuddio bwstiau o @{name}", "account.hide_reblogs": "Cuddio bwstiau o @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Følgere", "account.followers": "Følgere",
"account.followers.empty": "Der er endnu ingen der følger denne bruger.", "account.followers.empty": "Der er endnu ingen der følger denne bruger.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Denne bruger følger endnu ikke nogen.", "account.follows.empty": "Denne bruger følger endnu ikke nogen.",
"account.follows_you": "Følger dig", "account.follows_you": "Følger dig",
"account.hide_reblogs": "Skjul fremhævelserne fra @{name}", "account.hide_reblogs": "Skjul fremhævelserne fra @{name}",

View File

@ -146,7 +146,7 @@
"id": "account.statuses_counter" "id": "account.statuses_counter"
}, },
{ {
"defaultMessage": "{count, plural, other {{counter} Following}}", "defaultMessage": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"id": "account.following_counter" "id": "account.following_counter"
}, },
{ {
@ -2659,6 +2659,22 @@
"defaultMessage": "Boost", "defaultMessage": "Boost",
"id": "status.reblog" "id": "status.reblog"
}, },
{
"defaultMessage": "Public",
"id": "privacy.public.short"
},
{
"defaultMessage": "Unlisted",
"id": "privacy.unlisted.short"
},
{
"defaultMessage": "Followers-only",
"id": "privacy.private.short"
},
{
"defaultMessage": "Direct",
"id": "privacy.direct.short"
},
{ {
"defaultMessage": "You can press {combo} to skip this next time", "defaultMessage": "You can press {combo} to skip this next time",
"id": "boost_modal.combo" "id": "boost_modal.combo"

View File

@ -16,7 +16,7 @@
"account.followers": "Followers", "account.followers": "Followers",
"account.followers.empty": "No one follows this user yet.", "account.followers.empty": "No one follows this user yet.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "This user doesn't follow anyone yet.", "account.follows.empty": "This user doesn't follow anyone yet.",
"account.follows_you": "Follows you", "account.follows_you": "Follows you",
"account.hide_reblogs": "Hide boosts from @{name}", "account.hide_reblogs": "Hide boosts from @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Jälgijad", "account.followers": "Jälgijad",
"account.followers.empty": "Keegi ei jälgi seda kasutajat veel.", "account.followers.empty": "Keegi ei jälgi seda kasutajat veel.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "See kasutaja ei jälgi veel kedagi.", "account.follows.empty": "See kasutaja ei jälgi veel kedagi.",
"account.follows_you": "Jälgib Teid", "account.follows_you": "Jälgib Teid",
"account.hide_reblogs": "Peida upitused kasutajalt @{name}", "account.hide_reblogs": "Peida upitused kasutajalt @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Jarraitzaileak", "account.followers": "Jarraitzaileak",
"account.followers.empty": "Ez du inork erabiltzaile hau jarraitzen oraindik.", "account.followers.empty": "Ez du inork erabiltzaile hau jarraitzen oraindik.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Erabiltzaile honek ez du inor jarraitzen oraindik.", "account.follows.empty": "Erabiltzaile honek ez du inor jarraitzen oraindik.",
"account.follows_you": "Jarraitzen dizu", "account.follows_you": "Jarraitzen dizu",
"account.hide_reblogs": "Ezkutatu @{name}(r)en bultzadak", "account.hide_reblogs": "Ezkutatu @{name}(r)en bultzadak",

View File

@ -16,7 +16,7 @@
"account.followers": "Seuraajaa", "account.followers": "Seuraajaa",
"account.followers.empty": "Tällä käyttäjällä ei ole vielä seuraajia.", "account.followers.empty": "Tällä käyttäjällä ei ole vielä seuraajia.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Tämä käyttäjä ei vielä seuraa ketään.", "account.follows.empty": "Tämä käyttäjä ei vielä seuraa ketään.",
"account.follows_you": "Seuraa sinua", "account.follows_you": "Seuraa sinua",
"account.hide_reblogs": "Piilota buustaukset käyttäjältä @{name}", "account.hide_reblogs": "Piilota buustaukset käyttäjältä @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Followers", "account.followers": "Followers",
"account.followers.empty": "No one follows this user yet.", "account.followers.empty": "No one follows this user yet.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "This user doesn't follow anyone yet.", "account.follows.empty": "This user doesn't follow anyone yet.",
"account.follows_you": "Follows you", "account.follows_you": "Follows you",
"account.hide_reblogs": "Hide boosts from @{name}", "account.hide_reblogs": "Hide boosts from @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "עוקבים", "account.followers": "עוקבים",
"account.followers.empty": "אף אחד לא עוקב אחר המשתמש הזה עדיין.", "account.followers.empty": "אף אחד לא עוקב אחר המשתמש הזה עדיין.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "משתמש זה לא עוקב אחר אף אחד עדיין.", "account.follows.empty": "משתמש זה לא עוקב אחר אף אחד עדיין.",
"account.follows_you": "במעקב אחריך", "account.follows_you": "במעקב אחריך",
"account.hide_reblogs": "להסתיר הידהודים מאת @{name}", "account.hide_reblogs": "להסתיר הידהודים מאת @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "फॉलोवर", "account.followers": "फॉलोवर",
"account.followers.empty": "कोई भी इस यूज़र् को फ़ॉलो नहीं करता है", "account.followers.empty": "कोई भी इस यूज़र् को फ़ॉलो नहीं करता है",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "यह यूज़र् अभी तक किसी को फॉलो नहीं करता है।", "account.follows.empty": "यह यूज़र् अभी तक किसी को फॉलो नहीं करता है।",
"account.follows_you": "आपको फॉलो करता है", "account.follows_you": "आपको फॉलो करता है",
"account.hide_reblogs": "@{name} के बूस्ट छुपाएं", "account.hide_reblogs": "@{name} के बूस्ट छुपाएं",

View File

@ -16,7 +16,7 @@
"account.followers": "Sljedbenici", "account.followers": "Sljedbenici",
"account.followers.empty": "No one follows this user yet.", "account.followers.empty": "No one follows this user yet.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "This user doesn't follow anyone yet.", "account.follows.empty": "This user doesn't follow anyone yet.",
"account.follows_you": "te slijedi", "account.follows_you": "te slijedi",
"account.hide_reblogs": "Hide boosts from @{name}", "account.hide_reblogs": "Hide boosts from @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Pengikut", "account.followers": "Pengikut",
"account.followers.empty": "Tidak ada satupun yang mengkuti pengguna ini saat ini.", "account.followers.empty": "Tidak ada satupun yang mengkuti pengguna ini saat ini.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Pengguna ini belum mengikuti siapapun.", "account.follows.empty": "Pengguna ini belum mengikuti siapapun.",
"account.follows_you": "Mengikuti anda", "account.follows_you": "Mengikuti anda",
"account.hide_reblogs": "Sembunyikan boosts dari @{name}", "account.hide_reblogs": "Sembunyikan boosts dari @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Sequanti", "account.followers": "Sequanti",
"account.followers.empty": "No one follows this user yet.", "account.followers.empty": "No one follows this user yet.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "This user doesn't follow anyone yet.", "account.follows.empty": "This user doesn't follow anyone yet.",
"account.follows_you": "Sequas tu", "account.follows_you": "Sequas tu",
"account.hide_reblogs": "Hide boosts from @{name}", "account.hide_reblogs": "Hide boosts from @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Fylgjendur", "account.followers": "Fylgjendur",
"account.followers.empty": "Ennþá fylgist enginn með þessum notanda.", "account.followers.empty": "Ennþá fylgist enginn með þessum notanda.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Þessi notandi fylgist ennþá ekki með neinum.", "account.follows.empty": "Þessi notandi fylgist ennþá ekki með neinum.",
"account.follows_you": "Fylgir þér", "account.follows_you": "Fylgir þér",
"account.hide_reblogs": "Fela endurbirtingar fyrir @{name}", "account.hide_reblogs": "Fela endurbirtingar fyrir @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "მიმდევრები", "account.followers": "მიმდევრები",
"account.followers.empty": "No one follows this user yet.", "account.followers.empty": "No one follows this user yet.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "This user doesn't follow anyone yet.", "account.follows.empty": "This user doesn't follow anyone yet.",
"account.follows_you": "მოგყვებათ", "account.follows_you": "მოგყვებათ",
"account.hide_reblogs": "დაიმალოს ბუსტები @{name}-სგან", "account.hide_reblogs": "დაიმალოს ბუსტები @{name}-სგან",

View File

@ -16,7 +16,7 @@
"account.followers": "Imeḍfaren", "account.followers": "Imeḍfaren",
"account.followers.empty": "Ar tura, ulac yiwen i yeṭṭafaṛen amseqdac-agi.", "account.followers.empty": "Ar tura, ulac yiwen i yeṭṭafaṛen amseqdac-agi.",
"account.followers_counter": "{count, plural, one {{count} n umeḍfar} other {{count} n imeḍfaren}}", "account.followers_counter": "{count, plural, one {{count} n umeḍfar} other {{count} n imeḍfaren}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Ar tura, amseqdac-agi ur yeṭṭafaṛ yiwen.", "account.follows.empty": "Ar tura, amseqdac-agi ur yeṭṭafaṛ yiwen.",
"account.follows_you": "Yeṭṭafaṛ-ik", "account.follows_you": "Yeṭṭafaṛ-ik",
"account.hide_reblogs": "Ffer ayen i ibeṭṭu @{name}", "account.hide_reblogs": "Ffer ayen i ibeṭṭu @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Оқырмандар", "account.followers": "Оқырмандар",
"account.followers.empty": "Әлі ешкім жазылмаған.", "account.followers.empty": "Әлі ешкім жазылмаған.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Ешкімге жазылмапты.", "account.follows.empty": "Ешкімге жазылмапты.",
"account.follows_you": "Сізге жазылыпты", "account.follows_you": "Сізге жазылыпты",
"account.hide_reblogs": "@{name} атты қолданушының әрекеттерін жасыру", "account.hide_reblogs": "@{name} атты қолданушының әрекеттерін жасыру",

View File

@ -16,7 +16,7 @@
"account.followers": "Followers", "account.followers": "Followers",
"account.followers.empty": "No one follows this user yet.", "account.followers.empty": "No one follows this user yet.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "This user doesn't follow anyone yet.", "account.follows.empty": "This user doesn't follow anyone yet.",
"account.follows_you": "Follows you", "account.follows_you": "Follows you",
"account.hide_reblogs": "Hide boosts from @{name}", "account.hide_reblogs": "Hide boosts from @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Followers", "account.followers": "Followers",
"account.followers.empty": "No one follows this user yet.", "account.followers.empty": "No one follows this user yet.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "This user doesn't follow anyone yet.", "account.follows.empty": "This user doesn't follow anyone yet.",
"account.follows_you": "Follows you", "account.follows_you": "Follows you",
"account.hide_reblogs": "Hide boosts from @{name}", "account.hide_reblogs": "Hide boosts from @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Followers", "account.followers": "Followers",
"account.followers.empty": "No one follows this user yet.", "account.followers.empty": "No one follows this user yet.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "This user doesn't follow anyone yet.", "account.follows.empty": "This user doesn't follow anyone yet.",
"account.follows_you": "Follows you", "account.follows_you": "Follows you",
"account.hide_reblogs": "Hide boosts from @{name}", "account.hide_reblogs": "Hide boosts from @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Sekotāji", "account.followers": "Sekotāji",
"account.followers.empty": "Šim lietotājam nav sekotāju.", "account.followers.empty": "Šim lietotājam nav sekotāju.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Šis lietotājs pagaidām nevienam neseko.", "account.follows.empty": "Šis lietotājs pagaidām nevienam neseko.",
"account.follows_you": "Seko tev", "account.follows_you": "Seko tev",
"account.hide_reblogs": "Paslēpt paceltos ierakstus no lietotāja @{name}", "account.hide_reblogs": "Paslēpt paceltos ierakstus no lietotāja @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Следбеници", "account.followers": "Следбеници",
"account.followers.empty": "Никој не го следи овој корисник сеуште.", "account.followers.empty": "Никој не го следи овој корисник сеуште.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Корисникот не следи никој сеуште.", "account.follows.empty": "Корисникот не следи никој сеуште.",
"account.follows_you": "Те следи тебе", "account.follows_you": "Те следи тебе",
"account.hide_reblogs": "Сокриј буст од @{name}", "account.hide_reblogs": "Сокриј буст од @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "പിന്തുടരുന്നവർ", "account.followers": "പിന്തുടരുന്നവർ",
"account.followers.empty": "ഈ ഉപയോക്താവിനെ ആരും ഇതുവരെ പിന്തുടരുന്നില്ല.", "account.followers.empty": "ഈ ഉപയോക്താവിനെ ആരും ഇതുവരെ പിന്തുടരുന്നില്ല.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "ഈ ഉപയോക്താവ് ആരേയും ഇതുവരെ പിന്തുടരുന്നില്ല.", "account.follows.empty": "ഈ ഉപയോക്താവ് ആരേയും ഇതുവരെ പിന്തുടരുന്നില്ല.",
"account.follows_you": "നിങ്ങളെ പിന്തുടരുന്നു", "account.follows_you": "നിങ്ങളെ പിന്തുടരുന്നു",
"account.hide_reblogs": "@{name} ബൂസ്റ്റ് ചെയ്തവ മറയ്കുക", "account.hide_reblogs": "@{name} ബൂസ്റ്റ് ചെയ്തവ മറയ്കുക",

View File

@ -16,7 +16,7 @@
"account.followers": "अनुयायी", "account.followers": "अनुयायी",
"account.followers.empty": "ह्या वापरकर्त्याचा आतापर्यंत कोणी अनुयायी नाही.", "account.followers.empty": "ह्या वापरकर्त्याचा आतापर्यंत कोणी अनुयायी नाही.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "हा वापरकर्ता अजूनपर्यंत कोणाचा अनुयायी नाही.", "account.follows.empty": "हा वापरकर्ता अजूनपर्यंत कोणाचा अनुयायी नाही.",
"account.follows_you": "तुमचा अनुयायी आहे", "account.follows_you": "तुमचा अनुयायी आहे",
"account.hide_reblogs": "@{name} पासून सर्व बूस्ट लपवा", "account.hide_reblogs": "@{name} पासून सर्व बूस्ट लपवा",

View File

@ -16,7 +16,7 @@
"account.followers": "Followers", "account.followers": "Followers",
"account.followers.empty": "No one follows this user yet.", "account.followers.empty": "No one follows this user yet.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "This user doesn't follow anyone yet.", "account.follows.empty": "This user doesn't follow anyone yet.",
"account.follows_you": "Follows you", "account.follows_you": "Follows you",
"account.hide_reblogs": "Hide boosts from @{name}", "account.hide_reblogs": "Hide boosts from @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Volgers", "account.followers": "Volgers",
"account.followers.empty": "Niemand volgt nog deze gebruiker.", "account.followers.empty": "Niemand volgt nog deze gebruiker.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Deze gebruiker volgt nog niemand.", "account.follows.empty": "Deze gebruiker volgt nog niemand.",
"account.follows_you": "Volgt jou", "account.follows_you": "Volgt jou",
"account.hide_reblogs": "Verberg boosts van @{name}", "account.hide_reblogs": "Verberg boosts van @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Fylgjarar", "account.followers": "Fylgjarar",
"account.followers.empty": "Ingen fylgjer denne brukaren enno.", "account.followers.empty": "Ingen fylgjer denne brukaren enno.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Denne brukaren fylgjer ikkje nokon enno.", "account.follows.empty": "Denne brukaren fylgjer ikkje nokon enno.",
"account.follows_you": "Fylgjer deg", "account.follows_you": "Fylgjer deg",
"account.hide_reblogs": "Gøym fremhevingar frå @{name}", "account.hide_reblogs": "Gøym fremhevingar frå @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Følgere", "account.followers": "Følgere",
"account.followers.empty": "Ingen følger denne brukeren ennå.", "account.followers.empty": "Ingen følger denne brukeren ennå.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Denne brukeren følger ikke noen enda.", "account.follows.empty": "Denne brukeren følger ikke noen enda.",
"account.follows_you": "Følger deg", "account.follows_you": "Følger deg",
"account.hide_reblogs": "Skjul fremhevinger fra @{name}", "account.hide_reblogs": "Skjul fremhevinger fra @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Seguidors", "account.followers": "Seguidors",
"account.followers.empty": "Degun sèc pas aqueste utilizaire pel moment.", "account.followers.empty": "Degun sèc pas aqueste utilizaire pel moment.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Aqueste utilizaire sèc pas degun pel moment.", "account.follows.empty": "Aqueste utilizaire sèc pas degun pel moment.",
"account.follows_you": "Vos sèc", "account.follows_you": "Vos sèc",
"account.hide_reblogs": "Rescondre los partatges de @{name}", "account.hide_reblogs": "Rescondre los partatges de @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Śledzący", "account.followers": "Śledzący",
"account.followers.empty": "Nikt jeszcze nie śledzi tego użytkownika.", "account.followers.empty": "Nikt jeszcze nie śledzi tego użytkownika.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Ten użytkownik nie śledzi jeszcze nikogo.", "account.follows.empty": "Ten użytkownik nie śledzi jeszcze nikogo.",
"account.follows_you": "Śledzi Cię", "account.follows_you": "Śledzi Cię",
"account.hide_reblogs": "Ukryj podbicia od @{name}", "account.hide_reblogs": "Ukryj podbicia od @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Urmăritori", "account.followers": "Urmăritori",
"account.followers.empty": "Acest utilizator nu are încă urmăritori.", "account.followers.empty": "Acest utilizator nu are încă urmăritori.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Acest utilizator nu urmărește pe nimeni încă.", "account.follows.empty": "Acest utilizator nu urmărește pe nimeni încă.",
"account.follows_you": "Te urmărește", "account.follows_you": "Te urmărește",
"account.hide_reblogs": "Ascunde impulsurile de la @{name}", "account.hide_reblogs": "Ascunde impulsurile de la @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Sighiduras", "account.followers": "Sighiduras",
"account.followers.empty": "Nemos sighit ancora custa persone.", "account.followers.empty": "Nemos sighit ancora custa persone.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Custa persone non sighit ancora a nemos.", "account.follows.empty": "Custa persone non sighit ancora a nemos.",
"account.follows_you": "Ti sighit", "account.follows_you": "Ti sighit",
"account.hide_reblogs": "Cua is cumpartziduras de @{name}", "account.hide_reblogs": "Cua is cumpartziduras de @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Sledujúci", "account.followers": "Sledujúci",
"account.followers.empty": "Tohto používateľa ešte nikto nenásleduje.", "account.followers.empty": "Tohto používateľa ešte nikto nenásleduje.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Tento používateľ ešte nikoho nenasleduje.", "account.follows.empty": "Tento používateľ ešte nikoho nenasleduje.",
"account.follows_you": "Nasleduje ťa", "account.follows_you": "Nasleduje ťa",
"account.hide_reblogs": "Skry vyzdvihnutia od @{name}", "account.hide_reblogs": "Skry vyzdvihnutia od @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Sledilci", "account.followers": "Sledilci",
"account.followers.empty": "Nihče ne sledi temu uporabniku.", "account.followers.empty": "Nihče ne sledi temu uporabniku.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Ta uporabnik še ne sledi nikomur.", "account.follows.empty": "Ta uporabnik še ne sledi nikomur.",
"account.follows_you": "Sledi tebi", "account.follows_you": "Sledi tebi",
"account.hide_reblogs": "Skrij spodbude od @{name}", "account.hide_reblogs": "Skrij spodbude od @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Pratioca", "account.followers": "Pratioca",
"account.followers.empty": "No one follows this user yet.", "account.followers.empty": "No one follows this user yet.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "This user doesn't follow anyone yet.", "account.follows.empty": "This user doesn't follow anyone yet.",
"account.follows_you": "Prati Vas", "account.follows_you": "Prati Vas",
"account.hide_reblogs": "Sakrij podrške koje daje korisnika @{name}", "account.hide_reblogs": "Sakrij podrške koje daje korisnika @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Пратиоци", "account.followers": "Пратиоци",
"account.followers.empty": "Тренутно нико не прати овог корисника.", "account.followers.empty": "Тренутно нико не прати овог корисника.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Корисник тренутно не прати никога.", "account.follows.empty": "Корисник тренутно не прати никога.",
"account.follows_you": "Прати Вас", "account.follows_you": "Прати Вас",
"account.hide_reblogs": "Сакриј подршке које даје корисника @{name}", "account.hide_reblogs": "Сакриј подршке које даје корисника @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Följare", "account.followers": "Följare",
"account.followers.empty": "Ingen följer denna användare än.", "account.followers.empty": "Ingen följer denna användare än.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Denna användare följer inte någon än.", "account.follows.empty": "Denna användare följer inte någon än.",
"account.follows_you": "Följer dig", "account.follows_you": "Följer dig",
"account.hide_reblogs": "Dölj knuffar från @{name}", "account.hide_reblogs": "Dölj knuffar från @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Followers", "account.followers": "Followers",
"account.followers.empty": "No one follows this user yet.", "account.followers.empty": "No one follows this user yet.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "This user doesn't follow anyone yet.", "account.follows.empty": "This user doesn't follow anyone yet.",
"account.follows_you": "Follows you", "account.follows_you": "Follows you",
"account.hide_reblogs": "Hide boosts from @{name}", "account.hide_reblogs": "Hide boosts from @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "பின்தொடர்பவர்கள்", "account.followers": "பின்தொடர்பவர்கள்",
"account.followers.empty": "இதுவரை யாரும் இந்த பயனரைப் பின்தொடரவில்லை.", "account.followers.empty": "இதுவரை யாரும் இந்த பயனரைப் பின்தொடரவில்லை.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "இந்த பயனர் இதுவரை யாரையும் பின்தொடரவில்லை.", "account.follows.empty": "இந்த பயனர் இதுவரை யாரையும் பின்தொடரவில்லை.",
"account.follows_you": "உங்களைப் பின்தொடர்கிறார்", "account.follows_you": "உங்களைப் பின்தொடர்கிறார்",
"account.hide_reblogs": "இருந்து ஊக்கியாக மறை @{name}", "account.hide_reblogs": "இருந்து ஊக்கியாக மறை @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Followers", "account.followers": "Followers",
"account.followers.empty": "No one follows this user yet.", "account.followers.empty": "No one follows this user yet.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "This user doesn't follow anyone yet.", "account.follows.empty": "This user doesn't follow anyone yet.",
"account.follows_you": "Follows you", "account.follows_you": "Follows you",
"account.hide_reblogs": "Hide boosts from @{name}", "account.hide_reblogs": "Hide boosts from @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "అనుచరులు", "account.followers": "అనుచరులు",
"account.followers.empty": "ఈ వినియోగదారుడిని ఇంకా ఎవరూ అనుసరించడంలేదు.", "account.followers.empty": "ఈ వినియోగదారుడిని ఇంకా ఎవరూ అనుసరించడంలేదు.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "ఈ వినియోగదారి ఇంకా ఎవరినీ అనుసరించడంలేదు.", "account.follows.empty": "ఈ వినియోగదారి ఇంకా ఎవరినీ అనుసరించడంలేదు.",
"account.follows_you": "మిమ్మల్ని అనుసరిస్తున్నారు", "account.follows_you": "మిమ్మల్ని అనుసరిస్తున్నారు",
"account.hide_reblogs": "@{name} నుంచి బూస్ట్ లను దాచిపెట్టు", "account.hide_reblogs": "@{name} నుంచి బూస్ట్ లను దాచిపెట్టు",

View File

@ -16,7 +16,7 @@
"account.followers": "ผู้ติดตาม", "account.followers": "ผู้ติดตาม",
"account.followers.empty": "ยังไม่มีใครติดตามผู้ใช้นี้", "account.followers.empty": "ยังไม่มีใครติดตามผู้ใช้นี้",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "ผู้ใช้นี้ยังไม่ได้ติดตามใคร", "account.follows.empty": "ผู้ใช้นี้ยังไม่ได้ติดตามใคร",
"account.follows_you": "ติดตามคุณ", "account.follows_you": "ติดตามคุณ",
"account.hide_reblogs": "ซ่อนการดันจาก @{name}", "account.hide_reblogs": "ซ่อนการดันจาก @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Takipçi", "account.followers": "Takipçi",
"account.followers.empty": "Henüz kimse bu kullanıcıyı takip etmiyor.", "account.followers.empty": "Henüz kimse bu kullanıcıyı takip etmiyor.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Bu kullanıcı henüz kimseyi takip etmiyor.", "account.follows.empty": "Bu kullanıcı henüz kimseyi takip etmiyor.",
"account.follows_you": "Seni takip ediyor", "account.follows_you": "Seni takip ediyor",
"account.hide_reblogs": "@{name} kişisinin yinelemelerini gizle", "account.hide_reblogs": "@{name} kişisinin yinelemelerini gizle",

View File

@ -16,7 +16,7 @@
"account.followers": "Followers", "account.followers": "Followers",
"account.followers.empty": "No one follows this user yet.", "account.followers.empty": "No one follows this user yet.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "This user doesn't follow anyone yet.", "account.follows.empty": "This user doesn't follow anyone yet.",
"account.follows_you": "Follows you", "account.follows_you": "Follows you",
"account.hide_reblogs": "Hide boosts from @{name}", "account.hide_reblogs": "Hide boosts from @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "Підписники", "account.followers": "Підписники",
"account.followers.empty": "Ніхто ще не підписався на цього користувача.", "account.followers.empty": "Ніхто ще не підписався на цього користувача.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "Цей користувач ще ні на кого не підписався.", "account.follows.empty": "Цей користувач ще ні на кого не підписався.",
"account.follows_you": "Підписаний(-а) на вас", "account.follows_you": "Підписаний(-а) на вас",
"account.hide_reblogs": "Сховати передмухи від @{name}", "account.hide_reblogs": "Сховати передмухи від @{name}",

View File

@ -16,7 +16,7 @@
"account.followers": "پیروکار", "account.followers": "پیروکار",
"account.followers.empty": "\"ہنوز اس صارف کی کوئی پیروی نہیں کرتا\".", "account.followers.empty": "\"ہنوز اس صارف کی کوئی پیروی نہیں کرتا\".",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "\"یہ صارف ہنوز کسی کی پیروی نہیں کرتا ہے\".", "account.follows.empty": "\"یہ صارف ہنوز کسی کی پیروی نہیں کرتا ہے\".",
"account.follows_you": "آپ کا پیروکار ہے", "account.follows_you": "آپ کا پیروکار ہے",
"account.hide_reblogs": "@{name} سے فروغ چھپائیں", "account.hide_reblogs": "@{name} سے فروغ چھپائیں",

View File

@ -16,7 +16,7 @@
"account.followers": "關注的人", "account.followers": "關注的人",
"account.followers.empty": "尚沒有人關注這位使用者。", "account.followers.empty": "尚沒有人關注這位使用者。",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "這位使用者尚未關注任何使用者。", "account.follows.empty": "這位使用者尚未關注任何使用者。",
"account.follows_you": "關注你", "account.follows_you": "關注你",
"account.hide_reblogs": "隱藏 @{name} 的轉推", "account.hide_reblogs": "隱藏 @{name} 的轉推",

View File

@ -16,7 +16,7 @@
"account.followers": "關注者", "account.followers": "關注者",
"account.followers.empty": "尚沒有人關注這位使用者。", "account.followers.empty": "尚沒有人關注這位使用者。",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}", "account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Followers}}",
"account.following_counter": "{count, plural, other {{counter} Following}}", "account.following_counter": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"account.follows.empty": "這位使用者尚未關注任何使用者。", "account.follows.empty": "這位使用者尚未關注任何使用者。",
"account.follows_you": "關注了你", "account.follows_you": "關注了你",
"account.hide_reblogs": "隱藏來自 @{name} 的轉推", "account.hide_reblogs": "隱藏來自 @{name} 的轉推",

View File

@ -767,10 +767,3 @@ html {
.compose-form .compose-form__warning { .compose-form .compose-form__warning {
box-shadow: none; box-shadow: none;
} }
.audio-player .video-player__controls button,
.audio-player .video-player__time-sep,
.audio-player .video-player__time-current,
.audio-player .video-player__time-total {
color: $primary-text-color;
}

View File

@ -157,6 +157,34 @@ class ActivityPub::Activity
fetch_remote_original_status fetch_remote_original_status
end end
def dereference_object!
return unless @object.is_a?(String)
return if invalid_origin?(@object)
object = fetch_resource(@object, true, signed_fetch_account)
return unless object.present? && object.is_a?(Hash) && supported_context?(object)
@object = object
end
def signed_fetch_account
first_mentioned_local_account || first_local_follower
end
def first_mentioned_local_account
audience = (as_array(@json['to']) + as_array(@json['cc'])).uniq
local_usernames = audience.select { |uri| ActivityPub::TagManager.instance.local_uri?(uri) }
.map { |uri| ActivityPub::TagManager.instance.uri_to_local_id(uri, :username) }
return if local_usernames.empty?
Account.local.where(username: local_usernames).first
end
def first_local_follower
@account.followers.local.first
end
def follow_request_from_object def follow_request_from_object
@follow_request ||= FollowRequest.find_by(target_account: @account, uri: object_uri) unless object_uri.nil? @follow_request ||= FollowRequest.find_by(target_account: @account, uri: object_uri) unless object_uri.nil?
end end

View File

@ -4,25 +4,32 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity
def perform def perform
return reject_payload! if delete_arrived_first?(@json['id']) || !related_to_local_activity? return reject_payload! if delete_arrived_first?(@json['id']) || !related_to_local_activity?
original_status = status_from_object RedisLock.acquire(lock_options) do |lock|
if lock.acquired?
original_status = status_from_object
return reject_payload! if original_status.nil? || !announceable?(original_status) return reject_payload! if original_status.nil? || !announceable?(original_status)
status = Status.find_by(account: @account, reblog: original_status) @status = Status.find_by(account: @account, reblog: original_status)
return status unless status.nil? return @status unless @status.nil?
status = Status.create!( @status = Status.create!(
account: @account, account: @account,
reblog: original_status, reblog: original_status,
uri: @json['id'], uri: @json['id'],
created_at: @json['published'], created_at: @json['published'],
override_timestamps: @options[:override_timestamps], override_timestamps: @options[:override_timestamps],
visibility: visibility_from_audience visibility: visibility_from_audience
) )
distribute(status) distribute(@status)
status else
raise Mastodon::RaceConditionError
end
end
@status
end end
private private
@ -54,4 +61,8 @@ class ActivityPub::Activity::Announce < ActivityPub::Activity
def reblog_of_local_status? def reblog_of_local_status?
status_from_uri(object_uri)&.account&.local? status_from_uri(object_uri)&.account&.local?
end end
def lock_options
{ redis: Redis.current, key: "announce:#{@object['id']}" }
end
end end

View File

@ -4,7 +4,12 @@ class ActivityPub::Activity::Block < ActivityPub::Activity
def perform def perform
target_account = account_from_uri(object_uri) target_account = account_from_uri(object_uri)
return if target_account.nil? || !target_account.local? || @account.blocking?(target_account) return if target_account.nil? || !target_account.local?
if @account.blocking?(target_account)
@account.block_relationships.find_by(target_account: target_account).update(uri: @json['id']) if @json['id'].present?
return
end
UnfollowService.new.call(target_account, @account) if target_account.following?(@account) UnfollowService.new.call(target_account, @account) if target_account.following?(@account)

View File

@ -2,6 +2,8 @@
class ActivityPub::Activity::Create < ActivityPub::Activity class ActivityPub::Activity::Create < ActivityPub::Activity
def perform def perform
dereference_object!
case @object['type'] case @object['type']
when 'EncryptedMessage' when 'EncryptedMessage'
create_encrypted_message create_encrypted_message

View File

@ -13,11 +13,62 @@ class ActivityPub::Activity::Undo < ActivityPub::Activity
undo_like undo_like
when 'Block' when 'Block'
undo_block undo_block
when nil
handle_reference
end end
end end
private private
def handle_reference
# Some implementations do not inline the object, and as we don't have a
# global index, we have to guess what object it is.
return if object_uri.nil?
try_undo_announce || try_undo_accept || try_undo_follow || try_undo_like || try_undo_block || delete_later!(object_uri)
end
def try_undo_announce
status = Status.where.not(reblog_of_id: nil).find_by(uri: object_uri, account: @account)
if status.present?
RemoveStatusService.new.call(status)
true
else
false
end
end
def try_undo_accept
# We can't currently handle `Undo Accept` as we don't record `Accept`'s uri
false
end
def try_undo_follow
follow = @account.follow_requests.find_by(uri: object_uri) || @account.active_relationships.find_by(uri: object_uri)
if follow.present?
follow.destroy
true
else
false
end
end
def try_undo_like
# There is an index on accounts, but an account may have *many* favs, so this may be too costly
false
end
def try_undo_block
block = @account.block_relationships.find_by(uri: object_uri)
if block.present?
UnblockService.new.call(@account, block.target_account)
true
else
false
end
end
def undo_announce def undo_announce
return if object_uri.nil? return if object_uri.nil?

View File

@ -4,6 +4,8 @@ class ActivityPub::Activity::Update < ActivityPub::Activity
SUPPORTED_TYPES = %w(Application Group Organization Person Service).freeze SUPPORTED_TYPES = %w(Application Group Organization Person Service).freeze
def perform def perform
dereference_object!
if equals_or_includes_any?(@object['type'], SUPPORTED_TYPES) if equals_or_includes_any?(@object['type'], SUPPORTED_TYPES)
update_account update_account
elsif equals_or_includes_any?(@object['type'], %w(Question)) elsif equals_or_includes_any?(@object['type'], %w(Question))

View File

@ -7,6 +7,7 @@ module Mastodon
class HostValidationError < ValidationError; end class HostValidationError < ValidationError; end
class LengthValidationError < ValidationError; end class LengthValidationError < ValidationError; end
class DimensionsValidationError < ValidationError; end class DimensionsValidationError < ValidationError; end
class StreamValidationError < ValidationError; end
class RaceConditionError < Error; end class RaceConditionError < Error; end
class RateLimitExceededError < Error; end class RateLimitExceededError < Error; end

View File

@ -139,9 +139,15 @@ class FeedManager
end end
def clear_from_timeline(account, target_account) def clear_from_timeline(account, target_account)
# Clear from timeline all statuses from or mentionning target_account
timeline_key = key(:home, account.id) timeline_key = key(:home, account.id)
timeline_status_ids = redis.zrange(timeline_key, 0, -1) timeline_status_ids = redis.zrange(timeline_key, 0, -1)
target_statuses = Status.where(id: timeline_status_ids, account: target_account) statuses = Status.where(id: timeline_status_ids).select(:id, :reblog_of_id, :account_id).to_a
reblogged_ids = Status.where(id: statuses.map(&:reblog_of_id).compact, account: target_account).pluck(:id)
with_mentions_ids = Mention.active.where(status_id: statuses.flat_map { |s| [s.id, s.reblog_of_id] }.compact, account: target_account).pluck(:status_id)
target_statuses = statuses.filter do |status|
status.account_id == target_account.id || reblogged_ids.include?(status.reblog_of_id) || with_mentions_ids.include?(status.id) || with_mentions_ids.include?(status.reblog_of_id)
end
target_statuses.each do |status| target_statuses.each do |status|
unpush_from_home(account, status) unpush_from_home(account, status)

View File

@ -29,7 +29,7 @@ module Remotable
rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError => e rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError => e
Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}" Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}"
raise e unless suppress_errors raise e unless suppress_errors
rescue Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, Paperclip::Error, Mastodon::DimensionsValidationError => e rescue Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, Paperclip::Error, Mastodon::DimensionsValidationError, Mastodon::StreamValidationError => e
Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}" Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}"
end end

View File

@ -336,6 +336,7 @@ class MediaAttachment < ApplicationRecord
return unless movie.valid? return unless movie.valid?
raise Mastodon::StreamValidationError, 'Video has no video stream' if movie.width.nil? || movie.frame_rate.nil?
raise Mastodon::DimensionsValidationError, "#{movie.width}x#{movie.height} videos are not supported" if movie.width * movie.height > MAX_VIDEO_MATRIX_LIMIT raise Mastodon::DimensionsValidationError, "#{movie.width}x#{movie.height} videos are not supported" if movie.width * movie.height > MAX_VIDEO_MATRIX_LIMIT
raise Mastodon::DimensionsValidationError, "#{movie.frame_rate.to_i}fps videos are not supported" if movie.frame_rate > MAX_VIDEO_FRAME_RATE raise Mastodon::DimensionsValidationError, "#{movie.frame_rate.to_i}fps videos are not supported" if movie.frame_rate > MAX_VIDEO_FRAME_RATE
end end

View File

@ -4,7 +4,7 @@ class REST::MediaAttachmentSerializer < ActiveModel::Serializer
include RoutingHelper include RoutingHelper
attributes :id, :type, :url, :preview_url, attributes :id, :type, :url, :preview_url,
:remote_url, :text_url, :meta, :remote_url, :preview_remote_url, :text_url, :meta,
:description, :blurhash :description, :blurhash
def id def id
@ -35,6 +35,10 @@ class REST::MediaAttachmentSerializer < ActiveModel::Serializer
end end
end end
def preview_remote_url
object.thumbnail_remote_url.presence
end
def text_url def text_url
object.local? ? medium_url(object) : nil object.local? ? medium_url(object) : nil
end end

View File

@ -12,7 +12,7 @@ class UpdateAccountService < BaseService
check_links(account) check_links(account)
process_hashtags(account) process_hashtags(account)
end end
rescue Mastodon::DimensionsValidationError => de rescue Mastodon::DimensionsValidationError, Mastodon::StreamValidationError => de
account.errors.add(:avatar, de.message) account.errors.add(:avatar, de.message)
false false
end end

View File

@ -3,7 +3,7 @@
class ActivityPub::ProcessingWorker class ActivityPub::ProcessingWorker
include Sidekiq::Worker include Sidekiq::Worker
sidekiq_options backtrace: true sidekiq_options backtrace: true, retry: 8
def perform(account_id, body, delivered_to_account_id = nil) def perform(account_id, body, delivered_to_account_id = nil)
ActivityPub::ProcessCollectionService.new.call(body, Account.find(account_id), override_timestamps: true, delivered_to_account_id: delivered_to_account_id, delivery: true) ActivityPub::ProcessCollectionService.new.call(body, Account.find(account_id), override_timestamps: true, delivered_to_account_id: delivered_to_account_id, delivery: true)

View File

@ -16,6 +16,12 @@ ingress:
kubernetes.io/ingress.class: nginx kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: "true" kubernetes.io/tls-acme: "true"
# cert-manager.io/cluster-issuer: "letsencrypt" # cert-manager.io/cluster-issuer: "letsencrypt"
#
# ensure that NGINX's upload size matches Mastodon's
# for the K8s ingress controller:
# nginx.ingress.kubernetes.io/proxy-body-size: 40m
# for the NGINX ingress controller:
# nginx.org/client-max-body-size: 40m
# this value is used for LOCAL_DOMAIN # this value is used for LOCAL_DOMAIN
hostname: mastodon.local hostname: mastodon.local
tls: tls:

View File

@ -18,7 +18,7 @@ module Paperclip
@type_from_mime_magic ||= begin @type_from_mime_magic ||= begin
begin begin
File.open(@file.path) do |file| File.open(@file.path) do |file|
MimeMagic.by_magic(file)&.type MimeMagic.by_magic(file)&.type || ''
end end
rescue Errno::ENOENT rescue Errno::ENOENT
'' ''

View File

@ -110,7 +110,7 @@
"intl-relativeformat": "^6.4.3", "intl-relativeformat": "^6.4.3",
"is-nan": "^1.3.0", "is-nan": "^1.3.0",
"js-yaml": "^3.13.1", "js-yaml": "^3.13.1",
"lodash": "^4.17.14", "lodash": "^4.17.19",
"mark-loader": "^0.1.6", "mark-loader": "^0.1.6",
"marky": "^1.2.1", "marky": "^1.2.1",
"mini-css-extract-plugin": "^0.9.0", "mini-css-extract-plugin": "^0.9.0",

View File

@ -5,6 +5,21 @@ RSpec.describe AccountsController, type: :controller do
let(:account) { Fabricate(:user).account } let(:account) { Fabricate(:user).account }
shared_examples 'cachable response' do
it 'does not set cookies' do
expect(response.cookies).to be_empty
expect(response.headers['Set-Cookies']).to be nil
end
it 'does not set sessions' do
expect(session).to be_empty
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
end
describe 'GET #show' do describe 'GET #show' do
let(:format) { 'html' } let(:format) { 'html' }
@ -323,9 +338,7 @@ RSpec.describe AccountsController, type: :controller do
expect(response.content_type).to eq 'application/activity+json' expect(response.content_type).to eq 'application/activity+json'
end end
it 'returns public Cache-Control header' do it_behaves_like 'cachable response'
expect(response.headers['Cache-Control']).to include 'public'
end
it 'renders account' do it 'renders account' do
json = body_as_json json = body_as_json
@ -343,9 +356,7 @@ RSpec.describe AccountsController, type: :controller do
expect(response.content_type).to eq 'application/activity+json' expect(response.content_type).to eq 'application/activity+json'
end end
it 'returns public Cache-Control header' do it_behaves_like 'cachable response'
expect(response.headers['Cache-Control']).to include 'public'
end
it 'returns Vary header with Signature' do it 'returns Vary header with Signature' do
expect(response.headers['Vary']).to include 'Signature' expect(response.headers['Vary']).to include 'Signature'
@ -401,9 +412,7 @@ RSpec.describe AccountsController, type: :controller do
expect(response.content_type).to eq 'application/activity+json' expect(response.content_type).to eq 'application/activity+json'
end end
it 'returns public Cache-Control header' do it_behaves_like 'cachable response'
expect(response.headers['Cache-Control']).to include 'public'
end
it 'renders account' do it 'renders account' do
json = body_as_json json = body_as_json
@ -447,9 +456,7 @@ RSpec.describe AccountsController, type: :controller do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end end
it 'returns public Cache-Control header' do it_behaves_like 'cachable response'
expect(response.headers['Cache-Control']).to include 'public'
end
end end
context do context do

View File

@ -6,6 +6,21 @@ RSpec.describe ActivityPub::CollectionsController, type: :controller do
let!(:account) { Fabricate(:account) } let!(:account) { Fabricate(:account) }
let(:remote_account) { nil } let(:remote_account) { nil }
shared_examples 'cachable response' do
it 'does not set cookies' do
expect(response.cookies).to be_empty
expect(response.headers['Set-Cookies']).to be nil
end
it 'does not set sessions' do
expect(session).to be_empty
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
end
before do before do
allow(controller).to receive(:signed_request_account).and_return(remote_account) allow(controller).to receive(:signed_request_account).and_return(remote_account)
@ -31,9 +46,7 @@ RSpec.describe ActivityPub::CollectionsController, type: :controller do
expect(response.content_type).to eq 'application/activity+json' expect(response.content_type).to eq 'application/activity+json'
end end
it 'returns public Cache-Control header' do it_behaves_like 'cachable response'
expect(response.headers['Cache-Control']).to include 'public'
end
it 'returns orderedItems with pinned statuses' do it 'returns orderedItems with pinned statuses' do
json = body_as_json json = body_as_json
@ -58,9 +71,7 @@ RSpec.describe ActivityPub::CollectionsController, type: :controller do
expect(response.content_type).to eq 'application/activity+json' expect(response.content_type).to eq 'application/activity+json'
end end
it 'returns public Cache-Control header' do it_behaves_like 'cachable response'
expect(response.headers['Cache-Control']).to include 'public'
end
it 'returns orderedItems with pinned statuses' do it 'returns orderedItems with pinned statuses' do
json = body_as_json json = body_as_json

View File

@ -3,6 +3,21 @@ require 'rails_helper'
RSpec.describe ActivityPub::OutboxesController, type: :controller do RSpec.describe ActivityPub::OutboxesController, type: :controller do
let!(:account) { Fabricate(:account) } let!(:account) { Fabricate(:account) }
shared_examples 'cachable response' do
it 'does not set cookies' do
expect(response.cookies).to be_empty
expect(response.headers['Set-Cookies']).to be nil
end
it 'does not set sessions' do
expect(session).to be_empty
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
end
before do before do
Fabricate(:status, account: account, visibility: :public) Fabricate(:status, account: account, visibility: :public)
Fabricate(:status, account: account, visibility: :unlisted) Fabricate(:status, account: account, visibility: :unlisted)
@ -39,9 +54,7 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
expect(json[:totalItems]).to eq 4 expect(json[:totalItems]).to eq 4
end end
it 'returns public Cache-Control header' do it_behaves_like 'cachable response'
expect(response.headers['Cache-Control']).to include 'public'
end
end end
context 'with page requested' do context 'with page requested' do
@ -62,9 +75,7 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do
expect(json[:orderedItems].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true expect(json[:orderedItems].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
end end
it 'returns public Cache-Control header' do it_behaves_like 'cachable response'
expect(response.headers['Cache-Control']).to include 'public'
end
end end
end end

View File

@ -7,6 +7,21 @@ RSpec.describe ActivityPub::RepliesController, type: :controller do
let(:remote_reply_id) { nil } let(:remote_reply_id) { nil }
let(:remote_account) { nil } let(:remote_account) { nil }
shared_examples 'cachable response' do
it 'does not set cookies' do
expect(response.cookies).to be_empty
expect(response.headers['Set-Cookies']).to be nil
end
it 'does not set sessions' do
expect(session).to be_empty
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
end
before do before do
allow(controller).to receive(:signed_request_account).and_return(remote_account) allow(controller).to receive(:signed_request_account).and_return(remote_account)
@ -36,9 +51,7 @@ RSpec.describe ActivityPub::RepliesController, type: :controller do
expect(response.content_type).to eq 'application/activity+json' expect(response.content_type).to eq 'application/activity+json'
end end
it 'returns public Cache-Control header' do it_behaves_like 'cachable response'
expect(response.headers['Cache-Control']).to include 'public'
end
it 'returns items with account\'s own replies' do it 'returns items with account\'s own replies' do
json = body_as_json json = body_as_json
@ -87,9 +100,7 @@ RSpec.describe ActivityPub::RepliesController, type: :controller do
expect(response.content_type).to eq 'application/activity+json' expect(response.content_type).to eq 'application/activity+json'
end end
it 'returns public Cache-Control header' do it_behaves_like 'cachable response'
expect(response.headers['Cache-Control']).to include 'public'
end
context 'without only_other_accounts' do context 'without only_other_accounts' do
it 'returns items with account\'s own replies' do it 'returns items with account\'s own replies' do

View File

@ -5,6 +5,21 @@ require 'rails_helper'
describe StatusesController do describe StatusesController do
render_views render_views
shared_examples 'cachable response' do
it 'does not set cookies' do
expect(response.cookies).to be_empty
expect(response.headers['Set-Cookies']).to be nil
end
it 'does not set sessions' do
expect(session).to be_empty
end
it 'returns public Cache-Control header' do
expect(response.headers['Cache-Control']).to include 'public'
end
end
describe 'GET #show' do describe 'GET #show' do
let(:account) { Fabricate(:account) } let(:account) { Fabricate(:account) }
let(:status) { Fabricate(:status, account: account) } let(:status) { Fabricate(:status, account: account) }
@ -80,9 +95,7 @@ describe StatusesController do
expect(response.headers['Vary']).to eq 'Accept' expect(response.headers['Vary']).to eq 'Accept'
end end
it 'returns public Cache-Control header' do it_behaves_like 'cachable response'
expect(response.headers['Cache-Control']).to include 'public'
end
it 'returns Content-Type header' do it 'returns Content-Type header' do
expect(response.headers['Content-Type']).to include 'application/activity+json' expect(response.headers['Content-Type']).to include 'application/activity+json'
@ -470,9 +483,7 @@ describe StatusesController do
expect(response.headers['Vary']).to eq 'Accept' expect(response.headers['Vary']).to eq 'Accept'
end end
it 'returns public Cache-Control header' do it_behaves_like 'cachable response'
expect(response.headers['Cache-Control']).to include 'public'
end
it 'returns Content-Type header' do it 'returns Content-Type header' do
expect(response.headers['Content-Type']).to include 'application/activity+json' expect(response.headers['Content-Type']).to include 'application/activity+json'

View File

@ -28,6 +28,28 @@ RSpec.describe ActivityPub::Activity::Block do
end end
end end
context 'when the recipient is already blocked' do
before do
sender.block!(recipient, uri: 'old')
end
describe '#perform' do
subject { described_class.new(json, sender) }
before do
subject.perform
end
it 'creates a block from sender to recipient' do
expect(sender.blocking?(recipient)).to be true
end
it 'sets the uri to that of last received block activity' do
expect(sender.block_relationships.find_by(target_account: recipient).uri).to eq 'foo'
end
end
end
context 'when the recipient follows the sender' do context 'when the recipient follows the sender' do
before do before do
recipient.follow!(sender) recipient.follow!(sender)

View File

@ -50,6 +50,19 @@ RSpec.describe ActivityPub::Activity::Undo do
expect(sender.reblogged?(status)).to be false expect(sender.reblogged?(status)).to be false
end end
end end
context 'with only object uri' do
let(:object_json) { 'bar' }
before do
Fabricate(:status, reblog: status, account: sender, uri: 'bar')
end
it 'deletes the reblog by uri' do
subject.perform
expect(sender.reblogged?(status)).to be false
end
end
end end
context 'with Accept' do context 'with Accept' do
@ -91,13 +104,22 @@ RSpec.describe ActivityPub::Activity::Undo do
end end
before do before do
sender.block!(recipient) sender.block!(recipient, uri: 'bar')
end end
it 'deletes block from sender to recipient' do it 'deletes block from sender to recipient' do
subject.perform subject.perform
expect(sender.blocking?(recipient)).to be false expect(sender.blocking?(recipient)).to be false
end end
context 'with only object uri' do
let(:object_json) { 'bar' }
it 'deletes block from sender to recipient' do
subject.perform
expect(sender.blocking?(recipient)).to be false
end
end
end end
context 'with Follow' do context 'with Follow' do
@ -113,13 +135,22 @@ RSpec.describe ActivityPub::Activity::Undo do
end end
before do before do
sender.follow!(recipient) sender.follow!(recipient, uri: 'bar')
end end
it 'deletes follow from sender to recipient' do it 'deletes follow from sender to recipient' do
subject.perform subject.perform
expect(sender.following?(recipient)).to be false expect(sender.following?(recipient)).to be false
end end
context 'with only object uri' do
let(:object_json) { 'bar' }
it 'deletes follow from sender to recipient' do
subject.perform
expect(sender.following?(recipient)).to be false
end
end
end end
context 'with Like' do context 'with Like' do

View File

@ -531,4 +531,29 @@ RSpec.describe FeedManager do
expect(Redis.current).to have_received(:publish).with("timeline:#{receiver.id}", deletion) expect(Redis.current).to have_received(:publish).with("timeline:#{receiver.id}", deletion)
end end
end end
describe '#clear_from_timeline' do
let(:account) { Fabricate(:account) }
let(:followed_account) { Fabricate(:account) }
let(:target_account) { Fabricate(:account) }
let(:status_1) { Fabricate(:status, account: followed_account) }
let(:status_2) { Fabricate(:status, account: target_account) }
let(:status_3) { Fabricate(:status, account: followed_account, mentions: [Fabricate(:mention, account: target_account)]) }
let(:status_4) { Fabricate(:status, mentions: [Fabricate(:mention, account: target_account)]) }
let(:status_5) { Fabricate(:status, account: followed_account, reblog: status_4) }
let(:status_6) { Fabricate(:status, account: followed_account, reblog: status_2) }
let(:status_7) { Fabricate(:status, account: followed_account) }
before do
[status_1, status_3, status_5, status_6, status_7].each do |status|
Redis.current.zadd("feed:home:#{account.id}", status.id, status.id)
end
end
it 'correctly cleans the timeline' do
FeedManager.instance.clear_from_timeline(account, target_account)
expect(Redis.current.zrange("feed:home:#{account.id}", 0, -1)).to eq [status_1.id.to_s, status_7.id.to_s]
end
end
end end

View File

@ -6979,10 +6979,10 @@ lodash.uniq@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.3.0, lodash@~4.17.12: lodash@^4.0.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.3.0, lodash@~4.17.12:
version "4.17.15" version "4.17.19"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
loglevel@^1.6.8: loglevel@^1.6.8:
version "1.6.8" version "1.6.8"