Add media timeline (#6631)

th-downstream
Yamagishi Kazutoshi 7 years ago committed by Eugen Rochko
parent b634cb45dc
commit ccc240b59c

@ -40,10 +40,9 @@ const refreshHomeTimelineAndNotification = (dispatch, done) => {
dispatch(expandHomeTimeline({}, () => dispatch(expandNotifications({}, done)))); dispatch(expandHomeTimeline({}, () => dispatch(expandNotifications({}, done))));
}; };
export const connectUserStream = () => connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification); export const connectUserStream = () => connectTimelineStream('home', 'user', refreshHomeTimelineAndNotification);
export const connectCommunityStream = () => connectTimelineStream('community', 'public:local'); export const connectCommunityStream = ({ onlyMedia } = {}) => connectTimelineStream(`community${onlyMedia ? ':media' : ''}`, `public:local${onlyMedia ? ':media' : ''}`);
export const connectMediaStream = () => connectTimelineStream('community', 'public:local'); export const connectPublicStream = ({ onlyMedia } = {}) => connectTimelineStream(`public${onlyMedia ? ':media' : ''}`, `public${onlyMedia ? ':media' : ''}`);
export const connectPublicStream = () => connectTimelineStream('public', 'public'); export const connectHashtagStream = tag => connectTimelineStream(`hashtag:${tag}`, `hashtag&tag=${tag}`);
export const connectHashtagStream = (tag) => connectTimelineStream(`hashtag:${tag}`, `hashtag&tag=${tag}`); export const connectDirectStream = () => connectTimelineStream('direct', 'direct');
export const connectDirectStream = () => connectTimelineStream('direct', 'direct'); export const connectListStream = id => connectTimelineStream(`list:${id}`, `list&list=${id}`);
export const connectListStream = (id) => connectTimelineStream(`list:${id}`, `list&list=${id}`);

