:setting_show_application,
:setting_advanced_layout,
:setting_use_blurhash,
+ :setting_use_pending_items,
notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account),
interactions: %i(must_be_follower must_be_following must_be_following_dm)
)
import { List as ImmutableList } from 'immutable';
import { unescapeHTML } from '../utils/html';
import { getFiltersRegex } from '../selectors';
+import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
+import compareId from 'mastodon/compare_id';
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET';
-export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR';
-export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
+export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR';
+export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
+export const NOTIFICATIONS_LOAD_PENDING = 'NOTIFICATIONS_LOAD_PENDING';
defineMessages({
mention: { id: 'notification.mention', defaultMessage: '{name} mentioned you' },
}
};
+export const loadPending = () => ({
+ type: NOTIFICATIONS_LOAD_PENDING,
+});
+
export function updateNotifications(notification, intlMessages, intlLocale) {
return (dispatch, getState) => {
const showInColumn = getState().getIn(['settings', 'notifications', 'shows', notification.type], true);
dispatch({
type: NOTIFICATIONS_UPDATE,
notification,
+ usePendingItems: preferPendingItems,
meta: (playSound && !filtered) ? { sound: 'boop' } : undefined,
});
: excludeTypesFromFilter(activeFilter),
};
- if (!maxId && notifications.get('items').size > 0) {
- params.since_id = notifications.getIn(['items', 0, 'id']);
+ if (!params.max_id && (notifications.get('items', ImmutableList()).size + notifications.get('pendingItems', ImmutableList()).size) > 0) {
+ const a = notifications.getIn(['pendingItems', 0, 'id']);
+ const b = notifications.getIn(['items', 0, 'id']);
+
+ if (a && b && compareId(a, b) > 0) {
+ params.since_id = a;
+ } else {
+ params.since_id = b || a;
+ }
}
+ const isLoadingRecent = !!params.since_id;
+
dispatch(expandNotificationsRequest(isLoadingMore));
api(getState).get('/api/v1/notifications', { params }).then(response => {
dispatch(importFetchedAccounts(response.data.map(item => item.account)));
dispatch(importFetchedStatuses(response.data.map(item => item.status).filter(status => !!status)));
- dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore));
+ dispatch(expandNotificationsSuccess(response.data, next ? next.uri : null, isLoadingMore, isLoadingRecent && preferPendingItems));
fetchRelatedRelationships(dispatch, response.data);
done();
}).catch(error => {
};
};
-export function expandNotificationsSuccess(notifications, next, isLoadingMore) {
+export function expandNotificationsSuccess(notifications, next, isLoadingMore, usePendingItems) {
return {
type: NOTIFICATIONS_EXPAND_SUCCESS,
notifications,
next,
+ usePendingItems,
skipLoading: !isLoadingMore,
};
};
import { importFetchedStatus, importFetchedStatuses } from './importer';
-import api, { getLinks } from '../api';
+import api, { getLinks } from 'mastodon/api';
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
+import compareId from 'mastodon/compare_id';
+import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
export const TIMELINE_UPDATE = 'TIMELINE_UPDATE';
export const TIMELINE_DELETE = 'TIMELINE_DELETE';
export const TIMELINE_EXPAND_SUCCESS = 'TIMELINE_EXPAND_SUCCESS';
export const TIMELINE_EXPAND_FAIL = 'TIMELINE_EXPAND_FAIL';
-export const TIMELINE_SCROLL_TOP = 'TIMELINE_SCROLL_TOP';
+export const TIMELINE_SCROLL_TOP = 'TIMELINE_SCROLL_TOP';
+export const TIMELINE_LOAD_PENDING = 'TIMELINE_LOAD_PENDING';
+export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
+export const TIMELINE_CONNECT = 'TIMELINE_CONNECT';
-export const TIMELINE_CONNECT = 'TIMELINE_CONNECT';
-export const TIMELINE_DISCONNECT = 'TIMELINE_DISCONNECT';
+export const loadPending = timeline => ({
+ type: TIMELINE_LOAD_PENDING,
+ timeline,
+});
export function updateTimeline(timeline, status, accept) {
return dispatch => {
type: TIMELINE_UPDATE,
timeline,
status,
+ usePendingItems: preferPendingItems,
});
};
};
return;
}
- if (!params.max_id && !params.pinned && timeline.get('items', ImmutableList()).size > 0) {
- params.since_id = timeline.getIn(['items', 0]);
+ if (!params.max_id && !params.pinned && (timeline.get('items', ImmutableList()).size + timeline.get('pendingItems', ImmutableList()).size) > 0) {
+ const a = timeline.getIn(['pendingItems', 0]);
+ const b = timeline.getIn(['items', 0]);
+
+ if (a && b && compareId(a, b) > 0) {
+ params.since_id = a;
+ } else {
+ params.since_id = b || a;
+ }
}
const isLoadingRecent = !!params.since_id;
api(getState).get(path, { params }).then(response => {
const next = getLinks(response).refs.find(link => link.rel === 'next');
dispatch(importFetchedStatuses(response.data));
- dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.code === 206, isLoadingRecent, isLoadingMore));
+ dispatch(expandTimelineSuccess(timelineId, response.data, next ? next.uri : null, response.code === 206, isLoadingRecent, isLoadingMore, isLoadingRecent && preferPendingItems));
done();
}).catch(error => {
dispatch(expandTimelineFail(timelineId, error, isLoadingMore));
};
};
-export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingRecent, isLoadingMore) {
+export function expandTimelineSuccess(timeline, statuses, next, partial, isLoadingRecent, isLoadingMore, usePendingItems) {
return {
type: TIMELINE_EXPAND_SUCCESS,
timeline,
next,
partial,
isLoadingRecent,
+ usePendingItems,
skipLoading: !isLoadingMore,
};
};
};
};
-export function disconnectTimeline(timeline) {
- return {
- type: TIMELINE_DISCONNECT,
- timeline,
- };
-};
+export const disconnectTimeline = timeline => ({
+ type: TIMELINE_DISCONNECT,
+ timeline,
+ usePendingItems: preferPendingItems,
+});
-export default function compareId(id1, id2) {
+export default function compareId (id1, id2) {
if (id1 === id2) {
return 0;
}
+
if (id1.length === id2.length) {
return id1 > id2 ? 1 : -1;
} else {
return id1.length > id2.length ? 1 : -1;
}
-}
+};
--- /dev/null
+import React from 'react';
+import { FormattedMessage } from 'react-intl';
+import PropTypes from 'prop-types';
+
+export default class LoadPending extends React.PureComponent {
+
+ static propTypes = {
+ onClick: PropTypes.func,
+ count: PropTypes.number,
+ }
+
+ render() {
+ const { count } = this.props;
+
+ return (
+ <button className='load-more load-gap' onClick={this.props.onClick}>
+ <FormattedMessage id='load_pending' defaultMessage='{count, plural, one {# new item} other {# new items}}' values={{ count }} />
+ </button>
+ );
+ }
+
+}
import PropTypes from 'prop-types';
import IntersectionObserverArticleContainer from '../containers/intersection_observer_article_container';
import LoadMore from './load_more';
+import LoadPending from './load_pending';
import IntersectionObserverWrapper from '../features/ui/util/intersection_observer_wrapper';
import { throttle } from 'lodash';
import { List as ImmutableList } from 'immutable';
static propTypes = {
scrollKey: PropTypes.string.isRequired,
onLoadMore: PropTypes.func,
+ onLoadPending: PropTypes.func,
onScrollToTop: PropTypes.func,
onScroll: PropTypes.func,
trackScroll: PropTypes.bool,
isLoading: PropTypes.bool,
showLoading: PropTypes.bool,
hasMore: PropTypes.bool,
+ numPending: PropTypes.number,
prepend: PropTypes.node,
alwaysPrepend: PropTypes.bool,
emptyMessage: PropTypes.node,
this.props.onLoadMore();
}
+ handleLoadPending = e => {
+ e.preventDefault();
+ this.props.onLoadPending();
+ }
+
render () {
- const { children, scrollKey, trackScroll, shouldUpdateScroll, showLoading, isLoading, hasMore, prepend, alwaysPrepend, emptyMessage, onLoadMore } = this.props;
+ const { children, scrollKey, trackScroll, shouldUpdateScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, emptyMessage, onLoadMore } = this.props;
const { fullscreen } = this.state;
const childrenCount = React.Children.count(children);
const loadMore = (hasMore && onLoadMore) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null;
+ const loadPending = (numPending > 0) ? <LoadPending count={numPending} onClick={this.handleLoadPending} /> : null;
let scrollableArea = null;
if (showLoading) {
<div role='feed' className='item-list'>
{prepend}
+ {loadPending}
+
{React.Children.map(this.props.children, (child, index) => (
<IntersectionObserverArticleContainer
key={child.key}
return (
<div>
<div className='column-settings__row'>
- <SettingToggle settings={settings} settingPath={['other', 'onlyMedia']} onChange={onChange} label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media Only' />} />
+ <SettingToggle settings={settings} settingPath={['other', 'onlyMedia']} onChange={onChange} label={<FormattedMessage id='community.column_settings.media_only' defaultMessage='Media only' />} />
</div>
</div>
);
settingPath: PropTypes.array.isRequired,
label: PropTypes.node.isRequired,
onChange: PropTypes.func.isRequired,
+ defaultValue: PropTypes.bool,
}
onChange = ({ target }) => {
}
render () {
- const { prefix, settings, settingPath, label } = this.props;
+ const { prefix, settings, settingPath, label, defaultValue } = this.props;
const id = ['setting-toggle', prefix, ...settingPath].filter(Boolean).join('-');
return (
<div className='setting-toggle'>
- <Toggle id={id} checked={settings.getIn(settingPath)} onChange={this.onChange} onKeyDown={this.onKeyDown} />
+ <Toggle id={id} checked={settings.getIn(settingPath, defaultValue)} onChange={this.onChange} onKeyDown={this.onKeyDown} />
<label htmlFor={id} className='setting-toggle__label'>{label}</label>
</div>
);
import ImmutablePropTypes from 'react-immutable-proptypes';
import Column from '../../components/column';
import ColumnHeader from '../../components/column_header';
-import { expandNotifications, scrollTopNotifications } from '../../actions/notifications';
+import { expandNotifications, scrollTopNotifications, loadPending } from '../../actions/notifications';
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
import NotificationContainer from './containers/notification_container';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
isLoading: state.getIn(['notifications', 'isLoading'], true),
isUnread: state.getIn(['notifications', 'unread']) > 0,
hasMore: state.getIn(['notifications', 'hasMore']),
+ numPending: state.getIn(['notifications', 'pendingItems'], ImmutableList()).size,
});
export default @connect(mapStateToProps)
isUnread: PropTypes.bool,
multiColumn: PropTypes.bool,
hasMore: PropTypes.bool,
+ numPending: PropTypes.number,
};
static defaultProps = {
this.props.dispatch(expandNotifications({ maxId: last && last.get('id') }));
}, 300, { leading: true });
+ handleLoadPending = () => {
+ this.props.dispatch(loadPending());
+ };
+
handleScrollToTop = debounce(() => {
this.props.dispatch(scrollTopNotifications(true));
}, 100);
}
render () {
- const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore, showFilterBar } = this.props;
+ const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar } = this.props;
const pinned = !!columnId;
const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. Interact with others to start the conversation." />;
isLoading={isLoading}
showLoading={isLoading && notifications.size === 0}
hasMore={hasMore}
+ numPending={numPending}
emptyMessage={emptyMessage}
onLoadMore={this.handleLoadOlder}
+ onLoadPending={this.handleLoadPending}
onScrollToTop={this.handleScrollToTop}
onScroll={this.handleScroll}
shouldUpdateScroll={shouldUpdateScroll}
import { connect } from 'react-redux';
import StatusList from '../../../components/status_list';
-import { scrollTopTimeline } from '../../../actions/timelines';
+import { scrollTopTimeline, loadPending } from '../../../actions/timelines';
import { Map as ImmutableMap, List as ImmutableList } from 'immutable';
import { createSelector } from 'reselect';
import { debounce } from 'lodash';
isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true),
isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false),
hasMore: state.getIn(['timelines', timelineId, 'hasMore']),
+ numPending: state.getIn(['timelines', timelineId, 'pendingItems'], ImmutableList()).size,
});
return mapStateToProps;
dispatch(scrollTopTimeline(timelineId, false));
}, 100),
+ onLoadPending: () => dispatch(loadPending(timelineId)),
+
});
export default connect(makeMapStateToProps, mapDispatchToProps)(StatusList);
export const isStaff = getMeta('is_staff');
export const forceSingleColumn = !getMeta('advanced_layout');
export const useBlurhash = getMeta('use_blurhash');
+export const usePendingItems = getMeta('use_pending_items');
export default initialState;
NOTIFICATIONS_FILTER_SET,
NOTIFICATIONS_CLEAR,
NOTIFICATIONS_SCROLL_TOP,
+ NOTIFICATIONS_LOAD_PENDING,
} from '../actions/notifications';
import {
ACCOUNT_BLOCK_SUCCESS,
import compareId from '../compare_id';
const initialState = ImmutableMap({
+ pendingItems: ImmutableList(),
items: ImmutableList(),
hasMore: true,
top: false,
status: notification.status ? notification.status.id : null,
});
-const normalizeNotification = (state, notification) => {
+const normalizeNotification = (state, notification, usePendingItems) => {
+ if (usePendingItems) {
+ return state.update('pendingItems', list => list.unshift(notificationToMap(notification)));
+ }
+
const top = state.get('top');
if (!top) {
});
};
-const expandNormalizedNotifications = (state, notifications, next) => {
+const expandNormalizedNotifications = (state, notifications, next, usePendingItems) => {
let items = ImmutableList();
notifications.forEach((n, i) => {
return state.withMutations(mutable => {
if (!items.isEmpty()) {
- mutable.update('items', list => {
+ mutable.update(usePendingItems ? 'pendingItems' : 'items', list => {
const lastIndex = 1 + list.findLastIndex(
item => item !== null && (compareId(item.get('id'), items.last().get('id')) > 0 || item.get('id') === items.last().get('id'))
);
};
const filterNotifications = (state, relationship) => {
- return state.update('items', list => list.filterNot(item => item !== null && item.get('account') === relationship.id));
+ const helper = list => list.filterNot(item => item !== null && item.get('account') === relationship.id);
+ return state.update('items', helper).update('pendingItems', helper);
};
const updateTop = (state, top) => {
};
const deleteByStatus = (state, statusId) => {
- return state.update('items', list => list.filterNot(item => item !== null && item.get('status') === statusId));
+ const helper = list => list.filterNot(item => item !== null && item.get('status') === statusId);
+ return state.update('items', helper).update('pendingItems', helper);
};
export default function notifications(state = initialState, action) {
switch(action.type) {
+ case NOTIFICATIONS_LOAD_PENDING:
+ return state.update('items', list => state.get('pendingItems').concat(list.take(40))).set('pendingItems', ImmutableList()).set('unread', 0);
case NOTIFICATIONS_EXPAND_REQUEST:
return state.set('isLoading', true);
case NOTIFICATIONS_EXPAND_FAIL:
return state.set('isLoading', false);
case NOTIFICATIONS_FILTER_SET:
- return state.set('items', ImmutableList()).set('hasMore', true);
+ return state.set('items', ImmutableList()).set('pendingItems', ImmutableList()).set('hasMore', true);
case NOTIFICATIONS_SCROLL_TOP:
return updateTop(state, action.top);
case NOTIFICATIONS_UPDATE:
- return normalizeNotification(state, action.notification);
+ return normalizeNotification(state, action.notification, action.usePendingItems);
case NOTIFICATIONS_EXPAND_SUCCESS:
- return expandNormalizedNotifications(state, action.notifications, action.next);
+ return expandNormalizedNotifications(state, action.notifications, action.next, action.usePendingItems);
case ACCOUNT_BLOCK_SUCCESS:
return filterNotifications(state, action.relationship);
case ACCOUNT_MUTE_SUCCESS:
return action.relationship.muting_notifications ? filterNotifications(state, action.relationship) : state;
case NOTIFICATIONS_CLEAR:
- return state.set('items', ImmutableList()).set('hasMore', false);
+ return state.set('items', ImmutableList()).set('pendingItems', ImmutableList()).set('hasMore', false);
case TIMELINE_DELETE:
return deleteByStatus(state, action.id);
case TIMELINE_DISCONNECT:
return action.timeline === 'home' ?
- state.update('items', items => items.first() ? items.unshift(null) : items) :
+ state.update(action.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(null) : items) :
state;
default:
return state;
const initialState = ImmutableMap({
saved: true,
- onboarded: false,
-
skinTone: 1,
home: ImmutableMap({
body: '',
}),
}),
-
- trends: ImmutableMap({
- show: true,
- }),
});
const defaultColumns = fromJS([
TIMELINE_SCROLL_TOP,
TIMELINE_CONNECT,
TIMELINE_DISCONNECT,
+ TIMELINE_LOAD_PENDING,
} from '../actions/timelines';
import {
ACCOUNT_BLOCK_SUCCESS,
top: true,
isLoading: false,
hasMore: true,
+ pendingItems: ImmutableList(),
items: ImmutableList(),
});
-const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, isLoadingRecent) => {
+const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial, isLoadingRecent, usePendingItems) => {
return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
mMap.set('isLoading', false);
mMap.set('isPartial', isPartial);
if (timeline.endsWith(':pinned')) {
mMap.set('items', statuses.map(status => status.get('id')));
} else if (!statuses.isEmpty()) {
- mMap.update('items', ImmutableList(), oldIds => {
+ mMap.update(usePendingItems ? 'pendingItems' : 'items', ImmutableList(), oldIds => {
const newIds = statuses.map(status => status.get('id'));
const lastIndex = oldIds.findLastIndex(id => id !== null && compareId(id, newIds.last()) >= 0) + 1;
}));
};
-const updateTimeline = (state, timeline, status) => {
+const updateTimeline = (state, timeline, status, usePendingItems) => {
+ if (usePendingItems) {
+ if (state.getIn([timeline, 'pendingItems'], ImmutableList()).includes(status.get('id')) || state.getIn([timeline, 'items'], ImmutableList()).includes(status.get('id'))) {
+ return state;
+ }
+
+ return state.update(timeline, initialTimeline, map => map.update('pendingItems', list => list.unshift(status.get('id'))));
+ }
+
const top = state.getIn([timeline, 'top']);
const ids = state.getIn([timeline, 'items'], ImmutableList());
const includesId = ids.includes(status.get('id'));
const deleteStatus = (state, id, accountId, references, exclude_account = null) => {
state.keySeq().forEach(timeline => {
- if (exclude_account === null || (timeline !== `account:${exclude_account}` && !timeline.startsWith(`account:${exclude_account}:`)))
- state = state.updateIn([timeline, 'items'], list => list.filterNot(item => item === id));
+ if (exclude_account === null || (timeline !== `account:${exclude_account}` && !timeline.startsWith(`account:${exclude_account}:`))) {
+ const helper = list => list.filterNot(item => item === id);
+ state = state.updateIn([timeline, 'items'], helper).updateIn([timeline, 'pendingItems'], helper);
+ }
});
// Remove reblogs of deleted status
return state;
};
-const filterTimeline = (timeline, state, relationship, statuses) =>
- state.updateIn([timeline, 'items'], ImmutableList(), list =>
- list.filterNot(statusId =>
- statuses.getIn([statusId, 'account']) === relationship.id
- ));
+const filterTimeline = (timeline, state, relationship, statuses) => {
+ const helper = list => list.filterNot(statusId => statuses.getIn([statusId, 'account']) === relationship.id);
+ return state.updateIn([timeline, 'items'], ImmutableList(), helper).updateIn([timeline, 'pendingItems'], ImmutableList(), helper);
+};
const updateTop = (state, timeline, top) => {
return state.update(timeline, initialTimeline, map => map.withMutations(mMap => {
export default function timelines(state = initialState, action) {
switch(action.type) {
+ case TIMELINE_LOAD_PENDING:
+ return state.update(action.timeline, initialTimeline, map =>
+ map.update('items', list => map.get('pendingItems').concat(list.take(40))).set('pendingItems', ImmutableList()).set('unread', 0));
case TIMELINE_EXPAND_REQUEST:
return state.update(action.timeline, initialTimeline, map => map.set('isLoading', true));
case TIMELINE_EXPAND_FAIL:
return state.update(action.timeline, initialTimeline, map => map.set('isLoading', false));
case TIMELINE_EXPAND_SUCCESS:
- return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial, action.isLoadingRecent);
+ return expandNormalizedTimeline(state, action.timeline, fromJS(action.statuses), action.next, action.partial, action.isLoadingRecent, action.usePendingItems);
case TIMELINE_UPDATE:
- return updateTimeline(state, action.timeline, fromJS(action.status));
+ return updateTimeline(state, action.timeline, fromJS(action.status), action.usePendingItems);
case TIMELINE_DELETE:
return deleteStatus(state, action.id, action.accountId, action.references, action.reblogOf);
case TIMELINE_CLEAR:
return state.update(
action.timeline,
initialTimeline,
- map => map.set('online', false).update('items', items => items.first() ? items.unshift(null) : items)
+ map => map.set('online', false).update(action.usePendingItems ? 'pendingItems' : 'items', items => items.first() ? items.unshift(null) : items)
);
default:
return state;
user.settings['show_application'] = show_application_preference if change?('setting_show_application')
user.settings['advanced_layout'] = advanced_layout_preference if change?('setting_advanced_layout')
user.settings['use_blurhash'] = use_blurhash_preference if change?('setting_use_blurhash')
+ user.settings['use_pending_items'] = use_pending_items_preference if change?('setting_use_pending_items')
end
def merged_notification_emails
boolean_cast_setting 'setting_use_blurhash'
end
+ def use_pending_items_preference
+ boolean_cast_setting 'setting_use_pending_items'
+ end
+
def boolean_cast_setting(key)
ActiveModel::Type::Boolean.new.cast(settings[key])
end
delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal,
:reduce_motion, :system_font_ui, :noindex, :theme, :display_media, :hide_network,
:expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
- :advanced_layout, :use_blurhash, to: :settings, prefix: :setting, allow_nil: false
+ :advanced_layout, :use_blurhash, :use_pending_items, to: :settings, prefix: :setting, allow_nil: false
attr_reader :invite_code
attr_writer :external
}
if object.current_account
- store[:me] = object.current_account.id.to_s
- store[:unfollow_modal] = object.current_account.user.setting_unfollow_modal
- store[:boost_modal] = object.current_account.user.setting_boost_modal
- store[:delete_modal] = object.current_account.user.setting_delete_modal
- store[:auto_play_gif] = object.current_account.user.setting_auto_play_gif
- store[:display_media] = object.current_account.user.setting_display_media
- store[:expand_spoilers] = object.current_account.user.setting_expand_spoilers
- store[:reduce_motion] = object.current_account.user.setting_reduce_motion
- store[:advanced_layout] = object.current_account.user.setting_advanced_layout
- store[:use_blurhash] = object.current_account.user.setting_use_blurhash
- store[:is_staff] = object.current_account.user.staff?
+ store[:me] = object.current_account.id.to_s
+ store[:unfollow_modal] = object.current_account.user.setting_unfollow_modal
+ store[:boost_modal] = object.current_account.user.setting_boost_modal
+ store[:delete_modal] = object.current_account.user.setting_delete_modal
+ store[:auto_play_gif] = object.current_account.user.setting_auto_play_gif
+ store[:display_media] = object.current_account.user.setting_display_media
+ store[:expand_spoilers] = object.current_account.user.setting_expand_spoilers
+ store[:reduce_motion] = object.current_account.user.setting_reduce_motion
+ store[:advanced_layout] = object.current_account.user.setting_advanced_layout
+ store[:use_blurhash] = object.current_account.user.setting_use_blurhash
+ store[:use_pending_items] = object.current_account.user.setting_use_pending_items
+ store[:is_staff] = object.current_account.user.staff?
end
store
%h4= t 'appearance.animations_and_accessibility'
+ .fields-group
+ = f.input :setting_use_pending_items, as: :boolean, wrapper: :with_label
+
.fields-group
= f.input :setting_auto_play_gif, as: :boolean, wrapper: :with_label, recommended: true
= f.input :setting_reduce_motion, as: :boolean, wrapper: :with_label
setting_noindex: Affects your public profile and status pages
setting_show_application: The application you use to toot will be displayed in the detailed view of your toots
setting_use_blurhash: Gradients are based on the colors of the hidden visuals but obfuscate any details
+ setting_use_pending_items: Hide timeline updates behind a click instead of automatically scrolling the feed
username: Your username will be unique on %{domain}
whole_word: When the keyword or phrase is alphanumeric only, it will only be applied if it matches the whole word
featured_tag:
setting_theme: Site theme
setting_unfollow_modal: Show confirmation dialog before unfollowing someone
setting_use_blurhash: Show colorful gradients for hidden media
+ setting_use_pending_items: Slow mode
severity: Severity
type: Import type
username: Username
aggregate_reblogs: true
advanced_layout: false
use_blurhash: true
+ use_pending_items: false
notification_emails:
follow: false
reblog: false