diff options
author | Ondřej Hruška <ondra@ondrovo.com> | 2017-07-30 18:36:28 +0200 |
---|---|---|
committer | beatrix <beatrix.bitrot@gmail.com> | 2017-07-30 12:36:28 -0400 |
commit | 6ff084dbbb06e5c2f131546829066435f2bf8f8a (patch) | |
tree | 06386f3ffadc2148b1f49be93cff1eb9ac685262 /app | |
parent | 9aaf3218d24bb8b3eb1de697243c13637398ab46 (diff) |
Improved notifications cleaning UI with set operations (#109)
* added notification cleaning drawer * bugfix * fully implemented set operations for notif cleaning * i18n for notif cleaning drawer & improved logic slightly. Also added a confirm dialog * - notif dismiss "overlay" now shoves the notif aside to avoid overlap - added focus ring to header buttons - removed notif overlay entirely from DOM if mode is disabled * removed comment * CSS tuning - inconsistent division lines fix
Diffstat (limited to 'app')
14 files changed, 277 insertions, 160 deletions
diff --git a/app/javascript/glitch/components/column/notif_cleaning_widget/container.js b/app/javascript/glitch/components/column/notif_cleaning_widget/container.js index bf079e3c4..d3507d752 100644 --- a/app/javascript/glitch/components/column/notif_cleaning_widget/container.js +++ b/app/javascript/glitch/components/column/notif_cleaning_widget/container.js @@ -24,7 +24,10 @@ import NotificationPurgeButtons from './notification_purge_buttons'; import { deleteMarkedNotifications, enterNotificationClearingMode, + markAllNotifications, } from '../../../../mastodon/actions/notifications'; +import { defineMessages, injectIntl } from 'react-intl'; +import { openModal } from '../../../../mastodon/actions/modal'; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * @@ -39,18 +42,39 @@ deleting notifications. */ -const mapDispatchToProps = dispatch => ({ +const messages = defineMessages({ + clearMessage: { id: 'notifications.marked_clear_confirmation', defaultMessage: 'Are you sure you want to permanently clear all selected notifications?' }, + clearConfirm: { id: 'notifications.marked_clear', defaultMessage: 'Clear selected notifications' }, +}); + +const mapDispatchToProps = (dispatch, { intl }) => ({ onEnterCleaningMode(yes) { dispatch(enterNotificationClearingMode(yes)); }, - onDeleteMarkedNotifications() { - dispatch(deleteMarkedNotifications()); + onDeleteMarked() { + dispatch(openModal('CONFIRM', { + message: intl.formatMessage(messages.clearMessage), + confirm: intl.formatMessage(messages.clearConfirm), + onConfirm: () => dispatch(deleteMarkedNotifications()), + })); + }, + + onMarkAll() { + dispatch(markAllNotifications(true)); + }, + + onMarkNone() { + dispatch(markAllNotifications(false)); + }, + + onInvert() { + dispatch(markAllNotifications(null)); }, }); const mapStateToProps = state => ({ - active: state.getIn(['notifications', 'cleaningMode']), + markNewForDelete: state.getIn(['notifications', 'markNewForDelete']), }); -export default connect(mapStateToProps, mapDispatchToProps)(NotificationPurgeButtons); +export default injectIntl(connect(mapStateToProps, mapDispatchToProps)(NotificationPurgeButtons)); diff --git a/app/javascript/glitch/components/column/notif_cleaning_widget/notification_purge_buttons.js b/app/javascript/glitch/components/column/notif_cleaning_widget/notification_purge_buttons.js index e41572256..62c887fb7 100644 --- a/app/javascript/glitch/components/column/notif_cleaning_widget/notification_purge_buttons.js +++ b/app/javascript/glitch/components/column/notif_cleaning_widget/notification_purge_buttons.js @@ -16,83 +16,45 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * const messages = defineMessages({ - enter : { id: 'notification_purge.start', defaultMessage: 'Enter notification cleaning mode' }, - accept : { id: 'notification_purge.confirm', defaultMessage: 'Dismiss selected notifications' }, - abort : { id: 'notification_purge.abort', defaultMessage: 'Leave cleaning mode' }, + btnAll : { id: 'notification_purge.btn_all', defaultMessage: 'Select\nall' }, + btnNone : { id: 'notification_purge.btn_none', defaultMessage: 'Select\nnone' }, + btnInvert : { id: 'notification_purge.btn_invert', defaultMessage: 'Invert\nselection' }, + btnApply : { id: 'notification_purge.btn_apply', defaultMessage: 'Clear\nselected' }, }); @injectIntl export default class NotificationPurgeButtons extends ImmutablePureComponent { static propTypes = { - // Nukes all marked notifications - onDeleteMarkedNotifications : PropTypes.func.isRequired, - // Enables or disables the mode - // and also clears the marked status of all notifications - onEnterCleaningMode : PropTypes.func.isRequired, - // Active state, changed via onStateChange() - active: PropTypes.bool.isRequired, - // i18n + onDeleteMarked : PropTypes.func.isRequired, + onMarkAll : PropTypes.func.isRequired, + onMarkNone : PropTypes.func.isRequired, + onInvert : PropTypes.func.isRequired, intl: PropTypes.object.isRequired, + markNewForDelete: PropTypes.bool, }; - onEnterBtnClick = () => { - this.props.onEnterCleaningMode(true); - } - - onAcceptBtnClick = () => { - this.props.onDeleteMarkedNotifications(); - } - - onAbortBtnClick = () => { - this.props.onEnterCleaningMode(false); - } - render () { - const { intl, active } = this.props; - - const msgEnter = intl.formatMessage(messages.enter); - const msgAccept = intl.formatMessage(messages.accept); - const msgAbort = intl.formatMessage(messages.abort); - - let enterButton, acceptButton, abortButton; + const { intl, markNewForDelete } = this.props; - if (active) { - acceptButton = ( - <button - className='active' - aria-label={msgAccept} - title={msgAccept} - onClick={this.onAcceptBtnClick} - > - <i className='fa fa-check' /> + //className='active' + return ( + <div className='column-header__notif-cleaning-buttons'> + <button onClick={this.props.onMarkAll} className={markNewForDelete ? 'active' : ''}> + <b>∀</b><br />{intl.formatMessage(messages.btnAll)} </button> - ); - abortButton = ( - <button - className='active' - aria-label={msgAbort} - title={msgAbort} - onClick={this.onAbortBtnClick} - > - <i className='fa fa-times' /> + + <button onClick={this.props.onMarkNone} className={!markNewForDelete ? 'active' : ''}> + <b>∅</b><br />{intl.formatMessage(messages.btnNone)} </button> - ); - } else { - enterButton = ( - <button - aria-label={msgEnter} - title={msgEnter} - onClick={this.onEnterBtnClick} - > - <i className='fa fa-eraser' /> + + <button onClick={this.props.onInvert}> + <b>¬</b><br />{intl.formatMessage(messages.btnInvert)} </button> - ); - } - return ( - <div className='column-header__notif-cleaning-buttons'> - {acceptButton}{abortButton}{enterButton} + <button onClick={this.props.onDeleteMarked}> + <i className='fa fa-trash' /><br />{intl.formatMessage(messages.btnApply)} + </button> </div> ); } diff --git a/app/javascript/glitch/components/notification/container.js b/app/javascript/glitch/components/notification/container.js index 7d2590684..e29d6ba60 100644 --- a/app/javascript/glitch/components/notification/container.js +++ b/app/javascript/glitch/components/notification/container.js @@ -45,6 +45,7 @@ const makeMapStateToProps = () => { const mapStateToProps = (state, props) => ({ notification: getNotification(state, props.notification, props.accountId), settings: state.get('local_settings'), + notifCleaning: state.getIn(['notifications', 'cleaningMode']), }); return mapStateToProps; diff --git a/app/javascript/glitch/components/notification/overlay/container.js b/app/javascript/glitch/components/notification/overlay/container.js index 019b78d0b..089f615f0 100644 --- a/app/javascript/glitch/components/notification/overlay/container.js +++ b/app/javascript/glitch/components/notification/overlay/container.js @@ -43,7 +43,7 @@ const mapDispatchToProps = dispatch => ({ }); const mapStateToProps = state => ({ - revealed: state.getIn(['notifications', 'cleaningMode']), + show: state.getIn(['notifications', 'cleaningMode']), }); export default connect(mapStateToProps, mapDispatchToProps)(NotificationOverlay); diff --git a/app/javascript/glitch/components/notification/overlay/notification_overlay.js b/app/javascript/glitch/components/notification/overlay/notification_overlay.js index 73eda718f..aaca95cac 100644 --- a/app/javascript/glitch/components/notification/overlay/notification_overlay.js +++ b/app/javascript/glitch/components/notification/overlay/notification_overlay.js @@ -24,7 +24,7 @@ export default class NotificationOverlay extends ImmutablePureComponent { static propTypes = { notification : ImmutablePropTypes.map.isRequired, onMarkForDelete : PropTypes.func.isRequired, - revealed : PropTypes.bool.isRequired, + show : PropTypes.bool.isRequired, intl : PropTypes.object.isRequired, }; @@ -35,25 +35,27 @@ export default class NotificationOverlay extends ImmutablePureComponent { } render () { - const { notification, revealed, intl } = this.props; + const { notification, show, intl } = this.props; const active = notification.get('markedForDelete'); const label = intl.formatMessage(messages.markForDeletion); - return ( + return show ? ( <div aria-label={label} role='checkbox' aria-checked={active} tabIndex={0} - className={`notification__dismiss-overlay ${active ? 'active' : ''} ${revealed ? 'show' : ''}`} + className={`notification__dismiss-overlay ${active ? 'active' : ''}`} onClick={this.onToggleMark} > - <div className='notification__dismiss-overlay__ckbox' aria-hidden='true' title={label}> - {active ? (<i className='fa fa-check' />) : ''} + <div className='wrappy'> + <div className='ckbox' aria-hidden='true' title={label}> + {active ? (<i className='fa fa-check' />) : ''} + </div> </div> </div> - ); + ) : null; } } diff --git a/app/javascript/glitch/locales/en.json b/app/javascript/glitch/locales/en.json index 21616f556..7ec381de1 100644 --- a/app/javascript/glitch/locales/en.json +++ b/app/javascript/glitch/locales/en.json @@ -29,5 +29,14 @@ "settings.navbar_under": "Navbar at the bottom (Mobile only)", "status.collapse": "Collapse", "status.uncollapse": "Uncollapse", - "notification.markForDeletion": "Mark for deletion" + + "notification.markForDeletion": "Mark for deletion", + "notifications.clear": "Clear all my notifications", + "notifications.marked_clear_confirmation": "Are you sure you want to permanently clear all selected notifications?", + "notifications.marked_clear": "Clear selected notifications", + + "notification_purge.btn_all": "Select\nall", + "notification_purge.btn_none": "Select\nnone", + "notification_purge.btn_invert": "Invert\nselection", + "notification_purge.btn_apply": "Clear\nselected" } diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js index fca26516a..ebdf21322 100644 --- a/app/javascript/mastodon/actions/notifications.js +++ b/app/javascript/mastodon/actions/notifications.js @@ -10,6 +10,7 @@ export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE'; export const NOTIFICATIONS_DELETE_MARKED_REQUEST = 'NOTIFICATIONS_DELETE_MARKED_REQUEST'; export const NOTIFICATIONS_DELETE_MARKED_SUCCESS = 'NOTIFICATIONS_DELETE_MARKED_SUCCESS'; export const NOTIFICATIONS_DELETE_MARKED_FAIL = 'NOTIFICATIONS_DELETE_MARKED_FAIL'; +export const NOTIFICATIONS_MARK_ALL_FOR_DELETE = 'NOTIFICATIONS_MARK_ALL_FOR_DELETE'; export const NOTIFICATIONS_ENTER_CLEARING_MODE = 'NOTIFICATIONS_ENTER_CLEARING_MODE'; // arg: yes // Unmark notifications (when the cleaning mode is left) export const NOTIFICATIONS_UNMARK_ALL_FOR_DELETE = 'NOTIFICATIONS_UNMARK_ALL_FOR_DELETE'; @@ -210,13 +211,11 @@ export function deleteMarkedNotifications() { }); if (ids.length === 0) { - dispatch(enterNotificationClearingMode(false)); return; } api(getState).delete(`/api/v1/notifications/destroy_multiple?ids[]=${ids.join('&ids[]=')}`).then(() => { dispatch(deleteMarkedNotificationsSuccess()); - dispatch(expandNotifications()); // Load more (to fill the empty space) }).catch(error => { console.error(error); dispatch(deleteMarkedNotificationsFail(error)); @@ -231,6 +230,13 @@ export function enterNotificationClearingMode(yes) { }; }; +export function markAllNotifications(yes) { + return { + type: NOTIFICATIONS_MARK_ALL_FOR_DELETE, + yes: yes, // true, false or null. null = invert + }; +}; + export function deleteMarkedNotificationsRequest() { return { type: NOTIFICATIONS_DELETE_MARKED_REQUEST, diff --git a/app/javascript/mastodon/components/column.js b/app/javascript/mastodon/components/column.js index 93f1d6260..29c8f4389 100644 --- a/app/javascript/mastodon/components/column.js +++ b/app/javascript/mastodon/components/column.js @@ -7,6 +7,7 @@ export default class Column extends React.PureComponent { static propTypes = { children: PropTypes.node, + extraClasses: PropTypes.string, }; scrollTop () { @@ -40,10 +41,10 @@ export default class Column extends React.PureComponent { } render () { - const { children } = this.props; + const { children, extraClasses } = this.props; return ( - <div role='region' className='column' ref={this.setRef}> + <div role='region' className={`column ${extraClasses || ''}`} ref={this.setRef}> {children} </div> ); diff --git a/app/javascript/mastodon/components/column_header.js b/app/javascript/mastodon/components/column_header.js index 045904206..9945fc209 100644 --- a/app/javascript/mastodon/components/column_header.js +++ b/app/javascript/mastodon/components/column_header.js @@ -8,8 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import NotificationPurgeButtonsContainer from '../../glitch/components/column/notif_cleaning_widget/container'; const messages = defineMessages({ - titleNotifClearing: { id: 'column.notifications_clearing', defaultMessage: 'Dismiss selected notifications:' }, - titleNotifClearingShort: { id: 'column.notifications_clearing_short', defaultMessage: 'Dismiss selected:' }, + enterNotifCleaning : { id: 'notification_purge.start', defaultMessage: 'Enter notification cleaning mode' }, }); @injectIntl @@ -28,6 +27,7 @@ export default class ColumnHeader extends React.PureComponent { showBackButton: PropTypes.bool, notifCleaning: PropTypes.bool, // true only for the notification column notifCleaningActive: PropTypes.bool, + onEnterCleaningMode: PropTypes.func, children: PropTypes.node, pinned: PropTypes.bool, onPin: PropTypes.func, @@ -39,6 +39,7 @@ export default class ColumnHeader extends React.PureComponent { state = { collapsed: true, animating: false, + animatingNCD: false, }; handleToggleClick = (e) => { @@ -71,16 +72,21 @@ export default class ColumnHeader extends React.PureComponent { this.setState({ animating: false }); } + handleTransitionEndNCD = () => { + this.setState({ animatingNCD: false }); + } + + onEnterCleaningMode = () => { + this.setState({ animatingNCD: true }); + this.props.onEnterCleaningMode(!this.props.notifCleaningActive); + } + render () { - const { intl, icon, active, children, pinned, onPin, multiColumn, showBackButton, notifCleaning, localSettings } = this.props; - const { collapsed, animating } = this.state; + const { intl, icon, active, children, pinned, onPin, multiColumn, showBackButton, notifCleaning, notifCleaningActive } = this.props; + const { collapsed, animating, animatingNCD } = this.state; + let title = this.props.title; - if (notifCleaning && this.props.notifCleaningActive) { - title = intl.formatMessage(localSettings.getIn(['stretch']) ? - messages.titleNotifClearing : - messages.titleNotifClearingShort); - } const wrapperClassName = classNames('column-header__wrapper', { 'active': active, @@ -99,8 +105,20 @@ export default class ColumnHeader extends React.PureComponent { 'active': !collapsed, }); + const notifCleaningButtonClassName = classNames('column-header__button', { + 'active': notifCleaningActive, + }); + + const notifCleaningDrawerClassName = classNames('ncd column-header__collapsible', { + 'collapsed': !notifCleaningActive, + 'animating': animatingNCD, + }); + let extraContent, pinButton, moveButtons, backButton, collapseButton; + //*glitch + const msgEnterNotifCleaning = intl.formatMessage(messages.enterNotifCleaning); + if (children) { extraContent = ( <div key='extra-content' className='column-header__collapsible__extra'> @@ -149,14 +167,30 @@ export default class ColumnHeader extends React.PureComponent { <div role='button heading' tabIndex='0' className={buttonClassName} onClick={this.handleTitleClick}> <i className={`fa fa-fw fa-${icon} column-header__icon`} /> {title} - <div className='column-header__buttons'> - {notifCleaning ? (<NotificationPurgeButtonsContainer />) : null} {backButton} + { notifCleaning ? ( + <button + aria-label={msgEnterNotifCleaning} + title={msgEnterNotifCleaning} + onClick={this.onEnterCleaningMode} + className={notifCleaningButtonClassName} + > + <i className='fa fa-eraser' /> + </button> + ) : null} {collapseButton} </div> </div> + { notifCleaning ? ( + <div className={notifCleaningDrawerClassName} onTransitionEnd={this.handleTransitionEndNCD}> + <div className='column-header__collapsible-inner nopad-drawer'> + {(notifCleaningActive || animatingNCD) ? (<NotificationPurgeButtonsContainer />) : null } + </div> + </div> + ) : null} + <div className={collapsibleClassName} onTransitionEnd={this.handleTransitionEnd}> <div className='column-header__collapsible-inner'> {(!collapsed || animating) && collapsedContent} diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.js index 6a262a59e..0d86d41ce 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 ( <Column ref={this.setColumnRef} + extraClasses={this.props.notifCleaningActive ? 'notif-cleaning' : null} > <ColumnHeader icon='bell' @@ -186,6 +197,7 @@ export default class Notifications extends React.PureComponent { localSettings={this.props.localSettings} notifCleaning notifCleaningActive={this.props.notifCleaningActive} // this is used to toggle the header text + onEnterCleaningMode={this.props.onEnterCleaningMode} > <ColumnSettingsContainer /> </ColumnHeader> diff --git a/app/javascript/mastodon/reducers/notifications.js b/app/javascript/mastodon/reducers/notifications.js index dd81653d6..ecce8dcb6 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 f418ba699..b08b69449 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 fa44b825c..fe74bae84 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 000000000..44d3322f2 --- /dev/null +++ b/app/javascript/styles/variables-glitch.scss @@ -0,0 +1,3 @@ +// glitch-soc added variables + +$dismiss-overlay-width: 4rem; |