diff --git a/app/components/emoji/emoji.js b/app/components/emoji/emoji.js
index c2112dbe2..dcb6b2126 100644
--- a/app/components/emoji/emoji.js
+++ b/app/components/emoji/emoji.js
@@ -14,13 +14,6 @@ import {
import CustomPropTypes from 'app/constants/custom_prop_types';
import ImageCacheManager from 'app/utils/image_cache_manager';
-const scaleEmojiBasedOnDevice = (size) => {
- if (Platform.OS === 'ios') {
- return size * 1.1; // slightly larger emojis look better on ios
- }
- return size * PixelRatio.get();
-};
-
export default class Emoji extends React.PureComponent {
static propTypes = {
@@ -60,30 +53,28 @@ export default class Emoji extends React.PureComponent {
this.state = {
imageUrl: null,
- originalWidth: 0,
- originalHeight: 0,
};
}
componentWillMount() {
+ const {displayTextOnly, imageUrl} = this.props;
this.mounted = true;
- if (!this.props.displayTextOnly && this.props.imageUrl) {
- ImageCacheManager.cache(this.props.imageUrl, this.props.imageUrl, this.updateImageHeight);
+ if (!displayTextOnly && imageUrl) {
+ ImageCacheManager.cache(imageUrl, imageUrl, this.setImageUrl);
}
}
componentWillReceiveProps(nextProps) {
- if (nextProps.emojiName !== this.props.emojiName) {
+ const {displayTextOnly, emojiName, imageUrl} = nextProps;
+ if (emojiName !== this.props.emojiName) {
this.setState({
imageUrl: null,
- originalWidth: 0,
- originalHeight: 0,
});
}
- if (!nextProps.displayTextOnly && nextProps.imageUrl &&
- nextProps.imageUrl !== this.props.imageUrl) {
- ImageCacheManager.cache(nextProps.imageUrl, nextProps.imageUrl, this.updateImageHeight);
+ if (!displayTextOnly && imageUrl &&
+ imageUrl !== this.props.imageUrl) {
+ ImageCacheManager.cache(imageUrl, imageUrl, this.setImageUrl);
}
}
@@ -91,21 +82,15 @@ export default class Emoji extends React.PureComponent {
this.mounted = false;
}
- updateImageHeight = (imageUrl) => {
+ setImageUrl = (imageUrl) => {
let prefix = '';
if (Platform.OS === 'android') {
prefix = 'file://';
}
const uri = `${prefix}${imageUrl}`;
- Image.getSize(uri, (originalWidth, originalHeight) => {
- if (this.mounted) {
- this.setState({
- imageUrl: uri,
- originalWidth,
- originalHeight,
- });
- }
+ this.setState({
+ imageUrl: uri,
});
};
@@ -122,26 +107,15 @@ export default class Emoji extends React.PureComponent {
if (!size && textStyle) {
const flatten = StyleSheet.flatten(textStyle);
fontSize = flatten.fontSize;
- size = scaleEmojiBasedOnDevice(fontSize);
+ size = fontSize * (Platform.OS === 'android' ? PixelRatio.get() : 1);
}
if (displayTextOnly) {
return {literal};
}
- let width = size;
- let height = size;
- let {originalHeight, originalWidth} = this.state;
- originalHeight = scaleEmojiBasedOnDevice(originalHeight);
- originalWidth = scaleEmojiBasedOnDevice(originalWidth);
- if (originalHeight && originalWidth) {
- if (originalWidth > originalHeight) {
- height = (size * originalHeight) / originalWidth;
- } else if (originalWidth < originalHeight) {
- // This may cause text to reflow, but its impossible to add a horizontal margin
- width = (size * originalWidth) / originalHeight;
- }
- }
+ const width = size;
+ const height = size;
let marginTop = 0;
if (textStyle) {
diff --git a/app/components/emoji_picker/emoji_picker.js b/app/components/emoji_picker/emoji_picker.js
index 10a391c0a..509cf781d 100644
--- a/app/components/emoji_picker/emoji_picker.js
+++ b/app/components/emoji_picker/emoji_picker.js
@@ -407,6 +407,7 @@ export default class EmojiPicker extends PureComponent {
renderItem={this.flatListRenderItem}
pageSize={10}
initialListSize={10}
+ removeClippedSubviews={true}
/>
);
} else {
@@ -558,6 +559,7 @@ const getStyleSheetFromTheme = makeStyleSheetFromTheme((theme) => {
borderLeftColor: changeOpacity(theme.centerChannelColor, 0.2),
borderRightWidth: 1,
borderRightColor: changeOpacity(theme.centerChannelColor, 0.2),
+ overflow: 'hidden',
},
listView: {
backgroundColor: theme.centerChannelBg,
diff --git a/app/components/emoji_picker/emoji_picker_row.js b/app/components/emoji_picker/emoji_picker_row.js
index 0c1937bab..4f69eccac 100644
--- a/app/components/emoji_picker/emoji_picker_row.js
+++ b/app/components/emoji_picker/emoji_picker_row.js
@@ -64,7 +64,7 @@ export default class EmojiPickerRow extends Component {
/>
);
- }
+ };
render() {
const {emojiGutter, items} = this.props;
@@ -86,6 +86,7 @@ const styles = StyleSheet.create({
emoji: {
alignItems: 'center',
justifyContent: 'center',
+ overflow: 'hidden',
},
emojiLeft: {
marginLeft: 0,
diff --git a/app/components/options_context/options_context.android.js b/app/components/options_context/options_context.android.js
index 2f1c63a93..f0249d87c 100644
--- a/app/components/options_context/options_context.android.js
+++ b/app/components/options_context/options_context.android.js
@@ -8,7 +8,7 @@ import RNBottomSheet from 'react-native-bottom-sheet';
export default class OptionsContext extends PureComponent {
static propTypes = {
- actions: PropTypes.array,
+ getPostActions: PropTypes.func,
cancelText: PropTypes.string,
children: PropTypes.node.isRequired,
onPress: PropTypes.func.isRequired,
@@ -16,13 +16,13 @@ export default class OptionsContext extends PureComponent {
};
static defaultProps = {
- actions: [],
+ getPostActions: () => [],
cancelText: 'Cancel',
};
show = (additionalAction) => {
- const {actions, cancelText} = this.props;
- const nextActions = [...actions];
+ const {getPostActions, cancelText} = this.props;
+ const nextActions = getPostActions();
if (additionalAction && !additionalAction.nativeEvent && additionalAction.text) {
const copyPostIndex = nextActions.findIndex((action) => action.copyPost);
nextActions.splice(copyPostIndex + 1, 0, additionalAction);
@@ -45,11 +45,11 @@ export default class OptionsContext extends PureComponent {
};
handleHideUnderlay = () => {
- this.props.toggleSelected(false, this.props.actions.length > 0);
+ this.props.toggleSelected(false, this.props.getPostActions().length > 0);
};
handleShowUnderlay = () => {
- this.props.toggleSelected(true, this.props.actions.length > 0);
+ this.props.toggleSelected(true, this.props.getPostActions().length > 0);
};
render() {
diff --git a/app/components/options_context/options_context.ios.js b/app/components/options_context/options_context.ios.js
index 9c3fe3896..0e89b3f2d 100644
--- a/app/components/options_context/options_context.ios.js
+++ b/app/components/options_context/options_context.ios.js
@@ -8,14 +8,14 @@ import ToolTip from 'app/components/tooltip';
export default class OptionsContext extends PureComponent {
static propTypes = {
- actions: PropTypes.array,
+ getPostActions: PropTypes.func,
children: PropTypes.node.isRequired,
onPress: PropTypes.func.isRequired,
toggleSelected: PropTypes.func.isRequired,
};
static defaultProps = {
- actions: [],
+ getPostActions: () => [],
additionalActions: [],
};
@@ -23,16 +23,10 @@ export default class OptionsContext extends PureComponent {
super(props);
this.state = {
- actions: props.actions,
+ actions: props.getPostActions(),
};
}
- componentWillReceiveProps(nextProps) {
- if (this.props.actions !== nextProps.actions) {
- this.setState({actions: nextProps.actions});
- }
- }
-
handleHideUnderlay = () => {
if (!this.isShowing) {
this.props.toggleSelected(false, false);
@@ -45,11 +39,11 @@ export default class OptionsContext extends PureComponent {
handleHide = () => {
this.isShowing = false;
- this.props.toggleSelected(false, this.props.actions.length > 0);
+ this.props.toggleSelected(false, this.props.getPostActions().length > 0);
};
handleShow = () => {
- this.isShowing = this.props.actions.length > 0;
+ this.isShowing = this.props.getPostActions().length > 0;
this.props.toggleSelected(true, this.isShowing);
};
@@ -59,12 +53,12 @@ export default class OptionsContext extends PureComponent {
}
this.setState({
- actions: this.props.actions,
+ actions: this.props.getPostActions(),
});
};
show = (additionalAction) => {
- const nextActions = [...this.props.actions];
+ const nextActions = this.props.getPostActions();
if (additionalAction && additionalAction.text && !additionalAction.nativeEvent) {
const copyPostIndex = nextActions.findIndex((action) => action.copyPost);
nextActions.splice(copyPostIndex + 1, 0, additionalAction);
@@ -80,7 +74,7 @@ export default class OptionsContext extends PureComponent {
};
handlePress = () => {
- this.props.toggleSelected(false, this.props.actions.length > 0);
+ this.props.toggleSelected(false, this.props.getPostActions().length > 0);
this.props.onPress();
};
diff --git a/app/components/post/index.js b/app/components/post/index.js
index d719c3bd5..97d74b1cf 100644
--- a/app/components/post/index.js
+++ b/app/components/post/index.js
@@ -5,6 +5,7 @@ import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {createPost, deletePost, removePost} from 'mattermost-redux/actions/posts';
+import {General, Posts} from 'mattermost-redux/constants';
import {getCurrentChannelId, isCurrentChannelReadOnly} from 'mattermost-redux/selectors/entities/channels';
import {getPost, makeGetCommentCountForPost} from 'mattermost-redux/selectors/entities/posts';
import {getCurrentUserId, getCurrentUserRoles} from 'mattermost-redux/selectors/entities/users';
@@ -16,8 +17,6 @@ import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general
import {insertToDraft, setPostTooltipVisible} from 'app/actions/views/channel';
import {addReaction} from 'app/actions/views/emoji';
-import {getDimensions} from 'app/selectors/device';
-import {Posts} from 'mattermost-redux/constants';
import Post from './post';
@@ -78,33 +77,36 @@ function makeMapStateToProps() {
}
}
}
- const {deviceWidth} = getDimensions(state);
const isAdmin = checkIsAdmin(roles);
const isSystemAdmin = checkIsSystemAdmin(roles);
let canDelete = false;
let canEdit = false;
+ let canEditUntil = -1;
if (post) {
canDelete = canDeletePost(state, config, license, currentTeamId, currentChannelId, currentUserId, post, isAdmin, isSystemAdmin);
canEdit = canEditPost(state, config, license, currentTeamId, currentChannelId, currentUserId, post);
+ if (canEdit && license.IsLicensed === 'true' &&
+ (config.AllowEditPost === General.ALLOW_EDIT_POST_TIME_LIMIT || (config.PostEditTimeLimit !== -1 && config.PostEditTimeLimit !== '-1'))
+ ) {
+ canEditUntil = post.create_at + (config.PostEditTimeLimit * 1000);
+ }
}
return {
channelIsReadOnly: isCurrentChannelReadOnly(state),
- config,
canDelete,
canEdit,
+ canEditUntil,
currentTeamUrl: getCurrentTeamUrl(state),
currentUserId,
- deviceWidth,
post,
isFirstReply,
isLastReply,
consecutivePost: isConsecutivePost(state, ownProps),
hasComments: getCommentCountForPost(state, {post}) > 0,
commentedOnPost,
- license,
theme: getTheme(state),
isFlagged: isPostFlagged(post.id, myPreferences),
};
diff --git a/app/components/post/post.js b/app/components/post/post.js
index 2dbfa03bc..37bea8265 100644
--- a/app/components/post/post.js
+++ b/app/components/post/post.js
@@ -25,9 +25,8 @@ import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {getToolTipVisible} from 'app/utils/tooltip';
import {Posts} from 'mattermost-redux/constants';
-import DelayedAction from 'mattermost-redux/utils/delayed_action';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
-import {editDisable, isPostEphemeral, isPostPendingOrFailed, isSystemMessage} from 'mattermost-redux/utils/post_utils';
+import {isPostEphemeral, isPostPendingOrFailed, isSystemMessage} from 'mattermost-redux/utils/post_utils';
import Config from 'assets/config';
@@ -41,10 +40,8 @@ export default class Post extends PureComponent {
removePost: PropTypes.func.isRequired,
}).isRequired,
channelIsReadOnly: PropTypes.bool,
- config: PropTypes.object.isRequired,
currentTeamUrl: PropTypes.string.isRequired,
currentUserId: PropTypes.string.isRequired,
- deviceWidth: PropTypes.number.isRequired,
highlight: PropTypes.bool,
style: ViewPropTypes.style,
post: PropTypes.object,
@@ -56,10 +53,10 @@ export default class Post extends PureComponent {
hasComments: PropTypes.bool,
isSearchResult: PropTypes.bool,
commentedOnPost: PropTypes.object,
- license: PropTypes.object.isRequired,
managedConfig: PropTypes.object.isRequired,
navigator: PropTypes.object,
canEdit: PropTypes.bool.isRequired,
+ canEditUntil: PropTypes.number.isRequired,
canDelete: PropTypes.bool.isRequired,
onPermalinkPress: PropTypes.func,
shouldRenderReplyButton: PropTypes.bool,
@@ -83,37 +80,6 @@ export default class Post extends PureComponent {
intl: intlShape.isRequired,
};
- constructor(props) {
- super(props);
-
- const {config, license, currentUserId, post} = props;
- this.editDisableAction = new DelayedAction(this.handleEditDisable);
- if (post) {
- editDisable(config, license, currentUserId, post, this.editDisableAction);
- }
- this.state = {
- canEdit: this.props.canEdit,
- };
- }
-
- componentWillReceiveProps(nextProps) {
- if (nextProps.config !== this.props.config ||
- nextProps.license !== this.props.license ||
- nextProps.currentUserId !== this.props.currentUserId ||
- nextProps.post !== this.props.post) {
- const {config, license, currentUserId, post} = nextProps;
-
- editDisable(config, license, currentUserId, post, this.editDisableAction);
- this.setState({
- canEdit: nextProps.canEdit,
- });
- }
- }
-
- componentWillUnmount() {
- this.editDisableAction.cancel();
- }
-
goToUserProfile = () => {
const {intl} = this.context;
const {navigator, post, theme} = this.props;
@@ -165,7 +131,6 @@ export default class Post extends PureComponent {
text: formatMessage({id: 'post_info.del', defaultMessage: 'Delete'}),
style: 'destructive',
onPress: () => {
- this.editDisableAction.cancel();
actions.deletePost(post);
if (post.user_id === currentUserId) {
actions.removePost(post);
@@ -479,7 +444,8 @@ export default class Post extends PureComponent {
Date.now())) {
actions.push({text: formatMessage({id: 'post_info.edit', defaultMessage: 'Edit'}), onPress: onPostEdit});
}
@@ -435,7 +437,7 @@ export default class PostBody extends PureComponent {
body = (
+
{this.getDisplayName(style)}
@@ -253,7 +253,7 @@ export default class PostHeader extends PureComponent {
{this.renderCommentedOnMessage(style)}
}
-
+
);
}
}
diff --git a/app/components/post_list/post_list.js b/app/components/post_list/post_list.js
index af86fad6d..c933abc5b 100644
--- a/app/components/post_list/post_list.js
+++ b/app/components/post_list/post_list.js
@@ -234,9 +234,11 @@ export default class PostList extends PureComponent {
nextConfig = await mattermostManaged.getLocalConfig();
}
- this.setState({
- managedConfig: nextConfig,
- });
+ if (Object.keys(nextConfig).length) {
+ this.setState({
+ managedConfig: nextConfig,
+ });
+ }
};
keyExtractor = (item) => {
diff --git a/app/components/post_list/with_layout.js b/app/components/post_list/with_layout.js
index dcef92225..5ed73e34a 100644
--- a/app/components/post_list/with_layout.js
+++ b/app/components/post_list/with_layout.js
@@ -28,9 +28,10 @@ function withLayout(WrappedComponent) {
};
render() {
+ const {index, onLayoutCalled, shouldCallOnLayout, ...otherProps} = this.props; //eslint-disable-line no-unused-vars
return (
-
+
);
}
diff --git a/app/screens/entry/entry.js b/app/screens/entry/entry.js
index 640396b19..8f61e1884 100644
--- a/app/screens/entry/entry.js
+++ b/app/screens/entry/entry.js
@@ -11,6 +11,7 @@ import {
import DeviceInfo from 'react-native-device-info';
+import {setSystemEmojis} from 'mattermost-redux/actions/emojis';
import {Client4} from 'mattermost-redux/client';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
@@ -56,15 +57,14 @@ const lazyLoadReplyPushNotifications = () => {
*/
export default class Entry extends PureComponent {
static propTypes = {
- config: PropTypes.object,
theme: PropTypes.object,
navigator: PropTypes.object,
isLandscape: PropTypes.bool,
- hydrationComplete: PropTypes.bool,
enableTimezone: PropTypes.bool,
deviceTimezone: PropTypes.string,
initializeModules: PropTypes.func.isRequired,
actions: PropTypes.shape({
+ autoUpdateTimezone: PropTypes.func.isRequired,
setDeviceToken: PropTypes.func.isRequired,
}).isRequired,
};
@@ -132,6 +132,7 @@ export default class Entry extends PureComponent {
this.setAppCredentials();
this.setStartupThemes();
this.handleNotification();
+ this.loadSystemEmojis();
if (Platform.OS === 'android') {
this.launchForAndroid();
@@ -226,6 +227,11 @@ export default class Entry extends PureComponent {
}
};
+ loadSystemEmojis = () => {
+ const EmojiIndicesByAlias = require('app/utils/emojis').EmojiIndicesByAlias;
+ setSystemEmojis(EmojiIndicesByAlias);
+ };
+
renderLogin = () => {
const SelectServer = lazyLoadSelectServer();
const props = {
diff --git a/app/screens/entry/index.js b/app/screens/entry/index.js
index 5048a5537..dab3808b8 100644
--- a/app/screens/entry/index.js
+++ b/app/screens/entry/index.js
@@ -6,7 +6,6 @@ import {connect} from 'react-redux';
import {setDeviceToken} from 'mattermost-redux/actions/general';
import {autoUpdateTimezone} from 'mattermost-redux/actions/timezone';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
-import {getConfig} from 'mattermost-redux/selectors/entities/general';
import {isLandscape} from 'app/selectors/device';
import {getDeviceTimezone, isTimezoneEnabled} from 'app/utils/timezone';
@@ -16,16 +15,12 @@ const lazyLoadEntry = () => {
};
function mapStateToProps(state) {
- const config = getConfig(state);
-
const enableTimezone = isTimezoneEnabled(state);
const deviceTimezone = getDeviceTimezone();
return {
- config,
theme: getTheme(state),
isLandscape: isLandscape(state),
- hydrationComplete: state.views.root.hydrationComplete,
enableTimezone,
deviceTimezone,
};
@@ -34,8 +29,8 @@ function mapStateToProps(state) {
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators({
- setDeviceToken,
autoUpdateTimezone,
+ setDeviceToken,
}, dispatch),
};
}
diff --git a/app/utils/why_did_you_update.js b/app/utils/why_did_you_update.js
index 891a0aff7..973476d37 100644
--- a/app/utils/why_did_you_update.js
+++ b/app/utils/why_did_you_update.js
@@ -40,10 +40,10 @@ function deepDiff(o1, o2, p) {
}
}
-function whyDidYouUpdate(prevProps, prevState) {
+function whyDidYouUpdate(theClass, prevProps, prevState) {
deepDiff({props: prevProps, state: prevState},
- {props: this.props, state: this.state},
- this.constructor.name);
+ {props: theClass.props, state: theClass.state},
+ theClass.constructor.name);
}
export default whyDidYouUpdate;