@@ -149,14 +167,30 @@ export default class ColumnHeader extends React.PureComponent {
{(!collapsed || animating) && collapsedContent}
diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.js
index 6a262a59e0..0d86d41cee 100644
--- a/app/javascript/mastodon/features/notifications/index.js
+++ b/app/javascript/mastodon/features/notifications/index.js
@@ -5,6 +5,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import Column from '../../components/column';
import ColumnHeader from '../../components/column_header';
import {
+ enterNotificationClearingMode,
expandNotifications,
scrollTopNotifications,
} from '../../actions/notifications';
@@ -36,7 +37,15 @@ const mapStateToProps = state => ({
notifCleaningActive: state.getIn(['notifications', 'cleaningMode']),
});
-@connect(mapStateToProps)
+/* glitch */
+const mapDispatchToProps = dispatch => ({
+ onEnterCleaningMode(yes) {
+ dispatch(enterNotificationClearingMode(yes));
+ },
+ dispatch,
+});
+
+@connect(mapStateToProps, mapDispatchToProps)
@injectIntl
export default class Notifications extends React.PureComponent {
@@ -52,6 +61,7 @@ export default class Notifications extends React.PureComponent {
hasMore: PropTypes.bool,
localSettings: ImmutablePropTypes.map,
notifCleaningActive: PropTypes.bool,
+ onEnterCleaningMode: PropTypes.func,
};
static defaultProps = {
@@ -173,6 +183,7 @@ export default class Notifications extends React.PureComponent {
return (
diff --git a/app/javascript/mastodon/reducers/notifications.js b/app/javascript/mastodon/reducers/notifications.js
index dd81653d6a..ecce8dcb6e 100644
--- a/app/javascript/mastodon/reducers/notifications.js
+++ b/app/javascript/mastodon/reducers/notifications.js
@@ -13,6 +13,7 @@ import {
NOTIFICATION_MARK_FOR_DELETE,
NOTIFICATIONS_DELETE_MARKED_FAIL,
NOTIFICATIONS_ENTER_CLEARING_MODE,
+ NOTIFICATIONS_MARK_ALL_FOR_DELETE,
} from '../actions/notifications';
import { ACCOUNT_BLOCK_SUCCESS } from '../actions/accounts';
import { TIMELINE_DELETE } from '../actions/timelines';
@@ -26,13 +27,15 @@ const initialState = ImmutableMap({
loaded: false,
isLoading: true,
cleaningMode: false,
+ // notification removal mark of new notifs loaded whilst cleaningMode is true.
+ markNewForDelete: false,
});
-const notificationToMap = notification => ImmutableMap({
+const notificationToMap = (state, notification) => ImmutableMap({
id: notification.id,
type: notification.type,
account: notification.account.id,
- markedForDelete: false,
+ markedForDelete: state.get('markNewForDelete'),
status: notification.status ? notification.status.id : null,
});
@@ -48,7 +51,7 @@ const normalizeNotification = (state, notification) => {
list = list.take(20);
}
- return list.unshift(notificationToMap(notification));
+ return list.unshift(notificationToMap(state, notification));
});
};
@@ -57,7 +60,7 @@ const normalizeNotifications = (state, notifications, next) => {
const loaded = state.get('loaded');
notifications.forEach((n, i) => {
- items = items.set(i, notificationToMap(n));
+ items = items.set(i, notificationToMap(state, n));
});
if (state.get('next') === null) {
@@ -74,7 +77,7 @@ const appendNormalizedNotifications = (state, notifications, next) => {
let items = ImmutableList();
notifications.forEach((n, i) => {
- items = items.set(i, notificationToMap(n));
+ items = items.set(i, notificationToMap(state, n));
});
return state
@@ -109,6 +112,16 @@ const markForDelete = (state, notificationId, yes) => {
}));
};
+const markAllForDelete = (state, yes) => {
+ return state.update('items', list => list.map(item => {
+ if(yes !== null) {
+ return item.set('markedForDelete', yes);
+ } else {
+ return item.set('markedForDelete', !item.get('markedForDelete'));
+ }
+ }));
+};
+
const unmarkAllForDelete = (state) => {
return state.update('items', list => list.map(item => item.set('markedForDelete', false)));
};
@@ -118,6 +131,8 @@ const deleteMarkedNotifs = (state) => {
};
export default function notifications(state = initialState, action) {
+ let st;
+
switch(action.type) {
case NOTIFICATIONS_REFRESH_REQUEST:
case NOTIFICATIONS_EXPAND_REQUEST:
@@ -141,15 +156,31 @@ export default function notifications(state = initialState, action) {
return state.set('items', ImmutableList()).set('next', null);
case TIMELINE_DELETE:
return deleteByStatus(state, action.id);
+
case NOTIFICATION_MARK_FOR_DELETE:
return markForDelete(state, action.id, action.yes);
+
case NOTIFICATIONS_DELETE_MARKED_SUCCESS:
- return deleteMarkedNotifs(state).set('isLoading', false).set('cleaningMode', false);
+ return deleteMarkedNotifs(state).set('isLoading', false);
+
case NOTIFICATIONS_ENTER_CLEARING_MODE:
- const st = state.set('cleaningMode', action.yes);
- if (!action.yes)
- return unmarkAllForDelete(st);
- else return st;
+ st = state.set('cleaningMode', action.yes);
+ if (!action.yes) {
+ return unmarkAllForDelete(st).set('markNewForDelete', false);
+ } else {
+ return st;
+ }
+
+ case NOTIFICATIONS_MARK_ALL_FOR_DELETE:
+ st = state;
+ if (action.yes === null) {
+ // Toggle - this is a bit confusing, as it toggles the all-none mode
+ //st = st.set('markNewForDelete', !st.get('markNewForDelete'));
+ } else {
+ st = st.set('markNewForDelete', action.yes);
+ }
+ return markAllForDelete(st, action.yes);
+
default:
return state;
}
diff --git a/app/javascript/styles/application.scss b/app/javascript/styles/application.scss
index f418ba6993..b08b694498 100644
--- a/app/javascript/styles/application.scss
+++ b/app/javascript/styles/application.scss
@@ -1,5 +1,6 @@
@import 'mixins';
@import 'variables';
+@import 'variables-glitch';
@import 'fonts/roboto';
@import 'fonts/roboto-mono';
@import 'fonts/montserrat';
diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss
index fa44b825c2..fe74bae84f 100644
--- a/app/javascript/styles/components.scss
+++ b/app/javascript/styles/components.scss
@@ -1,4 +1,5 @@
@import 'variables';
+@import 'variables-glitch';
.app-body {
-webkit-overflow-scrolling: touch;
@@ -451,62 +452,6 @@
cursor: pointer;
}
-.notification__dismiss-overlay {
- position: absolute;
- left: 0; top: 0; right: 0; bottom: 0;
-
- $c1: #00000A;
- $c2: #222228;
- background: linear-gradient(to right,
- rgba($c1, 0.1),
- rgba($c1, 0.2) 60%,
- rgba($c2, 1) 90%,
- rgba($c2, 1));
-
- z-index: 999;
- align-items: center;
- justify-content: flex-end;
- cursor: pointer;
-
- display: none;
-
- &.show {
- display: flex;
- }
-
- // make it brighter
- &.active {
- $c: #222931;
- background: linear-gradient(to right,
- rgba($c, 0.1),
- rgba($c, 0.2) 60%,
- rgba($c, 1) 90%,
- rgba($c, 1));
- }
-
- &:focus {
- outline: 0 !important;
- }
-}
-
-.notification__dismiss-overlay__ckbox {
- border: 2px solid #9baec8;
- border-radius: 2px;
- width: 30px;
- height: 30px;
- margin-right: 20px;
- font-size: 20px;
- color: #c3dcfd;
- text-shadow: 0 0 5px black;
- display: flex;
- justify-content: center;
- align-items: center;
-
- :focus & {
- box-shadow: 0 0 2px 2px #3e6fc1;
- }
-}
-
// --- Extra clickable area in the status gutter ---
.ui.wide {
@mixin xtraspaces-full {
@@ -683,6 +628,12 @@
position: absolute;
}
+.notif-cleaning {
+ .status, .notification-follow {
+ padding-right: ($dismiss-overlay-width + 0.5rem);
+ }
+}
+
.notification-follow {
position: relative;
@@ -2479,17 +2430,88 @@ button.icon-button.active i.fa-retweet {
background: lighten($ui-base-color, 8%);
}
}
+
+ // glitch - added focus ring for keyboard navigation
+ &:focus {
+ text-shadow: 0 0 4px darken($ui-highlight-color, 5%);
+ }
+}
+
+.scrollable > div > :first-child .notification__dismiss-overlay > .wrappy {
+ border-top: 1px solid $ui-base-color;
+}
+
+.notification__dismiss-overlay {
+ overflow: hidden;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: -1px;
+ padding-left: 15px; // space for the box shadow to be visible
+
+ z-index: 999;
+ align-items: center;
+ justify-content: flex-end;
+ cursor: pointer;
+
+ display: flex;
+
+ .wrappy {
+ width: $dismiss-overlay-width;
+ align-self: stretch;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ background: lighten($ui-base-color, 8%);
+ border-left: 1px solid lighten($ui-base-color, 20%);
+ box-shadow: 0 0 5px black;
+ border-bottom: 1px solid $ui-base-color;
+ }
+
+ .ckbox {
+ border: 2px solid $ui-primary-color;
+ border-radius: 2px;
+ width: 30px;
+ height: 30px;
+ font-size: 20px;
+ color: $ui-primary-color;
+ text-shadow: 0 0 5px black;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+
+ &:focus {
+ outline: 0 !important;
+
+ .ckbox {
+ box-shadow: 0 0 1px 1px $ui-highlight-color;
+ }
+ }
}
.column-header__notif-cleaning-buttons {
display: flex;
align-items: stretch;
+ justify-content: space-around;
button {
@extend .column-header__button;
- padding-left: 12px;
- padding-right: 12px;
+ background: transparent;
+ text-align: center;
+ padding: 10px 0;
+ white-space: pre-wrap;
}
+
+ b {
+ font-weight: bold;
+ }
+}
+
+// The notifs drawer with no padding to have more space for the buttons
+.column-header__collapsible-inner.nopad-drawer {
+ padding: 0;
}
.column-header__collapsible {
@@ -2508,6 +2530,15 @@ button.icon-button.active i.fa-retweet {
&.animating {
overflow-y: hidden;
}
+
+ // notif cleaning drawer
+ &.ncd {
+ transition: none;
+ &.collapsed {
+ max-height: 0;
+ opacity: 0.7;
+ }
+ }
}
.column-header__collapsible-inner {
diff --git a/app/javascript/styles/variables-glitch.scss b/app/javascript/styles/variables-glitch.scss
new file mode 100644
index 0000000000..44d3322f2c
--- /dev/null
+++ b/app/javascript/styles/variables-glitch.scss
@@ -0,0 +1,3 @@
+// glitch-soc added variables
+
+$dismiss-overlay-width: 4rem;