mattermost-mobile/app/components/post_header/post_header.js
Stan Chan 583d6b74ee Timezone feature (#1456)
* Add automatic and manual timezone support

Update users timezone and scrollTo selected timezone in SelectTimezone coponent

Clean styles for SelectTimezone screen

Add auto-timezone update when login or enter app

Hide timezone feature behind config

Fix requested changes

Parse SupportedTimezone from config

Add localTime and localized post stamps

Add trailing commas

Include all timezone util methods from redux

Remove EnableTimezoneSelection flag

WIP get timezones from server

Pull supportedTimezones from api

Minor fixes

Remove wrapWithPreventDoubleTap

Revert back to react-intl formatDate

Include timeZone prop in FormattedTime

Refactor Timezone row into component

Minor cosmetic changes

Add minimum server support for the timezone feature

Move getSupportedTimezones to componentWillMount

Move autoUpdateTimezone function to handleSuccessfulLogin

Specify user timezone in profile_header

Remove format props from FormattedTime

Add ExperimentalTimezone flag

Replace Client().getServerVersion() with entities.general.serverVersion

Add isTimezoneEnabled helper function

Move isMinimumServerVersion to utils/timezone.js

* Fix style errors

* Remove date-time-format-timezone polyfill

* Feedback changes

* Use timezone selector from redux

* Explicitly pass hour12 props to intl.formatDate

* Update package-lock

* Revert iOS project file changes

* Fix license header

* Include timezone related paths in modulePaths

* Fix license header

* Fix minor issue with rebasing

* Fix issue with getconfig in GeneralSettings

* Update package-lock
2018-06-27 06:55:03 -04:00

337 lines
10 KiB
JavaScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
Platform,
Text,
TouchableOpacity,
View,
} from 'react-native';
import FormattedText from 'app/components/formatted_text';
import FormattedTime from 'app/components/formatted_time';
import FormattedDate from 'app/components/formatted_date';
import ReplyIcon from 'app/components/reply_icon';
import FlagIcon from 'app/components/flag_icon';
import {emptyFunction} from 'app/utils/general';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
export default class PostHeader extends PureComponent {
static propTypes = {
commentCount: PropTypes.number,
commentedOnDisplayName: PropTypes.string,
createAt: PropTypes.number.isRequired,
displayName: PropTypes.string,
enablePostUsernameOverride: PropTypes.bool,
fromWebHook: PropTypes.bool,
isPendingOrFailedPost: PropTypes.bool,
isSearchResult: PropTypes.bool,
isSystemMessage: PropTypes.bool,
fromAutoResponder: PropTypes.bool,
militaryTime: PropTypes.bool,
onPress: PropTypes.func,
onUsernamePress: PropTypes.func,
overrideUsername: PropTypes.string,
renderReplies: PropTypes.bool,
shouldRenderReplyButton: PropTypes.bool,
showFullDate: PropTypes.bool,
theme: PropTypes.object.isRequired,
username: PropTypes.string,
userTimezone: PropTypes.string,
isFlagged: PropTypes.bool,
enableTimezone: PropTypes.bool,
};
static defaultProps = {
commentCount: 0,
onPress: emptyFunction,
onUsernamePress: emptyFunction,
};
handleUsernamePress = () => {
if (this.props.username) {
this.props.onUsernamePress(this.props.username);
}
}
getDisplayName = (style) => {
const {
enablePostUsernameOverride,
fromWebHook,
isSystemMessage,
fromAutoResponder,
overrideUsername,
} = this.props;
if (fromWebHook) {
let name = this.props.displayName;
if (overrideUsername && enablePostUsernameOverride) {
name = overrideUsername;
}
return (
<View style={style.indicatorContainer}>
<Text style={style.displayName}>
{name}
</Text>
<FormattedText
id='post_info.bot'
defaultMessage='BOT'
style={style.bot}
/>
</View>
);
} else if (fromAutoResponder) {
let name = this.props.displayName;
if (overrideUsername && enablePostUsernameOverride) {
name = overrideUsername;
}
return (
<View style={style.indicatorContainer}>
<Text style={style.displayName}>
{name}
</Text>
<FormattedText
id='post_info.auto_responder'
defaultMessage='AUTOMATIC REPLY'
style={style.bot}
/>
</View>
);
} else if (isSystemMessage) {
return (
<FormattedText
id='post_info.system'
defaultMessage='System'
style={style.displayName}
/>
);
} else if (this.props.displayName) {
return (
<TouchableOpacity onPress={this.handleUsernamePress}>
<Text style={style.displayName}>
{this.props.displayName}
</Text>
</TouchableOpacity>
);
}
return (
<FormattedText
id='channel_loader.someone'
defaultMessage='Someone'
style={style.displayName}
/>
);
};
renderCommentedOnMessage = (style) => {
if (!this.props.renderReplies || !this.props.commentedOnDisplayName) {
return null;
}
const displayName = this.props.commentedOnDisplayName;
let name;
if (displayName) {
name = displayName;
} else {
name = (
<FormattedText
id='channel_loader.someone'
defaultMessage='Someone'
/>
);
}
let apostrophe;
if (displayName && displayName.slice(-1) === 's') {
apostrophe = '\'';
} else {
apostrophe = '\'s';
}
return (
<FormattedText
id='post_body.commentedOn'
defaultMessage='Commented on {name}{apostrophe} message: '
values={{
name,
apostrophe,
}}
style={style.commentedOn}
/>
);
};
render() {
const {
commentedOnDisplayName,
commentCount,
createAt,
isPendingOrFailedPost,
isSearchResult,
userTimezone,
militaryTime,
onPress,
renderReplies,
shouldRenderReplyButton,
showFullDate,
theme,
isFlagged,
} = this.props;
const style = getStyleSheet(theme);
const showReply = shouldRenderReplyButton || (!commentedOnDisplayName && commentCount > 0 && renderReplies);
let dateComponent;
if (showFullDate) {
dateComponent = (
<View style={style.datetime}>
<Text style={style.time}>
<FormattedDate value={createAt}/>
</Text>
<Text style={style.time}>
<FormattedTime
timeZone={userTimezone}
hour12={!militaryTime}
value={createAt}
/>
</Text>
</View>
);
} else {
dateComponent = (
<Text style={style.time}>
<FormattedTime
timeZone={userTimezone}
hour12={!militaryTime}
value={createAt}
/>
</Text>
);
}
return (
<View>
<View style={[style.postInfoContainer, (isPendingOrFailedPost && style.pendingPost)]}>
<View style={{flexDirection: 'row', flex: 1}}>
{this.getDisplayName(style)}
<View style={style.timeContainer}>
{dateComponent}
</View>
{isFlagged &&
<View style={style.flagContainer}>
<FlagIcon
height={11}
width={11}
color={theme.linkColor}
/>
</View>
}
</View>
{showReply &&
<TouchableOpacity
onPress={onPress}
style={style.replyIconContainer}
>
<ReplyIcon
height={15}
width={15}
color={theme.linkColor}
/>
{!isSearchResult &&
<Text style={style.replyText}>{commentCount}</Text>
}
</TouchableOpacity>
}
</View>
{commentedOnDisplayName !== '' &&
<View>
{this.renderCommentedOnMessage(style)}
</View>
}
</View>
);
}
}
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
commentedOn: {
color: changeOpacity(theme.centerChannelColor, 0.65),
marginBottom: 3,
lineHeight: 21,
},
postInfoContainer: {
alignItems: 'center',
flexDirection: 'row',
marginTop: 10,
},
pendingPost: {
opacity: 0.5,
},
timeContainer: {
justifyContent: 'center',
},
datetime: {
flex: 1,
flexDirection: 'row',
},
time: {
color: theme.centerChannelColor,
fontSize: 13,
marginLeft: 5,
marginBottom: 1,
opacity: 0.5,
},
replyIconContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
height: 35,
minWidth: 40,
paddingVertical: 10,
},
replyText: {
fontSize: 15,
marginLeft: 3,
color: theme.linkColor,
},
indicatorContainer: {
flexDirection: 'row',
},
bot: {
alignSelf: 'center',
backgroundColor: changeOpacity(theme.centerChannelColor, 0.15),
borderRadius: 2,
color: theme.centerChannelColor,
fontSize: 10,
fontWeight: '600',
marginRight: 5,
paddingVertical: 2,
paddingHorizontal: 4,
},
displayName: {
color: theme.centerChannelColor,
fontSize: 15,
fontWeight: '600',
marginRight: 5,
marginBottom: 3,
},
flagContainer: {
marginLeft: 10,
alignSelf: 'center',
...Platform.select({
ios: {
marginBottom: 2,
},
android: {
marginBottom: 1,
},
}),
},
};
});