diff options
author | Asaad Mahmood <asaadmahmoodspin@users.noreply.github.com> | 2016-05-16 17:06:26 +0500 |
---|---|---|
committer | Christopher Speller <crspeller@gmail.com> | 2016-05-16 08:06:26 -0400 |
commit | 0258fcfa5c8da92351371169b66ce30462a34853 (patch) | |
tree | d69803cff43a633202b877738e514867f4424e0c /webapp/components | |
parent | 565b111234f5566da632533109e1b4f4044e7116 (diff) | |
download | chat-0258fcfa5c8da92351371169b66ce30462a34853.tar.gz chat-0258fcfa5c8da92351371169b66ce30462a34853.tar.bz2 chat-0258fcfa5c8da92351371169b66ce30462a34853.zip |
Adding compact layout (#2991)
* Adding compact layout
* Fixing ESLint error
Diffstat (limited to 'webapp/components')
-rw-r--r-- | webapp/components/file_attachment.jsx | 53 | ||||
-rw-r--r-- | webapp/components/file_attachment_list.jsx | 5 | ||||
-rw-r--r-- | webapp/components/post.jsx | 18 | ||||
-rw-r--r-- | webapp/components/post_body.jsx | 9 | ||||
-rw-r--r-- | webapp/components/post_header.jsx | 5 | ||||
-rw-r--r-- | webapp/components/post_info.jsx | 4 | ||||
-rw-r--r-- | webapp/components/posts_view.jsx | 11 | ||||
-rw-r--r-- | webapp/components/time_since.jsx | 5 | ||||
-rw-r--r-- | webapp/components/user_settings/user_settings_display.jsx | 118 |
9 files changed, 202 insertions, 26 deletions
diff --git a/webapp/components/file_attachment.jsx b/webapp/components/file_attachment.jsx index c909b9afb..bbd42fc69 100644 --- a/webapp/components/file_attachment.jsx +++ b/webapp/components/file_attachment.jsx @@ -8,6 +8,7 @@ import Client from 'utils/web_client.jsx'; import Constants from 'utils/constants.jsx'; import {intlShape, injectIntl, defineMessages} from 'react-intl'; +import {Tooltip, OverlayTrigger} from 'react-bootstrap'; const holders = defineMessages({ download: { @@ -169,6 +170,42 @@ class FileAttachment extends React.Component { } else { trimmedFilename = filenameString; } + var filenameOverlay = ( + <OverlayTrigger + delayShow={1000} + placement='top' + overlay={<Tooltip id='file-name__tooltip'>{this.props.intl.formatMessage(holders.download) + ' "' + filenameString + '"'}</Tooltip>} + > + <a + href={fileUrl} + download={filenameString} + className='post-image__name' + target='_blank' + rel='noopener noreferrer' + > + {trimmedFilename} + </a> + </OverlayTrigger> + ); + + if (this.props.compactDisplay) { + filenameOverlay = ( + <OverlayTrigger + delayShow={1000} + placement='top' + overlay={<Tooltip id='file-name__tooltip'>{filenameString}</Tooltip>} + > + <a + href='#' + onClick={() => this.props.handleImageClick(this.props.index)} + className='post-image__name' + rel='noopener noreferrer' + > + <i className='glyphicon glyphicon-paperclip'/>{trimmedFilename} + </a> + </OverlayTrigger> + ); + } return ( <div @@ -183,17 +220,7 @@ class FileAttachment extends React.Component { {thumbnail} </a> <div className='post-image__details'> - <a - href={fileUrl} - download={filenameString} - data-toggle='tooltip' - title={this.props.intl.formatMessage(holders.download) + ' "' + filenameString + '"'} - className='post-image__name' - target='_blank' - rel='noopener noreferrer' - > - {trimmedFilename} - </a> + {filenameOverlay} <div> <a href={fileUrl} @@ -225,7 +252,9 @@ FileAttachment.propTypes = { index: React.PropTypes.number.isRequired, // handler for when the thumbnail is clicked passed the index above - handleImageClick: React.PropTypes.func + handleImageClick: React.PropTypes.func, + + compactDisplay: React.PropTypes.bool }; export default injectIntl(FileAttachment); diff --git a/webapp/components/file_attachment_list.jsx b/webapp/components/file_attachment_list.jsx index 59fd56bc3..e4b841769 100644 --- a/webapp/components/file_attachment_list.jsx +++ b/webapp/components/file_attachment_list.jsx @@ -29,6 +29,7 @@ export default class FileAttachmentList extends React.Component { filename={filenames[i]} index={i} handleImageClick={this.handleImageClick} + compactDisplay={this.props.compactDisplay} /> ); } @@ -60,5 +61,7 @@ FileAttachmentList.propTypes = { channelId: React.PropTypes.string, // the user that owns the post that this is attached to - userId: React.PropTypes.string + userId: React.PropTypes.string, + + compactDisplay: React.PropTypes.bool }; diff --git a/webapp/components/post.jsx b/webapp/components/post.jsx index ae3fa9c98..de32568d1 100644 --- a/webapp/components/post.jsx +++ b/webapp/components/post.jsx @@ -103,6 +103,10 @@ export default class Post extends React.Component { return true; } + if (nextProps.compactDisplay !== this.props.compactDisplay) { + return true; + } + if (!Utils.areObjectsEqual(nextProps.user, this.props.user)) { return true; } @@ -188,7 +192,7 @@ export default class Post extends React.Component { } let profilePic = null; - if (!this.props.hideProfilePic) { + if (!this.props.hideProfilePic || this.props.compactDisplay) { profilePic = ( <img src={Utils.getProfilePicSrcForPost(post, timestamp)} @@ -212,11 +216,16 @@ export default class Post extends React.Component { centerClass = 'center'; } + let compactClass = ''; + if (this.props.compactDisplay) { + compactClass = 'post--compact'; + } + return ( <div> <div id={'post_' + post.id} - className={'post ' + sameUserClass + ' ' + rootUser + ' ' + postType + ' ' + currentUserCss + ' ' + shouldHighlightClass + ' ' + systemMessageClass} + className={'post ' + sameUserClass + ' ' + compactClass + ' ' + rootUser + ' ' + postType + ' ' + currentUserCss + ' ' + shouldHighlightClass + ' ' + systemMessageClass} > <div className={'post__content ' + centerClass}> <div className='post__img'>{profilePic}</div> @@ -231,6 +240,7 @@ export default class Post extends React.Component { sameUser={this.props.sameUser} user={this.props.user} currentUser={this.props.currentUser} + compactDisplay={this.props.compactDisplay} /> <PostBody post={post} @@ -239,6 +249,7 @@ export default class Post extends React.Component { posts={posts} handleCommentClick={this.handleCommentClick} retryPost={this.retryPost} + compactDisplay={this.props.compactDisplay} /> </div> </div> @@ -261,5 +272,6 @@ Post.propTypes = { displayNameType: React.PropTypes.string, hasProfiles: React.PropTypes.bool, currentUser: React.PropTypes.object.isRequired, - center: React.PropTypes.bool + center: React.PropTypes.bool, + compactDisplay: React.PropTypes.bool }; diff --git a/webapp/components/post_body.jsx b/webapp/components/post_body.jsx index 6c4e97d8e..ed0a133b3 100644 --- a/webapp/components/post_body.jsx +++ b/webapp/components/post_body.jsx @@ -24,6 +24,10 @@ export default class PostBody extends React.Component { return true; } + if (!Utils.areObjectsEqual(nextProps.compactDisplay, this.props.compactDisplay)) { + return true; + } + if (nextProps.retryPost.toString() !== this.props.retryPost.toString()) { return true; } @@ -136,9 +140,11 @@ export default class PostBody extends React.Component { if (filenames && filenames.length > 0) { fileAttachmentHolder = ( <FileAttachmentList + filenames={filenames} channelId={post.channel_id} userId={post.user_id} + compactDisplay={this.props.compactDisplay} /> ); } @@ -189,5 +195,6 @@ PostBody.propTypes = { post: React.PropTypes.object.isRequired, parentPost: React.PropTypes.object, retryPost: React.PropTypes.func.isRequired, - handleCommentClick: React.PropTypes.func.isRequired + handleCommentClick: React.PropTypes.func.isRequired, + compactDisplay: React.PropTypes.bool }; diff --git a/webapp/components/post_header.jsx b/webapp/components/post_header.jsx index 9161d37f9..2b139471d 100644 --- a/webapp/components/post_header.jsx +++ b/webapp/components/post_header.jsx @@ -14,6 +14,7 @@ export default class PostHeader extends React.Component { super(props); this.state = {}; } + render() { const post = this.props.post; @@ -56,6 +57,7 @@ export default class PostHeader extends React.Component { isLastComment={this.props.isLastComment} sameUser={this.props.sameUser} currentUser={this.props.currentUser} + compactDisplay={this.props.compactDisplay} /> </li> </ul> @@ -76,5 +78,6 @@ PostHeader.propTypes = { commentCount: React.PropTypes.number.isRequired, isLastComment: React.PropTypes.bool.isRequired, handleCommentClick: React.PropTypes.func.isRequired, - sameUser: React.PropTypes.bool.isRequired + sameUser: React.PropTypes.bool.isRequired, + compactDisplay: React.PropTypes.bool }; diff --git a/webapp/components/post_info.jsx b/webapp/components/post_info.jsx index 50b03c0be..f86c63fd7 100644 --- a/webapp/components/post_info.jsx +++ b/webapp/components/post_info.jsx @@ -219,6 +219,7 @@ export default class PostInfo extends React.Component { <TimeSince eventTime={post.create_at} sameUser={this.props.sameUser} + compactDisplay={this.props.compactDisplay} /> </li> <li className='col col__reply'> @@ -250,5 +251,6 @@ PostInfo.propTypes = { allowReply: React.PropTypes.string.isRequired, handleCommentClick: React.PropTypes.func.isRequired, sameUser: React.PropTypes.bool.isRequired, - currentUser: React.PropTypes.object.isRequired + currentUser: React.PropTypes.object.isRequired, + compactDisplay: React.PropTypes.bool }; diff --git a/webapp/components/posts_view.jsx b/webapp/components/posts_view.jsx index cc9e738bc..74c249356 100644 --- a/webapp/components/posts_view.jsx +++ b/webapp/components/posts_view.jsx @@ -55,6 +55,7 @@ export default class PostsView extends React.Component { this.state = { displayNameType: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false'), centerPosts: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.CHANNEL_DISPLAY_MODE, Preferences.CHANNEL_DISPLAY_MODE_DEFAULT) === Preferences.CHANNEL_DISPLAY_MODE_CENTERED, + compactPosts: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT, isScrolling: false, topPostId: null, currentUser: UserStore.getCurrentUser(), @@ -79,7 +80,8 @@ export default class PostsView extends React.Component { updateState() { this.setState({ displayNameType: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'false'), - centerPosts: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.CHANNEL_DISPLAY_MODE, Preferences.CHANNEL_DISPLAY_MODE_DEFAULT) === Preferences.CHANNEL_DISPLAY_MODE_CENTERED + centerPosts: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.CHANNEL_DISPLAY_MODE, Preferences.CHANNEL_DISPLAY_MODE_DEFAULT) === Preferences.CHANNEL_DISPLAY_MODE_CENTERED, + compactPosts: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) === Preferences.MESSAGE_DISPLAY_COMPACT }); } onUserChange() { @@ -274,6 +276,7 @@ export default class PostsView extends React.Component { user={profile} currentUser={this.state.currentUser} center={this.state.centerPosts} + compactDisplay={this.state.compactPosts} /> ); @@ -479,6 +482,9 @@ export default class PostsView extends React.Component { if (this.state.centerPosts !== nextState.centerPosts) { return true; } + if (this.state.compactPosts !== nextState.compactPosts) { + return true; + } if (!Utils.areObjectsEqual(this.state.profiles, nextState.profiles)) { return true; } @@ -592,7 +598,8 @@ PostsView.propTypes = { showMoreMessagesBottom: React.PropTypes.bool, channel: React.PropTypes.object, messageSeparatorTime: React.PropTypes.number, - postsToHighlight: React.PropTypes.object + postsToHighlight: React.PropTypes.object, + compactDisplay: React.PropTypes.bool }; function ScrollToBottomArrows({isScrolling, atBottom, onClick}) { diff --git a/webapp/components/time_since.jsx b/webapp/components/time_since.jsx index f715193e2..50a0f7d04 100644 --- a/webapp/components/time_since.jsx +++ b/webapp/components/time_since.jsx @@ -26,7 +26,7 @@ export default class TimeSince extends React.Component { clearInterval(this.intervalId); } render() { - if (this.props.sameUser) { + if (this.props.sameUser || this.props.compactDisplay) { return ( <time className='post__time'> {Utils.displayTimeFormatted(this.props.eventTime)} @@ -69,5 +69,6 @@ TimeSince.defaultProps = { TimeSince.propTypes = { eventTime: React.PropTypes.number.isRequired, - sameUser: React.PropTypes.bool + sameUser: React.PropTypes.bool, + compactDisplay: React.PropTypes.bool }; diff --git a/webapp/components/user_settings/user_settings_display.jsx b/webapp/components/user_settings/user_settings_display.jsx index c4af57d4c..fa0118d1e 100644 --- a/webapp/components/user_settings/user_settings_display.jsx +++ b/webapp/components/user_settings/user_settings_display.jsx @@ -23,7 +23,8 @@ function getDisplayStateFromStores() { militaryTime: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'use_military_time', 'false'), nameFormat: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'name_format', 'username'), selectedFont: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, 'selected_font', Constants.DEFAULT_FONT), - channelDisplayMode: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.CHANNEL_DISPLAY_MODE, Preferences.CHANNEL_DISPLAY_MODE_DEFAULT) + channelDisplayMode: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.CHANNEL_DISPLAY_MODE, Preferences.CHANNEL_DISPLAY_MODE_DEFAULT), + messageDisplay: PreferenceStore.get(Preferences.CATEGORY_DISPLAY_SETTINGS, Preferences.MESSAGE_DISPLAY, Preferences.MESSAGE_DISPLAY_DEFAULT) }; } @@ -70,8 +71,14 @@ export default class UserSettingsDisplay extends React.Component { name: Preferences.CHANNEL_DISPLAY_MODE, value: this.state.channelDisplayMode }; + const messageDisplayPreference = { + user_id: userId, + category: Preferences.CATEGORY_DISPLAY_SETTINGS, + name: Preferences.MESSAGE_DISPLAY, + value: this.state.messageDisplay + }; - AsyncClient.savePreferences([timePreference, namePreference, fontPreference, channelDisplayModePreference], + AsyncClient.savePreferences([timePreference, namePreference, fontPreference, channelDisplayModePreference, messageDisplayPreference], () => { this.updateSection(''); }, @@ -89,6 +96,9 @@ export default class UserSettingsDisplay extends React.Component { handleChannelDisplayModeRadio(channelDisplayMode) { this.setState({channelDisplayMode}); } + handlemessageDisplayRadio(messageDisplay) { + this.setState({messageDisplay}); + } handleFont(selectedFont) { Utils.applyFont(selectedFont); this.setState({selectedFont}); @@ -115,6 +125,7 @@ export default class UserSettingsDisplay extends React.Component { let channelDisplayModeSection; let fontSection; let languagesSection; + let messageDisplaySection; if (this.props.activeSection === 'clock') { const clockFormat = [false, false]; @@ -350,6 +361,105 @@ export default class UserSettingsDisplay extends React.Component { ); } + if (this.props.activeSection === Preferences.MESSAGE_DISPLAY) { + const messageDisplay = [false, false]; + if (this.state.messageDisplay === Preferences.MESSAGE_DISPLAY_CLEAN) { + messageDisplay[0] = true; + } else { + messageDisplay[1] = true; + } + + const inputs = [ + <div key='userDisplayNameOptions'> + <div className='radio'> + <label> + <input + type='radio' + checked={messageDisplay[0]} + onChange={this.handlemessageDisplayRadio.bind(this, Preferences.MESSAGE_DISPLAY_CLEAN)} + /> + <FormattedMessage + id='user.settings.display.messageDisplayClean' + defaultMessage='Clean' + /> + </label> + <br/> + </div> + <div className='radio'> + <label> + <input + type='radio' + checked={messageDisplay[1]} + onChange={this.handlemessageDisplayRadio.bind(this, Preferences.MESSAGE_DISPLAY_COMPACT)} + /> + <FormattedMessage + id='user.settings.display.messageDisplayCompact' + defaultMessage='Compact' + /> + </label> + <br/> + </div> + <div> + <br/> + <FormattedMessage + id='user.settings.display.messageDisplayDescription' + defaultMessage='Select how messages in a channel should be displayed.' + /> + </div> + </div> + ]; + + messageDisplaySection = ( + <SettingItemMax + title={ + <FormattedMessage + id='user.settings.display.messageDisplayTitle' + defaultMessage='Message Display' + /> + } + inputs={inputs} + submit={this.handleSubmit} + server_error={serverError} + updateSection={(e) => { + this.updateSection(''); + e.preventDefault(); + }} + /> + ); + } else { + let describe; + if (this.state.messageDisplay === Preferences.MESSAGE_DISPLAY_CLEAN) { + describe = ( + <FormattedMessage + id='user.settings.display.messageDisplayClean' + defaultMessage='Clean' + /> + ); + } else { + describe = ( + <FormattedMessage + id='user.settings.display.messageDisplayCompact' + defaultMessage='Compact' + /> + ); + } + + messageDisplaySection = ( + <SettingItemMin + title={ + <FormattedMessage + id='user.settings.display.messageDisplayTitle' + defaultMessage='Message Display' + /> + } + describe={describe} + updateSection={() => { + this.props.updateSection(Preferences.MESSAGE_DISPLAY); + }} + /> + ); + } + if (this.props.activeSection === Preferences.CHANNEL_DISPLAY_MODE) { const channelDisplayMode = [false, false]; if (this.state.channelDisplayMode === Preferences.CHANNEL_DISPLAY_MODE_CENTERED) { @@ -392,7 +502,7 @@ export default class UserSettingsDisplay extends React.Component { <br/> <FormattedMessage id='user.settings.display.channeldisplaymode' - defaultMessage='Select how text in a channel is displayed.' + defaultMessage='Select the width of the center channel.' /> </div> </div> @@ -601,6 +711,8 @@ export default class UserSettingsDisplay extends React.Component { <div className='divider-dark'/> {nameFormatSection} <div className='divider-dark'/> + {messageDisplaySection} + <div className='divider-dark'/> {channelDisplayModeSection} <div className='divider-dark'/> {languagesSection} |