Adding compact layout (#2991)
* Adding compact layout * Fixing ESLint error
Этот коммит содержится в:
коммит произвёл
Christopher Speller
родитель
565b111234
Коммит
0258fcfa5c
@@ -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);
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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}) {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
@@ -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}
|
||||
|
||||
Ссылка в новой задаче
Block a user