forked from Ivasoft/mattermost-mobile
MM-18603 Fix post header to prevent overlaps (#3332)
* MM-18603 Fix post header to prevent overlaps * Export BotTag and GuestTag
This commit is contained in:
committed by
Harrison Healey
parent
51e5fc04d4
commit
3b3a696a55
@@ -9,11 +9,10 @@ import {
|
||||
} from 'react-native';
|
||||
|
||||
import ProfilePicture from 'app/components/profile_picture';
|
||||
import BotTag from 'app/components/bot_tag';
|
||||
import GuestTag from 'app/components/guest_tag';
|
||||
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
|
||||
import {BotTag, GuestTag} from 'app/components/tag';
|
||||
import TouchableWithFeedback from 'app/components/touchable_with_feedback';
|
||||
import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
|
||||
|
||||
export default class AtMentionItem extends PureComponent {
|
||||
static propTypes = {
|
||||
|
||||
@@ -8,8 +8,7 @@ import {
|
||||
} from 'react-native';
|
||||
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
import BotTag from 'app/components/bot_tag';
|
||||
import GuestTag from 'app/components/guest_tag';
|
||||
import {BotTag, GuestTag} from 'app/components/tag';
|
||||
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
|
||||
import TouchableWithFeedback from 'app/components/touchable_with_feedback';
|
||||
import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
// 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, View} from 'react-native';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
|
||||
export default class BotTag extends PureComponent {
|
||||
static defaultProps = {
|
||||
show: true,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
show: PropTypes.bool,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
render() {
|
||||
if (!this.props.show) {
|
||||
return null;
|
||||
}
|
||||
const style = createStyleSheet(this.props.theme);
|
||||
|
||||
return (
|
||||
<View style={style.bot}>
|
||||
<FormattedText
|
||||
id='post_info.bot'
|
||||
defaultMessage='BOT'
|
||||
style={style.botText}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const createStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
bot: {
|
||||
alignSelf: 'center',
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.15),
|
||||
borderRadius: 2,
|
||||
marginRight: 2,
|
||||
marginBottom: 1,
|
||||
...Platform.select({
|
||||
android: {
|
||||
marginBottom: 0,
|
||||
},
|
||||
}),
|
||||
marginLeft: 2,
|
||||
paddingVertical: 2,
|
||||
paddingHorizontal: 4,
|
||||
},
|
||||
botText: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 10,
|
||||
fontWeight: '600',
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -12,16 +12,15 @@ import {injectIntl, intlShape} from 'react-intl';
|
||||
import {getFullName} from 'mattermost-redux/utils/user_utils';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
|
||||
import BotTag from 'app/components/bot_tag';
|
||||
import GuestTag from 'app/components/guest_tag';
|
||||
import {goToScreen} from 'app/actions/navigation';
|
||||
import ProfilePicture from 'app/components/profile_picture';
|
||||
import {paddingHorizontal as padding} from 'app/components/safe_area_view/iphone_x_spacing';
|
||||
import {BotTag, GuestTag} from 'app/components/tag';
|
||||
import TouchableWithFeedback from 'app/components/touchable_with_feedback';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
import {t} from 'app/utils/i18n';
|
||||
import {isGuest} from 'app/utils/users';
|
||||
import {goToScreen} from 'app/actions/navigation';
|
||||
|
||||
class ChannelIntro extends PureComponent {
|
||||
static propTypes = {
|
||||
|
||||
@@ -10,12 +10,12 @@ import {
|
||||
} from 'react-native';
|
||||
|
||||
import {displayUsername} from 'mattermost-redux/utils/user_utils';
|
||||
|
||||
import CustomListRow from 'app/components/custom_list/custom_list_row';
|
||||
import ProfilePicture from 'app/components/profile_picture';
|
||||
import BotTag from 'app/components/bot_tag';
|
||||
import GuestTag from 'app/components/guest_tag';
|
||||
import {BotTag, GuestTag} from 'app/components/tag';
|
||||
import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme';
|
||||
import {isGuest} from 'app/utils/users';
|
||||
import CustomListRow from 'app/components/custom_list/custom_list_row';
|
||||
|
||||
export default class UserListRow extends React.PureComponent {
|
||||
static propTypes = {
|
||||
|
||||
@@ -6,12 +6,15 @@ import PropTypes from 'prop-types';
|
||||
import {Text} from 'react-native';
|
||||
import moment from 'moment-timezone';
|
||||
|
||||
import CustomPropTypes from 'app/constants/custom_prop_types';
|
||||
|
||||
export default class FormattedTime extends React.PureComponent {
|
||||
static propTypes = {
|
||||
value: PropTypes.any.isRequired,
|
||||
timeZone: PropTypes.string,
|
||||
children: PropTypes.func,
|
||||
hour12: PropTypes.bool,
|
||||
style: CustomPropTypes.Style,
|
||||
};
|
||||
|
||||
getFormattedTime = () => {
|
||||
@@ -30,13 +33,13 @@ export default class FormattedTime extends React.PureComponent {
|
||||
};
|
||||
|
||||
render() {
|
||||
const {children} = this.props;
|
||||
const {children, style} = this.props;
|
||||
const formattedTime = this.getFormattedTime();
|
||||
|
||||
if (typeof children === 'function') {
|
||||
return children(formattedTime);
|
||||
}
|
||||
|
||||
return <Text>{formattedTime}</Text>;
|
||||
return <Text style={style}>{formattedTime}</Text>;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,51 +306,52 @@ export default class Post extends PureComponent {
|
||||
const rightColumnStyle = [style.rightColumn, (commentedOnPost && isLastReply && style.rightColumnPadding)];
|
||||
|
||||
return (
|
||||
<TouchableWithFeedback
|
||||
style={[style.postStyle, highlighted, padding(isLandscape)]}
|
||||
onPress={this.handlePress}
|
||||
onLongPress={this.showPostOptions}
|
||||
delayLongPress={100}
|
||||
underlayColor={changeOpacity(theme.centerChannelColor, 0.1)}
|
||||
cancelTouchOnPanning={true}
|
||||
>
|
||||
<React.Fragment>
|
||||
<PostPreHeader
|
||||
isConsecutive={mergeMessage}
|
||||
isFlagged={isFlagged}
|
||||
isPinned={post.is_pinned}
|
||||
rightColumnStyle={style.rightColumn}
|
||||
skipFlaggedHeader={skipFlaggedHeader}
|
||||
skipPinnedHeader={skipPinnedHeader}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={[style.container, this.props.style, consecutiveStyle]}>
|
||||
{userProfile}
|
||||
<View style={rightColumnStyle}>
|
||||
{postHeader}
|
||||
<PostBody
|
||||
ref={this.postBodyRef}
|
||||
highlight={highlight}
|
||||
channelIsReadOnly={channelIsReadOnly}
|
||||
isLastPost={isLastPost}
|
||||
isSearchResult={isSearchResult}
|
||||
onFailedPostPress={this.handleFailedPostPress}
|
||||
onHashtagPress={onHashtagPress}
|
||||
onPermalinkPress={onPermalinkPress}
|
||||
onPress={this.handlePress}
|
||||
post={post}
|
||||
replyBarStyle={replyBarStyle}
|
||||
managedConfig={managedConfig}
|
||||
isFlagged={isFlagged}
|
||||
isReplyPost={isReplyPost}
|
||||
showAddReaction={showAddReaction}
|
||||
showLongPost={showLongPost}
|
||||
location={location}
|
||||
/>
|
||||
<View style={[style.postStyle, highlighted, padding(isLandscape)]}>
|
||||
<TouchableWithFeedback
|
||||
onPress={this.handlePress}
|
||||
onLongPress={this.showPostOptions}
|
||||
delayLongPress={100}
|
||||
underlayColor={changeOpacity(theme.centerChannelColor, 0.1)}
|
||||
cancelTouchOnPanning={true}
|
||||
>
|
||||
<React.Fragment>
|
||||
<PostPreHeader
|
||||
isConsecutive={mergeMessage}
|
||||
isFlagged={isFlagged}
|
||||
isPinned={post.is_pinned}
|
||||
rightColumnStyle={style.preHeaderRightColumn}
|
||||
skipFlaggedHeader={skipFlaggedHeader}
|
||||
skipPinnedHeader={skipPinnedHeader}
|
||||
theme={theme}
|
||||
/>
|
||||
<View style={[style.container, this.props.style, consecutiveStyle]}>
|
||||
{userProfile}
|
||||
<View style={rightColumnStyle}>
|
||||
{postHeader}
|
||||
<PostBody
|
||||
ref={this.postBodyRef}
|
||||
highlight={highlight}
|
||||
channelIsReadOnly={channelIsReadOnly}
|
||||
isLastPost={isLastPost}
|
||||
isSearchResult={isSearchResult}
|
||||
onFailedPostPress={this.handleFailedPostPress}
|
||||
onHashtagPress={onHashtagPress}
|
||||
onPermalinkPress={onPermalinkPress}
|
||||
onPress={this.handlePress}
|
||||
post={post}
|
||||
replyBarStyle={replyBarStyle}
|
||||
managedConfig={managedConfig}
|
||||
isFlagged={isFlagged}
|
||||
isReplyPost={isReplyPost}
|
||||
showAddReaction={showAddReaction}
|
||||
showLongPost={showLongPost}
|
||||
location={location}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</React.Fragment>
|
||||
</TouchableWithFeedback>
|
||||
</React.Fragment>
|
||||
</TouchableWithFeedback>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -359,6 +360,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
postStyle: {
|
||||
overflow: 'hidden',
|
||||
flex: 1,
|
||||
},
|
||||
container: {
|
||||
flexDirection: 'row',
|
||||
@@ -366,6 +368,11 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
pendingPost: {
|
||||
opacity: 0.5,
|
||||
},
|
||||
preHeaderRightColumn: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
marginLeft: 2,
|
||||
},
|
||||
rightColumn: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
@@ -398,6 +405,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
replyBar: {
|
||||
backgroundColor: theme.centerChannelColor,
|
||||
opacity: 0.1,
|
||||
marginLeft: 1,
|
||||
marginRight: 7,
|
||||
width: 3,
|
||||
flexBasis: 3,
|
||||
|
||||
@@ -8,7 +8,7 @@ exports[`PostPreHeader should match snapshot when flagged and not pinned 1`] = `
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"height": 15,
|
||||
"marginLeft": 12,
|
||||
"marginLeft": 10,
|
||||
"marginRight": 10,
|
||||
"marginTop": 10,
|
||||
},
|
||||
@@ -77,7 +77,7 @@ exports[`PostPreHeader should match snapshot when pinned and flagged 1`] = `
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"height": 15,
|
||||
"marginLeft": 12,
|
||||
"marginLeft": 10,
|
||||
"marginRight": 10,
|
||||
"marginTop": 10,
|
||||
},
|
||||
@@ -166,7 +166,7 @@ exports[`PostPreHeader should match snapshot when pinned and flagged but skippin
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"height": 15,
|
||||
"marginLeft": 12,
|
||||
"marginLeft": 10,
|
||||
"marginRight": 10,
|
||||
"marginTop": 10,
|
||||
},
|
||||
@@ -231,7 +231,7 @@ exports[`PostPreHeader should match snapshot when pinned and flagged but skippin
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"height": 15,
|
||||
"marginLeft": 12,
|
||||
"marginLeft": 10,
|
||||
"marginRight": 10,
|
||||
"marginTop": 10,
|
||||
},
|
||||
@@ -296,7 +296,7 @@ exports[`PostPreHeader should match snapshot when pinned and not flagged 1`] = `
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"height": 15,
|
||||
"marginLeft": 12,
|
||||
"marginLeft": 10,
|
||||
"marginRight": 10,
|
||||
"marginTop": 10,
|
||||
},
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Platform,
|
||||
Text,
|
||||
View,
|
||||
} from 'react-native';
|
||||
@@ -13,10 +12,10 @@ 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 BotTag from 'app/components/bot_tag';
|
||||
import GuestTag from 'app/components/guest_tag';
|
||||
import Tag, {BotTag, GuestTag} from 'app/components/tag';
|
||||
import TouchableWithFeedback from 'app/components/touchable_with_feedback';
|
||||
import {emptyFunction} from 'app/utils/general';
|
||||
import {t} from 'app/utils/i18n';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
|
||||
export default class PostHeader extends PureComponent {
|
||||
@@ -56,121 +55,16 @@ export default class PostHeader extends PureComponent {
|
||||
if (this.props.username) {
|
||||
this.props.onUsernamePress(this.props.username);
|
||||
}
|
||||
}
|
||||
|
||||
getDisplayName = (style) => {
|
||||
const {
|
||||
enablePostUsernameOverride,
|
||||
fromWebHook,
|
||||
isSystemMessage,
|
||||
fromAutoResponder,
|
||||
overrideUsername,
|
||||
isBot,
|
||||
isGuest,
|
||||
} = 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>
|
||||
<BotTag
|
||||
theme={this.props.theme}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
} else if (isBot) {
|
||||
return (
|
||||
<TouchableWithFeedback
|
||||
onPress={this.handleUsernamePress}
|
||||
type={'opacity'}
|
||||
>
|
||||
<View style={style.indicatorContainer}>
|
||||
<Text style={style.displayName}>
|
||||
{this.props.displayName}
|
||||
</Text>
|
||||
<BotTag
|
||||
theme={this.props.theme}
|
||||
/>
|
||||
</View>
|
||||
</TouchableWithFeedback>
|
||||
);
|
||||
} else if (isGuest) {
|
||||
return (
|
||||
<TouchableWithFeedback
|
||||
onPress={this.handleUsernamePress}
|
||||
type={'opacity'}
|
||||
>
|
||||
<View style={style.indicatorContainer}>
|
||||
<Text style={style.displayName}>
|
||||
{this.props.displayName}
|
||||
</Text>
|
||||
<GuestTag
|
||||
theme={this.props.theme}
|
||||
/>
|
||||
</View>
|
||||
</TouchableWithFeedback>
|
||||
);
|
||||
} 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 (
|
||||
<TouchableWithFeedback
|
||||
onPress={this.handleUsernamePress}
|
||||
type={'opacity'}
|
||||
>
|
||||
<Text style={style.displayName}>
|
||||
{this.props.displayName}
|
||||
</Text>
|
||||
</TouchableWithFeedback>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FormattedText
|
||||
id='channel_loader.someone'
|
||||
defaultMessage='Someone'
|
||||
style={style.displayName}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
renderCommentedOnMessage = (style) => {
|
||||
renderCommentedOnMessage = () => {
|
||||
if (!this.props.renderReplies || !this.props.commentedOnDisplayName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const displayName = this.props.commentedOnDisplayName;
|
||||
const {commentedOnDisplayName, theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
const displayName = commentedOnDisplayName;
|
||||
|
||||
let name;
|
||||
if (displayName) {
|
||||
@@ -204,86 +98,187 @@ export default class PostHeader extends PureComponent {
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
renderDisplayName = () => {
|
||||
const {
|
||||
displayName,
|
||||
enablePostUsernameOverride,
|
||||
fromWebHook,
|
||||
isSystemMessage,
|
||||
fromAutoResponder,
|
||||
overrideUsername,
|
||||
theme,
|
||||
} = this.props;
|
||||
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
if (fromAutoResponder || fromWebHook) {
|
||||
let name = displayName;
|
||||
if (overrideUsername && enablePostUsernameOverride) {
|
||||
name = overrideUsername;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={[style.displayNameContainer, {maxWidth: fromAutoResponder ? '30%' : '60%'}]}>
|
||||
<Text
|
||||
style={style.displayName}
|
||||
ellipsizeMode={'tail'}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{name}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
} else if (isSystemMessage) {
|
||||
return (
|
||||
<View style={style.displayNameContainer}>
|
||||
<FormattedText
|
||||
id='post_info.system'
|
||||
defaultMessage='System'
|
||||
style={[style.displayName]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
} else if (displayName) {
|
||||
return (
|
||||
<TouchableWithFeedback
|
||||
onPress={this.handleUsernamePress}
|
||||
style={style.displayNameContainer}
|
||||
type={'opacity'}
|
||||
>
|
||||
<Text
|
||||
style={style.displayName}
|
||||
ellipsizeMode={'tail'}
|
||||
numberOfLines={1}
|
||||
>
|
||||
{displayName}
|
||||
</Text>
|
||||
</TouchableWithFeedback>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={style.displayNameContainer}>
|
||||
<FormattedText
|
||||
id='channel_loader.someone'
|
||||
defaultMessage='Someone'
|
||||
style={style.displayName}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
renderReply = () => {
|
||||
const {
|
||||
commentedOnDisplayName,
|
||||
commentCount,
|
||||
createAt,
|
||||
isPendingOrFailedPost,
|
||||
commentedOnDisplayName,
|
||||
isSearchResult,
|
||||
userTimezone,
|
||||
militaryTime,
|
||||
onPress,
|
||||
renderReplies,
|
||||
shouldRenderReplyButton,
|
||||
showFullDate,
|
||||
theme,
|
||||
} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
const showReply = shouldRenderReplyButton || (!commentedOnDisplayName && commentCount > 0 && renderReplies);
|
||||
|
||||
if (!showReply) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={style.replyWrapper}>
|
||||
<TouchableWithFeedback
|
||||
onPress={onPress}
|
||||
style={style.replyIconContainer}
|
||||
type={'opacity'}
|
||||
>
|
||||
<ReplyIcon
|
||||
height={16}
|
||||
width={16}
|
||||
color={theme.linkColor}
|
||||
/>
|
||||
{!isSearchResult &&
|
||||
<Text style={style.replyText}>{commentCount}</Text>
|
||||
}
|
||||
</TouchableWithFeedback>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
renderTag = () => {
|
||||
const {fromAutoResponder, fromWebHook, isBot, isGuest, theme} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
if (fromWebHook || isBot) {
|
||||
return (
|
||||
<BotTag
|
||||
style={style.tag}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
} else if (isGuest) {
|
||||
return (
|
||||
<GuestTag
|
||||
style={style.tag}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
} else if (fromAutoResponder) {
|
||||
return (
|
||||
<Tag
|
||||
id={t('post_info.auto_responder')}
|
||||
defaultMessage={'AUTOMATIC REPLY'}
|
||||
style={style.tag}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
createAt,
|
||||
isPendingOrFailedPost,
|
||||
userTimezone,
|
||||
militaryTime,
|
||||
showFullDate,
|
||||
theme,
|
||||
} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
let dateComponent;
|
||||
if (showFullDate) {
|
||||
dateComponent = (
|
||||
<View style={style.datetime}>
|
||||
<Text style={style.time}>
|
||||
<FormattedDate
|
||||
timeZone={userTimezone}
|
||||
value={createAt}
|
||||
/>
|
||||
</Text>
|
||||
<Text style={style.time}>
|
||||
<FormattedTime
|
||||
timeZone={userTimezone}
|
||||
hour12={!militaryTime}
|
||||
value={createAt}
|
||||
/>
|
||||
</Text>
|
||||
</View>
|
||||
<FormattedDate
|
||||
format={`ddd, MMM DD, YYYY ${militaryTime ? 'HH:mm' : 'hh:mm A'}`}
|
||||
timeZone={userTimezone}
|
||||
value={createAt}
|
||||
style={style.time}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
dateComponent = (
|
||||
<Text style={style.time}>
|
||||
<FormattedTime
|
||||
timeZone={userTimezone}
|
||||
hour12={!militaryTime}
|
||||
value={createAt}
|
||||
/>
|
||||
</Text>
|
||||
<FormattedTime
|
||||
timeZone={userTimezone}
|
||||
hour12={!militaryTime}
|
||||
value={createAt}
|
||||
style={style.time}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<View style={[style.postInfoContainer, (isPendingOrFailedPost && style.pendingPost)]}>
|
||||
<View style={{flexDirection: 'row', flex: 1}}>
|
||||
{this.getDisplayName(style)}
|
||||
<View style={style.timeContainer}>
|
||||
{dateComponent}
|
||||
</View>
|
||||
<View style={[style.container, (isPendingOrFailedPost && style.pendingPost)]}>
|
||||
<View style={style.wrapper}>
|
||||
{this.renderDisplayName()}
|
||||
{this.renderTag()}
|
||||
{dateComponent}
|
||||
{this.renderReply()}
|
||||
</View>
|
||||
{showReply &&
|
||||
<TouchableWithFeedback
|
||||
onPress={onPress}
|
||||
style={style.replyIconContainer}
|
||||
type={'opacity'}
|
||||
>
|
||||
<ReplyIcon
|
||||
height={16}
|
||||
width={16}
|
||||
color={theme.linkColor}
|
||||
/>
|
||||
{!isSearchResult &&
|
||||
<Text style={style.replyText}>{commentCount}</Text>
|
||||
}
|
||||
</TouchableWithFeedback>
|
||||
}
|
||||
</View>
|
||||
{commentedOnDisplayName !== '' &&
|
||||
<View>
|
||||
{this.renderCommentedOnMessage(style)}
|
||||
</View>
|
||||
}
|
||||
{this.renderCommentedOnMessage(style)}
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
@@ -291,67 +286,64 @@ export default class PostHeader extends PureComponent {
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
commentedOn: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.65),
|
||||
marginBottom: 3,
|
||||
lineHeight: 21,
|
||||
},
|
||||
postInfoContainer: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
container: {
|
||||
flex: 1,
|
||||
marginTop: 10,
|
||||
},
|
||||
pendingPost: {
|
||||
opacity: 0.5,
|
||||
},
|
||||
timeContainer: {
|
||||
justifyContent: 'center',
|
||||
tag: {
|
||||
marginLeft: 0,
|
||||
marginRight: 5,
|
||||
marginBottom: 5,
|
||||
},
|
||||
datetime: {
|
||||
wrapper: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
},
|
||||
time: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 13,
|
||||
marginLeft: 5,
|
||||
marginBottom: 1,
|
||||
opacity: 0.5,
|
||||
},
|
||||
replyIconContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginVertical: -10,
|
||||
minWidth: 40,
|
||||
paddingVertical: 10,
|
||||
},
|
||||
replyText: {
|
||||
fontSize: 15,
|
||||
marginLeft: 3,
|
||||
color: theme.linkColor,
|
||||
},
|
||||
indicatorContainer: {
|
||||
flexDirection: 'row',
|
||||
displayNameContainer: {
|
||||
maxWidth: '60%',
|
||||
marginRight: 5,
|
||||
marginBottom: 3,
|
||||
},
|
||||
displayName: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 15,
|
||||
fontWeight: '600',
|
||||
marginRight: 5,
|
||||
marginBottom: 3,
|
||||
flexGrow: 1,
|
||||
paddingVertical: 2,
|
||||
},
|
||||
flagContainer: {
|
||||
marginLeft: 10,
|
||||
alignSelf: 'center',
|
||||
...Platform.select({
|
||||
ios: {
|
||||
marginBottom: 2,
|
||||
},
|
||||
android: {
|
||||
marginBottom: 1,
|
||||
},
|
||||
}),
|
||||
time: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 12,
|
||||
marginTop: 5,
|
||||
opacity: 0.5,
|
||||
flex: 1,
|
||||
},
|
||||
replyWrapper: {
|
||||
flex: 1,
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
replyIconContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'flex-end',
|
||||
minWidth: 40,
|
||||
paddingTop: 2,
|
||||
paddingBottom: 10,
|
||||
flex: 1,
|
||||
},
|
||||
replyText: {
|
||||
fontSize: 12,
|
||||
marginLeft: 2,
|
||||
marginTop: 2,
|
||||
color: theme.linkColor,
|
||||
},
|
||||
commentedOn: {
|
||||
color: changeOpacity(theme.centerChannelColor, 0.65),
|
||||
marginBottom: 3,
|
||||
lineHeight: 21,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -99,7 +99,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
height: 15,
|
||||
marginLeft: 12,
|
||||
marginLeft: 10,
|
||||
marginRight: 10,
|
||||
marginTop: 10,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {Image, View} from 'react-native';
|
||||
import {Image, Platform, StyleSheet, View} from 'react-native';
|
||||
|
||||
import AppIcon from 'app/components/app_icon';
|
||||
import ProfilePicture from 'app/components/profile_picture';
|
||||
@@ -46,7 +46,7 @@ export default class PostProfilePicture extends PureComponent {
|
||||
|
||||
if (isSystemMessage && !fromAutoResponder && !isBot) {
|
||||
return (
|
||||
<View>
|
||||
<View style={style.buffer}>
|
||||
<AppIcon
|
||||
color={theme.centerChannelColor}
|
||||
height={ViewTypes.PROFILE_PICTURE_SIZE}
|
||||
@@ -61,17 +61,16 @@ export default class PostProfilePicture extends PureComponent {
|
||||
const frameSize = ViewTypes.PROFILE_PICTURE_SIZE;
|
||||
const pictureSize = isEmoji ? ViewTypes.PROFILE_PICTURE_EMOJI_SIZE : ViewTypes.PROFILE_PICTURE_SIZE;
|
||||
const borderRadius = isEmoji ? 0 : ViewTypes.PROFILE_PICTURE_SIZE / 2;
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
style={[{
|
||||
borderRadius,
|
||||
overflow: 'hidden',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
height: frameSize,
|
||||
width: frameSize,
|
||||
}}
|
||||
}, style.buffer]}
|
||||
>
|
||||
<Image
|
||||
source={icon}
|
||||
@@ -107,3 +106,16 @@ export default class PostProfilePicture extends PureComponent {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
|
||||
const style = StyleSheet.create({
|
||||
buffer: {
|
||||
...Platform.select({
|
||||
android: {
|
||||
marginRight: 2,
|
||||
},
|
||||
ios: {
|
||||
marginRight: 3,
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,19 +2,51 @@
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import {View, ViewPropTypes} from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
|
||||
export default class GuestTag extends PureComponent {
|
||||
static defaultProps = {
|
||||
show: true,
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
import {t} from 'app/utils/i18n';
|
||||
|
||||
export function BotTag(props) {
|
||||
const id = t('post_info.bot');
|
||||
const defaultMessage = 'BOT';
|
||||
|
||||
return (
|
||||
<Tag
|
||||
id={id}
|
||||
defaultMessage={defaultMessage}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export function GuestTag(props) {
|
||||
const id = t('post_info.guest');
|
||||
const defaultMessage = 'GUEST';
|
||||
|
||||
return (
|
||||
<Tag
|
||||
id={id}
|
||||
defaultMessage={defaultMessage}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default class Tag extends PureComponent {
|
||||
static propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
defaultMessage: PropTypes.string.isRequired,
|
||||
inTitle: PropTypes.bool,
|
||||
show: PropTypes.bool,
|
||||
style: ViewPropTypes.style,
|
||||
theme: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
static propTypes = {
|
||||
show: PropTypes.bool,
|
||||
theme: PropTypes.object.isRequired,
|
||||
inTitle: PropTypes.bool,
|
||||
static defaultProps = {
|
||||
show: true,
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -24,30 +56,35 @@ export default class GuestTag extends PureComponent {
|
||||
const style = createStyleSheet(this.props.theme);
|
||||
|
||||
return (
|
||||
<FormattedText
|
||||
id='post_info.guest'
|
||||
defaultMessage='GUEST'
|
||||
style={[style.guest, this.props.inTitle ? style.guestTitle : null]}
|
||||
/>
|
||||
<View style={[style.container, this.props.style]}>
|
||||
<FormattedText
|
||||
id={this.props.id}
|
||||
defaultMessage={this.props.defaultMessage}
|
||||
style={[style.text, this.props.inTitle ? style.title : null]}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const createStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
guest: {
|
||||
container: {
|
||||
alignSelf: 'center',
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.15),
|
||||
borderRadius: 2,
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 10,
|
||||
fontWeight: '600',
|
||||
marginRight: 5,
|
||||
marginLeft: 5,
|
||||
marginRight: 2,
|
||||
marginBottom: 1,
|
||||
marginLeft: 2,
|
||||
paddingVertical: 2,
|
||||
paddingHorizontal: 4,
|
||||
},
|
||||
guestTitle: {
|
||||
text: {
|
||||
color: theme.centerChannelColor,
|
||||
fontSize: 10,
|
||||
fontWeight: '600',
|
||||
},
|
||||
title: {
|
||||
backgroundColor: changeOpacity(theme.sidebarHeaderTextColor, 0.15),
|
||||
color: changeOpacity(theme.sidebarHeaderTextColor, 0.6),
|
||||
},
|
||||
@@ -20,8 +20,7 @@ import ProfilePicture from 'app/components/profile_picture';
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import FormattedTime from 'app/components/formatted_time';
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import BotTag from 'app/components/bot_tag';
|
||||
import GuestTag from 'app/components/guest_tag';
|
||||
import {BotTag, GuestTag} from 'app/components/tag';
|
||||
|
||||
import {alertErrorWithFallback} from 'app/utils/general';
|
||||
import {changeOpacity, makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
|
||||
|
||||
@@ -8,8 +8,7 @@ import Preferences from 'mattermost-redux/constants/preferences';
|
||||
import * as NavigationActions from 'app/actions/navigation';
|
||||
|
||||
import UserProfile from './user_profile.js';
|
||||
import BotTag from 'app/components/bot_tag';
|
||||
import GuestTag from 'app/components/guest_tag';
|
||||
import {BotTag, GuestTag} from 'app/components/tag';
|
||||
|
||||
jest.mock('react-intl');
|
||||
jest.mock('app/utils/theme', () => {
|
||||
|
||||
Reference in New Issue
Block a user