diff --git a/app/components/post_body/post_body.js b/app/components/post_body/post_body.js index c09504003..018905393 100644 --- a/app/components/post_body/post_body.js +++ b/app/components/post_body/post_body.js @@ -4,6 +4,7 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import { + Keyboard, ScrollView, TouchableOpacity, View, @@ -204,7 +205,10 @@ export default class PostBody extends PureComponent { }, }; - navigator.showModal(options); + Keyboard.dismiss(); + requestAnimationFrame(() => { + navigator.showModal(options); + }); }; renderAddChannelMember = (style, messageStyle, textStyles) => { diff --git a/app/components/post_list/post_list.js b/app/components/post_list/post_list.js index 7e7f7cf1e..3cc733f04 100644 --- a/app/components/post_list/post_list.js +++ b/app/components/post_list/post_list.js @@ -83,6 +83,7 @@ export default class PostList extends PureComponent { this.hasDoneInitialScroll = false; this.contentOffsetY = 0; + this.shouldScrollToBottom = false; this.makeExtraData = makeExtraData(); this.flatListRef = React.createRef(); @@ -92,7 +93,7 @@ export default class PostList extends PureComponent { } componentDidMount() { - EventEmitter.on('scroll-to-bottom', this.scrollToBottom); + EventEmitter.on('scroll-to-bottom', this.handleSetScrollToBottom); } componentWillReceiveProps(nextProps) { @@ -102,15 +103,22 @@ export default class PostList extends PureComponent { } } - componentDidUpdate() { - if (this.props.deepLinkURL) { - this.handleDeepLink(this.props.deepLinkURL); - this.props.actions.setDeepLinkURL(''); + componentDidUpdate(prevProps) { + const {actions, channelId, deepLinkURL, postIds} = this.props; + + if (deepLinkURL && deepLinkURL !== prevProps.deepLinkURL) { + this.handleDeepLink(deepLinkURL); + actions.setDeepLinkURL(''); + } + + if (this.shouldScrollToBottom && prevProps.channelId === channelId && prevProps.postIds.length === postIds.length) { + this.scrollToBottom(); + this.shouldScrollToBottom = false; } } componentWillUnmount() { - EventEmitter.off('scroll-to-bottom', this.scrollToBottom); + EventEmitter.off('scroll-to-bottom', this.handleSetScrollToBottom); } handleClosePermalink = () => { @@ -199,6 +207,10 @@ export default class PostList extends PureComponent { }); }; + handleSetScrollToBottom = () => { + this.shouldScrollToBottom = true; + } + keyExtractor = (item) => { // All keys are strings (either post IDs or special keys) return item; @@ -268,7 +280,9 @@ export default class PostList extends PureComponent { }; scrollToBottom = () => { - this.flatListRef.current.scrollToOffset({offset: 0, animated: true}); + setTimeout(() => { + this.flatListRef.current.scrollToOffset({offset: 0, animated: true}); + }, 250); }; scrollToInitialIndexIfNeeded = (width, height) => { diff --git a/app/components/post_textbox/post_textbox.android.js b/app/components/post_textbox/post_textbox.android.js new file mode 100644 index 000000000..61f8f622a --- /dev/null +++ b/app/components/post_textbox/post_textbox.android.js @@ -0,0 +1,49 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +import Autocomplete from 'app/components/autocomplete'; +import FileUploadPreview from 'app/components/file_upload_preview'; + +import Typing from './components/typing'; +import PostTextBoxBase from './post_textbox_base'; + +const AUTOCOMPLETE_MARGIN = 20; +const AUTOCOMPLETE_MAX_HEIGHT = 200; + +export default class PostTextBoxAndroid extends PostTextBoxBase { + render() { + const { + channelId, + deactivatedChannel, + files, + rootId, + } = this.props; + + if (deactivatedChannel) { + return this.renderDeactivatedChannel(); + } + + const {cursorPosition, top} = this.state; + + return ( + + + + + {this.renderTextBox()} + + ); + } +} diff --git a/app/components/post_textbox/post_textbox.ios.js b/app/components/post_textbox/post_textbox.ios.js new file mode 100644 index 000000000..bdf842a71 --- /dev/null +++ b/app/components/post_textbox/post_textbox.ios.js @@ -0,0 +1,24 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +import Typing from './components/typing'; +import PostTextBoxBase from './post_textbox_base'; + +export default class PostTextBoxIOS extends PostTextBoxBase { + render() { + const {deactivatedChannel} = this.props; + + if (deactivatedChannel) { + return this.renderDeactivatedChannel(); + } + + return ( + + + {this.renderTextBox()} + + ); + } +} diff --git a/app/components/post_textbox/post_textbox.js b/app/components/post_textbox/post_textbox_base.js similarity index 82% rename from app/components/post_textbox/post_textbox.js rename to app/components/post_textbox/post_textbox_base.js index 3df81ebf3..d9d884743 100644 --- a/app/components/post_textbox/post_textbox.js +++ b/app/components/post_textbox/post_textbox_base.js @@ -3,7 +3,17 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; -import {Alert, BackHandler, findNodeHandle, Keyboard, NativeModules, Platform, Text, TextInput, View} from 'react-native'; +import { + Alert, + BackHandler, + findNodeHandle, + Keyboard, + NativeModules, + Platform, + Text, + TextInput, + View, +} from 'react-native'; import {intlShape} from 'react-intl'; import Button from 'react-native-button'; import {General, RequestStatus} from 'mattermost-redux/constants'; @@ -12,20 +22,19 @@ import {getFormattedFileSize} from 'mattermost-redux/utils/file_utils'; import AttachmentButton from 'app/components/attachment_button'; import Fade from 'app/components/fade'; -import {INSERT_TO_COMMENT, INSERT_TO_DRAFT, IS_REACTION_REGEX, MAX_CONTENT_HEIGHT, MAX_FILE_COUNT} from 'app/constants/post_textbox'; -import {confirmOutOfOfficeDisabled} from 'app/utils/status'; -import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; -import {t} from 'app/utils/i18n'; import FormattedMarkdownText from 'app/components/formatted_markdown_text'; import FormattedText from 'app/components/formatted_text'; import SendButton from 'app/components/send_button'; -import Typing from './components/typing'; +import {INSERT_TO_COMMENT, INSERT_TO_DRAFT, IS_REACTION_REGEX, MAX_CONTENT_HEIGHT, MAX_FILE_COUNT} from 'app/constants/post_textbox'; +import {t} from 'app/utils/i18n'; +import {confirmOutOfOfficeDisabled} from 'app/utils/status'; +import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; const PLACEHOLDER_COLOR = changeOpacity('#000', 0.5); const {RNTextInputReset} = NativeModules; -export default class PostTextbox extends PureComponent { +export default class PostTextBoxBase extends PureComponent { static propTypes = { actions: PropTypes.shape({ addReactionToLatestPost: PropTypes.func.isRequired, @@ -172,6 +181,60 @@ export default class PostTextbox extends PureComponent { } }; + getAttachmentButton = () => { + const {canUploadFiles, channelIsReadOnly, files, maxFileSize, navigator, theme} = this.props; + let attachmentButton = null; + + if (canUploadFiles && !channelIsReadOnly) { + attachmentButton = ( + + ); + } + + return attachmentButton; + }; + + getInputContainerStyle = () => { + const {canUploadFiles, channelIsReadOnly, theme} = this.props; + const style = getStyleSheet(theme); + const inputContainerStyle = [style.inputContainer]; + + if (!canUploadFiles) { + inputContainerStyle.push(style.inputContainerWithoutFileUpload); + } + + if (channelIsReadOnly) { + inputContainerStyle.push(style.readonlyContainer); + } + + return inputContainerStyle; + }; + + getPlaceHolder = () => { + const {channelIsReadOnly, rootId} = this.props; + let placeholder; + + if (channelIsReadOnly) { + placeholder = {id: t('mobile.create_post.read_only'), defaultMessage: 'This channel is read-only.'}; + } else if (rootId) { + placeholder = {id: t('create_comment.addComment'), defaultMessage: 'Add a comment...'}; + } else { + placeholder = {id: t('create_post.write'), defaultMessage: 'Write to {channelDisplayName}'}; + } + + return placeholder; + }; + handleAndroidKeyboard = () => { this.blur(); }; @@ -270,20 +333,31 @@ export default class PostTextbox extends PureComponent { actions, channelId, rootId, + cursorPositionEvent, valueEvent, } = this.props; - // Workaround for some Android keyboards that don't play well with cursors (e.g. Samsung keyboards) - if (autocomplete && Platform.OS === 'android' && this.input?.current) { - RNTextInputReset.resetKeyboardInput(findNodeHandle(this.input.current)); - } - - this.checkMessageLength(value); - if (!autocomplete && valueEvent) { + if (valueEvent) { EventEmitter.emit(valueEvent, value); } - this.setState({value}); + const nextState = {value}; + + // Workaround for some Android keyboards that don't play well with cursors (e.g. Samsung keyboards) + if (autocomplete && this.input?.current) { + if (Platform.OS === 'android') { + RNTextInputReset.resetKeyboardInput(findNodeHandle(this.input.current)); + } else { + nextState.cursorPosition = value.length; + if (cursorPositionEvent) { + EventEmitter.emit(cursorPositionEvent, nextState.cursorPosition); + } + } + } + + this.checkMessageLength(value); + + this.setState(nextState); if (value) { actions.userTyping(channelId, rootId); @@ -464,24 +538,26 @@ export default class PostTextbox extends PureComponent { }; archivedView = (theme, style) => { - return ( - - - ); + + + ); }; handleLayout = (e) => { @@ -490,110 +566,67 @@ export default class PostTextbox extends PureComponent { }); }; - render() { + renderDeactivatedChannel = () => { const {intl} = this.context; - const { - canUploadFiles, - channelDisplayName, - channelIsLoading, - channelIsReadOnly, - deactivatedChannel, - files, - maxFileSize, - navigator, - rootId, - theme, - channelIsArchived, - } = this.props; + const style = getStyleSheet(this.props.theme); + return ( + + {intl.formatMessage({ + id: 'create_post.deactivated', + defaultMessage: 'You are viewing an archived channel with a deactivated user.', + })} + + ); + } + + renderTextBox = () => { + const {intl} = this.context; + const {channelDisplayName, channelIsArchived, channelIsLoading, channelIsReadOnly, theme} = this.props; const style = getStyleSheet(theme); - if (deactivatedChannel) { - return ( - - {intl.formatMessage({ - id: 'create_post.deactivated', - defaultMessage: 'You are viewing an archived channel with a deactivated user.', - })} - - ); + + if (channelIsArchived) { + return this.archivedView(theme, style); } const {value} = this.state; const textValue = channelIsLoading ? '' : value; - - let placeholder; - if (channelIsReadOnly) { - placeholder = {id: t('mobile.create_post.read_only'), defaultMessage: 'This channel is read-only.'}; - } else if (rootId) { - placeholder = {id: t('create_comment.addComment'), defaultMessage: 'Add a comment...'}; - } else { - placeholder = {id: t('create_post.write'), defaultMessage: 'Write to {channelDisplayName}'}; - } - - let attachmentButton = null; - const inputContainerStyle = [style.inputContainer]; - if (canUploadFiles) { - attachmentButton = ( - - ); - } else { - inputContainerStyle.push(style.inputContainerWithoutFileUpload); - } - - if (channelIsReadOnly) { - inputContainerStyle.push(style.readonlyContainer); - } + const placeholder = this.getPlaceHolder(); return ( - - - {!channelIsArchived && ( - - {!channelIsReadOnly && attachmentButton} - - - - - - - - )} - {channelIsArchived && this.archivedView(theme, style)} - + + {this.getAttachmentButton()} + + + + + + + ); - } + }; } const getStyleSheet = makeStyleSheetFromTheme((theme) => { diff --git a/app/screens/channel/channel.android.js b/app/screens/channel/channel.android.js new file mode 100644 index 000000000..14748150e --- /dev/null +++ b/app/screens/channel/channel.android.js @@ -0,0 +1,57 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {Dimensions, View} from 'react-native'; + +import ChannelLoader from 'app/components/channel_loader'; +import KeyboardLayout from 'app/components/layout/keyboard_layout'; +import NetworkIndicator from 'app/components/network_indicator'; +import SafeAreaView from 'app/components/safe_area_view'; +import StatusBar from 'app/components/status_bar'; +import PostTextbox from 'app/components/post_textbox'; +import LocalConfig from 'assets/config'; + +import ChannelNavBar from './channel_nav_bar'; +import ChannelPostList from './channel_post_list'; + +import ChannelBase, {ClientUpgradeListener, style} from './channel_base'; + +export default class ChannelAndroid extends ChannelBase { + render() { + const {height} = Dimensions.get('window'); + const { + navigator, + } = this.props; + + const channelLoaderStyle = [style.channelLoader, {height}]; + const drawerContent = ( + + + + + + + + + + + + {LocalConfig.EnableMobileClientUpgrade && } + + ); + + return this.renderChannel(drawerContent); + } +} diff --git a/app/screens/channel/channel.ios.js b/app/screens/channel/channel.ios.js new file mode 100644 index 000000000..3e28b1e26 --- /dev/null +++ b/app/screens/channel/channel.ios.js @@ -0,0 +1,87 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; +import {Dimensions, View} from 'react-native'; +import {KeyboardTrackingView} from 'react-native-keyboard-tracking-view'; + +import Autocomplete, {AUTOCOMPLETE_MAX_HEIGHT} from 'app/components/autocomplete'; +import ChannelLoader from 'app/components/channel_loader'; +import FileUploadPreview from 'app/components/file_upload_preview'; +import NetworkIndicator from 'app/components/network_indicator'; +import PostTextbox from 'app/components/post_textbox'; +import SafeAreaView from 'app/components/safe_area_view'; +import StatusBar from 'app/components/status_bar'; +import {DeviceTypes} from 'app/constants'; + +import LocalConfig from 'assets/config'; + +import ChannelBase, {ClientUpgradeListener, style} from './channel_base'; +import ChannelNavBar from './channel_nav_bar'; +import ChannelPostList from './channel_post_list'; + +const ACCESSORIES_CONTAINER_NATIVE_ID = 'channelAccessoriesContainer'; +const CHANNEL_POST_TEXTBOX_CURSOR_CHANGE = 'onChannelTextBoxCursorChange'; +const CHANNEL_POST_TEXTBOX_VALUE_CHANGE = 'onChannelTextBoxValueChange'; + +export default class ChannelIOS extends ChannelBase { + render() { + const {height} = Dimensions.get('window'); + const { + currentChannelId, + navigator, + } = this.props; + + const channelLoaderStyle = [style.channelLoader, {height}]; + if ((DeviceTypes.IS_IPHONE_X || DeviceTypes.IS_TABLET)) { + channelLoaderStyle.push(style.iOSHomeIndicator); + } + + const drawerContent = ( + + + + + + + + + + + + {LocalConfig.EnableMobileClientUpgrade && } + + + + + + ); + + return this.renderChannel(drawerContent); + } +} diff --git a/app/screens/channel/channel.js b/app/screens/channel/channel_base.js similarity index 76% rename from app/screens/channel/channel.js rename to app/screens/channel/channel_base.js index 27a8aa83b..da40eacda 100644 --- a/app/screens/channel/channel.js +++ b/app/screens/channel/channel_base.js @@ -5,47 +5,33 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {intlShape} from 'react-intl'; import { - Dimensions, Keyboard, Platform, StyleSheet, View, } from 'react-native'; -import {KeyboardTrackingView} from 'react-native-keyboard-tracking-view'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; import EventEmitter from 'mattermost-redux/utils/event_emitter'; import {app} from 'app/mattermost'; -import Autocomplete, {AUTOCOMPLETE_MAX_HEIGHT} from 'app/components/autocomplete'; -import InteractiveDialogController from 'app/components/interactive_dialog_controller'; -import ChannelLoader from 'app/components/channel_loader'; import EmptyToolbar from 'app/components/start/empty_toolbar'; -import FileUploadPreview from 'app/components/file_upload_preview'; +import InteractiveDialogController from 'app/components/interactive_dialog_controller'; import MainSidebar from 'app/components/sidebars/main'; -import SettingsSidebar from 'app/components/sidebars/settings'; -import NetworkIndicator from 'app/components/network_indicator'; import SafeAreaView from 'app/components/safe_area_view'; -import StatusBar from 'app/components/status_bar'; -import {DeviceTypes} from 'app/constants'; +import SettingsSidebar from 'app/components/sidebars/settings'; + import {preventDoubleTap} from 'app/utils/tap'; -import PostTextbox from 'app/components/post_textbox'; import PushNotifications from 'app/push_notifications'; import tracker from 'app/utils/time_tracker'; -import LocalConfig from 'assets/config'; - import telemetry from 'app/telemetry'; -import ChannelNavBar from './channel_nav_bar'; -import ChannelPostList from './channel_post_list'; +import LocalConfig from 'assets/config'; -const CHANNEL_POST_TEXTBOX_CURSOR_CHANGE = 'onChannelTextBoxCursorChange'; -const CHANNEL_POST_TEXTBOX_VALUE_CHANGE = 'onChannelTextBoxValueChange'; +export let ClientUpgradeListener; -let ClientUpgradeListener; - -export default class Channel extends PureComponent { +export default class ChannelBase extends PureComponent { static propTypes = { actions: PropTypes.shape({ loadChannelsIfNecessary: PropTypes.func.isRequired, @@ -273,7 +259,7 @@ export default class Channel extends PureComponent { } }; - render() { + renderChannel(drawerContent) { const { channelsRequestFailed, currentChannelId, @@ -282,8 +268,6 @@ export default class Channel extends PureComponent { theme, } = this.props; - const {height} = Dimensions.get('window'); - if (!currentChannelId) { if (channelsRequestFailed) { const PostListRetry = require('app/components/post_list_retry').default; @@ -309,11 +293,6 @@ export default class Channel extends PureComponent { ); } - const channelLoaderStyle = [style.channelLoader, {height}]; - if (Platform.OS === 'ios' && (DeviceTypes.IS_IPHONE_X || DeviceTypes.IS_TABLET)) { - channelLoaderStyle.push(style.iOSHomeIndicator); - } - return ( - - - - - - - - - {LocalConfig.EnableMobileClientUpgrade && } - - - - + {drawerContent} 0) { + if (!prevProps.postIds?.length && this.props.postIds?.length > 0 && this.props.updateNativeScrollView) { // This is needed to re-bind the scrollview natively when getting the first posts this.props.updateNativeScrollView(); } diff --git a/app/screens/post_options/post_options.js b/app/screens/post_options/post_options.js index a4eb50384..cdc701fbe 100644 --- a/app/screens/post_options/post_options.js +++ b/app/screens/post_options/post_options.js @@ -270,7 +270,7 @@ export default class PostOptions extends PureComponent { const {navigator, theme} = this.props; this.close(); - requestAnimationFrame(() => { + setTimeout(() => { MaterialIcon.getImageSource('close', 20, theme.sidebarHeaderTextColor).then((source) => { navigator.showModal({ screen: 'AddReaction', @@ -288,7 +288,7 @@ export default class PostOptions extends PureComponent { }, }); }); - }); + }, 300); }; handleReply = () => { @@ -368,7 +368,7 @@ export default class PostOptions extends PureComponent { const {navigator, post, theme} = this.props; this.close(); - requestAnimationFrame(() => { + setTimeout(() => { MaterialIcon.getImageSource('close', 20, theme.sidebarHeaderTextColor).then((source) => { navigator.showModal({ screen: 'EditPost', @@ -386,7 +386,7 @@ export default class PostOptions extends PureComponent { }, }); }); - }); + }, 300); }; handleUnflagPost = () => { diff --git a/app/screens/thread/__snapshots__/thread.test.js.snap b/app/screens/thread/__snapshots__/thread.ios.test.js.snap similarity index 88% rename from app/screens/thread/__snapshots__/thread.test.js.snap rename to app/screens/thread/__snapshots__/thread.ios.test.js.snap index 6becaae55..c1773e4e6 100644 --- a/app/screens/thread/__snapshots__/thread.test.js.snap +++ b/app/screens/thread/__snapshots__/thread.ios.test.js.snap @@ -4,14 +4,14 @@ exports[`thread should match snapshot, has root post 1`] = ` - - + + + + + ); + + postTextBox = ( + + ); + } else { + content = ( + + ); + } + + return ( + + + + {content} + {postTextBox} + + + ); + } +} diff --git a/app/screens/thread/thread.ios.js b/app/screens/thread/thread.ios.js new file mode 100644 index 000000000..3b6db17ba --- /dev/null +++ b/app/screens/thread/thread.ios.js @@ -0,0 +1,104 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +import React from 'react'; + +import {View} from 'react-native'; +import {KeyboardTrackingView} from 'react-native-keyboard-tracking-view'; + +import {getLastPostIndex} from 'mattermost-redux/utils/post_list'; + +import Autocomplete, {AUTOCOMPLETE_MAX_HEIGHT} from 'app/components/autocomplete'; +import FileUploadPreview from 'app/components/file_upload_preview'; +import Loading from 'app/components/loading'; +import PostList from 'app/components/post_list'; +import PostTextbox from 'app/components/post_textbox'; +import SafeAreaView from 'app/components/safe_area_view'; +import StatusBar from 'app/components/status_bar'; +import {THREAD} from 'app/constants/screen'; + +import ThreadBase from './thread_base'; + +const ACCESSORIES_CONTAINER_NATIVE_ID = 'threadAccessoriesContainer'; +const THREAD_POST_TEXTBOX_CURSOR_CHANGE = 'onThreadTextBoxCursorChange'; +const THREAD_POST_TEXTBOX_VALUE_CHANGE = 'onThreadTextBoxValueChange'; +const SCROLLVIEW_NATIVE_ID = 'threadPostList'; + +export default class ThreadIOS extends ThreadBase { + render() { + const { + channelId, + myMember, + navigator, + postIds, + rootId, + channelIsArchived, + } = this.props; + + let content; + let postTextBox; + if (this.hasRootPost()) { + content = ( + + + + + + + + ); + + postTextBox = ( + + + + ); + } else { + content = ( + + ); + } + + return ( + + + + {content} + + {postTextBox} + + ); + } +} diff --git a/app/screens/thread/thread.test.js b/app/screens/thread/thread.ios.test.js similarity index 95% rename from app/screens/thread/thread.test.js rename to app/screens/thread/thread.ios.test.js index bad3a0235..309b71088 100644 --- a/app/screens/thread/thread.test.js +++ b/app/screens/thread/thread.ios.test.js @@ -8,7 +8,7 @@ import Preferences from 'mattermost-redux/constants/preferences'; import {General, RequestStatus} from 'mattermost-redux/constants'; import PostList from 'app/components/post_list'; -import Thread from './thread.js'; +import ThreadIOS from './thread.ios'; jest.mock('react-intl'); @@ -37,7 +37,7 @@ describe('thread', () => { test('should match snapshot, has root post', () => { const wrapper = shallow( - , + , {context: {intl: {formatMessage: jest.fn()}}}, ); expect(wrapper.getElement()).toMatchSnapshot(); @@ -46,7 +46,7 @@ describe('thread', () => { test('should match snapshot, no root post, loading', () => { const newPostIds = ['post_id_1', 'post_id_2']; const wrapper = shallow( - , @@ -75,7 +75,7 @@ describe('thread', () => { }; const newNavigator = {...navigator}; const wrapper = shallow( - , @@ -88,7 +88,7 @@ describe('thread', () => { test('should match snapshot, render footer', () => { const wrapper = shallow( - , + , {context: {intl: {formatMessage: jest.fn()}}}, ); diff --git a/app/screens/thread/thread.js b/app/screens/thread/thread_base.js similarity index 53% rename from app/screens/thread/thread.js rename to app/screens/thread/thread_base.js index 5fb8a1396..95718d020 100644 --- a/app/screens/thread/thread.js +++ b/app/screens/thread/thread_base.js @@ -5,28 +5,14 @@ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {Keyboard, Platform} from 'react-native'; import {intlShape} from 'react-intl'; -import {KeyboardTrackingView} from 'react-native-keyboard-tracking-view'; import {General, RequestStatus} from 'mattermost-redux/constants'; -import {getLastPostIndex} from 'mattermost-redux/utils/post_list'; -import {THREAD} from 'app/constants/screen'; - -import Autocomplete, {AUTOCOMPLETE_MAX_HEIGHT} from 'app/components/autocomplete'; -import FileUploadPreview from 'app/components/file_upload_preview'; import Loading from 'app/components/loading'; -import PostList from 'app/components/post_list'; -import PostTextbox from 'app/components/post_textbox'; -import SafeAreaView from 'app/components/safe_area_view'; -import StatusBar from 'app/components/status_bar'; import {setNavigatorStyles} from 'app/utils/theme'; import DeletedPost from 'app/components/deleted_post'; -const THREAD_POST_TEXTBOX_CURSOR_CHANGE = 'onThreadTextBoxCursorChange'; -const THREAD_POST_TEXTBOX_VALUE_CHANGE = 'onThreadTextBoxValueChange'; -const SCROLLVIEW_NATIVE_ID = 'threadPostList'; - -export default class Thread extends PureComponent { +export default class ThreadBase extends PureComponent { static propTypes = { actions: PropTypes.shape({ selectPost: PropTypes.func.isRequired, @@ -47,8 +33,6 @@ export default class Thread extends PureComponent { postIds: [], }; - state = {}; - static contextTypes = { intl: intlShape, }; @@ -71,6 +55,10 @@ export default class Thread extends PureComponent { this.props.navigator.setTitle({ title, }); + + this.state = { + lastViewedAt: props.myMember && props.myMember.last_viewed_at, + }; } componentWillReceiveProps(nextProps) { @@ -153,79 +141,4 @@ export default class Thread extends PureComponent { }, }); }; - - render() { - const { - channelId, - myMember, - navigator, - postIds, - rootId, - channelIsArchived, - } = this.props; - - let content; - let postTextBox; - if (this.hasRootPost()) { - content = ( - - - - - - ); - - postTextBox = ( - - - - ); - } else { - content = ( - - ); - } - - return ( - - - - {content} - - {postTextBox} - - ); - } } diff --git a/package-lock.json b/package-lock.json index f83783d42..780b99c5b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15552,8 +15552,8 @@ } }, "react-native-keyboard-tracking-view": { - "version": "github:enahum/react-native-keyboard-tracking-view#60c8a81e3ff2ad5a17d969f235e5990c88d465d6", - "from": "github:enahum/react-native-keyboard-tracking-view#60c8a81e3ff2ad5a17d969f235e5990c88d465d6" + "version": "github:enahum/react-native-keyboard-tracking-view#66979a750e42fefe06366f0c924b29368bb586f2", + "from": "github:enahum/react-native-keyboard-tracking-view#66979a750e42fefe06366f0c924b29368bb586f2" }, "react-native-keychain": { "version": "3.1.3", diff --git a/package.json b/package.json index 409c5394e..2ebbf9603 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "react-native-image-gallery": "github:mattermost/react-native-image-gallery#c1a9f7118e90cc87d47620bc0584c9cac4b0cf38", "react-native-image-picker": "0.28.1", "react-native-keyboard-aware-scroll-view": "0.8.0", - "react-native-keyboard-tracking-view": "github:enahum/react-native-keyboard-tracking-view#60c8a81e3ff2ad5a17d969f235e5990c88d465d6", + "react-native-keyboard-tracking-view": "github:enahum/react-native-keyboard-tracking-view#66979a750e42fefe06366f0c924b29368bb586f2", "react-native-keychain": "3.1.3", "react-native-linear-gradient": "2.5.4", "react-native-local-auth": "github:mattermost/react-native-local-auth#cc9ce2f468fbf7b431dfad3191a31aaa9227a6ab", diff --git a/packager/moduleNames.js b/packager/moduleNames.js index 2fd530343..2b780a989 100644 --- a/packager/moduleNames.js +++ b/packager/moduleNames.js @@ -17,6 +17,8 @@ module.exports = [ 'app/components/formatted_markdown_text.js', 'app/components/formatted_text.js', 'app/components/formatted_time.js', + 'app/components/interactive_dialog_controller/index.js', + 'app/components/interactive_dialog_controller/interactive_dialog_controller.js', 'app/components/layout/keyboard_layout/index.js', 'app/components/layout/keyboard_layout/keyboard_layout.js', 'app/components/loading.js', @@ -45,6 +47,7 @@ module.exports = [ 'app/components/markdown/markdown_table_row/markdown_table_row.js', 'app/components/network_indicator/index.js', 'app/components/network_indicator/network_indicator.js', + 'app/components/paper_plane.js', 'app/components/post/index.js', 'app/components/post/post.js', 'app/components/post_body/index.js', @@ -60,8 +63,11 @@ module.exports = [ 'app/components/post_list_retry.js', 'app/components/post_profile_picture/index.js', 'app/components/post_profile_picture/post_profile_picture.js', + 'app/components/post_textbox/components/typing/index.js', + 'app/components/post_textbox/components/typing/typing.js', 'app/components/post_textbox/index.js', - 'app/components/post_textbox/post_textbox.js', + 'app/components/post_textbox/post_textbox.android.js', + 'app/components/post_textbox/post_textbox_base.js', 'app/components/profile_picture/index.js', 'app/components/profile_picture/profile_picture.js', 'app/components/progressive_image/index.js', @@ -69,6 +75,7 @@ module.exports = [ 'app/components/reply_icon.js', 'app/components/safe_area_view/index.js', 'app/components/safe_area_view/safe_area_view.android.js', + 'app/components/send_button.js', 'app/components/show_more_button/index.js', 'app/components/show_more_button/show_more_button.js', 'app/components/start/empty_toolbar.js', @@ -121,7 +128,8 @@ module.exports = [ 'app/reducers/views/team.js', 'app/reducers/views/thread.js', 'app/reducers/views/user.js', - 'app/screens/channel/channel.js', + 'app/screens/channel/channel.android.js', + 'app/screens/channel/channel_base.js', 'app/screens/channel/channel_nav_bar/channel_drawer_button.js', 'app/screens/channel/channel_nav_bar/channel_nav_bar.js', 'app/screens/channel/channel_nav_bar/channel_search_button/channel_search_button.js', diff --git a/packager/modulePaths.js b/packager/modulePaths.js index dafe7f76a..0f657849a 100644 --- a/packager/modulePaths.js +++ b/packager/modulePaths.js @@ -15,6 +15,8 @@ module.exports = ['./node_modules/app/app.js', './node_modules/app/components/formatted_markdown_text.js', './node_modules/app/components/formatted_text.js', './node_modules/app/components/formatted_time.js', + './node_modules/app/components/interactive_dialog_controller/index.js', + './node_modules/app/components/interactive_dialog_controller/interactive_dialog_controller.js', './node_modules/app/components/layout/keyboard_layout/index.js', './node_modules/app/components/layout/keyboard_layout/keyboard_layout.js', './node_modules/app/components/loading.js', @@ -43,6 +45,7 @@ module.exports = ['./node_modules/app/app.js', './node_modules/app/components/markdown/markdown_table_row/markdown_table_row.js', './node_modules/app/components/network_indicator/index.js', './node_modules/app/components/network_indicator/network_indicator.js', + './node_modules/app/components/paper_plane.js', './node_modules/app/components/post/index.js', './node_modules/app/components/post/post.js', './node_modules/app/components/post_body/index.js', @@ -58,8 +61,11 @@ module.exports = ['./node_modules/app/app.js', './node_modules/app/components/post_list_retry.js', './node_modules/app/components/post_profile_picture/index.js', './node_modules/app/components/post_profile_picture/post_profile_picture.js', + './node_modules/app/components/post_textbox/components/typing/index.js', + './node_modules/app/components/post_textbox/components/typing/typing.js', './node_modules/app/components/post_textbox/index.js', - './node_modules/app/components/post_textbox/post_textbox.js', + './node_modules/app/components/post_textbox/post_textbox.android.js', + './node_modules/app/components/post_textbox/post_textbox_base.js', './node_modules/app/components/profile_picture/index.js', './node_modules/app/components/profile_picture/profile_picture.js', './node_modules/app/components/progressive_image/index.js', @@ -67,6 +73,7 @@ module.exports = ['./node_modules/app/app.js', './node_modules/app/components/reply_icon.js', './node_modules/app/components/safe_area_view/index.js', './node_modules/app/components/safe_area_view/safe_area_view.android.js', + './node_modules/app/components/send_button.js', './node_modules/app/components/show_more_button/index.js', './node_modules/app/components/show_more_button/show_more_button.js', './node_modules/app/components/start/empty_toolbar.js', @@ -119,7 +126,8 @@ module.exports = ['./node_modules/app/app.js', './node_modules/app/reducers/views/team.js', './node_modules/app/reducers/views/thread.js', './node_modules/app/reducers/views/user.js', - './node_modules/app/screens/channel/channel.js', + './node_modules/app/screens/channel/channel.android.js', + './node_modules/app/screens/channel/channel_base.js', './node_modules/app/screens/channel/channel_nav_bar/channel_drawer_button.js', './node_modules/app/screens/channel/channel_nav_bar/channel_nav_bar.js', './node_modules/app/screens/channel/channel_nav_bar/channel_search_button/channel_search_button.js',