Add notification quick-filter bar in the frontend app (#9399)
* create FilterBar componer and its container, unstyled * introduce basic styling for FilterBar * add selection css * allow FilterBar to display active CSS with js * connect the FilterBar to the Redux state * change getNotifications to use filter * remove temporary comments * add an option to turn the FilterBar off in settings * fix showFilterBar data type to boolean * fix eslint errors * add English and Polish translations * allowed filter bar overflow to accomodate for longer languages * fix mispelled translation key * add unified CSS look * replace text in FilterBar with icons * add tooltips * replace text @ with an icon * introduce simple and advanced filtering view * add ability to toggle the advanced view * add Polish translations * change Advanced View description to be more clear * make each filter flush notifications and load new ones, fixing pagination * simplify getNotifications once frontend filtering is not needed for FilterBar * add a semicolon * Revert "simplify getNotifications once frontend filtering is not needed for FilterBar" This reverts commit 9f4be7857135b0327814bd22a3e8a4e7b546f7cc. * reset filter to 'all' when turning off FilterBarpull/862/head
parent
5f0d3e8bad
commit
13dce12665
|
@ -8,6 +8,7 @@ import {
|
||||||
importFetchedStatuses,
|
importFetchedStatuses,
|
||||||
} from './importer';
|
} from './importer';
|
||||||
import { defineMessages } from 'react-intl';
|
import { defineMessages } from 'react-intl';
|
||||||
|
import { List as ImmutableList } from 'immutable';
|
||||||
import { unescapeHTML } from '../utils/html';
|
import { unescapeHTML } from '../utils/html';
|
||||||
import { getFilters, regexFromFilters } from '../selectors';
|
import { getFilters, regexFromFilters } from '../selectors';
|
||||||
|
|
||||||
|
@ -18,6 +19,8 @@ export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST';
|
||||||
export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS';
|
export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS';
|
||||||
export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL';
|
export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL';
|
||||||
|
|
||||||
|
export const NOTIFICATIONS_FILTER_SET = 'NOTIFICATIONS_FILTER_SET';
|
||||||
|
|
||||||
export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR';
|
export const NOTIFICATIONS_CLEAR = 'NOTIFICATIONS_CLEAR';
|
||||||
export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
|
export const NOTIFICATIONS_SCROLL_TOP = 'NOTIFICATIONS_SCROLL_TOP';
|
||||||
|
|
||||||
|
@ -88,10 +91,16 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
|
||||||
|
|
||||||
const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS();
|
const excludeTypesFromSettings = state => state.getIn(['settings', 'notifications', 'shows']).filter(enabled => !enabled).keySeq().toJS();
|
||||||
|
|
||||||
|
const excludeTypesFromFilter = filter => {
|
||||||
|
const allTypes = ImmutableList(['follow', 'favourite', 'reblog', 'mention']);
|
||||||
|
return allTypes.filterNot(item => item === filter).toJS();
|
||||||
|
};
|
||||||
|
|
||||||
const noOp = () => {};
|
const noOp = () => {};
|
||||||
|
|
||||||
export function expandNotifications({ maxId } = {}, done = noOp) {
|
export function expandNotifications({ maxId } = {}, done = noOp) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
|
const activeFilter = getState().getIn(['settings', 'notifications', 'quickFilter', 'active']);
|
||||||
const notifications = getState().get('notifications');
|
const notifications = getState().get('notifications');
|
||||||
const isLoadingMore = !!maxId;
|
const isLoadingMore = !!maxId;
|
||||||
|
|
||||||
|
@ -102,7 +111,9 @@ export function expandNotifications({ maxId } = {}, done = noOp) {
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
max_id: maxId,
|
max_id: maxId,
|
||||||
exclude_types: excludeTypesFromSettings(getState()),
|
exclude_types: activeFilter === 'all'
|
||||||
|
? excludeTypesFromSettings(getState())
|
||||||
|
: excludeTypesFromFilter(activeFilter),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!maxId && notifications.get('items').size > 0) {
|
if (!maxId && notifications.get('items').size > 0) {
|
||||||
|
@ -167,3 +178,14 @@ export function scrollTopNotifications(top) {
|
||||||
top,
|
top,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function setFilter (filterType) {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch({
|
||||||
|
type: NOTIFICATIONS_FILTER_SET,
|
||||||
|
path: ['notifications', 'quickFilter', 'active'],
|
||||||
|
value: filterType,
|
||||||
|
});
|
||||||
|
dispatch(expandNotifications());
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -21,6 +21,8 @@ export default class ColumnSettings extends React.PureComponent {
|
||||||
render () {
|
render () {
|
||||||
const { settings, pushSettings, onChange, onClear } = this.props;
|
const { settings, pushSettings, onChange, onClear } = this.props;
|
||||||
|
|
||||||
|
const filterShowStr = <FormattedMessage id='notifications.column_settings.filter_bar.show' defaultMessage='Show' />;
|
||||||
|
const filterAdvancedStr = <FormattedMessage id='notifications.column_settings.filter_bar.advanced' defaultMessage='Display all categories' />;
|
||||||
const alertStr = <FormattedMessage id='notifications.column_settings.alert' defaultMessage='Desktop notifications' />;
|
const alertStr = <FormattedMessage id='notifications.column_settings.alert' defaultMessage='Desktop notifications' />;
|
||||||
const showStr = <FormattedMessage id='notifications.column_settings.show' defaultMessage='Show in column' />;
|
const showStr = <FormattedMessage id='notifications.column_settings.show' defaultMessage='Show in column' />;
|
||||||
const soundStr = <FormattedMessage id='notifications.column_settings.sound' defaultMessage='Play sound' />;
|
const soundStr = <FormattedMessage id='notifications.column_settings.sound' defaultMessage='Play sound' />;
|
||||||
|
@ -34,6 +36,16 @@ export default class ColumnSettings extends React.PureComponent {
|
||||||
<ClearColumnButton onClick={onClear} />
|
<ClearColumnButton onClick={onClear} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div role='group' aria-labelledby='notifications-filter-bar'>
|
||||||
|
<span id='notifications-filter-bar' className='column-settings__section'>
|
||||||
|
<FormattedMessage id='notifications.column_settings.filter_bar.category' defaultMessage='Quick filter bar' />
|
||||||
|
</span>
|
||||||
|
<div className='column-settings__row'>
|
||||||
|
<SettingToggle id='show-filter-bar' prefix='notifications' settings={settings} settingPath={['quickFilter', 'show']} onChange={onChange} label={filterShowStr} />
|
||||||
|
<SettingToggle id='show-filter-bar' prefix='notifications' settings={settings} settingPath={['quickFilter', 'advanced']} onChange={onChange} label={filterAdvancedStr} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div role='group' aria-labelledby='notifications-follow'>
|
<div role='group' aria-labelledby='notifications-follow'>
|
||||||
<span id='notifications-follow' className='column-settings__section'><FormattedMessage id='notifications.column_settings.follow' defaultMessage='New followers:' /></span>
|
<span id='notifications-follow' className='column-settings__section'><FormattedMessage id='notifications.column_settings.follow' defaultMessage='New followers:' /></span>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
const tooltips = defineMessages({
|
||||||
|
mentions: { id: 'notifications.filter.mentions', defaultMessage: 'Mentions' },
|
||||||
|
favourites: { id: 'notifications.filter.favourites', defaultMessage: 'Favourites' },
|
||||||
|
boosts: { id: 'notifications.filter.boosts', defaultMessage: 'Boosts' },
|
||||||
|
follows: { id: 'notifications.filter.follows', defaultMessage: 'Follows' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default @injectIntl
|
||||||
|
class FilterBar extends React.PureComponent {
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
selectFilter: PropTypes.func.isRequired,
|
||||||
|
selectedFilter: PropTypes.string.isRequired,
|
||||||
|
advancedMode: PropTypes.bool.isRequired,
|
||||||
|
intl: PropTypes.object.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
onClick (notificationType) {
|
||||||
|
return () => this.props.selectFilter(notificationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { selectedFilter, advancedMode, intl } = this.props;
|
||||||
|
const renderedElement = !advancedMode ? (
|
||||||
|
<div className='notification__filter-bar'>
|
||||||
|
<button
|
||||||
|
className={selectedFilter === 'all' ? 'active' : ''}
|
||||||
|
onClick={this.onClick('all')}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='notifications.filter.all'
|
||||||
|
defaultMessage='All'
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={selectedFilter === 'mention' ? 'active' : ''}
|
||||||
|
onClick={this.onClick('mention')}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='notifications.filter.mentions'
|
||||||
|
defaultMessage='Mentions'
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className='notification__filter-bar'>
|
||||||
|
<button
|
||||||
|
className={selectedFilter === 'all' ? 'active' : ''}
|
||||||
|
onClick={this.onClick('all')}
|
||||||
|
>
|
||||||
|
<FormattedMessage
|
||||||
|
id='notifications.filter.all'
|
||||||
|
defaultMessage='All'
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={selectedFilter === 'mention' ? 'active' : ''}
|
||||||
|
onClick={this.onClick('mention')}
|
||||||
|
title={intl.formatMessage(tooltips.mentions)}
|
||||||
|
>
|
||||||
|
<i className='fa fa-fw fa-at' />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={selectedFilter === 'favourite' ? 'active' : ''}
|
||||||
|
onClick={this.onClick('favourite')}
|
||||||
|
title={intl.formatMessage(tooltips.favourites)}
|
||||||
|
>
|
||||||
|
<i className='fa fa-fw fa-star' />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={selectedFilter === 'reblog' ? 'active' : ''}
|
||||||
|
onClick={this.onClick('reblog')}
|
||||||
|
title={intl.formatMessage(tooltips.boosts)}
|
||||||
|
>
|
||||||
|
<i className='fa fa-fw fa-retweet' />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className={selectedFilter === 'follow' ? 'active' : ''}
|
||||||
|
onClick={this.onClick('follow')}
|
||||||
|
title={intl.formatMessage(tooltips.follows)}
|
||||||
|
>
|
||||||
|
<i className='fa fa-fw fa-user-plus' />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
return renderedElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ import { connect } from 'react-redux';
|
||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import ColumnSettings from '../components/column_settings';
|
import ColumnSettings from '../components/column_settings';
|
||||||
import { changeSetting } from '../../../actions/settings';
|
import { changeSetting } from '../../../actions/settings';
|
||||||
|
import { setFilter } from '../../../actions/notifications';
|
||||||
import { clearNotifications } from '../../../actions/notifications';
|
import { clearNotifications } from '../../../actions/notifications';
|
||||||
import { changeAlerts as changePushNotifications } from '../../../actions/push_notifications';
|
import { changeAlerts as changePushNotifications } from '../../../actions/push_notifications';
|
||||||
import { openModal } from '../../../actions/modal';
|
import { openModal } from '../../../actions/modal';
|
||||||
|
@ -21,6 +22,9 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||||
onChange (path, checked) {
|
onChange (path, checked) {
|
||||||
if (path[0] === 'push') {
|
if (path[0] === 'push') {
|
||||||
dispatch(changePushNotifications(path.slice(1), checked));
|
dispatch(changePushNotifications(path.slice(1), checked));
|
||||||
|
} else if (path[0] === 'quickFilter') {
|
||||||
|
dispatch(changeSetting(['notifications', ...path], checked));
|
||||||
|
dispatch(setFilter('all'));
|
||||||
} else {
|
} else {
|
||||||
dispatch(changeSetting(['notifications', ...path], checked));
|
dispatch(changeSetting(['notifications', ...path], checked));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import FilterBar from '../components/filter_bar';
|
||||||
|
import { setFilter } from '../../../actions/notifications';
|
||||||
|
|
||||||
|
const makeMapStateToProps = state => ({
|
||||||
|
selectedFilter: state.getIn(['settings', 'notifications', 'quickFilter', 'active']),
|
||||||
|
advancedMode: state.getIn(['settings', 'notifications', 'quickFilter', 'advanced']),
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
selectFilter (newActiveFilter) {
|
||||||
|
dispatch(setFilter(newActiveFilter));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(makeMapStateToProps, mapDispatchToProps)(FilterBar);
|
|
@ -9,6 +9,7 @@ import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||||
import NotificationContainer from './containers/notification_container';
|
import NotificationContainer from './containers/notification_container';
|
||||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import ColumnSettingsContainer from './containers/column_settings_container';
|
import ColumnSettingsContainer from './containers/column_settings_container';
|
||||||
|
import FilterBarContainer from './containers/filter_bar_container';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { List as ImmutableList } from 'immutable';
|
import { List as ImmutableList } from 'immutable';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
|
@ -20,11 +21,22 @@ const messages = defineMessages({
|
||||||
});
|
});
|
||||||
|
|
||||||
const getNotifications = createSelector([
|
const getNotifications = createSelector([
|
||||||
|
state => state.getIn(['settings', 'notifications', 'quickFilter', 'show']),
|
||||||
|
state => state.getIn(['settings', 'notifications', 'quickFilter', 'active']),
|
||||||
state => ImmutableList(state.getIn(['settings', 'notifications', 'shows']).filter(item => !item).keys()),
|
state => ImmutableList(state.getIn(['settings', 'notifications', 'shows']).filter(item => !item).keys()),
|
||||||
state => state.getIn(['notifications', 'items']),
|
state => state.getIn(['notifications', 'items']),
|
||||||
], (excludedTypes, notifications) => notifications.filterNot(item => item !== null && excludedTypes.includes(item.get('type'))));
|
], (showFilterBar, allowedType, excludedTypes, notifications) => {
|
||||||
|
if (!showFilterBar || allowedType === 'all') {
|
||||||
|
// used if user changed the notification settings after loading the notifications from the server
|
||||||
|
// otherwise a list of notifications will come pre-filtered from the backend
|
||||||
|
// we need to turn it off for FilterBar in order not to block ourselves from seeing a specific category
|
||||||
|
return notifications.filterNot(item => item !== null && excludedTypes.includes(item.get('type')));
|
||||||
|
}
|
||||||
|
return notifications.filter(item => item !== null && allowedType === item.get('type'));
|
||||||
|
});
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
|
showFilterBar: state.getIn(['settings', 'notifications', 'quickFilter', 'show']),
|
||||||
notifications: getNotifications(state),
|
notifications: getNotifications(state),
|
||||||
isLoading: state.getIn(['notifications', 'isLoading'], true),
|
isLoading: state.getIn(['notifications', 'isLoading'], true),
|
||||||
isUnread: state.getIn(['notifications', 'unread']) > 0,
|
isUnread: state.getIn(['notifications', 'unread']) > 0,
|
||||||
|
@ -38,6 +50,7 @@ class Notifications extends React.PureComponent {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
columnId: PropTypes.string,
|
columnId: PropTypes.string,
|
||||||
notifications: ImmutablePropTypes.list.isRequired,
|
notifications: ImmutablePropTypes.list.isRequired,
|
||||||
|
showFilterBar: PropTypes.bool.isRequired,
|
||||||
dispatch: PropTypes.func.isRequired,
|
dispatch: PropTypes.func.isRequired,
|
||||||
shouldUpdateScroll: PropTypes.func,
|
shouldUpdateScroll: PropTypes.func,
|
||||||
intl: PropTypes.object.isRequired,
|
intl: PropTypes.object.isRequired,
|
||||||
|
@ -117,12 +130,16 @@ class Notifications extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore } = this.props;
|
const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore, showFilterBar } = this.props;
|
||||||
const pinned = !!columnId;
|
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." />;
|
const emptyMessage = <FormattedMessage id='empty_column.notifications' defaultMessage="You don't have any notifications yet. Interact with others to start the conversation." />;
|
||||||
|
|
||||||
let scrollableContent = null;
|
let scrollableContent = null;
|
||||||
|
|
||||||
|
const filterBarContainer = showFilterBar
|
||||||
|
? (<FilterBarContainer />)
|
||||||
|
: null;
|
||||||
|
|
||||||
if (isLoading && this.scrollableContent) {
|
if (isLoading && this.scrollableContent) {
|
||||||
scrollableContent = this.scrollableContent;
|
scrollableContent = this.scrollableContent;
|
||||||
} else if (notifications.size > 0 || hasMore) {
|
} else if (notifications.size > 0 || hasMore) {
|
||||||
|
@ -179,7 +196,7 @@ class Notifications extends React.PureComponent {
|
||||||
>
|
>
|
||||||
<ColumnSettingsContainer />
|
<ColumnSettingsContainer />
|
||||||
</ColumnHeader>
|
</ColumnHeader>
|
||||||
|
{filterBarContainer}
|
||||||
{scrollContainer}
|
{scrollContainer}
|
||||||
</Column>
|
</Column>
|
||||||
);
|
);
|
||||||
|
|
|
@ -223,6 +223,14 @@
|
||||||
"notification.reblog": "{name} boosted your status",
|
"notification.reblog": "{name} boosted your status",
|
||||||
"notifications.clear": "Clear notifications",
|
"notifications.clear": "Clear notifications",
|
||||||
"notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
|
"notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
|
||||||
|
"notifications.filter.all": "All",
|
||||||
|
"notifications.filter.mentions": "Mentions",
|
||||||
|
"notifications.filter.favourites": "Favourites",
|
||||||
|
"notifications.filter.boosts": "Boosts",
|
||||||
|
"notifications.filter.follows": "Follows",
|
||||||
|
"notifications.column_settings.filter_bar.category": "Quick filter bar",
|
||||||
|
"notifications.column_settings.filter_bar.show": "Show",
|
||||||
|
"notifications.column_settings.filter_bar.advanced": "Display all categories",
|
||||||
"notifications.column_settings.alert": "Desktop notifications",
|
"notifications.column_settings.alert": "Desktop notifications",
|
||||||
"notifications.column_settings.favourite": "Favourites:",
|
"notifications.column_settings.favourite": "Favourites:",
|
||||||
"notifications.column_settings.follow": "New followers:",
|
"notifications.column_settings.follow": "New followers:",
|
||||||
|
|
|
@ -223,6 +223,14 @@
|
||||||
"notification.reblog": "{name} podbił(a) Twój wpis",
|
"notification.reblog": "{name} podbił(a) Twój wpis",
|
||||||
"notifications.clear": "Wyczyść powiadomienia",
|
"notifications.clear": "Wyczyść powiadomienia",
|
||||||
"notifications.clear_confirmation": "Czy na pewno chcesz bezpowrotnie usunąć wszystkie powiadomienia?",
|
"notifications.clear_confirmation": "Czy na pewno chcesz bezpowrotnie usunąć wszystkie powiadomienia?",
|
||||||
|
"notifications.filter.all": "Wszystkie",
|
||||||
|
"notifications.filter.mentions": "Wspomnienia",
|
||||||
|
"notifications.filter.favourites": "Ulubione",
|
||||||
|
"notifications.filter.boosts": "Podbicia",
|
||||||
|
"notifications.filter.follows": "Śledzenia",
|
||||||
|
"notifications.column_settings.filter_bar.category": "Szybkie filtrowanie",
|
||||||
|
"notifications.column_settings.filter_bar.show": "Pokaż",
|
||||||
|
"notifications.column_settings.filter_bar.advanced": "Wyświetl wszystkie kategorie",
|
||||||
"notifications.column_settings.alert": "Powiadomienia na pulpicie",
|
"notifications.column_settings.alert": "Powiadomienia na pulpicie",
|
||||||
"notifications.column_settings.favourite": "Dodanie do ulubionych:",
|
"notifications.column_settings.favourite": "Dodanie do ulubionych:",
|
||||||
"notifications.column_settings.follow": "Nowi śledzący:",
|
"notifications.column_settings.follow": "Nowi śledzący:",
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
NOTIFICATIONS_EXPAND_SUCCESS,
|
NOTIFICATIONS_EXPAND_SUCCESS,
|
||||||
NOTIFICATIONS_EXPAND_REQUEST,
|
NOTIFICATIONS_EXPAND_REQUEST,
|
||||||
NOTIFICATIONS_EXPAND_FAIL,
|
NOTIFICATIONS_EXPAND_FAIL,
|
||||||
|
NOTIFICATIONS_FILTER_SET,
|
||||||
NOTIFICATIONS_CLEAR,
|
NOTIFICATIONS_CLEAR,
|
||||||
NOTIFICATIONS_SCROLL_TOP,
|
NOTIFICATIONS_SCROLL_TOP,
|
||||||
} from '../actions/notifications';
|
} from '../actions/notifications';
|
||||||
|
@ -98,6 +99,8 @@ export default function notifications(state = initialState, action) {
|
||||||
return state.set('isLoading', true);
|
return state.set('isLoading', true);
|
||||||
case NOTIFICATIONS_EXPAND_FAIL:
|
case NOTIFICATIONS_EXPAND_FAIL:
|
||||||
return state.set('isLoading', false);
|
return state.set('isLoading', false);
|
||||||
|
case NOTIFICATIONS_FILTER_SET:
|
||||||
|
return state.set('items', ImmutableList()).set('hasMore', true);
|
||||||
case NOTIFICATIONS_SCROLL_TOP:
|
case NOTIFICATIONS_SCROLL_TOP:
|
||||||
return updateTop(state, action.top);
|
return updateTop(state, action.top);
|
||||||
case NOTIFICATIONS_UPDATE:
|
case NOTIFICATIONS_UPDATE:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { SETTING_CHANGE, SETTING_SAVE } from '../actions/settings';
|
import { SETTING_CHANGE, SETTING_SAVE } from '../actions/settings';
|
||||||
|
import { NOTIFICATIONS_FILTER_SET } from '../actions/notifications';
|
||||||
import { COLUMN_ADD, COLUMN_REMOVE, COLUMN_MOVE, COLUMN_PARAMS_CHANGE } from '../actions/columns';
|
import { COLUMN_ADD, COLUMN_REMOVE, COLUMN_MOVE, COLUMN_PARAMS_CHANGE } from '../actions/columns';
|
||||||
import { STORE_HYDRATE } from '../actions/store';
|
import { STORE_HYDRATE } from '../actions/store';
|
||||||
import { EMOJI_USE } from '../actions/emojis';
|
import { EMOJI_USE } from '../actions/emojis';
|
||||||
|
@ -32,6 +33,12 @@ const initialState = ImmutableMap({
|
||||||
mention: true,
|
mention: true,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
quickFilter: ImmutableMap({
|
||||||
|
active: 'all',
|
||||||
|
show: true,
|
||||||
|
advanced: false,
|
||||||
|
}),
|
||||||
|
|
||||||
shows: ImmutableMap({
|
shows: ImmutableMap({
|
||||||
follow: true,
|
follow: true,
|
||||||
favourite: true,
|
favourite: true,
|
||||||
|
@ -112,6 +119,7 @@ export default function settings(state = initialState, action) {
|
||||||
switch(action.type) {
|
switch(action.type) {
|
||||||
case STORE_HYDRATE:
|
case STORE_HYDRATE:
|
||||||
return hydrate(state, action.state.get('settings'));
|
return hydrate(state, action.state.get('settings'));
|
||||||
|
case NOTIFICATIONS_FILTER_SET:
|
||||||
case SETTING_CHANGE:
|
case SETTING_CHANGE:
|
||||||
return state
|
return state
|
||||||
.setIn(action.path, action.value)
|
.setIn(action.path, action.value)
|
||||||
|
|
|
@ -1484,6 +1484,52 @@ a.account__display-name {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.notification__filter-bar {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: $ui-base-color;
|
||||||
|
|
||||||
|
& > button {
|
||||||
|
position: relative;
|
||||||
|
flex-grow: 1;
|
||||||
|
color: $primary-text-color;
|
||||||
|
padding: 10px 5px 12px;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 18px;
|
||||||
|
background: darken($ui-base-color, 4%);
|
||||||
|
border: 0;
|
||||||
|
border-bottom: 1px solid lighten($ui-base-color, 8%);
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: $secondary-text-color;
|
||||||
|
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 50%;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 0 10px 10px;
|
||||||
|
border-color: transparent transparent lighten($ui-base-color, 8%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
bottom: -1px;
|
||||||
|
border-color: transparent transparent $ui-base-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.notification__message {
|
.notification__message {
|
||||||
margin: 0 10px 0 68px;
|
margin: 0 10px 0 68px;
|
||||||
padding: 8px 0 0;
|
padding: 8px 0 0;
|
||||||
|
|
Loading…
Reference in New Issue