@ -93,15 +93,15 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
}; };
}; };
export const expandHomeTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId }, done); export const expandHomeTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId }, done);
export const expandPublicTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('public', '/api/v1/timelines/public', { max_id: maxId }, done); export const expandPublicTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`public${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { max_id: maxId, only_media: !!onlyMedia }, done);
export const expandCommunityTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('community', '/api/v1/timelines/public', { local: true, max_id: maxId }, done); export const expandCommunityTimeline = ({ maxId, onlyMedia } = {}, done = noOp) => expandTimeline(`community${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { local: true, max_id: maxId, only_media: !!onlyMedia }, done);
export const expandDirectTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('direct', '/api/v1/timelines/direct', { max_id: maxId }, done); export const expandDirectTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('direct', '/api/v1/timelines/direct', { max_id: maxId }, done);
export const expandAccountTimeline = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId }); export const expandAccountTimeline = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId });
export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true }); export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true });
export const expandAccountMediaTimeline = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true }); export const expandAccountMediaTimeline = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true });
export const expandHashtagTimeline = (hashtag, { maxId } = {}, done = noOp) => expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`, { max_id: maxId }, done); export const expandHashtagTimeline = (hashtag, { maxId } = {}, done = noOp) => expandTimeline(`hashtag:${hashtag}`, `/api/v1/timelines/tag/${hashtag}`, { max_id: maxId }, done);
export const expandListTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done); export const expandListTimeline = (id, { maxId } = {}, done = noOp) => expandTimeline(`list:${id}`, `/api/v1/timelines/list/${id}`, { max_id: maxId }, done);
export function expandTimelineRequest(timeline) { export function expandTimelineRequest(timeline) {
return { return {

@ -1,12 +1,13 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { NavLink } from 'react-router-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import StatusListContainer from '../ui/containers/status_list_container'; import StatusListContainer from '../ui/containers/status_list_container';
import Column from '../../components/column'; import Column from '../../components/column';
import ColumnHeader from '../../components/column_header'; import ColumnHeader from '../../components/column_header';
import { expandCommunityTimeline } from '../../actions/timelines'; import { expandCommunityTimeline } from '../../actions/timelines';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ColumnSettingsContainer from './containers/column_settings_container'; import ColumnSettingsContainer from './containers/column_settings_container';
import { connectCommunityStream } from '../../actions/streaming'; import { connectCommunityStream } from '../../actions/streaming';
@ -14,20 +15,25 @@ const messages = defineMessages({
title: { id: 'column.community', defaultMessage: 'Local timeline' }, title: { id: 'column.community', defaultMessage: 'Local timeline' },
}); });
const mapStateToProps = state => ({ const mapStateToProps = (state, { onlyMedia }) => ({
hasUnread: state.getIn(['timelines', 'community', 'unread']) > 0, hasUnread: state.getIn(['timelines', `community${onlyMedia ? ':media' : ''}`, 'unread']) > 0,
}); });
@connect(mapStateToProps) @connect(mapStateToProps)
@injectIntl @injectIntl
export default class CommunityTimeline extends React.PureComponent { export default class CommunityTimeline extends React.PureComponent {
static defaultProps = {
onlyMedia: false,
};
static propTypes = { static propTypes = {
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
columnId: PropTypes.string, columnId: PropTypes.string,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
hasUnread: PropTypes.bool, hasUnread: PropTypes.bool,
multiColumn: PropTypes.bool, multiColumn: PropTypes.bool,
onlyMedia: PropTypes.bool,
}; };
handlePin = () => { handlePin = () => {
@ -50,10 +56,10 @@ export default class CommunityTimeline extends React.PureComponent {
} }
componentDidMount () { componentDidMount () {
const { dispatch } = this.props; const { dispatch, onlyMedia } = this.props;
dispatch(expandCommunityTimeline()); dispatch(expandCommunityTimeline({ onlyMedia }));
this.disconnect = dispatch(connectCommunityStream()); this.disconnect = dispatch(connectCommunityStream({ onlyMedia }));
} }
componentWillUnmount () { componentWillUnmount () {
@ -68,13 +74,22 @@ export default class CommunityTimeline extends React.PureComponent {
} }
handleLoadMore = maxId => { handleLoadMore = maxId => {
this.props.dispatch(expandCommunityTimeline({ maxId })); const { dispatch, onlyMedia } = this.props;
dispatch(expandCommunityTimeline({ maxId, onlyMedia }));
} }
render () { render () {
const { intl, hasUnread, columnId, multiColumn } = this.props; const { intl, hasUnread, columnId, multiColumn, onlyMedia } = this.props;
const pinned = !!columnId; const pinned = !!columnId;
const headline = (
<div className='community-timeline__section-headline'>
<NavLink exact to='/timelines/public/local' replace><FormattedMessage id='timeline.posts' defaultMessage='Toots' /></NavLink>
<NavLink exact to='/timelines/public/local/media' replace><FormattedMessage id='timeline.media' defaultMessage='Media' /></NavLink>
</div>
);
return ( return (
<Column ref={this.setRef}> <Column ref={this.setRef}>
<ColumnHeader <ColumnHeader
@ -91,9 +106,10 @@ export default class CommunityTimeline extends React.PureComponent {
</ColumnHeader> </ColumnHeader>
<StatusListContainer <StatusListContainer
prepend={headline}
trackScroll={!pinned} trackScroll={!pinned}
scrollKey={`community_timeline-${columnId}`} scrollKey={`community_timeline-${columnId}`}
timelineId='community' timelineId={`community${onlyMedia ? ':media' : ''}`}
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}
emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />} emptyMessage={<FormattedMessage id='empty_column.community' defaultMessage='The local timeline is empty. Write something publicly to get the ball rolling!' />}
/> />

@ -1,12 +1,13 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { NavLink } from 'react-router-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import StatusListContainer from '../ui/containers/status_list_container'; import StatusListContainer from '../ui/containers/status_list_container';
import Column from '../../components/column'; import Column from '../../components/column';
import ColumnHeader from '../../components/column_header'; import ColumnHeader from '../../components/column_header';
import { expandPublicTimeline } from '../../actions/timelines'; import { expandPublicTimeline } from '../../actions/timelines';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ColumnSettingsContainer from './containers/column_settings_container'; import ColumnSettingsContainer from './containers/column_settings_container';
import { connectPublicStream } from '../../actions/streaming'; import { connectPublicStream } from '../../actions/streaming';
@ -14,20 +15,25 @@ const messages = defineMessages({
title: { id: 'column.public', defaultMessage: 'Federated timeline' }, title: { id: 'column.public', defaultMessage: 'Federated timeline' },
}); });
const mapStateToProps = state => ({ const mapStateToProps = (state, { onlyMedia }) => ({
hasUnread: state.getIn(['timelines', 'public', 'unread']) > 0, hasUnread: state.getIn(['timelines', `public${onlyMedia ? ':media' : ''}`, 'unread']) > 0,
}); });
@connect(mapStateToProps) @connect(mapStateToProps)
@injectIntl @injectIntl
export default class PublicTimeline extends React.PureComponent { export default class PublicTimeline extends React.PureComponent {
static defaultProps = {
onlyMedia: false,
};
static propTypes = { static propTypes = {
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
columnId: PropTypes.string, columnId: PropTypes.string,
multiColumn: PropTypes.bool, multiColumn: PropTypes.bool,
hasUnread: PropTypes.bool, hasUnread: PropTypes.bool,
onlyMedia: PropTypes.bool,
}; };
handlePin = () => { handlePin = () => {
@ -50,10 +56,10 @@ export default class PublicTimeline extends React.PureComponent {
} }
componentDidMount () { componentDidMount () {
const { dispatch } = this.props; const { dispatch, onlyMedia } = this.props;
dispatch(expandPublicTimeline()); dispatch(expandPublicTimeline({ onlyMedia }));
this.disconnect = dispatch(connectPublicStream()); this.disconnect = dispatch(connectPublicStream({ onlyMedia }));
} }
componentWillUnmount () { componentWillUnmount () {
@ -68,13 +74,22 @@ export default class PublicTimeline extends React.PureComponent {
} }
handleLoadMore = maxId => { handleLoadMore = maxId => {
this.props.dispatch(expandPublicTimeline({ maxId })); const { dispatch, onlyMedia } = this.props;
dispatch(expandPublicTimeline({ maxId, onlyMedia }));
} }
render () { render () {
const { intl, columnId, hasUnread, multiColumn } = this.props; const { intl, columnId, hasUnread, multiColumn, onlyMedia } = this.props;
const pinned = !!columnId; const pinned = !!columnId;
const headline = (
<div className='public-timeline__section-headline'>
<NavLink exact to='/timelines/public' replace><FormattedMessage id='timeline.posts' defaultMessage='Toots' /></NavLink>
<NavLink exact to='/timelines/public/media' replace><FormattedMessage id='timeline.media' defaultMessage='Media' /></NavLink>
</div>
);
return ( return (
<Column ref={this.setRef}> <Column ref={this.setRef}>
<ColumnHeader <ColumnHeader
@ -91,7 +106,8 @@ export default class PublicTimeline extends React.PureComponent {
</ColumnHeader> </ColumnHeader>
<StatusListContainer <StatusListContainer
timelineId='public' prepend={headline}
timelineId={`public${onlyMedia ? ':media' : ''}`}
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}
trackScroll={!pinned} trackScroll={!pinned}
scrollKey={`public_timeline-${columnId}`} scrollKey={`public_timeline-${columnId}`}

@ -141,7 +141,9 @@ class SwitchingColumnsArea extends React.PureComponent {
<WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} /> <WrappedRoute path='/keyboard-shortcuts' component={KeyboardShortcuts} content={children} />
<WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} /> <WrappedRoute path='/timelines/home' component={HomeTimeline} content={children} />
<WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} /> <WrappedRoute path='/timelines/public' exact component={PublicTimeline} content={children} />
<WrappedRoute path='/timelines/public/media' extract component={PublicTimeline} content={children} componentParams={{ onlyMedia: true }} />
<WrappedRoute path='/timelines/public/local' component={CommunityTimeline} content={children} /> <WrappedRoute path='/timelines/public/local' component={CommunityTimeline} content={children} />
<WrappedRoute path='/timelines/public/local/media' component={CommunityTimeline} content={children} componentParams={{ onlyMedia: true }} />
<WrappedRoute path='/timelines/direct' component={DirectTimeline} content={children} /> <WrappedRoute path='/timelines/direct' component={DirectTimeline} content={children} />
<WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} /> <WrappedRoute path='/timelines/tag/:id' component={HashtagTimeline} content={children} />
<WrappedRoute path='/timelines/list/:id' component={ListTimeline} content={children} /> <WrappedRoute path='/timelines/list/:id' component={ListTimeline} content={children} />

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "المحلي", "tabs_bar.local_timeline": "المحلي",
"tabs_bar.notifications": "الإخطارات", "tabs_bar.notifications": "الإخطارات",
"tabs_bar.search": "البحث", "tabs_bar.search": "البحث",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "سوف تفقد مسودتك إن تركت ماستدون.", "ui.beforeunload": "سوف تفقد مسودتك إن تركت ماستدون.",
"upload_area.title": "إسحب ثم أفلت للرفع", "upload_area.title": "إسحب ثم أفلت للرفع",
"upload_button.label": "إضافة وسائط", "upload_button.label": "إضافة وسائط",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Local", "tabs_bar.local_timeline": "Local",
"tabs_bar.notifications": "Известия", "tabs_bar.notifications": "Известия",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
"upload_area.title": "Drag & drop to upload", "upload_area.title": "Drag & drop to upload",
"upload_button.label": "Добави медия", "upload_button.label": "Добави медия",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Local", "tabs_bar.local_timeline": "Local",
"tabs_bar.notifications": "Notificacions", "tabs_bar.notifications": "Notificacions",
"tabs_bar.search": "Cerca", "tabs_bar.search": "Cerca",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "El vostre esborrany es perdrà si sortiu de Mastodon.", "ui.beforeunload": "El vostre esborrany es perdrà si sortiu de Mastodon.",
"upload_area.title": "Arrossega i deixa anar per carregar", "upload_area.title": "Arrossega i deixa anar per carregar",
"upload_button.label": "Afegir multimèdia", "upload_button.label": "Afegir multimèdia",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Lucale", "tabs_bar.local_timeline": "Lucale",
"tabs_bar.notifications": "Nutificazione", "tabs_bar.notifications": "Nutificazione",
"tabs_bar.search": "Cercà", "tabs_bar.search": "Cercà",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "A bruttacopia sarà persa s'ellu hè chjosu Mastodon.", "ui.beforeunload": "A bruttacopia sarà persa s'ellu hè chjosu Mastodon.",
"upload_area.title": "Drag & drop per caricà un fugliale", "upload_area.title": "Drag & drop per caricà un fugliale",
"upload_button.label": "Aghjunghje un media", "upload_button.label": "Aghjunghje un media",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Lokal", "tabs_bar.local_timeline": "Lokal",
"tabs_bar.notifications": "Mitteilungen", "tabs_bar.notifications": "Mitteilungen",
"tabs_bar.search": "Suchen", "tabs_bar.search": "Suchen",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Dein Entwurf geht verloren, wenn du Mastodon verlässt.", "ui.beforeunload": "Dein Entwurf geht verloren, wenn du Mastodon verlässt.",
"upload_area.title": "Zum Hochladen hereinziehen", "upload_area.title": "Zum Hochladen hereinziehen",
"upload_button.label": "Mediendatei hinzufügen", "upload_button.label": "Mediendatei hinzufügen",

@ -596,6 +596,14 @@
"defaultMessage": "Local timeline", "defaultMessage": "Local timeline",
"id": "column.community" "id": "column.community"
}, },
{
"defaultMessage": "Toots",
"id": "timeline.posts"
},
{
"defaultMessage": "Media",
"id": "timeline.media"
},
{ {
"defaultMessage": "The local timeline is empty. Write something publicly to get the ball rolling!", "defaultMessage": "The local timeline is empty. Write something publicly to get the ball rolling!",
"id": "empty_column.community" "id": "empty_column.community"
@ -1393,6 +1401,14 @@
"defaultMessage": "Federated timeline", "defaultMessage": "Federated timeline",
"id": "column.public" "id": "column.public"
}, },
{
"defaultMessage": "Toots",
"id": "timeline.posts"
},
{
"defaultMessage": "Media",
"id": "timeline.media"
},
{ {
"defaultMessage": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", "defaultMessage": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up",
"id": "empty_column.public" "id": "empty_column.public"

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Local", "tabs_bar.local_timeline": "Local",
"tabs_bar.notifications": "Notifications", "tabs_bar.notifications": "Notifications",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
"upload_area.title": "Drag & drop to upload", "upload_area.title": "Drag & drop to upload",
"upload_button.label": "Add media", "upload_button.label": "Add media",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Local", "tabs_bar.local_timeline": "Local",
"tabs_bar.notifications": "Notifications", "tabs_bar.notifications": "Notifications",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
"upload_area.title": "Drag & drop to upload", "upload_area.title": "Drag & drop to upload",
"upload_button.label": "Add media", "upload_button.label": "Add media",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Loka tempolinio", "tabs_bar.local_timeline": "Loka tempolinio",
"tabs_bar.notifications": "Sciigoj", "tabs_bar.notifications": "Sciigoj",
"tabs_bar.search": "Serĉi", "tabs_bar.search": "Serĉi",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Via malneto perdiĝos se vi eliras de Mastodon.", "ui.beforeunload": "Via malneto perdiĝos se vi eliras de Mastodon.",
"upload_area.title": "Altreni kaj lasi por alŝuti", "upload_area.title": "Altreni kaj lasi por alŝuti",
"upload_button.label": "Aldoni aŭdovidaĵon", "upload_button.label": "Aldoni aŭdovidaĵon",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Local", "tabs_bar.local_timeline": "Local",
"tabs_bar.notifications": "Notificaciones", "tabs_bar.notifications": "Notificaciones",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Tu borrador se perderá si sales de Mastodon.", "ui.beforeunload": "Tu borrador se perderá si sales de Mastodon.",
"upload_area.title": "Arrastra y suelta para subir", "upload_area.title": "Arrastra y suelta para subir",
"upload_button.label": "Subir multimedia", "upload_button.label": "Subir multimedia",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Local", "tabs_bar.local_timeline": "Local",
"tabs_bar.notifications": "Notifications", "tabs_bar.notifications": "Notifications",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
"upload_area.title": "Drag & drop to upload", "upload_area.title": "Drag & drop to upload",
"upload_button.label": "Add media", "upload_button.label": "Add media",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "محلی", "tabs_bar.local_timeline": "محلی",
"tabs_bar.notifications": "اعلان‌ها", "tabs_bar.notifications": "اعلان‌ها",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "اگر از ماستدون خارج شوید پیش‌نویس شما پاک خواهد شد.", "ui.beforeunload": "اگر از ماستدون خارج شوید پیش‌نویس شما پاک خواهد شد.",
"upload_area.title": "برای بارگذاری به این‌جا بکشید", "upload_area.title": "برای بارگذاری به این‌جا بکشید",
"upload_button.label": "افزودن تصویر", "upload_button.label": "افزودن تصویر",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Paikallinen", "tabs_bar.local_timeline": "Paikallinen",
"tabs_bar.notifications": "Ilmoitukset", "tabs_bar.notifications": "Ilmoitukset",
"tabs_bar.search": "Hae", "tabs_bar.search": "Hae",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Luonnos häviää, jos poistut Mastodonista.", "ui.beforeunload": "Luonnos häviää, jos poistut Mastodonista.",
"upload_area.title": "Lataa raahaamalla ja pudottamalla tähän", "upload_area.title": "Lataa raahaamalla ja pudottamalla tähän",
"upload_button.label": "Lisää mediaa", "upload_button.label": "Lisää mediaa",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Fil public local", "tabs_bar.local_timeline": "Fil public local",
"tabs_bar.notifications": "Notifications", "tabs_bar.notifications": "Notifications",
"tabs_bar.search": "Chercher", "tabs_bar.search": "Chercher",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Votre brouillon sera perdu si vous quittez Mastodon.", "ui.beforeunload": "Votre brouillon sera perdu si vous quittez Mastodon.",
"upload_area.title": "Glissez et déposez pour envoyer", "upload_area.title": "Glissez et déposez pour envoyer",
"upload_button.label": "Joindre un média", "upload_button.label": "Joindre un média",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Local", "tabs_bar.local_timeline": "Local",
"tabs_bar.notifications": "Notificacións", "tabs_bar.notifications": "Notificacións",
"tabs_bar.search": "Buscar", "tabs_bar.search": "Buscar",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "O borrador perderase se sae de Mastodon.", "ui.beforeunload": "O borrador perderase se sae de Mastodon.",
"upload_area.title": "Arrastre e solte para subir", "upload_area.title": "Arrastre e solte para subir",
"upload_button.label": "Engadir medios", "upload_button.label": "Engadir medios",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "ציר זמן מקומי", "tabs_bar.local_timeline": "ציר זמן מקומי",
"tabs_bar.notifications": "התראות", "tabs_bar.notifications": "התראות",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "הטיוטא תאבד אם תעזבו את מסטודון.", "ui.beforeunload": "הטיוטא תאבד אם תעזבו את מסטודון.",
"upload_area.title": "ניתן להעלות על ידי Drag & drop", "upload_area.title": "ניתן להעלות על ידי Drag & drop",
"upload_button.label": "הוספת מדיה", "upload_button.label": "הוספת מדיה",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Lokalno", "tabs_bar.local_timeline": "Lokalno",
"tabs_bar.notifications": "Notifikacije", "tabs_bar.notifications": "Notifikacije",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
"upload_area.title": "Povuci i spusti kako bi uploadao", "upload_area.title": "Povuci i spusti kako bi uploadao",
"upload_button.label": "Dodaj media", "upload_button.label": "Dodaj media",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Local", "tabs_bar.local_timeline": "Local",
"tabs_bar.notifications": "Értesítések", "tabs_bar.notifications": "Értesítések",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "A piszkozata el fog vesztődni ha elhagyja Mastodon-t.", "ui.beforeunload": "A piszkozata el fog vesztődni ha elhagyja Mastodon-t.",
"upload_area.title": "Húzza ide a feltöltéshez", "upload_area.title": "Húzza ide a feltöltéshez",
"upload_button.label": "Média hozzáadása", "upload_button.label": "Média hozzáadása",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Տեղական", "tabs_bar.local_timeline": "Տեղական",
"tabs_bar.notifications": "Ծանուցումներ", "tabs_bar.notifications": "Ծանուցումներ",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Քո սեւագիրը կկորի, եթե լքես Մաստոդոնը։", "ui.beforeunload": "Քո սեւագիրը կկորի, եթե լքես Մաստոդոնը։",
"upload_area.title": "Քաշիր ու նետիր՝ վերբեռնելու համար", "upload_area.title": "Քաշիր ու նետիր՝ վերբեռնելու համար",
"upload_button.label": "Ավելացնել մեդիա", "upload_button.label": "Ավելացնել մեդիա",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Lokal", "tabs_bar.local_timeline": "Lokal",
"tabs_bar.notifications": "Notifikasi", "tabs_bar.notifications": "Notifikasi",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Naskah anda akan hilang jika anda keluar dari Mastodon.", "ui.beforeunload": "Naskah anda akan hilang jika anda keluar dari Mastodon.",
"upload_area.title": "Seret & lepaskan untuk mengunggah", "upload_area.title": "Seret & lepaskan untuk mengunggah",
"upload_button.label": "Tambahkan media", "upload_button.label": "Tambahkan media",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Lokala", "tabs_bar.local_timeline": "Lokala",
"tabs_bar.notifications": "Savigi", "tabs_bar.notifications": "Savigi",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
"upload_area.title": "Tranar faligar por kargar", "upload_area.title": "Tranar faligar por kargar",
"upload_button.label": "Adjuntar kontenajo", "upload_button.label": "Adjuntar kontenajo",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Locale", "tabs_bar.local_timeline": "Locale",
"tabs_bar.notifications": "Notifiche", "tabs_bar.notifications": "Notifiche",
"tabs_bar.search": "Cerca", "tabs_bar.search": "Cerca",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "La bozza andrà persa se esci da Mastodon.", "ui.beforeunload": "La bozza andrà persa se esci da Mastodon.",
"upload_area.title": "Trascina per caricare", "upload_area.title": "Trascina per caricare",
"upload_button.label": "Aggiungi file multimediale", "upload_button.label": "Aggiungi file multimediale",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "ローカル", "tabs_bar.local_timeline": "ローカル",
"tabs_bar.notifications": "通知", "tabs_bar.notifications": "通知",
"tabs_bar.search": "検索", "tabs_bar.search": "検索",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Mastodonから離れると送信前の投稿は失われます。", "ui.beforeunload": "Mastodonから離れると送信前の投稿は失われます。",
"upload_area.title": "ドラッグ&ドロップでアップロード", "upload_area.title": "ドラッグ&ドロップでアップロード",
"upload_button.label": "メディアを追加", "upload_button.label": "メディアを追加",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "로컬", "tabs_bar.local_timeline": "로컬",
"tabs_bar.notifications": "알림", "tabs_bar.notifications": "알림",
"tabs_bar.search": "검색", "tabs_bar.search": "검색",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.", "ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.",
"upload_area.title": "드래그 & 드롭으로 업로드", "upload_area.title": "드래그 & 드롭으로 업로드",
"upload_button.label": "미디어 추가", "upload_button.label": "미디어 추가",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Lokaal", "tabs_bar.local_timeline": "Lokaal",
"tabs_bar.notifications": "Meldingen", "tabs_bar.notifications": "Meldingen",
"tabs_bar.search": "Zoeken", "tabs_bar.search": "Zoeken",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Je concept zal verloren gaan als je Mastodon verlaat.", "ui.beforeunload": "Je concept zal verloren gaan als je Mastodon verlaat.",
"upload_area.title": "Hierin slepen om te uploaden", "upload_area.title": "Hierin slepen om te uploaden",
"upload_button.label": "Media toevoegen", "upload_button.label": "Media toevoegen",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Lokal", "tabs_bar.local_timeline": "Lokal",
"tabs_bar.notifications": "Varslinger", "tabs_bar.notifications": "Varslinger",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Din kladd vil bli forkastet om du forlater Mastodon.", "ui.beforeunload": "Din kladd vil bli forkastet om du forlater Mastodon.",
"upload_area.title": "Dra og slipp for å laste opp", "upload_area.title": "Dra og slipp for å laste opp",
"upload_button.label": "Legg til media", "upload_button.label": "Legg til media",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Flux public local", "tabs_bar.local_timeline": "Flux public local",
"tabs_bar.notifications": "Notificacions", "tabs_bar.notifications": "Notificacions",
"tabs_bar.search": "Recèrcas", "tabs_bar.search": "Recèrcas",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Vòstre brolhon serà perdut se quitatz Mastodon.", "ui.beforeunload": "Vòstre brolhon serà perdut se quitatz Mastodon.",
"upload_area.title": "Lisatz e depausatz per mandar", "upload_area.title": "Lisatz e depausatz per mandar",
"upload_button.label": "Ajustar un mèdia", "upload_button.label": "Ajustar un mèdia",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Lokalne", "tabs_bar.local_timeline": "Lokalne",
"tabs_bar.notifications": "Powiadomienia", "tabs_bar.notifications": "Powiadomienia",
"tabs_bar.search": "Szukaj", "tabs_bar.search": "Szukaj",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Utracisz tworzony wpis, jeżeli opuścisz Mastodona.", "ui.beforeunload": "Utracisz tworzony wpis, jeżeli opuścisz Mastodona.",
"upload_area.title": "Przeciągnij i upuść aby wysłać", "upload_area.title": "Przeciągnij i upuść aby wysłać",
"upload_button.label": "Dodaj zawartość multimedialną", "upload_button.label": "Dodaj zawartość multimedialną",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Local", "tabs_bar.local_timeline": "Local",
"tabs_bar.notifications": "Notificações", "tabs_bar.notifications": "Notificações",
"tabs_bar.search": "Buscar", "tabs_bar.search": "Buscar",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Seu rascunho será perdido se você sair do Mastodon.", "ui.beforeunload": "Seu rascunho será perdido se você sair do Mastodon.",
"upload_area.title": "Arraste e solte para enviar", "upload_area.title": "Arraste e solte para enviar",
"upload_button.label": "Adicionar mídia", "upload_button.label": "Adicionar mídia",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Local", "tabs_bar.local_timeline": "Local",
"tabs_bar.notifications": "Notificações", "tabs_bar.notifications": "Notificações",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "O teu rascunho vai ser perdido se abandonares o Mastodon.", "ui.beforeunload": "O teu rascunho vai ser perdido se abandonares o Mastodon.",
"upload_area.title": "Arraste e solte para enviar", "upload_area.title": "Arraste e solte para enviar",
"upload_button.label": "Adicionar media", "upload_button.label": "Adicionar media",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Локальная", "tabs_bar.local_timeline": "Локальная",
"tabs_bar.notifications": "Уведомления", "tabs_bar.notifications": "Уведомления",
"tabs_bar.search": "Поиск", "tabs_bar.search": "Поиск",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Ваш черновик будет утерян, если вы покинете Mastodon.", "ui.beforeunload": "Ваш черновик будет утерян, если вы покинете Mastodon.",
"upload_area.title": "Перетащите сюда, чтобы загрузить", "upload_area.title": "Перетащите сюда, чтобы загрузить",
"upload_button.label": "Добавить медиаконтент", "upload_button.label": "Добавить медиаконтент",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Lokálna", "tabs_bar.local_timeline": "Lokálna",
"tabs_bar.notifications": "Notifikácie", "tabs_bar.notifications": "Notifikácie",
"tabs_bar.search": "Hľadaj", "tabs_bar.search": "Hľadaj",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Čo máte rozpísané sa stratí, ak opustíte Mastodon.", "ui.beforeunload": "Čo máte rozpísané sa stratí, ak opustíte Mastodon.",
"upload_area.title": "Ťahaj a pusti pre nahratie", "upload_area.title": "Ťahaj a pusti pre nahratie",
"upload_button.label": "Pridať médiá", "upload_button.label": "Pridať médiá",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Lokalno", "tabs_bar.local_timeline": "Lokalno",
"tabs_bar.notifications": "Obvestila", "tabs_bar.notifications": "Obvestila",
"tabs_bar.search": "Poišči", "tabs_bar.search": "Poišči",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Vaš osnutek bo izgubljen, če zapustite Mastodona.", "ui.beforeunload": "Vaš osnutek bo izgubljen, če zapustite Mastodona.",
"upload_area.title": "Povlecite in spustite za pošiljanje", "upload_area.title": "Povlecite in spustite za pošiljanje",
"upload_button.label": "Dodaj medij", "upload_button.label": "Dodaj medij",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Lokalno", "tabs_bar.local_timeline": "Lokalno",
"tabs_bar.notifications": "Obaveštenja", "tabs_bar.notifications": "Obaveštenja",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Ako napustite Mastodont, izgubićete napisani nacrt.", "ui.beforeunload": "Ako napustite Mastodont, izgubićete napisani nacrt.",
"upload_area.title": "Prevucite ovde da otpremite", "upload_area.title": "Prevucite ovde da otpremite",
"upload_button.label": "Dodaj multimediju", "upload_button.label": "Dodaj multimediju",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Локално", "tabs_bar.local_timeline": "Локално",
"tabs_bar.notifications": "Обавештења", "tabs_bar.notifications": "Обавештења",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Ако напустите Мастодонт, изгубићете написани нацрт.", "ui.beforeunload": "Ако напустите Мастодонт, изгубићете написани нацрт.",
"upload_area.title": "Превуците овде да отпремите", "upload_area.title": "Превуците овде да отпремите",
"upload_button.label": "Додај мултимедију", "upload_button.label": "Додај мултимедију",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Lokal", "tabs_bar.local_timeline": "Lokal",
"tabs_bar.notifications": "Meddelanden", "tabs_bar.notifications": "Meddelanden",
"tabs_bar.search": "Sök", "tabs_bar.search": "Sök",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Ditt utkast kommer att förloras om du lämnar Mastodon.", "ui.beforeunload": "Ditt utkast kommer att förloras om du lämnar Mastodon.",
"upload_area.title": "Dra & släpp för att ladda upp", "upload_area.title": "Dra & släpp för att ladda upp",
"upload_button.label": "Lägg till media", "upload_button.label": "Lägg till media",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Local", "tabs_bar.local_timeline": "Local",
"tabs_bar.notifications": "Notifications", "tabs_bar.notifications": "Notifications",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
"upload_area.title": "Drag & drop to upload", "upload_area.title": "Drag & drop to upload",
"upload_button.label": "Add media", "upload_button.label": "Add media",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Local", "tabs_bar.local_timeline": "Local",
"tabs_bar.notifications": "Notifications", "tabs_bar.notifications": "Notifications",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
"upload_area.title": "Drag & drop to upload", "upload_area.title": "Drag & drop to upload",
"upload_button.label": "Add media", "upload_button.label": "Add media",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Yerel", "tabs_bar.local_timeline": "Yerel",
"tabs_bar.notifications": "Bildirimler", "tabs_bar.notifications": "Bildirimler",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
"upload_area.title": "Upload için sürükle bırak yapınız", "upload_area.title": "Upload için sürükle bırak yapınız",
"upload_button.label": "Görsel ekle", "upload_button.label": "Görsel ekle",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "Локальна", "tabs_bar.local_timeline": "Локальна",
"tabs_bar.notifications": "Сповіщення", "tabs_bar.notifications": "Сповіщення",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
"upload_area.title": "Перетягніть сюди, щоб завантажити", "upload_area.title": "Перетягніть сюди, щоб завантажити",
"upload_button.label": "Додати медіаконтент", "upload_button.label": "Додати медіаконтент",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "本站", "tabs_bar.local_timeline": "本站",
"tabs_bar.notifications": "通知", "tabs_bar.notifications": "通知",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "如果你现在离开 Mastodon你的草稿内容将会被丢弃。", "ui.beforeunload": "如果你现在离开 Mastodon你的草稿内容将会被丢弃。",
"upload_area.title": "将文件拖放到此处开始上传", "upload_area.title": "将文件拖放到此处开始上传",
"upload_button.label": "上传媒体文件", "upload_button.label": "上传媒体文件",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "本站", "tabs_bar.local_timeline": "本站",
"tabs_bar.notifications": "通知", "tabs_bar.notifications": "通知",
"tabs_bar.search": "搜尋", "tabs_bar.search": "搜尋",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "如果你現在離開 Mastodon你的草稿內容將會被丟棄。", "ui.beforeunload": "如果你現在離開 Mastodon你的草稿內容將會被丟棄。",
"upload_area.title": "將檔案拖放至此上載", "upload_area.title": "將檔案拖放至此上載",
"upload_button.label": "上載媒體檔案", "upload_button.label": "上載媒體檔案",

@ -280,6 +280,8 @@
"tabs_bar.local_timeline": "本地", "tabs_bar.local_timeline": "本地",
"tabs_bar.notifications": "通知", "tabs_bar.notifications": "通知",
"tabs_bar.search": "Search", "tabs_bar.search": "Search",
"timeline.media": "Media",
"timeline.posts": "Toots",
"ui.beforeunload": "如果離開 Mastodon你的草稿將會不見。", "ui.beforeunload": "如果離開 Mastodon你的草稿將會不見。",
"upload_area.title": "拖放來上傳", "upload_area.title": "拖放來上傳",
"upload_button.label": "增加媒體", "upload_button.label": "增加媒體",

@ -4737,6 +4737,8 @@ a.status-card {
} }
} }
.community-timeline__section-headline,
.public-timeline__section-headline,
.account__section-headline { .account__section-headline {
background: darken($ui-base-color, 4%); background: darken($ui-base-color, 4%);
border-bottom: 1px solid lighten($ui-base-color, 8%); border-bottom: 1px solid lighten($ui-base-color, 8%);

@ -81,6 +81,11 @@ class BatchedRemoveStatusService < BaseService
redis.publish('timeline:public', payload) redis.publish('timeline:public', payload)
redis.publish('timeline:public:local', payload) if status.local? redis.publish('timeline:public:local', payload) if status.local?
if status.media_attachments.exists?
redis.publish('timeline:public:media', payload)
redis.publish('timeline:public:local:media', payload) if status.local?
end
@tags[status.id].each do |hashtag| @tags[status.id].each do |hashtag|
redis.publish("timeline:hashtag:#{hashtag}", payload) redis.publish("timeline:hashtag:#{hashtag}", payload)
redis.publish("timeline:hashtag:#{hashtag}:local", payload) if status.local? redis.publish("timeline:hashtag:#{hashtag}:local", payload) if status.local?

@ -25,6 +25,7 @@ class FanOutOnWriteService < BaseService
return if status.reply? && status.in_reply_to_account_id != status.account_id return if status.reply? && status.in_reply_to_account_id != status.account_id
deliver_to_public(status) deliver_to_public(status)
deliver_to_media(status) if status.media_attachments.exists?
end end
private private
@ -85,6 +86,13 @@ class FanOutOnWriteService < BaseService
Redis.current.publish('timeline:public:local', @payload) if status.local? Redis.current.publish('timeline:public:local', @payload) if status.local?
end end
def deliver_to_media(status)
Rails.logger.debug "Delivering status #{status.id} to media timeline"
Redis.current.publish('timeline:public:media', @payload)
Redis.current.publish('timeline:public:local:media', @payload) if status.local?
end
def deliver_to_direct_timelines(status) def deliver_to_direct_timelines(status)
Rails.logger.debug "Delivering status #{status.id} to direct timelines" Rails.logger.debug "Delivering status #{status.id} to direct timelines"

@ -20,6 +20,7 @@ class RemoveStatusService < BaseService
remove_reblogs remove_reblogs
remove_from_hashtags remove_from_hashtags
remove_from_public remove_from_public
remove_from_media if status.media_attachments.exists?
remove_from_direct if status.direct_visibility? remove_from_direct if status.direct_visibility?
@status.destroy! @status.destroy!
@ -131,6 +132,13 @@ class RemoveStatusService < BaseService
Redis.current.publish('timeline:public:local', @payload) if @status.local? Redis.current.publish('timeline:public:local', @payload) if @status.local?
end end
def remove_from_media
return unless @status.public_visibility?
Redis.current.publish('timeline:public:media', @payload)
Redis.current.publish('timeline:public:local:media', @payload) if @status.local?
end
def remove_from_direct def remove_from_direct
@mentions.each do |mention| @mentions.each do |mention|
Redis.current.publish("timeline:direct:#{mention.account.id}", @payload) if mention.account.local? Redis.current.publish("timeline:direct:#{mention.account.id}", @payload) if mention.account.local?

@ -241,7 +241,9 @@ const startWorker = (workerId) => {
const PUBLIC_STREAMS = [ const PUBLIC_STREAMS = [
'public', 'public',
'public:media',
'public:local', 'public:local',
'public:local:media',
'hashtag', 'hashtag',
'hashtag:local', 'hashtag:local',
]; ];
@ -459,11 +461,17 @@ const startWorker = (workerId) => {
}); });
app.get('/api/v1/streaming/public', (req, res) => { app.get('/api/v1/streaming/public', (req, res) => {
streamFrom('timeline:public', req, streamToHttp(req, res), streamHttpEnd(req), true); const onlyMedia = req.query.only_media === '1' || req.query.only_media === 'true';
const channel = onlyMedia ? 'timeline:public:media' : 'timeline:public';
streamFrom(channel, req, streamToHttp(req, res), streamHttpEnd(req), true);
}); });
app.get('/api/v1/streaming/public/local', (req, res) => { app.get('/api/v1/streaming/public/local', (req, res) => {
streamFrom('timeline:public:local', req, streamToHttp(req, res), streamHttpEnd(req), true); const onlyMedia = req.query.only_media === '1' || req.query.only_media === 'true';
const channel = onlyMedia ? 'timeline:public:local:media' : 'timeline:public:local';
streamFrom(channel, req, streamToHttp(req, res), streamHttpEnd(req), true);
}); });
app.get('/api/v1/streaming/direct', (req, res) => { app.get('/api/v1/streaming/direct', (req, res) => {
@ -521,6 +529,12 @@ const startWorker = (workerId) => {
case 'public:local': case 'public:local':
streamFrom('timeline:public:local', req, streamToWs(req, ws), streamWsEnd(req, ws), true); streamFrom('timeline:public:local', req, streamToWs(req, ws), streamWsEnd(req, ws), true);
break; break;
case 'public:media':
streamFrom('timeline:public:media', req, streamToWs(req, ws), streamWsEnd(req, ws), true);
break;
case 'public:local:media':
streamFrom('timeline:public:local:media', req, streamToWs(req, ws), streamWsEnd(req, ws), true);
break;
case 'direct': case 'direct':
streamFrom(`timeline:direct:${req.accountId}`, req, streamToWs(req, ws), streamWsEnd(req, ws), true); streamFrom(`timeline:direct:${req.accountId}`, req, streamToWs(req, ws), streamWsEnd(req, ws), true);
break; break;

Loading…
Cancel
Save