about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Yip <yipdw@member.fsf.org>2017-09-21 16:11:03 -0500
committerGitHub <noreply@github.com>2017-09-21 16:11:03 -0500
commit9512db920c6056a6cca746491bfb0c298ab44420 (patch)
tree977feb44d1f287f3223ebbb8110e1dac75dd2a97
parentc89cce0219646502b4d338213d112a528373bdc4 (diff)
parent9ed51cecd0f41eeca0e303c0b0787d1928034156 (diff)
Merge pull request #148 from glitch-soc/better-header
Improvements to status headers and content
-rw-r--r--app/javascript/glitch/components/notification/follow.js75
-rw-r--r--app/javascript/glitch/components/status/header.js159
-rw-r--r--app/javascript/mastodon/components/icon_button.js17
-rw-r--r--app/javascript/styles/components.scss115
4 files changed, 112 insertions, 254 deletions
diff --git a/app/javascript/glitch/components/notification/follow.js b/app/javascript/glitch/components/notification/follow.js
index d340e83c8..f471307b9 100644
--- a/app/javascript/glitch/components/notification/follow.js
+++ b/app/javascript/glitch/components/notification/follow.js
@@ -1,38 +1,12 @@
-/*
+//  `<NotificationFollow>`
+//  ======================
 
-`<NotificationFollow>`
-======================
+//  * * * * * * *  //
 
-This component renders a follow notification.
+//  Imports
+//  -------
 
-__Props:__
-
- -  __`id` (`PropTypes.number.isRequired`) :__
-    This is the id of the notification.
-
- -  __`onDeleteNotification` (`PropTypes.func.isRequired`) :__
-    The function to call when a notification should be
-    dismissed/deleted.
-
- -  __`account` (`PropTypes.object.isRequired`) :__
-    The account associated with the follow notification, ie the account
-    which followed the user.
-
- -  __`intl` (`PropTypes.object.isRequired`) :__
-    Our internationalization object, inserted by `@injectIntl`.
-
-*/
-
-//  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
-/*
-
-Imports:
---------
-
-*/
-
-//  Package imports  //
+//  Package imports.
 import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
@@ -40,22 +14,18 @@ import { FormattedMessage } from 'react-intl';
 import escapeTextContentForBrowser from 'escape-html';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 
-//  Mastodon imports  //
+//  Mastodon imports.
 import emojify from '../../../mastodon/emoji';
 import Permalink from '../../../mastodon/components/permalink';
 import AccountContainer from '../../../mastodon/containers/account_container';
 
-// Our imports //
+// Our imports.
 import NotificationOverlayContainer from '../notification/overlay/container';
 
