@@ -109,7 +83,7 @@ export default class ColumnSettings extends React.PureComponent {
-
+
{showPushSettings &&
}
@@ -120,7 +94,7 @@ export default class ColumnSettings extends React.PureComponent {
-
+
{showPushSettings &&
}
@@ -131,7 +105,7 @@ export default class ColumnSettings extends React.PureComponent {
-
+
{showPushSettings &&
}
@@ -142,12 +116,23 @@ export default class ColumnSettings extends React.PureComponent {
-
+
{showPushSettings && }
+
+
+
+
+
+
+ {showPushSettings && }
+
+
+
+
);
}
diff --git a/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.js b/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.js
new file mode 100644
index 0000000000..766c9bb5ba
--- /dev/null
+++ b/app/javascript/mastodon/features/notifications/components/notifications_permission_banner.js
@@ -0,0 +1,30 @@
+import React from 'react';
+import Icon from 'mastodon/components/icon';
+import Button from 'mastodon/components/button';
+import { requestBrowserPermission } from 'mastodon/actions/notifications';
+import { connect } from 'react-redux';
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+
+export default @connect(() => {})
+class NotificationsPermissionBanner extends React.PureComponent {
+
+ static propTypes = {
+ dispatch: PropTypes.func.isRequired,
+ };
+
+ handleClick = () => {
+ this.props.dispatch(requestBrowserPermission());
+ }
+
+ render () {
+ return (
+
+ );
+ }
+
+}
diff --git a/app/javascript/mastodon/features/notifications/components/setting_toggle.js b/app/javascript/mastodon/features/notifications/components/setting_toggle.js
index e6f593ef89..c4c8bffbe3 100644
--- a/app/javascript/mastodon/features/notifications/components/setting_toggle.js
+++ b/app/javascript/mastodon/features/notifications/components/setting_toggle.js
@@ -12,6 +12,7 @@ export default class SettingToggle extends React.PureComponent {
label: PropTypes.node.isRequired,
onChange: PropTypes.func.isRequired,
defaultValue: PropTypes.bool,
+ disabled: PropTypes.bool,
}
onChange = ({ target }) => {
@@ -19,12 +20,12 @@ export default class SettingToggle extends React.PureComponent {
}
render () {
- const { prefix, settings, settingPath, label, defaultValue } = this.props;
+ const { prefix, settings, settingPath, label, defaultValue, disabled } = this.props;
const id = ['setting-toggle', prefix, ...settingPath].filter(Boolean).join('-');
return (
-
+
);
diff --git a/app/javascript/mastodon/features/notifications/containers/column_settings_container.js b/app/javascript/mastodon/features/notifications/containers/column_settings_container.js
index 664c379806..9a70bd4f36 100644
--- a/app/javascript/mastodon/features/notifications/containers/column_settings_container.js
+++ b/app/javascript/mastodon/features/notifications/containers/column_settings_container.js
@@ -11,7 +11,7 @@ import { showAlert } from '../../../actions/alerts';
const messages = defineMessages({
clearMessage: { id: 'notifications.clear_confirmation', defaultMessage: 'Are you sure you want to permanently clear all your notifications?' },
clearConfirm: { id: 'notifications.clear', defaultMessage: 'Clear notifications' },
- permissionDenied: { id: 'notifications.permission_denied', defaultMessage: 'Cannot enable desktop notifications as permission has been denied.' },
+ permissionDenied: { id: 'notifications.permission_denied_alert', defaultMessage: 'Desktop notifications can\'t be enabled, as browser permission has been denied before' },
});
const mapStateToProps = state => ({
diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.js
index 5e87faa082..73df7f49d0 100644
--- a/app/javascript/mastodon/features/notifications/index.js
+++ b/app/javascript/mastodon/features/notifications/index.js
@@ -25,6 +25,7 @@ import ScrollableList from '../../components/scrollable_list';
import LoadGap from '../../components/load_gap';
import Icon from 'mastodon/components/icon';
import compareId from 'mastodon/compare_id';
+import NotificationsPermissionBanner from './components/notifications_permission_banner';
const messages = defineMessages({
title: { id: 'column.notifications', defaultMessage: 'Notifications' },
@@ -55,7 +56,7 @@ const mapStateToProps = state => ({
numPending: state.getIn(['notifications', 'pendingItems'], ImmutableList()).size,
lastReadId: state.getIn(['notifications', 'readMarkerId']),
canMarkAsRead: state.getIn(['notifications', 'readMarkerId']) !== '0' && getNotifications(state).some(item => item !== null && compareId(item.get('id'), state.getIn(['notifications', 'readMarkerId'])) > 0),
- needsNotificationPermission: state.getIn(['settings', 'notifications', 'alerts']).includes(true) && state.getIn(['notifications', 'browserSupport']) && state.getIn(['notifications', 'browserPermission']) !== 'granted',
+ needsNotificationPermission: state.getIn(['settings', 'notifications', 'alerts']).includes(true) && state.getIn(['notifications', 'browserSupport']) && state.getIn(['notifications', 'browserPermission']) === 'default',
});
export default @connect(mapStateToProps)
@@ -169,7 +170,7 @@ class Notifications extends React.PureComponent {
};
render () {
- const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar, lastReadId, canMarkAsRead } = this.props;
+ const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar, lastReadId, canMarkAsRead, needsNotificationPermission } = this.props;
const pinned = !!columnId;
const emptyMessage =
;
@@ -213,6 +214,8 @@ class Notifications extends React.PureComponent {
showLoading={isLoading && notifications.size === 0}
hasMore={hasMore}
numPending={numPending}
+ prepend={needsNotificationPermission &&
}
+ alwaysPrepend
emptyMessage={emptyMessage}
onLoadMore={this.handleLoadOlder}
onLoadPending={this.handleLoadPending}
@@ -252,7 +255,6 @@ class Notifications extends React.PureComponent {
pinned={pinned}
multiColumn={multiColumn}
extraButton={extraButton}
- collapseIssues={this.props.needsNotificationPermission}
>
diff --git a/app/javascript/mastodon/features/ui/components/notifications_counter_icon.js b/app/javascript/mastodon/features/ui/components/notifications_counter_icon.js
index b8932704b5..da553cd9f0 100644
--- a/app/javascript/mastodon/features/ui/components/notifications_counter_icon.js
+++ b/app/javascript/mastodon/features/ui/components/notifications_counter_icon.js
@@ -3,7 +3,6 @@ import IconWithBadge from 'mastodon/components/icon_with_badge';
const mapStateToProps = state => ({
count: state.getIn(['notifications', 'unread']),
- issueBadge: state.getIn(['settings', 'notifications', 'alerts']).includes(true) && state.getIn(['notifications', 'browserSupport']) && state.getIn(['notifications', 'browserPermission']) !== 'granted',
id: 'bell',
});
diff --git a/app/javascript/mastodon/reducers/settings.js b/app/javascript/mastodon/reducers/settings.js
index 886353de3d..057fa353a2 100644
--- a/app/javascript/mastodon/reducers/settings.js
+++ b/app/javascript/mastodon/reducers/settings.js
@@ -35,6 +35,7 @@ const initialState = ImmutableMap({
reblog: false,
mention: false,
poll: false,
+ status: false,
}),
quickFilter: ImmutableMap({
@@ -50,6 +51,7 @@ const initialState = ImmutableMap({
reblog: true,
mention: true,
poll: true,
+ status: true,
}),
sounds: ImmutableMap({
@@ -59,6 +61,7 @@ const initialState = ImmutableMap({
reblog: true,
mention: true,
poll: true,
+ status: true,
}),
}),
diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss
index 378d1640fd..0d32b9d6c0 100644
--- a/app/javascript/styles/mastodon/components.scss
+++ b/app/javascript/styles/mastodon/components.scss
@@ -3732,6 +3732,10 @@ a.status-card.compact:hover {
margin-bottom: 10px;
}
+.column-settings__row--with-margin {
+ margin-bottom: 15px;
+}
+
.column-settings__hashtags {
.column-settings__row {
margin-bottom: 15px;
@@ -7168,3 +7172,25 @@ noscript {
border-color: lighten($ui-base-color, 12%);
}
}
+
+.notifications-permission-banner {
+ padding: 30px;
+ border-bottom: 1px solid lighten($ui-base-color, 8%);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+
+ h2 {
+ font-size: 16px;
+ font-weight: 500;
+ margin-bottom: 15px;
+ text-align: center;
+ }
+
+ p {
+ color: $darker-text-color;
+ margin-bottom: 15px;
+ text-align: center;
+ }
+}