Improvements to status headers
This commit is contained in:
		
							parent
							
								
									7321cea33e
								
							
						
					
					
						commit
						76fda20779
					
				
					 2 changed files with 76 additions and 184 deletions
				
			
		|  | @ -9,41 +9,30 @@ component for better documentation and maintainance by | ||||||
| 
 | 
 | ||||||
| */ | */ | ||||||
| 
 | 
 | ||||||
|                             /* * * * */ | //  * * * * * * *  //
 | ||||||
| 
 | 
 | ||||||
| /* | //  Imports
 | ||||||
|  | //  -------
 | ||||||
| 
 | 
 | ||||||
| Imports: | //  Package imports.
 | ||||||
| -------- |  | ||||||
| 
 |  | ||||||
| */ |  | ||||||
| 
 |  | ||||||
| //  Package imports  //
 |  | ||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import PropTypes from 'prop-types'; | import PropTypes from 'prop-types'; | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import { defineMessages, injectIntl } from 'react-intl'; | import { defineMessages, injectIntl } from 'react-intl'; | ||||||
| 
 | 
 | ||||||
| //  Mastodon imports  //
 | //  Mastodon imports.
 | ||||||
| import Avatar from '../../../mastodon/components/avatar'; | import Avatar from '../../../mastodon/components/avatar'; | ||||||
| import AvatarOverlay from '../../../mastodon/components/avatar_overlay'; | import AvatarOverlay from '../../../mastodon/components/avatar_overlay'; | ||||||
| import DisplayName from '../../../mastodon/components/display_name'; | import DisplayName from '../../../mastodon/components/display_name'; | ||||||
| import IconButton from '../../../mastodon/components/icon_button'; | import IconButton from '../../../mastodon/components/icon_button'; | ||||||
| import VisibilityIcon from './visibility_icon'; | import VisibilityIcon from './visibility_icon'; | ||||||
| 
 | 
 | ||||||
|                             /* * * * */ | //  * * * * * * *  //
 | ||||||
| 
 | 
 | ||||||
| /* | //  Initial setup
 | ||||||
| 
 | //  -------------
 | ||||||
| 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. |  | ||||||
| 
 |  | ||||||
| */ |  | ||||||
| 
 | 
 | ||||||
|  | //  Messages for use with internationalization stuff.
 | ||||||