-//  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-
-/*
+//  * * * * * * *  //
 
-Implementation:
----------------
-
-*/
+//  Implementation
+//  --------------
 
 export default class NotificationFollow extends ImmutablePureComponent {
 
@@ -65,24 +35,10 @@ export default class NotificationFollow extends ImmutablePureComponent {
     notification         : ImmutablePropTypes.map.isRequired,
   };
 
-/*
-
-###  `render()`
-
-This actually renders the component.
-
-*/
-
   render () {
     const { account, notification } = this.props;
 
-/*
-
-`link` is a container for the account's `displayName`, which links to
-the account timeline using a `<Permalink>`.
-
-*/
-
+    //  Links to the display name.
     const displayName = account.get('display_name') || account.get('username');
     const displayNameHTML = { __html: emojify(escapeTextContentForBrowser(displayName)) };
     const link = (
@@ -95,12 +51,7 @@ the account timeline using a `<Permalink>`.
       />
     );
 
-/*
-
-We can now render our component.
-
-*/
-
+    //  Renders.
     return (
       <div className='notification notification-follow'>
         <div className='notification__message'>
diff --git a/app/javascript/glitch/components/status/header.js b/app/javascript/glitch/components/status/header.js
index bdb868e4d..f741950b1 100644
--- a/app/javascript/glitch/components/status/header.js
+++ b/app/javascript/glitch/components/status/header.js
@@ -9,41 +9,30 @@ component for better documentation and maintainance by
 
 */
 
-                            /* * * * */
+//  * * * * * * *  //
 
-/*
-
-Imports:
---------
-
-*/
+//  Imports
+//  -------
 
-//  Package imports  //
+//  Package imports.
 import React from 'react';
 import PropTypes from 'prop-types';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import { defineMessages, injectIntl } from 'react-intl';
 
-//  Mastodon imports  //
+//  Mastodon imports.
 import Avatar from '../../../mastodon/components/avatar';
 import AvatarOverlay from '../../../mastodon/components/avatar_overlay';
 import DisplayName from '../../../mastodon/components/display_name';
 import IconButton from '../../../mastodon/components/icon_button';
 import VisibilityIcon from './visibility_icon';
 
-                            /* * * * */
-
-/*
+//  * * * * * * *  //
 
-Inital setup:
--------------
-
-The `messages` constant is used to define any messages that we need
-from inside props. In our case, these are the `collapse` and
-`uncollapse` messages used with our collapse/uncollapse buttons.
-
-*/
+//  Initial setup
+//  -------------
 
+//  Messages for use with internationalization stuff.
 const messages = defineMessages({
   collapse: { id: 'status.collapse', defaultMessage: 'Collapse' },
   uncollapse: { id: 'status.uncollapse', defaultMessage: 'Uncollapse' },
@@ -53,43 +42,10 @@ const messages = defineMessages({
   direct: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
 });
 
-                            /* * * * */
-
-/*
+//  * * * * * * *  //
 
-The `<StatusHeader>` component:
--------------------------------
-
-The `<StatusHeader>` component wraps together the header information
-(avatar, display name) and upper buttons and icons (collapsing, media
-icons) into a single `<header>` element.
-
-###  Props
-
- -  __`account`, `friend` (`ImmutablePropTypes.map`) :__
-    These give the accounts associated with the status. `account` is
-    the author of the post; `friend` will have their avatar appear
-    in the overlay if provided.
-
- -  __`mediaIcon` (`PropTypes.string`) :__
-    If a mediaIcon should be placed in the header, this string
-    specifies it.
-
- -  __`collapsible`, `collapsed` (`PropTypes.bool`) :__
-    These props tell whether a post can be, and is, collapsed.
-
- -  __`parseClick` (`PropTypes.func`) :__
-    This function will be called when the user clicks inside the header
-    information.
-
- -  __`setExpansion` (`PropTypes.func`) :__
-    This function is used to set the expansion state of the post.
-
- -  __`intl` (`PropTypes.object`) :__
-    This is our internationalization object, provided by
-    `injectIntl()`.
-
-*/
+//  The component
+//  -------------
 
 @injectIntl
 export default class StatusHeader extends React.PureComponent {
@@ -105,18 +61,7 @@ export default class StatusHeader extends React.PureComponent {
     intl: PropTypes.object.isRequired,
   };
 
-/*
-
-###  Implementation
-
-####  `handleCollapsedClick()`.
-
-`handleCollapsedClick()` is just a simple callback for our collapsing
-button. It calls `setExpansion` to set the collapsed state of the
-status.
-
-*/
-
+  //  Handles clicks on collapsed button
   handleCollapsedClick = (e) => {
     const { collapsed, setExpansion } = this.props;
     if (e.button === 0) {
@@ -125,29 +70,13 @@ status.
     }
   }
 
-/*
-
-####  `handleAccountClick()`.
-
-`handleAccountClick()` handles any clicks on the header info. It calls
-`parseClick()` with our `account` as the anticipatory `destination`.
-
-*/
-
+  //  Handles clicks on account name/image
   handleAccountClick = (e) => {
     const { status, parseClick } = this.props;
     parseClick(e, `/accounts/${+status.getIn(['account', 'id'])}`);
   }
 
-/*
-
-####  `render()`.
-
-`render()` actually puts our element on the screen. `<StatusHeader>`
-has a very straightforward rendering process.
-
-*/
-
+  //  Rendering.
   render () {
     const {
       status,
@@ -162,16 +91,28 @@ has a very straightforward rendering process.
 
     return (
       <header className='status__info'>
-        {
-
-/*
-
-We have to include the status icons before the header content because
-it is rendered as a float.
-
-*/
-
-        }
+        <a
+          href={account.get('url')}
+          target='_blank'
+          className='status__avatar'
+          onClick={this.handleAccountClick}
+        >
+          {
+            friend ? (
+              <AvatarOverlay account={account} friend={friend} />
+            ) : (
+              <Avatar account={account} size={48} />
+            )
+          }
+        </a>
+        <a
+          href={account.get('url')}
+          target='_blank'
+          className='status__display-name'
+          onClick={this.handleAccountClick}
+        >
+          <DisplayName account={account} />
+        </a>
         <div className='status__info__icons'>
           {mediaIcon ? (
             <i
@@ -197,32 +138,6 @@ it is rendered as a float.
             />
           ) : null}
         </div>
-        {
-
-/*
-
-This begins our header content. It is all wrapped inside of a link
-which gets handled by `handleAccountClick`. We use an `<AvatarOverlay>`
-if we have a `friend` and a normal `<Avatar>` if we don't.
-
-*/
-
-        }
-        <a
-          href={account.get('url')}
-          target='_blank'
-          className='status__display-name'
-          onClick={this.handleAccountClick}
-        >
-          <div className='status__avatar'>{
-            friend ? (
-              <AvatarOverlay account={account} friend={friend} />
-            ) : (
-              <Avatar account={account} size={48} />
-            )
-          }</div>
-          <DisplayName account={account} />
-        </a>
 
       </header>
     );
diff --git a/app/javascript/mastodon/components/icon_button.js b/app/javascript/mastodon/components/icon_button.js
index 8c5b5e0b9..ca4b14b82 100644
--- a/app/javascript/mastodon/components/icon_button.js
+++ b/app/javascript/mastodon/components/icon_button.js
@@ -73,8 +73,23 @@ export default class IconButton extends React.PureComponent {
       classes.push(this.props.className);
     }
 
+    const flipDeg = this.props.flip ? -180 : -360;
+    const rotateDeg = this.props.active ? flipDeg : 0;
+
+    const motionDefaultStyle = {
+      rotate: rotateDeg,
+    };
+
+    const springOpts = {
+      stiffness: this.props.flip ? 60 : 120,
+      damping: 7,
+    };
+    const motionStyle = {
+      rotate: this.props.animate ? spring(rotateDeg, springOpts) : 0,
+    };
+
     return (
-      <Motion defaultStyle={{ rotate: this.props.active ? (this.props.flip ? -180 : -360) : 0 }} style={{ rotate: this.props.animate ? spring(this.props.active ? (this.props.flip ? -180 : -360) : 0, { stiffness: this.props.flip ? 60 : 120, damping: 7 }) : 0 }}>
+      <Motion defaultStyle={motionDefaultStyle} style={motionStyle}>
         {({ rotate }) =>
           <button
             aria-label={this.props.title}
diff --git a/app/javascript/styles/components.scss b/app/javascript/styles/components.scss
index 03f4f0800..fb1922113 100644
--- a/app/javascript/styles/components.scss
+++ b/app/javascript/styles/components.scss
@@ -550,6 +550,7 @@
 .status__content,
 .reply-indicator__content {
   position: relative;
+  padding: 5px 12px;
   font-size: 15px;
   line-height: 20px;
   color: $primary-text-color;
@@ -660,7 +661,6 @@
 
 .status {
   padding: 8px 10px;
-  padding-left: 68px;
   position: relative;
   height: auto;
   min-height: 48px;
@@ -736,7 +736,7 @@
       content: "";
     }
 
-    .status__display-name:hover strong {
+    .display-name:hover .display-name__html {
       text-decoration: none;
     }
 
@@ -752,7 +752,7 @@
   }
 
   .notification__message {
-    margin: -10px 0 10px;
+    margin: -10px -10px 10px;
   }
 }
 
@@ -780,26 +780,21 @@
 }
 
 .status__display-name {
+  margin: 0 auto 0 0;
   color: $ui-base-lighter-color;
 }
 
-.status__info .status__display-name {
-  display: block;
-  max-width: 100%;
-}
-
 .status__info {
-  margin: 2px 0 0;
+  display: flex;
+  margin: 2px 0 5px;
   font-size: 15px;
   line-height: 24px;
 }
 
 .status__info__icons {
-  display: inline-block;
+  flex: none;
   position: relative;
-  float: right;
   color: lighten($ui-base-color, 26%);
-  z-index: 5; // to make it clickable
 
   .status__visibility-icon {
     padding-left: 6px;
@@ -842,15 +837,7 @@
 .status__action-bar {
   align-items: center;
   display: flex;
-  margin-top: 10px;
-  margin-left: -58px;
-
-  &::before {
-    display: block;
-    flex: 1 1 0;
-    max-width: 58px;
-    content: "";
-  }
+  margin: 10px 12px 0;
 }
 
 .status__action-bar-button {
@@ -983,8 +970,7 @@
 
 .account__avatar-wrapper {
   float: left;
-  margin-left: 12px;
-  margin-right: 12px;
+  margin: 6px 16px 6px 6px;
 }
 
 .account__avatar {
@@ -1000,6 +986,7 @@
 }
 
 .account__avatar-overlay {
+  position: relative;
   @include avatar-size(48px);
 
   &-base {
@@ -1020,7 +1007,7 @@
 
 .account__relationship {
   height: 18px;
-  padding: 10px;
+  padding: 12px 10px;
   white-space: nowrap;
 }
 
@@ -1268,15 +1255,6 @@
   }
 }
 
-.status__display-name,
-.reply-indicator__display-name,
-.detailed-status__display-name,
-.account__display-name {
-  &:hover strong {
-    text-decoration: underline;
-  }
-}
-
 .account__display-name strong {
   display: block;
 }
@@ -1312,8 +1290,8 @@
 }
 
 .status__avatar {
-  position: absolute;
-  margin-left: -58px;
+  flex: none;
+  margin: 0 10px 0 0;
   height: 48px;
   width: 48px;
 }
@@ -1344,9 +1322,7 @@
 }
 
 .notification__message {
-  margin-left: 68px;
-  padding: 8px 0;
-  padding-bottom: 0;
+  padding: 8px 10px 0;
   cursor: default;
   color: $ui-primary-color;
   font-size: 15px;
@@ -1358,8 +1334,10 @@
 }
 
 .notification__favourite-icon-wrapper {
-  left: -26px;
-  position: absolute;
+  float: left;
+  margin: 0 10px 0 0;
+  width: 48px;
+  text-align: right;
 
   .star-icon {
     color: $gold-star;
@@ -1383,28 +1361,37 @@
 
 .display-name {
   display: block;
-  position: relative;
+  padding: 6px 0;
   max-width: 100%;
-  //overflow: hidden;
-  //text-overflow: ellipsis;
-  //white-space: nowrap;
-}
+  height: 36px;
+  overflow: hidden;
 
-.display-name__html {
-  font-weight: 500;
-}
+  strong {
+    display: block;
+    height: 18px;
+    font-size: 16px;
+    font-weight: 500;
+    line-height: 18px;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+  }
 
-.display-name__account {
-  font-size: 14px;
-  display: block;
-  line-height: 1.1; // reduce the distance from the display name
-  padding-bottom: 3px;
+  span {
+    display: block;
+    height: 18px;
+    font-size: 15px;
+    line-height: 18px;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+  }
 
-  // block ellipsis
-  max-width: 100%;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
+  &:hover {
+    strong {
+      text-decoration: underline;
+    }
+  }
 }
 
 .status__relative-time,
@@ -3896,17 +3883,7 @@ button.icon-button.active i.fa-retweet {
   flex-direction: column;
 
   .status__display-name {
-    display: block;
-    max-width: 100%;
-    padding-right: 25px;
-  }
-
-  .status__avatar {
-    height: 28px;
-    left: 10px;
-    position: absolute;
-    top: 10px;
-    width: 48px;
+    display: flex;
   }
 }