Display unread marker for notifications

This commit is contained in:
Thibaut Girka 2020-09-15 18:09:08 +02:00 committed by ThibG
parent ae698469d0
commit 66e9a77e36
6 changed files with 39 additions and 5 deletions

View file

@ -122,6 +122,7 @@ class Status extends ImmutablePureComponent {
'notification', 'notification',
'hidden', 'hidden',
'expanded', 'expanded',
'unread',
] ]
updateOnStates = [ updateOnStates = [

View file

@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { HotKeys } from 'react-hotkeys'; import { HotKeys } from 'react-hotkeys';
import classNames from 'classnames';
// Our imports. // Our imports.
import Permalink from 'flavours/glitch/components/permalink'; import Permalink from 'flavours/glitch/components/permalink';
@ -19,6 +20,7 @@ export default class NotificationFollow extends ImmutablePureComponent {
id: PropTypes.string.isRequired, id: PropTypes.string.isRequired,
account: ImmutablePropTypes.map.isRequired, account: ImmutablePropTypes.map.isRequired,
notification: ImmutablePropTypes.map.isRequired, notification: ImmutablePropTypes.map.isRequired,
unread: PropTypes.bool,
}; };
handleMoveUp = () => { handleMoveUp = () => {
@ -59,7 +61,7 @@ export default class NotificationFollow extends ImmutablePureComponent {
} }
render () { render () {
const { account, notification, hidden } = this.props; const { account, notification, hidden, unread } = this.props;
// Links to the display name. // Links to the display name.
const displayName = account.get('display_name_html') || account.get('username'); const displayName = account.get('display_name_html') || account.get('username');
@ -76,7 +78,7 @@ export default class NotificationFollow extends ImmutablePureComponent {
// Renders. // Renders.
return ( return (
<HotKeys handlers={this.getHandlers()}> <HotKeys handlers={this.getHandlers()}>
<div className='notification notification-follow focusable' tabIndex='0'> <div className={classNames('notification notification-follow focusable', { unread })} tabIndex='0'>
<div className='notification__message'> <div className='notification__message'>
<div className='notification__favourite-icon-wrapper'> <div className='notification__favourite-icon-wrapper'>
<Icon fixedWidth id='user-plus' /> <Icon fixedWidth id='user-plus' />

View file

@ -10,6 +10,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import NotificationOverlayContainer from '../containers/overlay_container'; import NotificationOverlayContainer from '../containers/overlay_container';
import { HotKeys } from 'react-hotkeys'; import { HotKeys } from 'react-hotkeys';
import Icon from 'flavours/glitch/components/icon'; import Icon from 'flavours/glitch/components/icon';
import classNames from 'classnames';
const messages = defineMessages({ const messages = defineMessages({
authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' }, authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
@ -25,6 +26,7 @@ class FollowRequest extends ImmutablePureComponent {
onReject: PropTypes.func.isRequired, onReject: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
notification: ImmutablePropTypes.map.isRequired, notification: ImmutablePropTypes.map.isRequired,
unread: PropTypes.bool,
}; };
handleMoveUp = () => { handleMoveUp = () => {
@ -65,7 +67,7 @@ class FollowRequest extends ImmutablePureComponent {
} }
render () { render () {
const { intl, hidden, account, onAuthorize, onReject, notification } = this.props; const { intl, hidden, account, onAuthorize, onReject, notification, unread } = this.props;
if (!account) { if (!account) {
return <div />; return <div />;
@ -94,7 +96,7 @@ class FollowRequest extends ImmutablePureComponent {
return ( return (
<HotKeys handlers={this.getHandlers()}> <HotKeys handlers={this.getHandlers()}>
<div className='notification notification-follow-request focusable' tabIndex='0'> <div className={classNames('notification notification-follow-request focusable', { unread })} tabIndex='0'>
<div className='notification__message'> <div className='notification__message'>
<div className='notification__favourite-icon-wrapper'> <div className='notification__favourite-icon-wrapper'>
<Icon id='user' fixedWidth /> <Icon id='user' fixedWidth />

View file

@ -46,6 +46,7 @@ export default class Notification extends ImmutablePureComponent {
onMoveDown={onMoveDown} onMoveDown={onMoveDown}
onMoveUp={onMoveUp} onMoveUp={onMoveUp}
onMention={onMention} onMention={onMention}
unread={this.props.unread}
/> />
); );
case 'follow_request': case 'follow_request':
@ -58,6 +59,7 @@ export default class Notification extends ImmutablePureComponent {
onMoveDown={onMoveDown} onMoveDown={onMoveDown}
onMoveUp={onMoveUp} onMoveUp={onMoveUp}
onMention={onMention} onMention={onMention}
unread={this.props.unread}
/> />
); );
case 'mention': case 'mention':
@ -77,6 +79,7 @@ export default class Notification extends ImmutablePureComponent {
cacheMediaWidth={this.props.cacheMediaWidth} cacheMediaWidth={this.props.cacheMediaWidth}
onUnmount={this.props.onUnmount} onUnmount={this.props.onUnmount}
withDismiss withDismiss
unread={this.props.unread}
/> />
); );
case 'favourite': case 'favourite':
@ -98,6 +101,7 @@ export default class Notification extends ImmutablePureComponent {
cacheMediaWidth={this.props.cacheMediaWidth} cacheMediaWidth={this.props.cacheMediaWidth}
onUnmount={this.props.onUnmount} onUnmount={this.props.onUnmount}
withDismiss withDismiss
unread={this.props.unread}
/> />
); );
case 'reblog': case 'reblog':
@ -119,6 +123,7 @@ export default class Notification extends ImmutablePureComponent {
cacheMediaWidth={this.props.cacheMediaWidth} cacheMediaWidth={this.props.cacheMediaWidth}
onUnmount={this.props.onUnmount} onUnmount={this.props.onUnmount}
withDismiss withDismiss
unread={this.props.unread}
/> />
); );
case 'poll': case 'poll':
@ -140,6 +145,7 @@ export default class Notification extends ImmutablePureComponent {
cacheMediaWidth={this.props.cacheMediaWidth} cacheMediaWidth={this.props.cacheMediaWidth}
onUnmount={this.props.onUnmount} onUnmount={this.props.onUnmount}
withDismiss withDismiss
unread={this.props.unread}
/> />
); );
default: default:

View file

@ -24,6 +24,7 @@ import { debounce } from 'lodash';
import ScrollableList from 'flavours/glitch/components/scrollable_list'; import ScrollableList from 'flavours/glitch/components/scrollable_list';
import LoadGap from 'flavours/glitch/components/load_gap'; import LoadGap from 'flavours/glitch/components/load_gap';
import Icon from 'flavours/glitch/components/icon'; import Icon from 'flavours/glitch/components/icon';
import compareId from 'flavours/glitch/util/compare_id';
import NotificationPurgeButtonsContainer from 'flavours/glitch/containers/notification_purge_buttons_container'; import NotificationPurgeButtonsContainer from 'flavours/glitch/containers/notification_purge_buttons_container';
@ -56,6 +57,7 @@ const mapStateToProps = state => ({
hasMore: state.getIn(['notifications', 'hasMore']), hasMore: state.getIn(['notifications', 'hasMore']),
numPending: state.getIn(['notifications', 'pendingItems'], ImmutableList()).size, numPending: state.getIn(['notifications', 'pendingItems'], ImmutableList()).size,
notifCleaningActive: state.getIn(['notifications', 'cleaningMode']), notifCleaningActive: state.getIn(['notifications', 'cleaningMode']),
lastReadId: state.getIn(['notifications', 'lastReadId']),
}); });
/* glitch */ /* glitch */
@ -93,6 +95,7 @@ class Notifications extends React.PureComponent {
onEnterCleaningMode: PropTypes.func, onEnterCleaningMode: PropTypes.func,
onMount: PropTypes.func, onMount: PropTypes.func,
onUnmount: PropTypes.func, onUnmount: PropTypes.func,
lastReadId: PropTypes.string,
}; };
static defaultProps = { static defaultProps = {
@ -195,7 +198,7 @@ class Notifications extends React.PureComponent {
} }
render () { render () {
const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar } = this.props; const { intl, notifications, shouldUpdateScroll, isLoading, isUnread, columnId, multiColumn, hasMore, numPending, showFilterBar, lastReadId } = this.props;
const { notifCleaning, notifCleaningActive } = this.props; const { notifCleaning, notifCleaningActive } = this.props;
const { animatingNCD } = this.state; const { animatingNCD } = this.state;
const pinned = !!columnId; const pinned = !!columnId;
@ -224,6 +227,7 @@ class Notifications extends React.PureComponent {
accountId={item.get('account')} accountId={item.get('account')}
onMoveUp={this.handleMoveUp} onMoveUp={this.handleMoveUp}
onMoveDown={this.handleMoveDown} onMoveDown={this.handleMoveDown}
unread={lastReadId && compareId(item.get('id'), lastReadId) > 0}
/> />
)); ));
} else { } else {

View file

@ -1054,3 +1054,22 @@ a.status-card.compact:hover {
text-decoration: underline; text-decoration: underline;
} }
} }
.notification,
.status {
position: relative;
&.unread {
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
pointer-events: 0;
width: 100%;
height: 100%;
border-left: 2px solid $highlight-text-color;
pointer-events: none;
}
}
}