forked from treehouse/mastodon
Display unread marker for notifications
parent
ae698469d0
commit
66e9a77e36
|
@ -122,6 +122,7 @@ class Status extends ImmutablePureComponent {
|
||||||
'notification',
|
'notification',
|
||||||
'hidden',
|
'hidden',
|
||||||
'expanded',
|
'expanded',
|
||||||
|
'unread',
|
||||||
]
|
]
|
||||||
|
|
||||||
updateOnStates = [
|
updateOnStates = [
|
||||||
|
|
|
@ -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' />
|
||||||
|
|
|
@ -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 />
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue