MM-14038 Access recent reactions from the long press menu (#4058)
* WIP: Implementing quick reaction functionality * Making closing animations smoother - Added haptic feedback + timeouts for a smooth flow * Fixing clipping of post options on android * updating snapshots * Refactoring code & fixing styles - Fixed paddings element sizes to comply with design specs - Replaced reaction image to one with a higher resolution - Made sizes of emojis to be conditional on deviceWidth * Apply suggestions from code review * Adding component to avoid extra re-renders * Refactoring function call in reaction_button Am ashamed i didn't catch this before pushing the rest Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
parent
536eb0311b
commit
d03d504d00
11 changed files with 860 additions and 135 deletions
|
|
@ -0,0 +1,529 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Reactions Should match snapshot with default emojis 1`] = `
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"height": 72,
|
||||
"justifyContent": "space-between",
|
||||
"paddingLeft": 12,
|
||||
"paddingRight": 12,
|
||||
}
|
||||
}
|
||||
>
|
||||
<ReactionButton
|
||||
addReaction={[Function]}
|
||||
containerSize={48}
|
||||
emoji="thumbsup"
|
||||
iconSize={32}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ReactionButton
|
||||
addReaction={[Function]}
|
||||
containerSize={48}
|
||||
emoji="smiley"
|
||||
iconSize={32}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ReactionButton
|
||||
addReaction={[Function]}
|
||||
containerSize={48}
|
||||
emoji="white_check_mark"
|
||||
iconSize={32}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ReactionButton
|
||||
addReaction={[Function]}
|
||||
containerSize={48}
|
||||
emoji="heart"
|
||||
iconSize={32}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ReactionButton
|
||||
addReaction={[Function]}
|
||||
containerSize={48}
|
||||
emoji="eyes"
|
||||
iconSize={32}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ReactionButton
|
||||
addReaction={[Function]}
|
||||
containerSize={48}
|
||||
emoji="raised_hands"
|
||||
iconSize={32}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<TouchableWithoutFeedback
|
||||
onPress={[MockFunction]}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"backgroundColor": "rgba(61,60,64,0.04)",
|
||||
"borderRadius": 4,
|
||||
"justifyContent": "center",
|
||||
},
|
||||
Object {
|
||||
"height": 48,
|
||||
"width": 48,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<Image
|
||||
height={32}
|
||||
source={
|
||||
Object {
|
||||
"testUri": "../../../dist/assets/images/icons/reaction.png",
|
||||
}
|
||||
}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"tintColor": "#3d3c40",
|
||||
},
|
||||
]
|
||||
}
|
||||
width={32}
|
||||
/>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
</View>
|
||||
`;
|
||||
|
||||
exports[`Reactions Should match snapshot with default emojis 2`] = `
|
||||
<View
|
||||
style={
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"height": 72,
|
||||
"justifyContent": "space-between",
|
||||
"paddingLeft": 12,
|
||||
"paddingRight": 12,
|
||||
}
|
||||
}
|
||||
>
|
||||
<ReactionButton
|
||||
addReaction={[Function]}
|
||||
containerSize={48}
|
||||
emoji="rocket"
|
||||
iconSize={32}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ReactionButton
|
||||
addReaction={[Function]}
|
||||
containerSize={48}
|
||||
emoji="100"
|
||||
iconSize={32}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ReactionButton
|
||||
addReaction={[Function]}
|
||||
containerSize={48}
|
||||
emoji="thumbsup"
|
||||
iconSize={32}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ReactionButton
|
||||
addReaction={[Function]}
|
||||
containerSize={48}
|
||||
emoji="smiley"
|
||||
iconSize={32}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ReactionButton
|
||||
addReaction={[Function]}
|
||||
containerSize={48}
|
||||
emoji="white_check_mark"
|
||||
iconSize={32}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<ReactionButton
|
||||
addReaction={[Function]}
|
||||
containerSize={48}
|
||||
emoji="heart"
|
||||
iconSize={32}
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<TouchableWithoutFeedback
|
||||
onPress={[MockFunction]}
|
||||
>
|
||||
<View
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"alignItems": "center",
|
||||
"backgroundColor": "rgba(61,60,64,0.04)",
|
||||
"borderRadius": 4,
|
||||
"justifyContent": "center",
|
||||
},
|
||||
Object {
|
||||
"height": 48,
|
||||
"width": 48,
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<Image
|
||||
height={32}
|
||||
source={
|
||||
Object {
|
||||
"testUri": "../../../dist/assets/images/icons/reaction.png",
|
||||
}
|
||||
}
|
||||
style={
|
||||
Array [
|
||||
Object {
|
||||
"tintColor": "#3d3c40",
|
||||
},
|
||||
]
|
||||
}
|
||||
width={32}
|
||||
/>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
</View>
|
||||
`;
|
||||
18
app/components/reaction_picker/index.js
Normal file
18
app/components/reaction_picker/index.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// 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 {getDimensions} from 'app/selectors/device';
|
||||
import ReactionPicker from './reaction_picker';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
const {deviceWidth} = getDimensions(state);
|
||||
return {
|
||||
theme: getTheme(state),
|
||||
recentEmojis: state.views.recentEmojis,
|
||||
deviceWidth,
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(ReactionPicker);
|
||||
98
app/components/reaction_picker/reaction_button.js
Normal file
98
app/components/reaction_picker/reaction_button.js
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
// 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 {
|
||||
View,
|
||||
TouchableWithoutFeedback,
|
||||
} from 'react-native';
|
||||
|
||||
import Emoji from 'app/components/emoji';
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
import {hapticFeedback} from 'app/utils/general';
|
||||
import {
|
||||
LARGE_CONTAINER_SIZE,
|
||||
LARGE_ICON_SIZE,
|
||||
} from 'app/constants/reaction_picker';
|
||||
|
||||
export default class ReactionButton extends PureComponent {
|
||||
static propTypes = {
|
||||
addReaction: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
emoji: PropTypes.string.isRequired,
|
||||
iconSize: PropTypes.number,
|
||||
containerSize: PropTypes.number,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
iconSize: LARGE_ICON_SIZE,
|
||||
containerSize: LARGE_CONTAINER_SIZE,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
isSelected: false,
|
||||
};
|
||||
}
|
||||
|
||||
handlePress = () => {
|
||||
hapticFeedback();
|
||||
this.setState({isSelected: true}, () => {
|
||||
this.props.addReaction(this.props.emoji);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
theme,
|
||||
emoji,
|
||||
iconSize,
|
||||
containerSize,
|
||||
} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
return (
|
||||
<TouchableWithoutFeedback
|
||||
key={emoji}
|
||||
onPress={this.handlePress}
|
||||
>
|
||||
<View
|
||||
style={[
|
||||
style.reactionContainer,
|
||||
this.state.isSelected ? style.highlight : null,
|
||||
{
|
||||
width: containerSize,
|
||||
height: containerSize,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Emoji
|
||||
emojiName={emoji}
|
||||
textStyle={style.emoji}
|
||||
size={iconSize}
|
||||
/>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
emoji: {
|
||||
color: '#000',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
highlight: {
|
||||
backgroundColor: changeOpacity(theme.linkColor, 0.1),
|
||||
},
|
||||
reactionContainer: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.04),
|
||||
borderRadius: 4,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
};
|
||||
});
|
||||
117
app/components/reaction_picker/reaction_picker.js
Normal file
117
app/components/reaction_picker/reaction_picker.js
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// 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 {
|
||||
Image,
|
||||
View,
|
||||
TouchableWithoutFeedback,
|
||||
} from 'react-native';
|
||||
|
||||
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
import addReactionIcon from 'assets/images/icons/reaction.png';
|
||||
import ReactionButton from './reaction_button';
|
||||
import {
|
||||
REACTION_PICKER_HEIGHT,
|
||||
DEFAULT_EMOJIS,
|
||||
SMALL_ICON_BREAKPOINT,
|
||||
SMALL_CONTAINER_SIZE,
|
||||
LARGE_CONTAINER_SIZE,
|
||||
SMALL_ICON_SIZE,
|
||||
LARGE_ICON_SIZE,
|
||||
} from 'app/constants/reaction_picker';
|
||||
|
||||
export default class ReactionPicker extends PureComponent {
|
||||
static propTypes = {
|
||||
addReaction: PropTypes.func.isRequired,
|
||||
openReactionScreen: PropTypes.func.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
recentEmojis: PropTypes.array,
|
||||
deviceWidth: PropTypes.number,
|
||||
}
|
||||
|
||||
handlePress = (emoji) => {
|
||||
this.props.addReaction(emoji);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
theme,
|
||||
deviceWidth,
|
||||
} = this.props;
|
||||
const style = getStyleSheet(theme);
|
||||
const isSmallDevice = deviceWidth < SMALL_ICON_BREAKPOINT;
|
||||
|
||||
let containerSize = LARGE_CONTAINER_SIZE;
|
||||
let iconSize = LARGE_ICON_SIZE;
|
||||
|
||||
if (isSmallDevice) {
|
||||
containerSize = SMALL_CONTAINER_SIZE;
|
||||
iconSize = SMALL_ICON_SIZE;
|
||||
}
|
||||
|
||||
// Mixing recent emojis with default list and removing duplicates
|
||||
const emojis = Array.from(new Set(this.props.recentEmojis.concat(DEFAULT_EMOJIS))).splice(0, 6);
|
||||
|
||||
const list = emojis.map((emoji) => {
|
||||
return (
|
||||
<ReactionButton
|
||||
key={emoji}
|
||||
theme={theme}
|
||||
addReaction={this.handlePress}
|
||||
emoji={emoji}
|
||||
iconSize={iconSize}
|
||||
containerSize={containerSize}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<View style={style.reactionListContainer}>
|
||||
{list}
|
||||
<TouchableWithoutFeedback onPress={this.props.openReactionScreen}>
|
||||
<View
|
||||
style={[
|
||||
style.reactionContainer,
|
||||
{
|
||||
width: containerSize,
|
||||
height: containerSize,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Image
|
||||
source={addReactionIcon}
|
||||
style={[style.iconImage]}
|
||||
width={iconSize}
|
||||
height={iconSize}
|
||||
/>
|
||||
</View>
|
||||
</TouchableWithoutFeedback>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
iconImage: {
|
||||
tintColor: theme.centerChannelColor,
|
||||
},
|
||||
reactionListContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
paddingLeft: 12,
|
||||
paddingRight: 12,
|
||||
height: REACTION_PICKER_HEIGHT,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
reactionContainer: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.04),
|
||||
borderRadius: 4,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
};
|
||||
});
|
||||
35
app/components/reaction_picker/reaction_picker.test.js
Normal file
35
app/components/reaction_picker/reaction_picker.test.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {shallow} from 'enzyme';
|
||||
|
||||
import ReactionPicker from './reaction_picker';
|
||||
|
||||
import Preferences from 'mattermost-redux/constants/preferences';
|
||||
|
||||
describe('Reactions', () => {
|
||||
const baseProps = {
|
||||
theme: Preferences.THEMES.default,
|
||||
recentEmojis: [],
|
||||
addReaction: jest.fn(),
|
||||
openReactionScreen: jest.fn(),
|
||||
};
|
||||
|
||||
test('Should match snapshot with default emojis', () => {
|
||||
const wrapper = shallow(<ReactionPicker {...baseProps}/>);
|
||||
expect(wrapper.getElement()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('Should match snapshot with default emojis', () => {
|
||||
const props = {
|
||||
...baseProps,
|
||||
recentEmojis: [
|
||||
'rocket',
|
||||
'100',
|
||||
],
|
||||
};
|
||||
const wrapper = shallow(<ReactionPicker {...props}/>);
|
||||
expect(wrapper.getElement()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
@ -153,7 +153,7 @@ exports[`Reactions should match snapshot 1`] = `
|
|||
}
|
||||
style={
|
||||
Object {
|
||||
"height": 20,
|
||||
"height": 23,
|
||||
"tintColor": "rgba(61,60,64,0.5)",
|
||||
"width": 23,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,7 +186,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
|||
addReaction: {
|
||||
tintColor: changeOpacity(theme.centerChannelColor, 0.5),
|
||||
width: 23,
|
||||
height: 20,
|
||||
height: 23,
|
||||
},
|
||||
reaction: {
|
||||
alignItems: 'center',
|
||||
|
|
|
|||
17
app/constants/reaction_picker.js
Normal file
17
app/constants/reaction_picker.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
export const REACTION_PICKER_HEIGHT = 72;
|
||||
export const SMALL_ICON_BREAKPOINT = 410;
|
||||
export const SMALL_CONTAINER_SIZE = 44;
|
||||
export const LARGE_CONTAINER_SIZE = 48;
|
||||
export const SMALL_ICON_SIZE = 28;
|
||||
export const LARGE_ICON_SIZE = 32;
|
||||
export const DEFAULT_EMOJIS = [
|
||||
'thumbsup',
|
||||
'smiley',
|
||||
'white_check_mark',
|
||||
'heart',
|
||||
'eyes',
|
||||
'raised_hands',
|
||||
];
|
||||
|
|
@ -10,8 +10,8 @@ exports[`PostOptions should match snapshot, no option for system message to user
|
|||
>
|
||||
<Connect(SlideUpPanel)
|
||||
allowStayMiddle={false}
|
||||
initialPosition={300}
|
||||
marginFromTop={300}
|
||||
initialPosition={322}
|
||||
marginFromTop={278}
|
||||
onRequestClose={[Function]}
|
||||
theme={
|
||||
Object {
|
||||
|
|
@ -43,6 +43,10 @@ exports[`PostOptions should match snapshot, no option for system message to user
|
|||
}
|
||||
}
|
||||
>
|
||||
<Connect(ReactionPicker)
|
||||
addReaction={[Function]}
|
||||
openReactionScreen={[Function]}
|
||||
/>
|
||||
<PostOption
|
||||
destructive={false}
|
||||
icon="reply"
|
||||
|
|
@ -79,42 +83,6 @@ exports[`PostOptions should match snapshot, no option for system message to user
|
|||
}
|
||||
}
|
||||
/>
|
||||
<PostOption
|
||||
destructive={false}
|
||||
icon="emoji"
|
||||
isLandscape={false}
|
||||
onPress={[Function]}
|
||||
text="Add Reaction"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<PostOption
|
||||
icon="bookmark"
|
||||
isLandscape={false}
|
||||
|
|
@ -236,8 +204,8 @@ exports[`PostOptions should match snapshot, showing Delete option only for syste
|
|||
>
|
||||
<Connect(SlideUpPanel)
|
||||
allowStayMiddle={false}
|
||||
initialPosition={350}
|
||||
marginFromTop={250}
|
||||
initialPosition={372}
|
||||
marginFromTop={228}
|
||||
onRequestClose={[Function]}
|
||||
theme={
|
||||
Object {
|
||||
|
|
@ -269,6 +237,10 @@ exports[`PostOptions should match snapshot, showing Delete option only for syste
|
|||
}
|
||||
}
|
||||
>
|
||||
<Connect(ReactionPicker)
|
||||
addReaction={[Function]}
|
||||
openReactionScreen={[Function]}
|
||||
/>
|
||||
<PostOption
|
||||
destructive={false}
|
||||
icon="reply"
|
||||
|
|
@ -305,42 +277,6 @@ exports[`PostOptions should match snapshot, showing Delete option only for syste
|
|||
}
|
||||
}
|
||||
/>
|
||||
<PostOption
|
||||
destructive={false}
|
||||
icon="emoji"
|
||||
isLandscape={false}
|
||||
onPress={[Function]}
|
||||
text="Add Reaction"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<PostOption
|
||||
icon="bookmark"
|
||||
isLandscape={false}
|
||||
|
|
@ -498,8 +434,8 @@ exports[`PostOptions should match snapshot, showing all possible options 1`] = `
|
|||
>
|
||||
<Connect(SlideUpPanel)
|
||||
allowStayMiddle={false}
|
||||
initialPosition={350}
|
||||
marginFromTop={250}
|
||||
initialPosition={372}
|
||||
marginFromTop={228}
|
||||
onRequestClose={[Function]}
|
||||
theme={
|
||||
Object {
|
||||
|
|
@ -531,6 +467,10 @@ exports[`PostOptions should match snapshot, showing all possible options 1`] = `
|
|||
}
|
||||
}
|
||||
>
|
||||
<Connect(ReactionPicker)
|
||||
addReaction={[Function]}
|
||||
openReactionScreen={[Function]}
|
||||
/>
|
||||
<PostOption
|
||||
destructive={false}
|
||||
icon="reply"
|
||||
|
|
@ -567,42 +507,6 @@ exports[`PostOptions should match snapshot, showing all possible options 1`] = `
|
|||
}
|
||||
}
|
||||
/>
|
||||
<PostOption
|
||||
destructive={false}
|
||||
icon="emoji"
|
||||
isLandscape={false}
|
||||
onPress={[Function]}
|
||||
text="Add Reaction"
|
||||
theme={
|
||||
Object {
|
||||
"awayIndicator": "#ffbc42",
|
||||
"buttonBg": "#166de0",
|
||||
"buttonColor": "#ffffff",
|
||||
"centerChannelBg": "#ffffff",
|
||||
"centerChannelColor": "#3d3c40",
|
||||
"codeTheme": "github",
|
||||
"dndIndicator": "#f74343",
|
||||
"errorTextColor": "#fd5960",
|
||||
"linkColor": "#2389d7",
|
||||
"mentionBg": "#ffffff",
|
||||
"mentionBj": "#ffffff",
|
||||
"mentionColor": "#145dbf",
|
||||
"mentionHighlightBg": "#ffe577",
|
||||
"mentionHighlightLink": "#166de0",
|
||||
"newMessageSeparator": "#ff8800",
|
||||
"onlineIndicator": "#06d6a0",
|
||||
"sidebarBg": "#145dbf",
|
||||
"sidebarHeaderBg": "#1153ab",
|
||||
"sidebarHeaderTextColor": "#ffffff",
|
||||
"sidebarText": "#ffffff",
|
||||
"sidebarTextActiveBorder": "#579eff",
|
||||
"sidebarTextActiveColor": "#ffffff",
|
||||
"sidebarTextHoverBg": "#4578bf",
|
||||
"sidebarUnreadText": "#ffffff",
|
||||
"type": "Mattermost",
|
||||
}
|
||||
}
|
||||
/>
|
||||
<PostOption
|
||||
icon="bookmark"
|
||||
isLandscape={false}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ import {showModal, dismissModal} from 'app/actions/navigation';
|
|||
|
||||
import {isSystemMessage} from '@mm-redux/utils/post_utils';
|
||||
import {OPTION_HEIGHT, getInitialPosition} from './post_options_utils';
|
||||
import {REACTION_PICKER_HEIGHT} from 'app/constants/reaction_picker';
|
||||
import ReactionPicker from 'app/components/reaction_picker';
|
||||
|
||||
import PostOption from './post_option';
|
||||
|
||||
export default class PostOptions extends PureComponent {
|
||||
|
|
@ -86,21 +89,6 @@ export default class PostOptions extends PureComponent {
|
|||
);
|
||||
}
|
||||
|
||||
getAddReactionOption = () => {
|
||||
const {canAddReaction} = this.props;
|
||||
|
||||
if (canAddReaction) {
|
||||
const key = 'reaction';
|
||||
const icon = 'emoji';
|
||||
const message = {id: t('mobile.post_info.add_reaction'), defaultMessage: 'Add Reaction'};
|
||||
const onPress = this.handleAddReaction;
|
||||
|
||||
return this.getOption(key, icon, message, onPress);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
getReplyOption = () => {
|
||||
const {canReply} = this.props;
|
||||
|
||||
|
|
@ -249,7 +237,6 @@ export default class PostOptions extends PureComponent {
|
|||
getPostOptions = () => {
|
||||
const actions = [
|
||||
this.getReplyOption(),
|
||||
this.getAddReactionOption(),
|
||||
this.getMarkAsUnreadOption(),
|
||||
this.getCopyPermalink(),
|
||||
this.getFlagOption(),
|
||||
|
|
@ -262,7 +249,7 @@ export default class PostOptions extends PureComponent {
|
|||
return actions.filter((a) => a !== null);
|
||||
};
|
||||
|
||||
handleAddReaction = () => {
|
||||
handleAddReactionScreen = () => {
|
||||
const {theme} = this.props;
|
||||
const {formatMessage} = this.context.intl;
|
||||
|
||||
|
|
@ -287,6 +274,12 @@ export default class PostOptions extends PureComponent {
|
|||
});
|
||||
};
|
||||
|
||||
handleAddReaction = (emoji) => {
|
||||
this.closeWithAnimation(() => {
|
||||
this.handleAddReactionToPost(emoji);
|
||||
});
|
||||
}
|
||||
|
||||
handleAddReactionToPost = (emoji) => {
|
||||
const {actions, post} = this.props;
|
||||
|
||||
|
|
@ -402,13 +395,26 @@ export default class PostOptions extends PureComponent {
|
|||
};
|
||||
|
||||
render() {
|
||||
const {deviceHeight, theme} = this.props;
|
||||
const {deviceHeight, theme, canAddReaction} = this.props;
|
||||
const options = this.getPostOptions();
|
||||
let reactionHeight = 0;
|
||||
let reactionPicker;
|
||||
|
||||
if (!options || !options.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const marginFromTop = deviceHeight - BOTTOM_MARGIN - ((options.length + 1) * OPTION_HEIGHT);
|
||||
if (canAddReaction) {
|
||||
reactionHeight = REACTION_PICKER_HEIGHT;
|
||||
reactionPicker = (
|
||||
<ReactionPicker
|
||||
addReaction={this.handleAddReaction}
|
||||
openReactionScreen={this.handleAddReactionScreen}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const marginFromTop = deviceHeight - BOTTOM_MARGIN - ((options.length + 1) * OPTION_HEIGHT) - reactionHeight;
|
||||
const initialPosition = getInitialPosition(deviceHeight, marginFromTop);
|
||||
|
||||
return (
|
||||
|
|
@ -422,6 +428,7 @@ export default class PostOptions extends PureComponent {
|
|||
key={marginFromTop}
|
||||
theme={theme}
|
||||
>
|
||||
{reactionPicker}
|
||||
{options}
|
||||
</SlideUpPanel>
|
||||
</View>
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 929 B After Width: | Height: | Size: 2.1 KiB |
Loading…
Reference in a new issue