diff --git a/app/assets/javascripts/components/actions/notifications.jsx b/app/assets/javascripts/components/actions/notifications.jsx
index 6a8b1b05b..8bd835406 100644
--- a/app/assets/javascripts/components/actions/notifications.jsx
+++ b/app/assets/javascripts/components/actions/notifications.jsx
@@ -14,6 +14,8 @@ export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST';
export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS';
export const NOTIFICATIONS_EXPAND_FAIL = 'NOTIFICATIONS_EXPAND_FAIL';
+export const NOTIFICATIONS_SETTING_CHANGE = 'NOTIFICATIONS_SETTING_CHANGE';
+
const fetchRelatedRelationships = (dispatch, notifications) => {
const accountIds = notifications.filter(item => item.type === 'follow').map(item => item.account.id);
@@ -23,7 +25,7 @@ const fetchRelatedRelationships = (dispatch, notifications) => {
};
export function updateNotifications(notification, intlMessages, intlLocale) {
- return dispatch => {
+ return (dispatch, getState) => {
dispatch({
type: NOTIFICATIONS_UPDATE,
notification,
@@ -34,7 +36,7 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
fetchRelatedRelationships(dispatch, [notification]);
// Desktop notifications
- if (typeof window.Notification !== 'undefined') {
+ if (typeof window.Notification !== 'undefined' && getState().getIn(['notifications', 'settings', 'alerts', notification.type], false)) {
const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username });
const body = $('
').html(notification.status ? notification.status.content : '').text();
@@ -131,3 +133,11 @@ export function expandNotificationsFail(error) {
error
};
};
+
+export function changeNotificationsSetting(key, checked) {
+ return {
+ type: NOTIFICATIONS_SETTING_CHANGE,
+ key,
+ checked
+ };
+};
diff --git a/app/assets/javascripts/components/features/notifications/components/column_settings.jsx b/app/assets/javascripts/components/features/notifications/components/column_settings.jsx
new file mode 100644
index 000000000..b4035c20d
--- /dev/null
+++ b/app/assets/javascripts/components/features/notifications/components/column_settings.jsx
@@ -0,0 +1,150 @@
+import PureRenderMixin from 'react-addons-pure-render-mixin';
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import Toggle from 'react-toggle';
+import { Motion, spring } from 'react-motion';
+import { FormattedMessage } from 'react-intl';
+
+const outerStyle = {
+ background: '#373b4a',
+ padding: '15px'
+};
+
+const iconStyle = {
+ fontSize: '16px',
+ padding: '15px',
+ position: 'absolute',
+ right: '0',
+ top: '-48px',
+ cursor: 'pointer'
+};
+
+const labelStyle = {
+ display: 'block',
+ lineHeight: '24px',
+ verticalAlign: 'middle'
+};
+
+const labelSpanStyle = {
+ display: 'inline-block',
+ verticalAlign: 'middle',
+ marginBottom: '14px',
+ marginLeft: '8px',
+ color: '#9baec8'
+};
+
+const sectionStyle = {
+ cursor: 'default',
+ display: 'block',
+ fontWeight: '500',
+ color: '#9baec8',
+ marginBottom: '10px'
+};
+
+const rowStyle = {
+
+};
+
+const ColumnSettings = React.createClass({
+
+ propTypes: {
+ settings: ImmutablePropTypes.map.isRequired,
+ onChange: React.PropTypes.func.isRequired
+ },
+
+ getInitialState () {
+ return {
+ collapsed: true
+ };
+ },
+
+ mixins: [PureRenderMixin],
+
+ handleToggleCollapsed () {
+ this.setState({ collapsed: !this.state.collapsed });
+ },
+
+ handleChange (key, e) {
+ this.props.onChange(key, e.target.checked);
+ },
+
+ render () {
+ const { settings } = this.props;
+ const { collapsed } = this.state;
+
+ const alertStr = ;
+ const showStr = ;
+
+ return (
+
+
+
+
+ {({ opacity, height }) =>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+
+ );
+ }
+
+});
+
+export default ColumnSettings;
diff --git a/app/assets/javascripts/components/features/notifications/containers/column_settings_container.jsx b/app/assets/javascripts/components/features/notifications/containers/column_settings_container.jsx
new file mode 100644
index 000000000..6907fd351
--- /dev/null
+++ b/app/assets/javascripts/components/features/notifications/containers/column_settings_container.jsx
@@ -0,0 +1,17 @@
+import { connect } from 'react-redux';
+import ColumnSettings from '../components/column_settings';
+import { changeNotificationsSetting } from '../../../actions/notifications';
+
+const mapStateToProps = state => ({
+ settings: state.getIn(['notifications', 'settings'])
+});
+
+const mapDispatchToProps = dispatch => ({
+
+ onChange (key, checked) {
+ dispatch(changeNotificationsSetting(key, checked));
+ }
+
+});
+
+export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings);
diff --git a/app/assets/javascripts/components/features/notifications/index.jsx b/app/assets/javascripts/components/features/notifications/index.jsx
index 218196cfd..7e706ad6a 100644
--- a/app/assets/javascripts/components/features/notifications/index.jsx
+++ b/app/assets/javascripts/components/features/notifications/index.jsx
@@ -9,13 +9,21 @@ import {
import NotificationContainer from './containers/notification_container';
import { ScrollContainer } from 'react-router-scroll';
import { defineMessages, injectIntl } from 'react-intl';
+import ColumnSettingsContainer from './containers/column_settings_container';
+import { createSelector } from 'reselect';
+import Immutable from 'immutable';
const messages = defineMessages({
title: { id: 'column.notifications', defaultMessage: 'Notifications' }
});
+const getNotifications = createSelector([
+ state => Immutable.List(state.getIn(['notifications', 'settings', 'shows']).filter(item => !item).keys()),
+ state => state.getIn(['notifications', 'items'])
+], (excludedTypes, notifications) => notifications.filterNot(item => excludedTypes.includes(item.get('type'))));
+
const mapStateToProps = state => ({
- notifications: state.getIn(['notifications', 'items'])
+ notifications: getNotifications(state)
});
const Notifications = React.createClass({
@@ -23,7 +31,8 @@ const Notifications = React.createClass({
propTypes: {
notifications: ImmutablePropTypes.list.isRequired,
dispatch: React.PropTypes.func.isRequired,
- trackScroll: React.PropTypes.bool
+ trackScroll: React.PropTypes.bool,
+ intl: React.PropTypes.object.isRequired
},
getDefaultProps () {
@@ -69,6 +78,7 @@ const Notifications = React.createClass({
} else {
return (
+
{scrollableArea}
);
diff --git a/app/assets/javascripts/components/features/ui/components/column.jsx b/app/assets/javascripts/components/features/ui/components/column.jsx
index c2060749a..c382e108d 100644
--- a/app/assets/javascripts/components/features/ui/components/column.jsx
+++ b/app/assets/javascripts/components/features/ui/components/column.jsx
@@ -40,7 +40,8 @@ const Column = React.createClass({
propTypes: {
heading: React.PropTypes.string,
- icon: React.PropTypes.string
+ icon: React.PropTypes.string,
+ children: React.PropTypes.node
},
mixins: [PureRenderMixin],
diff --git a/app/assets/javascripts/components/locales/en.jsx b/app/assets/javascripts/components/locales/en.jsx
index 50007a7da..3d4a38919 100644
--- a/app/assets/javascripts/components/locales/en.jsx
+++ b/app/assets/javascripts/components/locales/en.jsx
@@ -52,7 +52,13 @@ const en = {
"notification.follow": "{name} followed you",
"notification.favourite": "{name} favourited your status",
"notification.reblog": "{name} boosted your status",
- "notification.mention": "{name} mentioned you"
+ "notification.mention": "{name} mentioned you",
+ "notifications.column_settings.alert": "Desktop notifications",
+ "notifications.column_settings.show": "Show in column",
+ "notifications.column_settings.follow": "New followers:",
+ "notifications.column_settings.favourite": "Favourites:",
+ "notifications.column_settings.mention": "Mentions:",
+ "notifications.column_settings.reblog": "Boosts:",
};
export default en;
diff --git a/app/assets/javascripts/components/reducers/notifications.jsx b/app/assets/javascripts/components/reducers/notifications.jsx
index 617a833d2..e0d1ccf83 100644
--- a/app/assets/javascripts/components/reducers/notifications.jsx
+++ b/app/assets/javascripts/components/reducers/notifications.jsx
@@ -1,7 +1,8 @@
import {
NOTIFICATIONS_UPDATE,
NOTIFICATIONS_REFRESH_SUCCESS,
- NOTIFICATIONS_EXPAND_SUCCESS
+ NOTIFICATIONS_EXPAND_SUCCESS,
+ NOTIFICATIONS_SETTING_CHANGE
} from '../actions/notifications';
import { ACCOUNT_BLOCK_SUCCESS } from '../actions/accounts';
import Immutable from 'immutable';
@@ -9,7 +10,23 @@ import Immutable from 'immutable';
const initialState = Immutable.Map({
items: Immutable.List(),
next: null,
- loaded: false
+ loaded: false,
+
+ settings: Immutable.Map({
+ alerts: Immutable.Map({
+ follow: true,
+ favourite: true,
+ reblog: true,
+ mention: true
+ }),
+
+ shows: Immutable.Map({
+ follow: true,
+ favourite: true,
+ reblog: true,
+ mention: true
+ })
+ })
});
const notificationToMap = notification => Immutable.Map({
@@ -58,6 +75,8 @@ export default function notifications(state = initialState, action) {
return appendNormalizedNotifications(state, action.notifications, action.next);
case ACCOUNT_BLOCK_SUCCESS:
return filterNotifications(state, action.relationship);
+ case NOTIFICATIONS_SETTING_CHANGE:
+ return state.setIn(['settings', ...action.key], action.checked);
default:
return state;
}