Add media filter to bookmark, favorite, emoji_reaction and personal column
This commit is contained in:
parent
45986dc4dc
commit
fc11b20384
23 changed files with 484 additions and 79 deletions
|
@ -28,20 +28,43 @@ class Api::V1::BookmarksController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def results
|
def results
|
||||||
@_results ||= account_bookmarks.joins(:status).eager_load(:status).to_a_paginated_by_id(
|
@_results ||= filtered_account_bookmarks.to_a_paginated_by_id(
|
||||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||||
params_slice(:max_id, :since_id, :min_id)
|
params_slice(:max_id, :since_id, :min_id)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def filtered_account_bookmarks
|
||||||
|
account_bookmarks.joins(:status).eager_load(:status).tap do |scope|
|
||||||
|
scope.merge!(media_only_scope) if media_only?
|
||||||
|
scope.merge!(without_media_scope) if without_media?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def account_bookmarks
|
def account_bookmarks
|
||||||
current_account.bookmarks
|
current_account.bookmarks
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def media_only?
|
||||||
|
truthy_param?(:only_media)
|
||||||
|
end
|
||||||
|
|
||||||
|
def without_media?
|
||||||
|
truthy_param?(:without_media)
|
||||||
|
end
|
||||||
|
|
||||||
def compact?
|
def compact?
|
||||||
truthy_param?(:compact)
|
truthy_param?(:compact)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def media_only_scope
|
||||||
|
Status.joins(:media_attachments)
|
||||||
|
end
|
||||||
|
|
||||||
|
def without_media_scope
|
||||||
|
Status.left_joins(:media_attachments).where(media_attachments: {status_id: nil})
|
||||||
|
end
|
||||||
|
|
||||||
def insert_pagination_headers
|
def insert_pagination_headers
|
||||||
set_pagination_headers(next_path, prev_path)
|
set_pagination_headers(next_path, prev_path)
|
||||||
end
|
end
|
||||||
|
@ -67,6 +90,6 @@ class Api::V1::BookmarksController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
def pagination_params(core_params)
|
||||||
params.slice(:limit, :compact).permit(:limi, :compact).merge(core_params)
|
params_slice(:limit, :compact, :only_media, :without_media).merge(core_params)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,15 +28,17 @@ class Api::V1::EmojiReactionsController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def results
|
def results
|
||||||
@_results ||= filtered_emoji_reactions.joins(:status).eager_load(:status).to_a_paginated_by_id(
|
@_results ||= filtered_emoji_reactions.to_a_paginated_by_id(
|
||||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||||
params_slice(:max_id, :since_id, :min_id)
|
params_slice(:max_id, :since_id, :min_id)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def filtered_emoji_reactions
|
def filtered_emoji_reactions
|
||||||
account_emoji_reactions.tap do |emoji_reactions|
|
account_emoji_reactions.joins(:status).eager_load(:status).tap do |emoji_reactions|
|
||||||
emoji_reactions.merge!(emojis_scope) if emojis_requested?
|
emoji_reactions.merge!(emojis_scope) if emojis_requested?
|
||||||
|
emoji_reactions.merge!(media_only_scope) if media_only?
|
||||||
|
emoji_reactions.merge!(without_media_scope) if without_media?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -44,10 +46,43 @@ class Api::V1::EmojiReactionsController < Api::BaseController
|
||||||
current_account.emoji_reactions
|
current_account.emoji_reactions
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def emojis_requested?
|
||||||
|
emoji_reactions_params[:emojis].present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def media_only?
|
||||||
|
truthy_param?(:only_media)
|
||||||
|
end
|
||||||
|
|
||||||
|
def without_media?
|
||||||
|
truthy_param?(:without_media)
|
||||||
|
end
|
||||||
|
|
||||||
def compact?
|
def compact?
|
||||||
truthy_param?(:compact)
|
truthy_param?(:compact)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def emojis_scope
|
||||||
|
emoji_reactions = EmojiReaction.none
|
||||||
|
|
||||||
|
emoji_reactions_params[:emojis].each do |emoji|
|
||||||
|
shortcode, domain = emoji.split('@')
|
||||||
|
custom_emoji = CustomEmoji.find_by(shortcode: shortcode, domain: domain)
|
||||||
|
|
||||||
|
emoji_reactions = emoji_reactions.or(EmojiReaction.where(name: shortcode, custom_emoji: custom_emoji))
|
||||||
|
end
|
||||||
|
|
||||||
|
emoji_reactions
|
||||||
|
end
|
||||||
|
|
||||||
|
def media_only_scope
|
||||||
|
Status.joins(:media_attachments)
|
||||||
|
end
|
||||||
|
|
||||||
|
def without_media_scope
|
||||||
|
Status.left_joins(:media_attachments).where(media_attachments: {status_id: nil})
|
||||||
|
end
|
||||||
|
|
||||||
def insert_pagination_headers
|
def insert_pagination_headers
|
||||||
set_pagination_headers(next_path, prev_path)
|
set_pagination_headers(next_path, prev_path)
|
||||||
end
|
end
|
||||||
|
@ -72,25 +107,8 @@ class Api::V1::EmojiReactionsController < Api::BaseController
|
||||||
results.size == limit_param(DEFAULT_STATUSES_LIMIT)
|
results.size == limit_param(DEFAULT_STATUSES_LIMIT)
|
||||||
end
|
end
|
||||||
|
|
||||||
def emojis_requested?
|
|
||||||
emoji_reactions_params[:emojis].present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def emojis_scope
|
|
||||||
emoji_reactions = EmojiReaction.none
|
|
||||||
|
|
||||||
emoji_reactions_params[:emojis].each do |emoji|
|
|
||||||
shortcode, domain = emoji.split('@')
|
|
||||||
custom_emoji = CustomEmoji.find_by(shortcode: shortcode, domain: domain)
|
|
||||||
|
|
||||||
emoji_reactions = emoji_reactions.or(EmojiReaction.where(name: shortcode, custom_emoji: custom_emoji))
|
|
||||||
end
|
|
||||||
|
|
||||||
emoji_reactions
|
|
||||||
end
|
|
||||||
|
|
||||||
def pagination_params(core_params)
|
def pagination_params(core_params)
|
||||||
params.slice(:limit, :compact).permit(:limit, :compact).merge(core_params)
|
params_slice(:limit, :compact, :only_media, :without_media).merge(core_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
def emoji_reactions_params
|
def emoji_reactions_params
|
||||||
|
|
|
@ -28,20 +28,43 @@ class Api::V1::FavouritesController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def results
|
def results
|
||||||
@_results ||= account_favourites.joins(:status).eager_load(:status).to_a_paginated_by_id(
|
@_results ||= filtered_account_favourites.to_a_paginated_by_id(
|
||||||
limit_param(DEFAULT_STATUSES_LIMIT),
|
limit_param(DEFAULT_STATUSES_LIMIT),
|
||||||
params_slice(:max_id, :since_id, :min_id)
|
params_slice(:max_id, :since_id, :min_id)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def filtered_account_favourites
|
||||||
|
account_favourites.joins(:status).eager_load(:status).tap do |scope|
|
||||||
|
scope.merge!(media_only_scope) if media_only?
|
||||||
|
scope.merge!(without_media_scope) if without_media?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def account_favourites
|
def account_favourites
|
||||||
current_account.favourites
|
current_account.favourites
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def media_only?
|
||||||
|
truthy_param?(:only_media)
|
||||||
|
end
|
||||||
|
|
||||||
|
def without_media?
|
||||||
|
truthy_param?(:without_media)
|
||||||
|
end
|
||||||
|
|
||||||
def compact?
|
def compact?
|
||||||
truthy_param?(:compact)
|
truthy_param?(:compact)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def media_only_scope
|
||||||
|
Status.joins(:media_attachments)
|
||||||
|
end
|
||||||
|
|
||||||
|
def without_media_scope
|
||||||
|
Status.left_joins(:media_attachments).where(media_attachments: {status_id: nil})
|
||||||
|
end
|
||||||
|
|
||||||
def insert_pagination_headers
|
def insert_pagination_headers
|
||||||
set_pagination_headers(next_path, prev_path)
|
set_pagination_headers(next_path, prev_path)
|
||||||
end
|
end
|
||||||
|
@ -71,6 +94,6 @@ class Api::V1::FavouritesController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
def pagination_params(core_params)
|
||||||
params.slice(:limit, :compact).permit(:limit, :compact).merge(core_params)
|
params_slice(:limit, :compact, :only_media, :without_media).merge(core_params)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -37,7 +37,19 @@ class Api::V1::Timelines::PersonalController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def personal_feed
|
def personal_feed
|
||||||
PersonalFeed.new(current_account)
|
PersonalFeed.new(
|
||||||
|
current_account,
|
||||||
|
only_media: media_only?,
|
||||||
|
without_media: without_media?,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def media_only?
|
||||||
|
truthy_param?(:only_media)
|
||||||
|
end
|
||||||
|
|
||||||
|
def without_media?
|
||||||
|
truthy_param?(:without_media)
|
||||||
end
|
end
|
||||||
|
|
||||||
def compact?
|
def compact?
|
||||||
|
@ -49,7 +61,7 @@ class Api::V1::Timelines::PersonalController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def pagination_params(core_params)
|
def pagination_params(core_params)
|
||||||
params.slice(:local, :limit).permit(:local, :limit).merge(core_params)
|
params_slice(:limit, :compact, :only_media, :without_media).merge(core_params)
|
||||||
end
|
end
|
||||||
|
|
||||||
def next_path
|
def next_path
|
||||||
|
|
|
@ -10,15 +10,18 @@ export const BOOKMARKED_STATUSES_EXPAND_REQUEST = 'BOOKMARKED_STATUSES_EXPAND_RE
|
||||||
export const BOOKMARKED_STATUSES_EXPAND_SUCCESS = 'BOOKMARKED_STATUSES_EXPAND_SUCCESS';
|
export const BOOKMARKED_STATUSES_EXPAND_SUCCESS = 'BOOKMARKED_STATUSES_EXPAND_SUCCESS';
|
||||||
export const BOOKMARKED_STATUSES_EXPAND_FAIL = 'BOOKMARKED_STATUSES_EXPAND_FAIL';
|
export const BOOKMARKED_STATUSES_EXPAND_FAIL = 'BOOKMARKED_STATUSES_EXPAND_FAIL';
|
||||||
|
|
||||||
export function fetchBookmarkedStatuses() {
|
export function fetchBookmarkedStatuses({ onlyMedia, withoutMedia } = {}) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
if (getState().getIn(['status_lists', 'bookmarks', 'isLoading'])) {
|
if (getState().getIn(['status_lists', 'bookmarks', 'isLoading'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const params = ['compact=true', onlyMedia ? 'only_media=true' : null, withoutMedia ? 'without_media=true' : null];
|
||||||
|
const param_string = params.filter(e => !!e).join('&');
|
||||||
|
|
||||||
dispatch(fetchBookmarkedStatusesRequest());
|
dispatch(fetchBookmarkedStatusesRequest());
|
||||||
|
|
||||||
api(getState).get('/api/v1/bookmarks?compact=true').then(response => {
|
api(getState).get(`/api/v1/bookmarks?${param_string}`).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
if ('statuses' in response.data && 'accounts' in response.data) {
|
if ('statuses' in response.data && 'accounts' in response.data) {
|
||||||
const { statuses, referenced_statuses, accounts, relationships } = response.data;
|
const { statuses, referenced_statuses, accounts, relationships } = response.data;
|
||||||
|
|
|
@ -10,15 +10,18 @@ export const EMOJI_REACTIONED_STATUSES_EXPAND_REQUEST = 'EMOJI_REACTIONED_STATUS
|
||||||
export const EMOJI_REACTIONED_STATUSES_EXPAND_SUCCESS = 'EMOJI_REACTIONED_STATUSES_EXPAND_SUCCESS';
|
export const EMOJI_REACTIONED_STATUSES_EXPAND_SUCCESS = 'EMOJI_REACTIONED_STATUSES_EXPAND_SUCCESS';
|
||||||
export const EMOJI_REACTIONED_STATUSES_EXPAND_FAIL = 'EMOJI_REACTIONED_STATUSES_EXPAND_FAIL';
|
export const EMOJI_REACTIONED_STATUSES_EXPAND_FAIL = 'EMOJI_REACTIONED_STATUSES_EXPAND_FAIL';
|
||||||
|
|
||||||
export function fetchEmojiReactionedStatuses() {
|
export function fetchEmojiReactionedStatuses({ onlyMedia, withoutMedia } = {}) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
if (getState().getIn(['status_lists', 'emoji_reactions', 'isLoading'])) {
|
if (getState().getIn(['status_lists', 'emoji_reactions', 'isLoading'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const params = ['compact=true', onlyMedia ? 'only_media=true' : null, withoutMedia ? 'without_media=true' : null];
|
||||||
|
const param_string = params.filter(e => !!e).join('&');
|
||||||
|
|
||||||
dispatch(fetchEmojiReactionedStatusesRequest());
|
dispatch(fetchEmojiReactionedStatusesRequest());
|
||||||
|
|
||||||
api(getState).get('/api/v1/emoji_reactions?compact=true').then(response => {
|
api(getState).get(`/api/v1/emoji_reactions?${param_string}`).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
if ('statuses' in response.data && 'accounts' in response.data) {
|
if ('statuses' in response.data && 'accounts' in response.data) {
|
||||||
const { statuses, referenced_statuses, accounts, relationships } = response.data;
|
const { statuses, referenced_statuses, accounts, relationships } = response.data;
|
||||||
|
|
|
@ -10,15 +10,18 @@ export const FAVOURITED_STATUSES_EXPAND_REQUEST = 'FAVOURITED_STATUSES_EXPAND_RE
|
||||||
export const FAVOURITED_STATUSES_EXPAND_SUCCESS = 'FAVOURITED_STATUSES_EXPAND_SUCCESS';
|
export const FAVOURITED_STATUSES_EXPAND_SUCCESS = 'FAVOURITED_STATUSES_EXPAND_SUCCESS';
|
||||||
export const FAVOURITED_STATUSES_EXPAND_FAIL = 'FAVOURITED_STATUSES_EXPAND_FAIL';
|
export const FAVOURITED_STATUSES_EXPAND_FAIL = 'FAVOURITED_STATUSES_EXPAND_FAIL';
|
||||||
|
|
||||||
export function fetchFavouritedStatuses() {
|
export function fetchFavouritedStatuses({ onlyMedia, withoutMedia } = {}) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
if (getState().getIn(['status_lists', 'favourites', 'isLoading'])) {
|
if (getState().getIn(['status_lists', 'favourites', 'isLoading'])) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const params = ['compact=true', onlyMedia ? 'only_media=true' : null, withoutMedia ? 'without_media=true' : null];
|
||||||
|
const param_string = params.filter(e => !!e).join('&');
|
||||||
|
|
||||||
dispatch(fetchFavouritedStatusesRequest());
|
dispatch(fetchFavouritedStatusesRequest());
|
||||||
|
|
||||||
api(getState).get('/api/v1/favourites?compact=true').then(response => {
|
api(getState).get(`/api/v1/favourites?${param_string}`).then(response => {
|
||||||
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
const next = getLinks(response).refs.find(link => link.rel === 'next');
|
||||||
if ('statuses' in response.data && 'accounts' in response.data) {
|
if ('statuses' in response.data && 'accounts' in response.data) {
|
||||||
const { statuses, referenced_statuses, accounts, relationships } = response.data;
|
const { statuses, referenced_statuses, accounts, relationships } = response.data;
|
||||||
|
|
|
@ -183,7 +183,7 @@ export function expandTimeline(timelineId, path, params = {}, done = noOp) {
|
||||||
|
|
||||||
export const expandHomeTimeline = ({ maxId, visibilities } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId, visibilities: visibilities }, done);
|
export const expandHomeTimeline = ({ maxId, visibilities } = {}, done = noOp) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId, visibilities: visibilities }, done);
|
||||||
export const expandLimitedTimeline = ({ maxId, visibilities } = {}, done = noOp) => expandTimeline('limited', '/api/v1/timelines/home', { max_id: maxId, visibilities: visibilities }, done);
|
export const expandLimitedTimeline = ({ maxId, visibilities } = {}, done = noOp) => expandTimeline('limited', '/api/v1/timelines/home', { max_id: maxId, visibilities: visibilities }, done);
|
||||||
export const expandPersonalTimeline = ({ maxId } = {}, done = noOp) => expandTimeline('personal', '/api/v1/timelines/personal', { max_id: maxId}, done);
|
export const expandPersonalTimeline = ({ maxId, onlyMedia, withoutMedia } = {}, done = noOp) => expandTimeline(`personal${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/personal', { max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia }, done);
|
||||||
export const expandPublicTimeline = ({ maxId, onlyMedia, withoutMedia, withoutBot, onlyRemote } = {}, done = noOp) => expandTimeline(`public${onlyRemote ? ':remote' : ''}${withoutBot ? ':nobot' : ':bot'}${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { remote: !!onlyRemote, max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia, without_bot: !!withoutBot }, done);
|
export const expandPublicTimeline = ({ maxId, onlyMedia, withoutMedia, withoutBot, onlyRemote } = {}, done = noOp) => expandTimeline(`public${onlyRemote ? ':remote' : ''}${withoutBot ? ':nobot' : ':bot'}${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}`, '/api/v1/timelines/public', { remote: !!onlyRemote, max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia, without_bot: !!withoutBot }, done);
|
||||||
export const expandDomainTimeline = (domain, { maxId, onlyMedia, withoutMedia, withoutBot } = {}, done = noOp) => expandTimeline(`domain${withoutBot ? ':nobot' : ':bot'}${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}:${domain}`, '/api/v1/timelines/public', { local: false, domain: domain, max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia, without_bot: !!withoutBot }, done);
|
export const expandDomainTimeline = (domain, { maxId, onlyMedia, withoutMedia, withoutBot } = {}, done = noOp) => expandTimeline(`domain${withoutBot ? ':nobot' : ':bot'}${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}:${domain}`, '/api/v1/timelines/public', { local: false, domain: domain, max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia, without_bot: !!withoutBot }, done);
|
||||||
export const expandGroupTimeline = (id, { maxId, onlyMedia, withoutMedia, tagged } = {}, done = noOp) => expandTimeline(`group:${id}${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}${tagged ? `:${tagged}` : ''}`, `/api/v1/timelines/group/${id}`, { max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia, tagged: tagged }, done);
|
export const expandGroupTimeline = (id, { maxId, onlyMedia, withoutMedia, tagged } = {}, done = noOp) => expandTimeline(`group:${id}${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}${tagged ? `:${tagged}` : ''}`, `/api/v1/timelines/group/${id}`, { max_id: maxId, only_media: !!onlyMedia, without_media: !!withoutMedia, tagged: tagged }, done);
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import { injectIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import SettingToggle from '../../notifications/components/setting_toggle';
|
||||||
|
|
||||||
|
export default @injectIntl
|
||||||
|
class ColumnSettings extends React.PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
settings: ImmutablePropTypes.map.isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
columnId: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { settings, onChange } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='column-settings__row'>
|
||||||
|
<SettingToggle settings={settings} settingPath={['other', 'onlyMedia']} exclusiveSettingPaths={[['other', 'withoutMedia']]} onChange={onChange} label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media only' />} />
|
||||||
|
<SettingToggle settings={settings} settingPath={['other', 'withoutMedia']} exclusiveSettingPaths={[['other', 'onlyMedia']]} onChange={onChange} label={<FormattedMessage id='community.column_settings.without_media' defaultMessage='Without media' />} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import ColumnSettings from '../components/column_settings';
|
||||||
|
import { changeSetting } from '../../../actions/settings';
|
||||||
|
import { changeColumnParams } from '../../../actions/columns';
|
||||||
|
|
||||||
|
const mapStateToProps = (state, { columnId }) => {
|
||||||
|
const uuid = columnId;
|
||||||
|
const columns = state.getIn(['settings', 'columns']);
|
||||||
|
const index = columns.findIndex(c => c.get('uuid') === uuid);
|
||||||
|
|
||||||
|
return {
|
||||||
|
settings: (uuid && index >= 0) ? columns.get(index).get('params') : state.getIn(['settings', 'bookmarked_statuses']),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch, { columnId }) => {
|
||||||
|
return {
|
||||||
|
onChange (key, checked) {
|
||||||
|
if (columnId) {
|
||||||
|
dispatch(changeColumnParams(columnId, key, checked));
|
||||||
|
} else {
|
||||||
|
dispatch(changeSetting(['bookmarked_statuses', ...key], checked));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
|
|
@ -6,6 +6,7 @@ import { fetchBookmarkedStatuses, expandBookmarkedStatuses } from '../../actions
|
||||||
import Column from '../ui/components/column';
|
import Column from '../ui/components/column';
|
||||||
import ColumnHeader from '../../components/column_header';
|
import ColumnHeader from '../../components/column_header';
|
||||||
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||||
|
import ColumnSettingsContainer from './containers/column_settings_container';
|
||||||
import StatusList from '../../components/status_list';
|
import StatusList from '../../components/status_list';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
@ -22,12 +23,16 @@ const mapStateToProps = (state, { columnId }) => {
|
||||||
const uuid = columnId;
|
const uuid = columnId;
|
||||||
const columns = state.getIn(['settings', 'columns']);
|
const columns = state.getIn(['settings', 'columns']);
|
||||||
const index = columns.findIndex(c => c.get('uuid') === uuid);
|
const index = columns.findIndex(c => c.get('uuid') === uuid);
|
||||||
|
const onlyMedia = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'onlyMedia']) : state.getIn(['settings', 'bookmarked_statuses', 'other', 'onlyMedia']);
|
||||||
|
const withoutMedia = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'withoutMedia']) : state.getIn(['settings', 'bookmarked_statuses', 'other', 'withoutMedia']);
|
||||||
const columnWidth = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'columnWidth']) : state.getIn(['settings', 'bookmarked_statuses', 'columnWidth']);
|
const columnWidth = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'columnWidth']) : state.getIn(['settings', 'bookmarked_statuses', 'columnWidth']);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusIds: state.getIn(['status_lists', 'bookmarks', 'items']),
|
statusIds: state.getIn(['status_lists', 'bookmarks', 'items']),
|
||||||
isLoading: state.getIn(['status_lists', 'bookmarks', 'isLoading'], true),
|
isLoading: state.getIn(['status_lists', 'bookmarks', 'isLoading'], true),
|
||||||
hasMore: !!state.getIn(['status_lists', 'bookmarks', 'next']),
|
hasMore: !!state.getIn(['status_lists', 'bookmarks', 'next']),
|
||||||
|
onlyMedia,
|
||||||
|
withoutMedia,
|
||||||
columnWidth: columnWidth ?? defaultColumnWidth,
|
columnWidth: columnWidth ?? defaultColumnWidth,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -43,21 +48,38 @@ class Bookmarks extends ImmutablePureComponent {
|
||||||
columnId: PropTypes.string,
|
columnId: PropTypes.string,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
columnWidth: PropTypes.string,
|
columnWidth: PropTypes.string,
|
||||||
|
onlyMedia: PropTypes.bool,
|
||||||
|
withoutMedia: PropTypes.bool,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount () {
|
static defaultProps = {
|
||||||
this.props.dispatch(fetchBookmarkedStatuses());
|
onlyMedia: false,
|
||||||
|
withoutMedia: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
const { dispatch, onlyMedia, withoutMedia } = this.props;
|
||||||
|
|
||||||
|
dispatch(fetchBookmarkedStatuses({ onlyMedia, withoutMedia }));
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
const { dispatch, onlyMedia, withoutMedia } = this.props;
|
||||||
|
|
||||||
|
if (prevProps.onlyMedia !== onlyMedia || prevProps.withoutMedia !== withoutMedia) {
|
||||||
|
dispatch(fetchBookmarkedStatuses({ onlyMedia, withoutMedia }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePin = () => {
|
handlePin = () => {
|
||||||
const { columnId, dispatch } = this.props;
|
const { columnId, dispatch, onlyMedia, withoutMedia } = this.props;
|
||||||
|
|
||||||
if (columnId) {
|
if (columnId) {
|
||||||
dispatch(removeColumn(columnId));
|
dispatch(removeColumn(columnId));
|
||||||
} else {
|
} else {
|
||||||
dispatch(addColumn('BOOKMARKS', {}));
|
dispatch(addColumn('BOOKMARKS', { other: { onlyMedia, withoutMedia } }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +111,7 @@ class Bookmarks extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, statusIds, columnId, multiColumn, hasMore, isLoading, columnWidth } = this.props;
|
const { intl, statusIds, columnId, multiColumn, hasMore, isLoading, columnWidth, withoutMedia } = this.props;
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
|
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.bookmarked_statuses' defaultMessage="You don't have any bookmarked toots yet. When you bookmark one, it will show up here." />;
|
const emptyMessage = <FormattedMessage id='empty_column.bookmarked_statuses' defaultMessage="You don't have any bookmarked toots yet. When you bookmark one, it will show up here." />;
|
||||||
|
@ -107,7 +129,9 @@ class Bookmarks extends ImmutablePureComponent {
|
||||||
columnWidth={columnWidth}
|
columnWidth={columnWidth}
|
||||||
onWidthChange={this.handleWidthChange}
|
onWidthChange={this.handleWidthChange}
|
||||||
showBackButton
|
showBackButton
|
||||||
/>
|
>
|
||||||
|
<ColumnSettingsContainer columnId={columnId} />
|
||||||
|
</ColumnHeader>
|
||||||
|
|
||||||
<StatusList
|
<StatusList
|
||||||
trackScroll={!pinned}
|
trackScroll={!pinned}
|
||||||
|
@ -118,6 +142,7 @@ class Bookmarks extends ImmutablePureComponent {
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
|
showCard={!withoutMedia}
|
||||||
/>
|
/>
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import { injectIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import SettingToggle from '../../notifications/components/setting_toggle';
|
||||||
|
|
||||||
|
export default @injectIntl
|
||||||
|
class ColumnSettings extends React.PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
settings: ImmutablePropTypes.map.isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
columnId: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { settings, onChange } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='column-settings__row'>
|
||||||
|
<SettingToggle settings={settings} settingPath={['other', 'onlyMedia']} exclusiveSettingPaths={[['other', 'withoutMedia']]} onChange={onChange} label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media only' />} />
|
||||||
|
<SettingToggle settings={settings} settingPath={['other', 'withoutMedia']} exclusiveSettingPaths={[['other', 'onlyMedia']]} onChange={onChange} label={<FormattedMessage id='community.column_settings.without_media' defaultMessage='Without media' />} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import ColumnSettings from '../components/column_settings';
|
||||||
|
import { changeSetting } from '../../../actions/settings';
|
||||||
|
import { changeColumnParams } from '../../../actions/columns';
|
||||||
|
|
||||||
|
const mapStateToProps = (state, { columnId }) => {
|
||||||
|
const uuid = columnId;
|
||||||
|
const columns = state.getIn(['settings', 'columns']);
|
||||||
|
const index = columns.findIndex(c => c.get('uuid') === uuid);
|
||||||
|
|
||||||
|
return {
|
||||||
|
settings: (uuid && index >= 0) ? columns.get(index).get('params') : state.getIn(['settings', 'emoji_reactioned_statuses']),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch, { columnId }) => {
|
||||||
|
return {
|
||||||
|
onChange (key, checked) {
|
||||||
|
if (columnId) {
|
||||||
|
dispatch(changeColumnParams(columnId, key, checked));
|
||||||
|
} else {
|
||||||
|
dispatch(changeSetting(['emoji_reactioned_statuses', ...key], checked));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
|
|
@ -6,6 +6,7 @@ import { fetchEmojiReactionedStatuses, expandEmojiReactionedStatuses } from '../
|
||||||
import Column from '../ui/components/column';
|
import Column from '../ui/components/column';
|
||||||
import ColumnHeader from '../../components/column_header';
|
import ColumnHeader from '../../components/column_header';
|
||||||
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||||
|
import ColumnSettingsContainer from './containers/column_settings_container';
|
||||||
import StatusList from '../../components/status_list';
|
import StatusList from '../../components/status_list';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
@ -22,12 +23,16 @@ const mapStateToProps = (state, { columnId }) => {
|
||||||
const uuid = columnId;
|
const uuid = columnId;
|
||||||
const columns = state.getIn(['settings', 'columns']);
|
const columns = state.getIn(['settings', 'columns']);
|
||||||
const index = columns.findIndex(c => c.get('uuid') === uuid);
|
const index = columns.findIndex(c => c.get('uuid') === uuid);
|
||||||
|
const onlyMedia = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'onlyMedia']) : state.getIn(['settings', 'emoji_reactioned_statuses', 'other', 'onlyMedia']);
|
||||||
|
const withoutMedia = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'withoutMedia']) : state.getIn(['settings', 'emoji_reactioned_statuses', 'other', 'withoutMedia']);
|
||||||
const columnWidth = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'columnWidth']) : state.getIn(['settings', 'emoji_reactioned_statuses', 'columnWidth']);
|
const columnWidth = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'columnWidth']) : state.getIn(['settings', 'emoji_reactioned_statuses', 'columnWidth']);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusIds: state.getIn(['status_lists', 'emoji_reactions', 'items']),
|
statusIds: state.getIn(['status_lists', 'emoji_reactions', 'items']),
|
||||||
isLoading: state.getIn(['status_lists', 'emoji_reactions', 'isLoading'], true),
|
isLoading: state.getIn(['status_lists', 'emoji_reactions', 'isLoading'], true),
|
||||||
hasMore: !!state.getIn(['status_lists', 'emoji_reactions', 'next']),
|
hasMore: !!state.getIn(['status_lists', 'emoji_reactions', 'next']),
|
||||||
|
onlyMedia,
|
||||||
|
withoutMedia,
|
||||||
columnWidth: columnWidth ?? defaultColumnWidth,
|
columnWidth: columnWidth ?? defaultColumnWidth,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -43,21 +48,38 @@ class EmojiReactions extends ImmutablePureComponent {
|
||||||
columnId: PropTypes.string,
|
columnId: PropTypes.string,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
columnWidth: PropTypes.string,
|
columnWidth: PropTypes.string,
|
||||||
|
onlyMedia: PropTypes.bool,
|
||||||
|
withoutMedia: PropTypes.bool,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount () {
|
static defaultProps = {
|
||||||
this.props.dispatch(fetchEmojiReactionedStatuses());
|
onlyMedia: false,
|
||||||
|
withoutMedia: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
const { dispatch, onlyMedia, withoutMedia } = this.props;
|
||||||
|
|
||||||
|
dispatch(fetchEmojiReactionedStatuses({ onlyMedia, withoutMedia }));
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
const { dispatch, onlyMedia, withoutMedia } = this.props;
|
||||||
|
|
||||||
|
if (prevProps.onlyMedia !== onlyMedia || prevProps.withoutMedia !== withoutMedia) {
|
||||||
|
dispatch(fetchEmojiReactionedStatuses({ onlyMedia, withoutMedia }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePin = () => {
|
handlePin = () => {
|
||||||
const { columnId, dispatch } = this.props;
|
const { columnId, dispatch, onlyMedia, withoutMedia } = this.props;
|
||||||
|
|
||||||
if (columnId) {
|
if (columnId) {
|
||||||
dispatch(removeColumn(columnId));
|
dispatch(removeColumn(columnId));
|
||||||
} else {
|
} else {
|
||||||
dispatch(addColumn('EMOJI_REACTIONS', {}));
|
dispatch(addColumn('EMOJI_REACTIONS', { other: { onlyMedia, withoutMedia } }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +111,7 @@ class EmojiReactions extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, statusIds, columnId, multiColumn, hasMore, isLoading, columnWidth } = this.props;
|
const { intl, statusIds, columnId, multiColumn, hasMore, isLoading, columnWidth, withoutMedia } = this.props;
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
|
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.emoji_reactioned_statuses' defaultMessage="You don't have any reaction posts yet. When you reaction one, it will show up here." />;
|
const emptyMessage = <FormattedMessage id='empty_column.emoji_reactioned_statuses' defaultMessage="You don't have any reaction posts yet. When you reaction one, it will show up here." />;
|
||||||
|
@ -107,7 +129,9 @@ class EmojiReactions extends ImmutablePureComponent {
|
||||||
columnWidth={columnWidth}
|
columnWidth={columnWidth}
|
||||||
onWidthChange={this.handleWidthChange}
|
onWidthChange={this.handleWidthChange}
|
||||||
showBackButton
|
showBackButton
|
||||||
/>
|
>
|
||||||
|
<ColumnSettingsContainer columnId={columnId} />
|
||||||
|
</ColumnHeader>
|
||||||
|
|
||||||
<StatusList
|
<StatusList
|
||||||
trackScroll={!pinned}
|
trackScroll={!pinned}
|
||||||
|
@ -118,6 +142,7 @@ class EmojiReactions extends ImmutablePureComponent {
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
|
showCard={!withoutMedia}
|
||||||
/>
|
/>
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
import { injectIntl, FormattedMessage } from 'react-intl';
|
||||||
|
import SettingToggle from '../../notifications/components/setting_toggle';
|
||||||
|
|
||||||
|
export default @injectIntl
|
||||||
|
class ColumnSettings extends React.PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
settings: ImmutablePropTypes.map.isRequired,
|
||||||
|
onChange: PropTypes.func.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
columnId: PropTypes.string,
|
||||||
|
};
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { settings, onChange } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div className='column-settings__row'>
|
||||||
|
<SettingToggle settings={settings} settingPath={['other', 'onlyMedia']} exclusiveSettingPaths={[['other', 'withoutMedia']]} onChange={onChange} label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media only' />} />
|
||||||
|
<SettingToggle settings={settings} settingPath={['other', 'withoutMedia']} exclusiveSettingPaths={[['other', 'onlyMedia']]} onChange={onChange} label={<FormattedMessage id='community.column_settings.without_media' defaultMessage='Without media' />} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import ColumnSettings from '../components/column_settings';
|
||||||
|
import { changeSetting } from '../../../actions/settings';
|
||||||
|
import { changeColumnParams } from '../../../actions/columns';
|
||||||
|
|
||||||
|
const mapStateToProps = (state, { columnId }) => {
|
||||||
|
const uuid = columnId;
|
||||||
|
const columns = state.getIn(['settings', 'columns']);
|
||||||
|
const index = columns.findIndex(c => c.get('uuid') === uuid);
|
||||||
|
|
||||||
|
return {
|
||||||
|
settings: (uuid && index >= 0) ? columns.get(index).get('params') : state.getIn(['settings', 'favourited_statuses']),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch, { columnId }) => {
|
||||||
|
return {
|
||||||
|
onChange (key, checked) {
|
||||||
|
if (columnId) {
|
||||||
|
dispatch(changeColumnParams(columnId, key, checked));
|
||||||
|
} else {
|
||||||
|
dispatch(changeSetting(['favourited_statuses', ...key], checked));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
|
|
@ -6,6 +6,7 @@ import { fetchFavouritedStatuses, expandFavouritedStatuses } from '../../actions
|
||||||
import Column from '../ui/components/column';
|
import Column from '../ui/components/column';
|
||||||
import ColumnHeader from '../../components/column_header';
|
import ColumnHeader from '../../components/column_header';
|
||||||
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||||
|
import ColumnSettingsContainer from './containers/column_settings_container';
|
||||||
import StatusList from '../../components/status_list';
|
import StatusList from '../../components/status_list';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
@ -22,12 +23,16 @@ const mapStateToProps = (state, { columnId }) => {
|
||||||
const uuid = columnId;
|
const uuid = columnId;
|
||||||
const columns = state.getIn(['settings', 'columns']);
|
const columns = state.getIn(['settings', 'columns']);
|
||||||
const index = columns.findIndex(c => c.get('uuid') === uuid);
|
const index = columns.findIndex(c => c.get('uuid') === uuid);
|
||||||
|
const onlyMedia = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'onlyMedia']) : state.getIn(['settings', 'favourited_statuses', 'other', 'onlyMedia']);
|
||||||
|
const withoutMedia = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'withoutMedia']) : state.getIn(['settings', 'favourited_statuses', 'other', 'withoutMedia']);
|
||||||
const columnWidth = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'columnWidth']) : state.getIn(['settings', 'favourited_statuses', 'columnWidth']);
|
const columnWidth = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'columnWidth']) : state.getIn(['settings', 'favourited_statuses', 'columnWidth']);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
statusIds: state.getIn(['status_lists', 'favourites', 'items']),
|
statusIds: state.getIn(['status_lists', 'favourites', 'items']),
|
||||||
isLoading: state.getIn(['status_lists', 'favourites', 'isLoading'], true),
|
isLoading: state.getIn(['status_lists', 'favourites', 'isLoading'], true),
|
||||||
hasMore: !!state.getIn(['status_lists', 'favourites', 'next']),
|
hasMore: !!state.getIn(['status_lists', 'favourites', 'next']),
|
||||||
|
onlyMedia,
|
||||||
|
withoutMedia,
|
||||||
columnWidth: columnWidth ?? defaultColumnWidth,
|
columnWidth: columnWidth ?? defaultColumnWidth,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -43,21 +48,38 @@ class Favourites extends ImmutablePureComponent {
|
||||||
columnId: PropTypes.string,
|
columnId: PropTypes.string,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
columnWidth: PropTypes.string,
|
columnWidth: PropTypes.string,
|
||||||
|
onlyMedia: PropTypes.bool,
|
||||||
|
withoutMedia: PropTypes.bool,
|
||||||
hasMore: PropTypes.bool,
|
hasMore: PropTypes.bool,
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount () {
|
static defaultProps = {
|
||||||
this.props.dispatch(fetchFavouritedStatuses());
|
onlyMedia: false,
|
||||||
|
withoutMedia: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidMount () {
|
||||||
|
const { dispatch, onlyMedia, withoutMedia } = this.props;
|
||||||
|
|
||||||
|
dispatch(fetchFavouritedStatuses({ onlyMedia, withoutMedia }));
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
const { dispatch, onlyMedia, withoutMedia } = this.props;
|
||||||
|
|
||||||
|
if (prevProps.onlyMedia !== onlyMedia || prevProps.withoutMedia !== withoutMedia) {
|
||||||
|
dispatch(fetchFavouritedStatuses({ onlyMedia, withoutMedia }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePin = () => {
|
handlePin = () => {
|
||||||
const { columnId, dispatch } = this.props;
|
const { columnId, dispatch, onlyMedia, withoutMedia } = this.props;
|
||||||
|
|
||||||
if (columnId) {
|
if (columnId) {
|
||||||
dispatch(removeColumn(columnId));
|
dispatch(removeColumn(columnId));
|
||||||
} else {
|
} else {
|
||||||
dispatch(addColumn('FAVOURITES', {}));
|
dispatch(addColumn('FAVOURITES', { other: { onlyMedia, withoutMedia } }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +111,7 @@ class Favourites extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, statusIds, columnId, multiColumn, hasMore, isLoading, columnWidth } = this.props;
|
const { intl, statusIds, columnId, multiColumn, hasMore, isLoading, columnWidth, withoutMedia } = this.props;
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
|
|
||||||
const emptyMessage = <FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any favourite posts yet. When you favourite one, it will show up here." />;
|
const emptyMessage = <FormattedMessage id='empty_column.favourited_statuses' defaultMessage="You don't have any favourite posts yet. When you favourite one, it will show up here." />;
|
||||||
|
@ -107,7 +129,9 @@ class Favourites extends ImmutablePureComponent {
|
||||||
columnWidth={columnWidth}
|
columnWidth={columnWidth}
|
||||||
onWidthChange={this.handleWidthChange}
|
onWidthChange={this.handleWidthChange}
|
||||||
showBackButton
|
showBackButton
|
||||||
/>
|
>
|
||||||
|
<ColumnSettingsContainer columnId={columnId} />
|
||||||
|
</ColumnHeader>
|
||||||
|
|
||||||
<StatusList
|
<StatusList
|
||||||
trackScroll={!pinned}
|
trackScroll={!pinned}
|
||||||
|
@ -118,6 +142,7 @@ class Favourites extends ImmutablePureComponent {
|
||||||
onLoadMore={this.handleLoadMore}
|
onLoadMore={this.handleLoadMore}
|
||||||
emptyMessage={emptyMessage}
|
emptyMessage={emptyMessage}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
|
showCard={!withoutMedia}
|
||||||
/>
|
/>
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,7 +10,6 @@ class ColumnSettings extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
settings: ImmutablePropTypes.map.isRequired,
|
settings: ImmutablePropTypes.map.isRequired,
|
||||||
onChange: PropTypes.func.isRequired,
|
onChange: PropTypes.func.isRequired,
|
||||||
onChangeClear: PropTypes.func.isRequired,
|
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -22,7 +21,8 @@ class ColumnSettings extends React.PureComponent {
|
||||||
<span className='column-settings__section'><FormattedMessage id='personal.column_settings.basic' defaultMessage='Basic' /></span>
|
<span className='column-settings__section'><FormattedMessage id='personal.column_settings.basic' defaultMessage='Basic' /></span>
|
||||||
|
|
||||||
<div className='column-settings__row'>
|
<div className='column-settings__row'>
|
||||||
<SettingToggle prefix='personal_timeline' settings={settings} settingPath={['shows', 'reply']} onChange={onChange} label={<FormattedMessage id='personal.column_settings.show_replies' defaultMessage='Show replies' />} />
|
<SettingToggle settings={settings} settingPath={['other', 'onlyMedia']} exclusiveSettingPaths={[['other', 'withoutMedia']]} onChange={onChange} label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media only' />} />
|
||||||
|
<SettingToggle settings={settings} settingPath={['other', 'withoutMedia']} exclusiveSettingPaths={[['other', 'onlyMedia']]} onChange={onChange} label={<FormattedMessage id='community.column_settings.without_media' defaultMessage='Without media' />} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,21 +1,28 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import ColumnSettings from '../components/column_settings';
|
import ColumnSettings from '../components/column_settings';
|
||||||
import { changeSetting, saveSettings } from '../../../actions/settings';
|
import { changeSetting } from '../../../actions/settings';
|
||||||
|
import { changeColumnParams } from '../../../actions/columns';
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = (state, { columnId }) => {
|
||||||
settings: state.getIn(['settings', 'personal']),
|
const uuid = columnId;
|
||||||
});
|
const columns = state.getIn(['settings', 'columns']);
|
||||||
|
const index = columns.findIndex(c => c.get('uuid') === uuid);
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
return {
|
||||||
|
settings: (uuid && index >= 0) ? columns.get(index).get('params') : state.getIn(['settings', 'personal']),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch, { columnId }) => {
|
||||||
|
return {
|
||||||
onChange (key, checked) {
|
onChange (key, checked) {
|
||||||
|
if (columnId) {
|
||||||
|
dispatch(changeColumnParams(columnId, key, checked));
|
||||||
|
} else {
|
||||||
dispatch(changeSetting(['personal', ...key], checked));
|
dispatch(changeSetting(['personal', ...key], checked));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
};
|
||||||
onSave () {
|
};
|
||||||
dispatch(saveSettings());
|
|
||||||
},
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
|
export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
|
||||||
|
|
|
@ -20,10 +20,14 @@ const mapStateToProps = (state, { columnId }) => {
|
||||||
const uuid = columnId;
|
const uuid = columnId;
|
||||||
const columns = state.getIn(['settings', 'columns']);
|
const columns = state.getIn(['settings', 'columns']);
|
||||||
const index = columns.findIndex(c => c.get('uuid') === uuid);
|
const index = columns.findIndex(c => c.get('uuid') === uuid);
|
||||||
|
const onlyMedia = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'onlyMedia']) : state.getIn(['settings', 'personal', 'other', 'onlyMedia']);
|
||||||
|
const withoutMedia = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'other', 'withoutMedia']) : state.getIn(['settings', 'personal', 'other', 'withoutMedia']);
|
||||||
const columnWidth = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'columnWidth']) : state.getIn(['settings', 'personal', 'columnWidth']);
|
const columnWidth = (columnId && index >= 0) ? columns.get(index).getIn(['params', 'columnWidth']) : state.getIn(['settings', 'personal', 'columnWidth']);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hasUnread: state.getIn(['timelines', 'personal', 'unread']) > 0,
|
hasUnread: state.getIn(['timelines', 'personal', 'unread']) > 0,
|
||||||
|
onlyMedia,
|
||||||
|
withoutMedia,
|
||||||
columnWidth: columnWidth ?? defaultColumnWidth,
|
columnWidth: columnWidth ?? defaultColumnWidth,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -37,17 +41,24 @@ class PersonalTimeline extends React.PureComponent {
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
hasUnread: PropTypes.bool,
|
hasUnread: PropTypes.bool,
|
||||||
columnId: PropTypes.string,
|
columnId: PropTypes.string,
|
||||||
|
onlyMedia: PropTypes.bool,
|
||||||
|
withoutMedia: PropTypes.bool,
|
||||||
multiColumn: PropTypes.bool,
|
multiColumn: PropTypes.bool,
|
||||||
columnWidth: PropTypes.string,
|
columnWidth: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
onlyMedia: false,
|
||||||
|
withoutMedia: false,
|
||||||
|
};
|
||||||
|
|
||||||
handlePin = () => {
|
handlePin = () => {
|
||||||
const { columnId, dispatch } = this.props;
|
const { columnId, dispatch, onlyMedia, withoutMedia } = this.props;
|
||||||
|
|
||||||
if (columnId) {
|
if (columnId) {
|
||||||
dispatch(removeColumn(columnId));
|
dispatch(removeColumn(columnId));
|
||||||
} else {
|
} else {
|
||||||
dispatch(addColumn('PERSONAL', {}));
|
dispatch(addColumn('PERSONAL', { other: { onlyMedia, withoutMedia } }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,15 +76,23 @@ class PersonalTimeline extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleLoadMore = maxId => {
|
handleLoadMore = maxId => {
|
||||||
const { dispatch } = this.props;
|
const { dispatch, onlyMedia, withoutMedia } = this.props;
|
||||||
|
|
||||||
dispatch(expandPersonalTimeline({ maxId }));
|
dispatch(expandPersonalTimeline({ maxId, onlyMedia, withoutMedia }));
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
const { dispatch } = this.props;
|
const { dispatch, onlyMedia, withoutMedia } = this.props;
|
||||||
|
|
||||||
dispatch(expandPersonalTimeline({}));
|
dispatch(expandPersonalTimeline({ onlyMedia, withoutMedia }));
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate (prevProps) {
|
||||||
|
const { dispatch, onlyMedia, withoutMedia } = this.props;
|
||||||
|
|
||||||
|
if (prevProps.onlyMedia !== onlyMedia || prevProps.withoutMedia !== withoutMedia) {
|
||||||
|
dispatch(expandPersonalTimeline({ onlyMedia, withoutMedia }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleWidthChange = (value) => {
|
handleWidthChange = (value) => {
|
||||||
|
@ -87,7 +106,7 @@ class PersonalTimeline extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, hasUnread, columnId, multiColumn, columnWidth } = this.props;
|
const { intl, hasUnread, columnId, multiColumn, columnWidth, onlyMedia, withoutMedia } = this.props;
|
||||||
const pinned = !!columnId;
|
const pinned = !!columnId;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -103,15 +122,18 @@ class PersonalTimeline extends React.PureComponent {
|
||||||
multiColumn={multiColumn}
|
multiColumn={multiColumn}
|
||||||
columnWidth={columnWidth}
|
columnWidth={columnWidth}
|
||||||
onWidthChange={this.handleWidthChange}
|
onWidthChange={this.handleWidthChange}
|
||||||
/>
|
>
|
||||||
|
<ColumnSettingsContainer columnId={columnId} />
|
||||||
|
</ColumnHeader>
|
||||||
|
|
||||||
<StatusListContainer
|
<StatusListContainer
|
||||||
|
timelineId={`personal${withoutMedia ? ':nomedia' : ''}${onlyMedia ? ':media' : ''}`}
|
||||||
|
onLoadMore={this.handleLoadMore}
|
||||||
trackScroll={!pinned}
|
trackScroll={!pinned}
|
||||||
scrollKey={`personal_timeline-${columnId}`}
|
scrollKey={`personal_timeline-${columnId}`}
|
||||||
onLoadMore={this.handleLoadMore}
|
|
||||||
timelineId='personal'
|
|
||||||
emptyMessage={<FormattedMessage id='empty_column.personal' defaultMessage='Personal posts unavailable' />}
|
emptyMessage={<FormattedMessage id='empty_column.personal' defaultMessage='Personal posts unavailable' />}
|
||||||
bindToDocument={!multiColumn}
|
bindToDocument={!multiColumn}
|
||||||
|
showCard={!withoutMedia}
|
||||||
/>
|
/>
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
|
|
|
@ -140,6 +140,24 @@ const initialState = ImmutableMap({
|
||||||
body: '',
|
body: '',
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
bookmarked_statuses: ImmutableMap({
|
||||||
|
regex: ImmutableMap({
|
||||||
|
body: '',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
favourited_statuses: ImmutableMap({
|
||||||
|
regex: ImmutableMap({
|
||||||
|
body: '',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
|
||||||
|
emoji_reactioned_statuses: ImmutableMap({
|
||||||
|
regex: ImmutableMap({
|
||||||
|
body: '',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const defaultColumns = fromJS([
|
const defaultColumns = fromJS([
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
class PersonalFeed
|
class PersonalFeed
|
||||||
# @param [Account] account
|
# @param [Account] account
|
||||||
# @param [Hash] options
|
# @param [Hash] options
|
||||||
# @option [Boolean] :with_replies
|
# @option [Boolean] :only_media
|
||||||
|
# @option [Boolean] :without_media
|
||||||
def initialize(account, options = {})
|
def initialize(account, options = {})
|
||||||
@account = account
|
@account = account
|
||||||
@options = options
|
@options = options
|
||||||
|
@ -17,6 +18,9 @@ class PersonalFeed
|
||||||
def get(limit, max_id = nil, since_id = nil, min_id = nil)
|
def get(limit, max_id = nil, since_id = nil, min_id = nil)
|
||||||
scope = personal_scope
|
scope = personal_scope
|
||||||
|
|
||||||
|
scope.merge!(media_only_scope) if media_only?
|
||||||
|
scope.merge!(without_media_scope) if without_media?
|
||||||
|
|
||||||
scope.cache_ids.to_a_paginated_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id)
|
scope.cache_ids.to_a_paginated_by_id(limit, max_id: max_id, since_id: since_id, min_id: min_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -24,6 +28,22 @@ class PersonalFeed
|
||||||
|
|
||||||
attr_reader :account, :options
|
attr_reader :account, :options
|
||||||
|
|
||||||
|
def media_only?
|
||||||
|
options[:only_media]
|
||||||
|
end
|
||||||
|
|
||||||
|
def without_media?
|
||||||
|
options[:without_media]
|
||||||
|
end
|
||||||
|
|
||||||
|
def media_only_scope
|
||||||
|
Status.joins(:media_attachments).group(:id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def without_media_scope
|
||||||
|
Status.left_joins(:media_attachments).where(media_attachments: {status_id: nil})
|
||||||
|
end
|
||||||
|
|
||||||
def personal_scope
|
def personal_scope
|
||||||
Status.include_expired.where(account_id: account.id).without_reblogs.with_personal_visibility
|
Status.include_expired.where(account_id: account.id).without_reblogs.with_personal_visibility
|
||||||
end
|
end
|
||||||
|
|
|
@ -150,6 +150,10 @@ class REST::InstanceSerializer < ActiveModel::Serializer
|
||||||
:account_conversations,
|
:account_conversations,
|
||||||
:enable_wide_emoji,
|
:enable_wide_emoji,
|
||||||
:enable_wide_emoji_reaction,
|
:enable_wide_emoji_reaction,
|
||||||
|
:timeline_bookmark_media_option,
|
||||||
|
:timeline_favourite_media_option,
|
||||||
|
:timeline_emoji_reaction_media_option,
|
||||||
|
:timeline_personal_media_option,
|
||||||
]
|
]
|
||||||
|
|
||||||
capabilities << :profile_search unless Chewy.enabled?
|
capabilities << :profile_search unless Chewy.enabled?
|
||||||
|
|
Loading…
Reference in a new issue