diff --git a/app/components/message_attachments/message_attachment.js b/app/components/message_attachments/message_attachment.js index d057df232..2ccd9f1a0 100644 --- a/app/components/message_attachments/message_attachment.js +++ b/app/components/message_attachments/message_attachment.js @@ -13,9 +13,10 @@ import { View, } from 'react-native'; -import FormattedText from 'app/components/formatted_text'; import Markdown from 'app/components/markdown'; import ProgressiveImage from 'app/components/progressive_image'; +import ShowMoreButton from 'app/components/show_more_button'; + import CustomPropTypes from 'app/constants/custom_prop_types'; import ImageCacheManager from 'app/utils/image_cache_manager'; import {previewImageAtIndex, calculateDimensions} from 'app/utils/images'; @@ -103,10 +104,10 @@ export default class MessageAttachment extends PureComponent { const {height} = event.nativeEvent.layout; const {height: deviceHeight} = Dimensions.get('window'); - if (height >= (deviceHeight * 1.2)) { + if (height >= (deviceHeight * 0.6)) { this.setState({ isLongText: true, - maxHeight: (deviceHeight * 0.6), + maxHeight: (deviceHeight * 0.4), }); } }; @@ -385,23 +386,6 @@ export default class MessageAttachment extends PureComponent { let text; if (attachment.text) { - let moreLessLocale = {id: 'post_attachment.collapse', defaultMessage: 'Show less...'}; - if (collapsed) { - moreLessLocale = {id: 'post_attachment.more', defaultMessage: 'Show more...'}; - } - - let moreLess; - if (isLongText) { - moreLess = ( - - ); - } - text = ( - {moreLess} + {isLongText && + + } ); } @@ -523,11 +512,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => { height: 45, width: 45, }, - moreLess: { - color: theme.linkColor, - fontSize: 12, - marginTop: 5, - }, headingContainer: { alignSelf: 'stretch', flexDirection: 'row', diff --git a/app/components/post_body/post_body.js b/app/components/post_body/post_body.js index f5ef0166b..5208c955e 100644 --- a/app/components/post_body/post_body.js +++ b/app/components/post_body/post_body.js @@ -6,14 +6,12 @@ import PropTypes from 'prop-types'; import { Dimensions, Platform, - Text, TouchableHighlight, TouchableOpacity, View, } from 'react-native'; import {intlShape} from 'react-intl'; import Icon from 'react-native-vector-icons/Ionicons'; -import LinearGradient from 'react-native-linear-gradient'; import {Posts} from 'mattermost-redux/constants'; @@ -21,6 +19,7 @@ import CombinedSystemMessage from 'app/components/combined_system_message'; import FormattedText from 'app/components/formatted_text'; import Markdown from 'app/components/markdown'; import OptionsContext from 'app/components/options_context'; +import ShowMoreButton from 'app/components/show_more_button'; import {emptyFunction} from 'app/utils/general'; import {getMarkdownTextStyles, getMarkdownBlockStyles} from 'app/utils/markdown'; @@ -336,64 +335,12 @@ export default class PostBody extends PureComponent { ); }; - renderShowMoreOption = (style) => { - const {highlight, theme} = this.props; - const {isLongPost} = this.state; - - if (!isLongPost) { - return null; - } - - const gradientColors = []; - if (highlight) { - gradientColors.push( - changeOpacity(theme.mentionHighlightBg, 0), - changeOpacity(theme.mentionHighlightBg, 0.15), - changeOpacity(theme.mentionHighlightBg, 0.5), - ); - } else { - gradientColors.push( - changeOpacity(theme.centerChannelBg, 0), - changeOpacity(theme.centerChannelBg, 0.75), - theme.centerChannelBg, - ); - } - - return ( - - - - - - - - {'+'} - - - - - - - - ); - }; - render() { const {formatMessage} = this.context.intl; const { hasBeenDeleted, hasBeenEdited, + highlight, isFailed, isPending, isPostAddChannelMember, @@ -496,7 +443,12 @@ export default class PostBody extends PureComponent { > {messageComponent} - {this.renderShowMoreOption(style)} + {isLongPost && + + } {this.renderPostAdditionalContent(blockStyles, messageStyle, textStyles)} {this.renderFileAttachments()} @@ -562,56 +514,5 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => { systemMessage: { opacity: 0.6, }, - showMoreGradient: { - flex: 1, - height: 50, - position: 'absolute', - top: -50, - width: '100%', - }, - showMoreContainer: { - alignItems: 'center', - justifyContent: 'center', - flex: 1, - flexDirection: 'row', - position: 'relative', - top: -7.5, - }, - showMoreDividerLeft: { - backgroundColor: changeOpacity(theme.centerChannelColor, 0.2), - flex: 1, - height: 1, - marginRight: 10, - }, - showMoreDividerRight: { - backgroundColor: changeOpacity(theme.centerChannelColor, 0.2), - flex: 1, - height: 1, - marginLeft: 10, - }, - showMoreButtonContainer: { - backgroundColor: theme.centerChannelBg, - borderColor: changeOpacity(theme.centerChannelColor, 0.2), - borderRadius: 4, - borderWidth: 1, - height: 37, - paddingHorizontal: 10, - }, - showMoreButton: { - alignItems: 'center', - flex: 1, - flexDirection: 'row', - }, - showMorePlusSign: { - color: theme.linkColor, - fontSize: 16, - fontWeight: '600', - marginRight: 8, - }, - showMoreText: { - color: theme.linkColor, - fontSize: 13, - fontWeight: '600', - }, }; }); diff --git a/app/components/show_more_button/__snapshots__/show_more_button.test.js.snap b/app/components/show_more_button/__snapshots__/show_more_button.test.js.snap new file mode 100644 index 000000000..283dae457 --- /dev/null +++ b/app/components/show_more_button/__snapshots__/show_more_button.test.js.snap @@ -0,0 +1,893 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ShowMoreButton should match, button snapshot 1`] = ` + + + + + + + +`; + +exports[`ShowMoreButton should match, button snapshot 2`] = ` + + + - + + + +`; + +exports[`ShowMoreButton should match, full snapshot 1`] = ` +ShallowWrapper { + "length": 1, + Symbol(enzyme.__root__): [Circular], + Symbol(enzyme.__unrendered__): , + Symbol(enzyme.__renderer__): Object { + "batchedUpdates": [Function], + "getNode": [Function], + "render": [Function], + "simulateEvent": [Function], + "unmount": [Function], + }, + Symbol(enzyme.__node__): Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + + + + + + + + + + + + + , + ], + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "colors": Array [ + "rgba(47,62,78,0)", + "rgba(47,62,78,0.75)", + "#2f3e4e", + ], + "locations": Array [ + 0, + 0.7, + 1, + ], + "style": Object { + "flex": 1, + "height": 50, + "position": "absolute", + "top": -50, + "width": "100%", + }, + }, + "ref": null, + "rendered": null, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + + + + + + + + + , + , + ], + "style": Object { + "alignItems": "center", + "flex": 1, + "flexDirection": "row", + "justifyContent": "center", + "position": "relative", + "top": 10, + }, + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "style": Object { + "backgroundColor": "rgba(221,221,221,0.2)", + "flex": 1, + "height": 1, + "marginRight": 10, + }, + }, + "ref": null, + "rendered": null, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "activeOpacity": 0.2, + "children": + + + + + + , + "onPress": [MockFunction], + "style": Object { + "backgroundColor": "#2f3e4e", + "borderColor": "rgba(221,221,221,0.2)", + "borderRadius": 4, + "borderWidth": 1, + "height": 37, + "paddingHorizontal": 10, + }, + }, + "ref": null, + "rendered": Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + + + + , + , + ], + "style": Object { + "alignItems": "center", + "flex": 1, + "flexDirection": "row", + }, + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "accessible": true, + "allowFontScaling": true, + "children": "+", + "ellipsizeMode": "tail", + "style": Object { + "color": undefined, + "fontSize": 16, + "fontWeight": "600", + "marginRight": 8, + }, + }, + "ref": null, + "rendered": "+", + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "defaultMessage": "Show More", + "id": "post_info.message.show_more", + "style": Object { + "color": undefined, + "fontSize": 13, + "fontWeight": "600", + }, + }, + "ref": null, + "rendered": null, + "type": [Function], + }, + ], + "type": [Function], + }, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "style": Object { + "backgroundColor": "rgba(221,221,221,0.2)", + "flex": 1, + "height": 1, + "marginLeft": 10, + }, + }, + "ref": null, + "rendered": null, + "type": [Function], + }, + ], + "type": [Function], + }, + ], + "type": [Function], + }, + Symbol(enzyme.__nodes__): Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + + + + + + + + + + + + + , + ], + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "colors": Array [ + "rgba(47,62,78,0)", + "rgba(47,62,78,0.75)", + "#2f3e4e", + ], + "locations": Array [ + 0, + 0.7, + 1, + ], + "style": Object { + "flex": 1, + "height": 50, + "position": "absolute", + "top": -50, + "width": "100%", + }, + }, + "ref": null, + "rendered": null, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + , + + + + + + + + + , + , + ], + "style": Object { + "alignItems": "center", + "flex": 1, + "flexDirection": "row", + "justifyContent": "center", + "position": "relative", + "top": 10, + }, + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "style": Object { + "backgroundColor": "rgba(221,221,221,0.2)", + "flex": 1, + "height": 1, + "marginRight": 10, + }, + }, + "ref": null, + "rendered": null, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "activeOpacity": 0.2, + "children": + + + + + + , + "onPress": [MockFunction], + "style": Object { + "backgroundColor": "#2f3e4e", + "borderColor": "rgba(221,221,221,0.2)", + "borderRadius": 4, + "borderWidth": 1, + "height": 37, + "paddingHorizontal": 10, + }, + }, + "ref": null, + "rendered": Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "children": Array [ + + + + , + , + ], + "style": Object { + "alignItems": "center", + "flex": 1, + "flexDirection": "row", + }, + }, + "ref": null, + "rendered": Array [ + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "accessible": true, + "allowFontScaling": true, + "children": "+", + "ellipsizeMode": "tail", + "style": Object { + "color": undefined, + "fontSize": 16, + "fontWeight": "600", + "marginRight": 8, + }, + }, + "ref": null, + "rendered": "+", + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "defaultMessage": "Show More", + "id": "post_info.message.show_more", + "style": Object { + "color": undefined, + "fontSize": 13, + "fontWeight": "600", + }, + }, + "ref": null, + "rendered": null, + "type": [Function], + }, + ], + "type": [Function], + }, + "type": [Function], + }, + Object { + "instance": null, + "key": undefined, + "nodeType": "class", + "props": Object { + "style": Object { + "backgroundColor": "rgba(221,221,221,0.2)", + "flex": 1, + "height": 1, + "marginLeft": 10, + }, + }, + "ref": null, + "rendered": null, + "type": [Function], + }, + ], + "type": [Function], + }, + ], + "type": [Function], + }, + ], + Symbol(enzyme.__options__): Object { + "adapter": ReactSixteenAdapter { + "options": Object { + "enableComponentDidUpdateOnSetState": true, + }, + }, + }, +} +`; diff --git a/app/components/show_more_button/index.js b/app/components/show_more_button/index.js new file mode 100644 index 000000000..0002f53a2 --- /dev/null +++ b/app/components/show_more_button/index.js @@ -0,0 +1,16 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import {connect} from 'react-redux'; + +import {getTheme} from 'mattermost-redux/selectors/entities/preferences'; + +import ShowMoreButton from './show_more_button'; + +function mapStateToProps(state) { + return { + theme: getTheme(state), + }; +} + +export default connect(mapStateToProps)(ShowMoreButton); diff --git a/app/components/show_more_button/show_more_button.js b/app/components/show_more_button/show_more_button.js new file mode 100644 index 000000000..48e783378 --- /dev/null +++ b/app/components/show_more_button/show_more_button.js @@ -0,0 +1,142 @@ +// 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 {Text, TouchableOpacity, View} from 'react-native'; +import LinearGradient from 'react-native-linear-gradient'; + +import FormattedText from 'app/components/formatted_text'; + +import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; + +export default class ShowMoreButton extends PureComponent { + static propTypes = { + highlight: PropTypes.bool, + onPress: PropTypes.func.isRequired, + showMore: PropTypes.bool.isRequired, + theme: PropTypes.object.isRequired, + }; + + static defaultProps = { + showMore: true, + }; + + renderButton(showMore, style) { + let sign = '+'; + let textId = 'post_info.message.show_more'; + let textMessage = 'Show More'; + if (!showMore) { + sign = '-'; + textId = 'post_info.message.show_less'; + textMessage = 'Show Less'; + } + + return ( + + {sign} + + + ); + } + + render() { + const {highlight, showMore, theme} = this.props; + const style = getStyleSheet(theme, showMore); + + let gradientColors = [ + changeOpacity(theme.centerChannelBg, 0), + changeOpacity(theme.centerChannelBg, 0.75), + theme.centerChannelBg, + ]; + if (highlight) { + gradientColors = [ + changeOpacity(theme.mentionHighlightBg, 0), + changeOpacity(theme.mentionHighlightBg, 0.15), + changeOpacity(theme.mentionHighlightBg, 0.5), + ]; + } + + return ( + + {showMore && + + } + + + + {this.renderButton(showMore, style)} + + + + + ); + } +} + +const getStyleSheet = makeStyleSheetFromTheme((theme, showMore) => { + return { + gradient: { + flex: 1, + height: 50, + position: 'absolute', + top: -50, + width: '100%', + }, + container: { + alignItems: 'center', + justifyContent: 'center', + flex: 1, + flexDirection: 'row', + position: 'relative', + top: showMore ? -7.5 : 10, + }, + dividerLeft: { + backgroundColor: changeOpacity(theme.centerChannelColor, 0.2), + flex: 1, + height: 1, + marginRight: 10, + }, + buttonContainer: { + backgroundColor: theme.centerChannelBg, + borderColor: changeOpacity(theme.centerChannelColor, 0.2), + borderRadius: 4, + borderWidth: 1, + height: 37, + paddingHorizontal: 10, + }, + button: { + alignItems: 'center', + flex: 1, + flexDirection: 'row', + }, + sign: { + color: theme.linkColor, + fontSize: 16, + fontWeight: '600', + marginRight: 8, + }, + text: { + color: theme.linkColor, + fontSize: 13, + fontWeight: '600', + }, + dividerRight: { + backgroundColor: changeOpacity(theme.centerChannelColor, 0.2), + flex: 1, + height: 1, + marginLeft: 10, + }, + }; +}); diff --git a/app/components/show_more_button/show_more_button.test.js b/app/components/show_more_button/show_more_button.test.js new file mode 100644 index 000000000..7c63aa8cd --- /dev/null +++ b/app/components/show_more_button/show_more_button.test.js @@ -0,0 +1,64 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {TouchableOpacity} from 'react-native'; +import {configure, shallow} from 'enzyme'; +import Adapter from 'enzyme-adapter-react-16'; +configure({adapter: new Adapter()}); + +import LinearGradient from 'react-native-linear-gradient'; + +import ShowMoreButton from './show_more_button'; + +describe('ShowMoreButton', () => { + const baseProps = { + highlight: false, + onPress: jest.fn(), + showMore: true, + theme: { + centerChannelBg: '#2f3e4e', + centerChannelColor: '#dddddd', + }, + }; + + test('should match, full snapshot', () => { + const wrapper = shallow( + + ); + + expect(wrapper).toMatchSnapshot(); + }); + + test('should match, button snapshot', () => { + const wrapper = shallow( + + ); + + expect(wrapper.instance().renderButton(true, {button: {}, sign: {}, text: {}})).toMatchSnapshot(); + expect(wrapper.instance().renderButton(false, {button: {}, sign: {}, text: {}})).toMatchSnapshot(); + }); + + test('should LinearGradient exists', () => { + const wrapper = shallow( + + ); + + expect(wrapper.find(LinearGradient).exists()).toBe(true); + wrapper.setProps({showMore: false}); + expect(wrapper.find(LinearGradient).exists()).toBe(false); + }); + + test('should call props.onPress on press of TouchableOpacity', () => { + const onPress = jest.fn(); + const wrapper = shallow( + + ); + + wrapper.find(TouchableOpacity).props().onPress(); + expect(onPress).toHaveBeenCalledTimes(1); + }); +});