diff --git a/app/components/autocomplete/at_mention_item/at_mention_item.js b/app/components/autocomplete/at_mention_item/at_mention_item.js index 9592d3093..a91cc399c 100644 --- a/app/components/autocomplete/at_mention_item/at_mention_item.js +++ b/app/components/autocomplete/at_mention_item/at_mention_item.js @@ -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 = { diff --git a/app/components/autocomplete/channel_mention_item/channel_mention_item.js b/app/components/autocomplete/channel_mention_item/channel_mention_item.js index b110f26cf..226af6fe9 100644 --- a/app/components/autocomplete/channel_mention_item/channel_mention_item.js +++ b/app/components/autocomplete/channel_mention_item/channel_mention_item.js @@ -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'; diff --git a/app/components/bot_tag.js b/app/components/bot_tag.js deleted file mode 100644 index e18201091..000000000 --- a/app/components/bot_tag.js +++ /dev/null @@ -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 ( - - - - ); - } -} - -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', - }, - }; -}); diff --git a/app/components/channel_intro/channel_intro.js b/app/components/channel_intro/channel_intro.js index a2ae4392f..0e7c1e17a 100644 --- a/app/components/channel_intro/channel_intro.js +++ b/app/components/channel_intro/channel_intro.js @@ -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 = { diff --git a/app/components/custom_list/user_list_row/user_list_row.js b/app/components/custom_list/user_list_row/user_list_row.js index 10309de12..4e8ebf708 100644 --- a/app/components/custom_list/user_list_row/user_list_row.js +++ b/app/components/custom_list/user_list_row/user_list_row.js @@ -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 = { diff --git a/app/components/formatted_time.js b/app/components/formatted_time.js index 64cc06569..88c903882 100644 --- a/app/components/formatted_time.js +++ b/app/components/formatted_time.js @@ -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 {formattedTime}; + return {formattedTime}; } } diff --git a/app/components/post/post.js b/app/components/post/post.js index 2ba71ef42..a1a555fb5 100644 --- a/app/components/post/post.js +++ b/app/components/post/post.js @@ -306,51 +306,52 @@ export default class Post extends PureComponent { const rightColumnStyle = [style.rightColumn, (commentedOnPost && isLastReply && style.rightColumnPadding)]; return ( - - - - - {userProfile} - - {postHeader} - + + + + + + {userProfile} + + {postHeader} + + - - - + + + ); } } @@ -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, diff --git a/app/components/post_header/__snapshots__/post_pre_header.test.js.snap b/app/components/post_header/__snapshots__/post_pre_header.test.js.snap index 24150d3ad..79cce8349 100644 --- a/app/components/post_header/__snapshots__/post_pre_header.test.js.snap +++ b/app/components/post_header/__snapshots__/post_pre_header.test.js.snap @@ -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, }, diff --git a/app/components/post_header/post_header.js b/app/components/post_header/post_header.js index b2dd60d4f..591aecebc 100644 --- a/app/components/post_header/post_header.js +++ b/app/components/post_header/post_header.js @@ -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 ( - - - {name} - - - - ); - } else if (isBot) { - return ( - - - - {this.props.displayName} - - - - - ); - } else if (isGuest) { - return ( - - - - {this.props.displayName} - - - - - ); - } else if (fromAutoResponder) { - let name = this.props.displayName; - if (overrideUsername && enablePostUsernameOverride) { - name = overrideUsername; - } - - return ( - - - {name} - - - - ); - } else if (isSystemMessage) { - return ( - - ); - } else if (this.props.displayName) { - return ( - - - {this.props.displayName} - - - ); - } - - return ( - - ); }; - 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 ( + + + {name} + + + ); + } else if (isSystemMessage) { + return ( + + + + ); + } else if (displayName) { + return ( + + + {displayName} + + + ); + } + + return ( + + + + ); + }; + + 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 ( + + + + {!isSearchResult && + {commentCount} + } + + + ); + }; + + renderTag = () => { + const {fromAutoResponder, fromWebHook, isBot, isGuest, theme} = this.props; + const style = getStyleSheet(theme); + + if (fromWebHook || isBot) { + return ( + + ); + } else if (isGuest) { + return ( + + ); + } else if (fromAutoResponder) { + return ( + + ); + } + + return null; + }; + + render() { + const { + createAt, + isPendingOrFailedPost, + userTimezone, + militaryTime, + showFullDate, + theme, + } = this.props; + const style = getStyleSheet(theme); + let dateComponent; if (showFullDate) { dateComponent = ( - - - - - - - - + ); } else { dateComponent = ( - - - + ); } return ( - - - {this.getDisplayName(style)} - - {dateComponent} - + + + {this.renderDisplayName()} + {this.renderTag()} + {dateComponent} + {this.renderReply()} - {showReply && - - - {!isSearchResult && - {commentCount} - } - - } - {commentedOnDisplayName !== '' && - - {this.renderCommentedOnMessage(style)} - - } + {this.renderCommentedOnMessage(style)} ); } @@ -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, }, }; }); diff --git a/app/components/post_header/post_pre_header.js b/app/components/post_header/post_pre_header.js index 982ae93a3..1b9edcf5a 100644 --- a/app/components/post_header/post_pre_header.js +++ b/app/components/post_header/post_pre_header.js @@ -99,7 +99,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => { flex: 1, flexDirection: 'row', height: 15, - marginLeft: 12, + marginLeft: 10, marginRight: 10, marginTop: 10, }, diff --git a/app/components/post_profile_picture/post_profile_picture.js b/app/components/post_profile_picture/post_profile_picture.js index ec2ec981a..699979dac 100644 --- a/app/components/post_profile_picture/post_profile_picture.js +++ b/app/components/post_profile_picture/post_profile_picture.js @@ -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 ( - + + ); +} + +export function GuestTag(props) { + const id = t('post_info.guest'); + const defaultMessage = 'GUEST'; + + return ( + + ); +} + +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 ( - + + + ); } } 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), }, diff --git a/app/screens/user_profile/user_profile.js b/app/screens/user_profile/user_profile.js index e7675eaf0..215c1e005 100644 --- a/app/screens/user_profile/user_profile.js +++ b/app/screens/user_profile/user_profile.js @@ -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'; diff --git a/app/screens/user_profile/user_profile.test.js b/app/screens/user_profile/user_profile.test.js index 79d36b16c..8cc4296b7 100644 --- a/app/screens/user_profile/user_profile.test.js +++ b/app/screens/user_profile/user_profile.test.js @@ -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', () => {