| const messages = defineMessages({ | const messages = defineMessages({ | ||||||
|   collapse: { id: 'status.collapse', defaultMessage: 'Collapse' }, |   collapse: { id: 'status.collapse', defaultMessage: 'Collapse' }, | ||||||
|   uncollapse: { id: 'status.uncollapse', defaultMessage: 'Uncollapse' }, |   uncollapse: { id: 'status.uncollapse', defaultMessage: 'Uncollapse' }, | ||||||
|  | @ -53,43 +42,10 @@ const messages = defineMessages({ | ||||||
|   direct: { id: 'privacy.direct.short', defaultMessage: 'Direct' }, |   direct: { id: 'privacy.direct.short', defaultMessage: 'Direct' }, | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
|                             /* * * * */ | //  * * * * * * *  //
 | ||||||
| 
 | 
 | ||||||
| /* | //  The component
 | ||||||
| 
 | //  -------------
 | ||||||
| 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()`. |  | ||||||
| 
 |  | ||||||
| */ |  | ||||||
| 
 | 
 | ||||||
| @injectIntl | @injectIntl | ||||||
| export default class StatusHeader extends React.PureComponent { | export default class StatusHeader extends React.PureComponent { | ||||||
|  | @ -105,18 +61,7 @@ export default class StatusHeader extends React.PureComponent { | ||||||
|     intl: PropTypes.object.isRequired, |     intl: PropTypes.object.isRequired, | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
| /* |   //  Handles clicks on collapsed button
 | ||||||
| 
 |  | ||||||
| ###  Implementation |  | ||||||
| 
 |  | ||||||
| ####  `handleCollapsedClick()`. |  | ||||||
| 
 |  | ||||||
| `handleCollapsedClick()` is just a simple callback for our collapsing |  | ||||||
| button. It calls `setExpansion` to set the collapsed state of the |  | ||||||
| status. |  | ||||||
| 
 |  | ||||||
| */ |  | ||||||
| 
 |  | ||||||
|   handleCollapsedClick = (e) => { |   handleCollapsedClick = (e) => { | ||||||
|     const { collapsed, setExpansion } = this.props; |     const { collapsed, setExpansion } = this.props; | ||||||
|     if (e.button === 0) { |     if (e.button === 0) { | ||||||
|  | @ -125,29 +70,13 @@ status. | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| /* |   //  Handles clicks on account name/image
 | ||||||
| 
 |  | ||||||
| ####  `handleAccountClick()`. |  | ||||||
| 
 |  | ||||||
| `handleAccountClick()` handles any clicks on the header info. It calls |  | ||||||
| `parseClick()` with our `account` as the anticipatory `destination`. |  | ||||||
| 
 |  | ||||||
| */ |  | ||||||
| 
 |  | ||||||
|   handleAccountClick = (e) => { |   handleAccountClick = (e) => { | ||||||
|     const { status, parseClick } = this.props; |     const { status, parseClick } = this.props; | ||||||
|     parseClick(e, `/accounts/${+status.getIn(['account', 'id'])}`); |     parseClick(e, `/accounts/${+status.getIn(['account', 'id'])}`); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
| /* |   //  Rendering.
 | ||||||
| 
 |  | ||||||
| ####  `render()`. |  | ||||||
| 
 |  | ||||||
| `render()` actually puts our element on the screen. `<StatusHeader>` |  | ||||||
| has a very straightforward rendering process. |  | ||||||
| 
 |  | ||||||
| */ |  | ||||||
| 
 |  | ||||||
|   render () { |   render () { | ||||||
|     const { |     const { | ||||||
|       status, |       status, | ||||||
|  | @ -162,16 +91,28 @@ has a very straightforward rendering process. | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <header className='status__info'> |       <header className='status__info'> | ||||||
|         { |         <a | ||||||
| 
 |           href={account.get('url')} | ||||||
| /* |           target='_blank' | ||||||
| 
 |           className='status__avatar' | ||||||
| We have to include the status icons before the header content because |           onClick={this.handleAccountClick} | ||||||
| it is rendered as a float. |         > | ||||||
| 
 |             { | ||||||
| */ |               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'> |         <div className='status__info__icons'> | ||||||
|           {mediaIcon ? ( |           {mediaIcon ? ( | ||||||
|             <i |             <i | ||||||
|  | @ -197,32 +138,6 @@ it is rendered as a float. | ||||||
|             /> |             /> | ||||||
|           ) : null} |           ) : null} | ||||||
|         </div> |         </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> |       </header> | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|  | @ -550,6 +550,7 @@ | ||||||
| .status__content, | .status__content, | ||||||
| .reply-indicator__content { | .reply-indicator__content { | ||||||
|   position: relative; |   position: relative; | ||||||
|  |   padding: 5px 12px; | ||||||
|   font-size: 15px; |   font-size: 15px; | ||||||
|   line-height: 20px; |   line-height: 20px; | ||||||
|   color: $primary-text-color; |   color: $primary-text-color; | ||||||
|  | @ -660,7 +661,6 @@ | ||||||
| 
 | 
 | ||||||
| .status { | .status { | ||||||
|   padding: 8px 10px; |   padding: 8px 10px; | ||||||
|   padding-left: 68px; |  | ||||||
|   position: relative; |   position: relative; | ||||||
|   height: auto; |   height: auto; | ||||||
|   min-height: 48px; |   min-height: 48px; | ||||||
|  | @ -736,7 +736,7 @@ | ||||||
|       content: ""; |       content: ""; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     .status__display-name:hover strong { |     .display-name:hover .display-name__html { | ||||||
|       text-decoration: none; |       text-decoration: none; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -780,26 +780,21 @@ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .status__display-name { | .status__display-name { | ||||||
|  |   margin: 0 auto 0 0; | ||||||
|   color: $ui-base-lighter-color; |   color: $ui-base-lighter-color; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .status__info .status__display-name { |  | ||||||
|   display: block; |  | ||||||
|   max-width: 100%; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .status__info { | .status__info { | ||||||
|   margin: 2px 0 0; |   display: flex; | ||||||
|  |   margin: 2px 0 5px; | ||||||
|   font-size: 15px; |   font-size: 15px; | ||||||
|   line-height: 24px; |   line-height: 24px; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .status__info__icons { | .status__info__icons { | ||||||
|   display: inline-block; |   flex: none; | ||||||
|   position: relative; |   position: relative; | ||||||
|   float: right; |  | ||||||
|   color: lighten($ui-base-color, 26%); |   color: lighten($ui-base-color, 26%); | ||||||
|   z-index: 5; // to make it clickable |  | ||||||
| 
 | 
 | ||||||
|   .status__visibility-icon { |   .status__visibility-icon { | ||||||
|     padding-left: 6px; |     padding-left: 6px; | ||||||
|  | @ -842,15 +837,7 @@ | ||||||
| .status__action-bar { | .status__action-bar { | ||||||
|   align-items: center; |   align-items: center; | ||||||
|   display: flex; |   display: flex; | ||||||
|   margin-top: 10px; |   margin: 10px 12px 0; | ||||||
|   margin-left: -58px; |  | ||||||
| 
 |  | ||||||
|   &::before { |  | ||||||
|     display: block; |  | ||||||
|     flex: 1 1 0; |  | ||||||
|     max-width: 58px; |  | ||||||
|     content: ""; |  | ||||||
|   } |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .status__action-bar-button { | .status__action-bar-button { | ||||||
|  | @ -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 { | .account__display-name strong { | ||||||
|   display: block; |   display: block; | ||||||
| } | } | ||||||
|  | @ -1312,8 +1290,8 @@ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .status__avatar { | .status__avatar { | ||||||
|   position: absolute; |   flex: none; | ||||||
|   margin-left: -58px; |   margin: 0 10px 0 0; | ||||||
|   height: 48px; |   height: 48px; | ||||||
|   width: 48px; |   width: 48px; | ||||||
| } | } | ||||||
|  | @ -1383,28 +1361,37 @@ | ||||||
| 
 | 
 | ||||||
| .display-name { | .display-name { | ||||||
|   display: block; |   display: block; | ||||||
|   position: relative; |   padding: 6px 0; | ||||||
|   max-width: 100%; |  | ||||||
|   //overflow: hidden; |  | ||||||
|   //text-overflow: ellipsis; |  | ||||||
|   //white-space: nowrap; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .display-name__html { |  | ||||||
|   font-weight: 500; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| .display-name__account { |  | ||||||
|   font-size: 14px; |  | ||||||
|   display: block; |  | ||||||
|   line-height: 1.1; // reduce the distance from the display name |  | ||||||
|   padding-bottom: 3px; |  | ||||||
| 
 |  | ||||||
|   // block ellipsis |  | ||||||
|   max-width: 100%; |   max-width: 100%; | ||||||
|  |   height: 36px; | ||||||
|   overflow: hidden; |   overflow: hidden; | ||||||
|   text-overflow: ellipsis; | 
 | ||||||
|   white-space: nowrap; |   strong { | ||||||
|  |     display: block; | ||||||
|  |     height: 18px; | ||||||
|  |     font-size: 16px; | ||||||
|  |     font-weight: 500; | ||||||
|  |     line-height: 18px; | ||||||
|  |     text-overflow: ellipsis; | ||||||
|  |     overflow: hidden; | ||||||
|  |     white-space: nowrap; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   span { | ||||||
|  |     display: block; | ||||||
|  |     height: 18px; | ||||||
|  |     font-size: 15px; | ||||||
|  |     line-height: 18px; | ||||||
|  |     text-overflow: ellipsis; | ||||||
|  |     overflow: hidden; | ||||||
|  |     white-space: nowrap; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &:hover { | ||||||
|  |     strong { | ||||||
|  |       text-decoration: underline; | ||||||
|  |     } | ||||||
|  |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| .status__relative-time, | .status__relative-time, | ||||||
|  | @ -3894,17 +3881,7 @@ button.icon-button.active i.fa-retweet { | ||||||
|   flex-direction: column; |   flex-direction: column; | ||||||
| 
 | 
 | ||||||
|   .status__display-name { |   .status__display-name { | ||||||
|     display: block; |     display: flex; | ||||||
|     max-width: 100%; |  | ||||||
|     padding-right: 25px; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   .status__avatar { |  | ||||||
|     height: 28px; |  | ||||||
|     left: 10px; |  | ||||||
|     position: absolute; |  | ||||||
|     top: 10px; |  | ||||||
|     width: 48px; |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue