]> cat aescling's git repositories - mastodon.git/commitdiff
Display unread marker for notifications
authorThibaut Girka <thib@sitedethib.com>
Tue, 15 Sep 2020 16:09:08 +0000 (18:09 +0200)
committerThibG <thib@sitedethib.com>
Thu, 17 Sep 2020 13:22:56 +0000 (15:22 +0200)
app/javascript/flavours/glitch/components/status.js
app/javascript/flavours/glitch/features/notifications/components/follow.js
app/javascript/flavours/glitch/features/notifications/components/follow_request.js
app/javascript/flavours/glitch/features/notifications/components/notification.js
app/javascript/flavours/glitch/features/notifications/index.js
app/javascript/flavours/glitch/styles/components/status.scss

index fe0234d842d3467f34f261aa8952f80301c7c8b9..8da5db96194aab0237c7ef7b06866cc36ae953bd 100644 (file)
@@ -122,6 +122,7 @@ class Status extends ImmutablePureComponent {
     'notification',
     'hidden',
     'expanded',
+    'unread',
   ]
 
   updateOnStates = [
index 2b71f31076392326c0c0cdeafa1eff4130875582..0d3162fc91bbaf44080c8ff96fc06d201c49550d 100644 (file)
@@ -5,6 +5,7 @@ import PropTypes from 'prop-types';
 import { FormattedMessage } from 'react-intl';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import { HotKeys } from 'react-hotkeys';
+import classNames from 'classnames';
 
 // Our imports.
 import Permalink from 'flavours/glitch/components/permalink';
@@ -19,6 +20,7 @@ export default class NotificationFollow extends ImmutablePureComponent {
     id: PropTypes.string.isRequired,
     account: ImmutablePropTypes.map.isRequired,
     notification: ImmutablePropTypes.map.isRequired,
+    unread: PropTypes.bool,
   };
 
   handleMoveUp = () => {
@@ -59,7 +61,7 @@ export default class NotificationFollow extends ImmutablePureComponent {
   }
 
   render () {
-    const { account, notification, hidden } = this.props;
+    const { account, notification, hidden, unread } = this.props;
 
     //  Links to the display name.
     const displayName = account.get('display_name_html') || account.get('username');
@@ -76,7 +78,7 @@ export default class NotificationFollow extends ImmutablePureComponent {
     //  Renders.
     return (
       <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__favourite-icon-wrapper'>
               <Icon fixedWidth id='user-plus' />
index d73dac43484078c7d36d3a00977d8f3f8e42c1b9..f351c1035fabc461327e72a015cf1b7ded8a24f1 100644 (file)
@@ -10,6 +10,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
 import NotificationOverlayContainer from '../containers/overlay_container';
 import { HotKeys } from 'react-hotkeys';
 import Icon from 'flavours/glitch/components/icon';
+import classNames from 'classnames';
 
 const messages = defineMessages({
   authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
@@ -25,6 +26,7 @@ class FollowRequest extends ImmutablePureComponent {
     onReject: PropTypes.func.isRequired,
     intl: PropTypes.object.isRequired,
     notification: ImmutablePropTypes.map.isRequired,
+    unread: PropTypes.bool,
   };
 
   handleMoveUp = () => {
@@ -65,7 +67,7 @@ class FollowRequest extends ImmutablePureComponent {
   }
 
   render () {
-    const { intl, hidden, account, onAuthorize, onReject, notification } = this.props;
+    const { intl, hidden, account, onAuthorize, onReject, notification, unread } = this.props;
 
     if (!account) {
       return <div />;
@@ -94,7 +96,7 @@ class FollowRequest extends ImmutablePureComponent {
 
     return (
       <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__favourite-icon-wrapper'>
               <Icon id='user' fixedWidth />
index 62fc28386b3cccbc2007f1a0ca61b00b0e0d511c..7b80c2228c97f9b56341b5f01c37c35ee3288910 100644 (file)
@@ -46,6 +46,7 @@ export default class Notification extends ImmutablePureComponent {
           onMoveDown={onMoveDown}
           onMoveUp={onMoveUp}
           onMention={onMention}
+          unread={this.props.unread}
         />
       );
     case 'follow_request':
@@ -58,6 +59,7 @@ export default class Notification extends ImmutablePureComponent {
           onMoveDown={onMoveDown}
           onMoveUp={onMoveUp}
           onMention={onMention}
+          unread={this.props.unread}
         />
       );
     case 'mention':
@@ -77,6 +79,7 @@ export default class Notification extends ImmutablePureComponent {
           cacheMediaWidth={this.props.cacheMediaWidth}
           onUnmount={this.props.onUnmount}
           withDismiss
+          unread={this.props.unread}
         />
       );
     case 'favourite':
@@ -98,6 +101,7 @@ export default class Notification extends ImmutablePureComponent {
           cacheMediaWidth={this.props.cacheMediaWidth}
           onUnmount={this.props.onUnmount}
           withDismiss
+          unread={this.props.unread}
         />
       );
     case 'reblog':
@@ -119,6 +123,7 @@ export default class Notification extends ImmutablePureComponent {
           cacheMediaWidth={this.props.cacheMediaWidth}
           onUnmount={this.props.onUnmount}
           withDismiss
+          unread={this.props.unread}
         />
       );
     case 'poll':
@@ -140,6 +145,7 @@ export default class Notification extends ImmutablePureComponent {
           cacheMediaWidth={this.props.cacheMediaWidth}
           onUnmount={this.props.onUnmount}
           withDismiss
+          unread={this.props.unread}
         />
       );
     default:
index 26710feffd1ef0dfc4f85090cfe6be1c1a3ce23e..cef0a0d2ad090933688d7879d9909c4f74b7d046 100644 (file)
@@ -24,6 +24,7 @@ import { debounce } from 'lodash';
 import ScrollableList from 'flavours/glitch/components/scrollable_list';
 import LoadGap from 'flavours/glitch/components/load_gap';
 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';
 
@@ -56,6 +57,7 @@ const mapStateToProps = state => ({
   hasMore: state.getIn(['notifications', 'hasMore']),
   numPending: state.getIn(['notifications', 'pendingItems'], ImmutableList()).size,
   notifCleaningActive: state.getIn(['notifications', 'cleaningMode']),
+  lastReadId: state.getIn(['notifications', 'lastReadId']),
 });
 
 /* glitch */
@@ -93,6 +95,7 @@ class Notifications extends React.PureComponent {
     onEnterCleaningMode: PropTypes.func,
     onMount: PropTypes.func,
     onUnmount: PropTypes.func,
+    lastReadId: PropTypes.string,
   };
 
   static defaultProps = {
@@ -195,7 +198,7 @@ class Notifications extends React.PureComponent {
   }
 
   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 { animatingNCD } = this.state;
     const pinned = !!columnId;
@@ -224,6 +227,7 @@ class Notifications extends React.PureComponent {
           accountId={item.get('account')}
           onMoveUp={this.handleMoveUp}
           onMoveDown={this.handleMoveDown}
+          unread={lastReadId && compareId(item.get('id'), lastReadId) > 0}
         />
       ));
     } else {
index f3403a2439e3eac5d259ec897f9f870f43808742..ba75e3ffec6180d9122446e288973b008539779e 100644 (file)
@@ -1054,3 +1054,22 @@ a.status-card.compact:hover {
     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;
+    }
+  }
+}