From de2c39d46228d23cbf173faae661f67f98978f53 Mon Sep 17 00:00:00 2001 From: Boris Yankov Date: Fri, 19 Oct 2018 01:15:18 +0300 Subject: [PATCH] composebox: Unify two platform's code Fixes #2434 Fixes #3053 Remove the iOS code which was our old 'controlled' version and keep the new Android 'uncontrolled' version. While iOS does not have the extreme performance issues the Android app had because of bugs in React Native, it is still faster to not have the component controlled - a controlled compoennt would block any time the UI thread blocks. Another big benefit is removing the need to support two versions. --- src/compose/ComposeBox.ios.js | 339 ------------------ .../{ComposeBox.android.js => ComposeBox.js} | 0 2 files changed, 339 deletions(-) delete mode 100644 src/compose/ComposeBox.ios.js rename src/compose/{ComposeBox.android.js => ComposeBox.js} (100%) diff --git a/src/compose/ComposeBox.ios.js b/src/compose/ComposeBox.ios.js deleted file mode 100644 index 47fcfb5bfff..00000000000 --- a/src/compose/ComposeBox.ios.js +++ /dev/null @@ -1,339 +0,0 @@ -/* @flow */ -import React, { PureComponent } from 'react'; -import { View, TextInput, findNodeHandle } from 'react-native'; -import { connect } from 'react-redux'; -import TextInputReset from 'react-native-text-input-reset'; - -import type { - Auth, - Context, - Narrow, - EditMessage, - InputSelectionType, - User, - Dispatch, - Dimensions, - GlobalState, -} from '../types'; -import { - addToOutbox, - cancelEditMessage, - draftUpdate, - fetchTopicsForActiveStream, - sendTypingEvent, -} from '../actions'; -import { updateMessage } from '../api'; -import { FloatingActionButton, Input, MultilineInput } from '../common'; -import { showErrorAlert } from '../utils/info'; -import { IconDone, IconSend } from '../common/Icons'; -import { isStreamNarrow, isStreamOrTopicNarrow, topicNarrow } from '../utils/narrow'; -import ComposeMenu from './ComposeMenu'; -import AutocompleteViewWrapper from '../autocomplete/AutocompleteViewWrapper'; -import getComposeInputPlaceholder from './getComposeInputPlaceholder'; -import NotSubscribed from '../message/NotSubscribed'; -import AnnouncementOnly from '../message/AnnouncementOnly'; - -import { - getAuth, - getIsAdmin, - getSession, - canSendToActiveNarrow, - getLastMessageTopic, - getActiveUsers, - getShowMessagePlaceholders, -} from '../selectors'; -import { - getIsActiveStreamSubscribed, - getIsActiveStreamAnnouncementOnly, -} from '../subscriptions/subscriptionSelectors'; -import { getDraftForActiveNarrow } from '../drafts/draftsSelectors'; - -type Props = { - auth: Auth, - canSend: boolean, - narrow: Narrow, - users: User[], - draft: string, - lastMessageTopic: string, - isAdmin: boolean, - isAnnouncementOnly: boolean, - isSubscribed: boolean, - editMessage: EditMessage, - safeAreaInsets: Dimensions, - dispatch: Dispatch, - messageInputRef: (component: any) => void, -}; - -type State = { - isMessageFocused: boolean, - isTopicFocused: boolean, - isMenuExpanded: boolean, - topic: string, - message: string, - height: number, - selection: InputSelectionType, -}; - -class ComposeBox extends PureComponent { - context: Context; - props: Props; - state: State = { - isMessageFocused: false, - isTopicFocused: false, - isMenuExpanded: false, - height: 20, - topic: '', - message: this.props.draft, - selection: { start: 0, end: 0 }, - }; - - messageInput: ?TextInput = null; - topicInput: ?TextInput = null; - - static contextTypes = { - styles: () => null, - }; - - getCanSelectTopic = () => { - const { isMessageFocused, isTopicFocused } = this.state; - const { editMessage, narrow } = this.props; - if (editMessage) { - return isStreamOrTopicNarrow(narrow); - } - if (!isStreamNarrow(narrow)) { - return false; - } - return isMessageFocused || isTopicFocused; - }; - - handleComposeMenuToggle = () => { - this.setState(({ isMenuExpanded }) => ({ - isMenuExpanded: !isMenuExpanded, - })); - }; - - handleLayoutChange = (event: Object) => { - this.setState({ - height: event.nativeEvent.layout.height, - }); - }; - - handleTopicChange = (topic: string) => { - this.setState({ topic, isMenuExpanded: false }); - }; - - handleMessageChange = (message: string) => { - this.setState({ message, isMenuExpanded: false }); - const { dispatch, narrow } = this.props; - dispatch(sendTypingEvent(narrow)); - dispatch(draftUpdate(narrow, message)); - }; - - handleMessageSelectionChange = (event: Object) => { - const { selection } = event.nativeEvent; - this.setState({ selection }); - }; - - handleMessageFocus = () => { - const { topic } = this.state; - const { lastMessageTopic } = this.props; - this.setState({ - isMessageFocused: true, - isMenuExpanded: false, - }); - setTimeout(() => { - this.handleTopicChange(topic || lastMessageTopic); - }, 200); // wait, to hope the component is shown - }; - - handleMessageBlur = () => { - setTimeout(() => { - this.setState({ - isMessageFocused: false, - isMenuExpanded: false, - }); - }, 200); // give a chance to the topic input to get the focus - }; - - handleTopicFocus = () => { - const { dispatch, narrow } = this.props; - this.setState({ - isTopicFocused: true, - isMenuExpanded: false, - }); - dispatch(fetchTopicsForActiveStream(narrow)); - }; - - handleTopicBlur = () => { - setTimeout(() => { - this.setState({ - isTopicFocused: false, - isMenuExpanded: false, - }); - }, 200); // give a chance to the message input to get the focus - }; - - handleInputTouchStart = () => { - this.setState({ isMenuExpanded: false }); - }; - - clearMessageInput = () => { - if (this.messageInput) { - this.messageInput.clear(); - if (TextInputReset) { - TextInputReset.resetKeyboardInput(findNodeHandle(this.messageInput)); - } - } - - this.handleMessageChange(''); - }; - - handleSend = () => { - const { dispatch, narrow } = this.props; - const { topic, message } = this.state; - - const destinationNarrow = isStreamNarrow(narrow) - ? topicNarrow(narrow[0].operand, topic || '(no topic)') - : narrow; - - dispatch(addToOutbox(destinationNarrow, message)); - - this.clearMessageInput(); - }; - - handleEdit = () => { - const { auth, editMessage, dispatch } = this.props; - const { message, topic } = this.state; - const content = editMessage.content !== message ? message : undefined; - const subject = topic !== editMessage.topic ? topic : undefined; - if (content || subject) { - updateMessage(auth, { content, subject }, editMessage.id).catch(error => { - showErrorAlert(error.message, 'Failed to edit message'); - }); - } - dispatch(cancelEditMessage()); - }; - - componentWillReceiveProps(nextProps: Props) { - if (nextProps.editMessage !== this.props.editMessage) { - const topic = - isStreamNarrow(nextProps.narrow) && nextProps.editMessage - ? nextProps.editMessage.topic - : ''; - this.handleMessageChange(nextProps.editMessage ? nextProps.editMessage.content : ''); - this.handleTopicChange(topic); - if (this.messageInput) { - this.messageInput.focus(); - } - } - } - - render() { - const { styles } = this.context; - const { isTopicFocused, isMenuExpanded, height, message, topic, selection } = this.state; - const { - auth, - canSend, - narrow, - users, - editMessage, - safeAreaInsets, - messageInputRef, - isAdmin, - isAnnouncementOnly, - isSubscribed, - } = this.props; - - if (!canSend) { - return null; - } - - if (!isSubscribed) { - return ; - } else if (isAnnouncementOnly && !isAdmin) { - return ; - } - - const placeholder = getComposeInputPlaceholder(narrow, auth.email, users); - - return ( - - - - - - - - {this.getCanSelectTopic() && ( - { - this.topicInput = component; - }} - onChangeText={this.handleTopicChange} - onFocus={this.handleTopicFocus} - onBlur={this.handleTopicBlur} - onTouchStart={this.handleInputTouchStart} - value={topic} - /> - )} - { - if (component) { - this.messageInput = component; - messageInputRef(component); - } - }} - value={message} - onBlur={this.handleMessageBlur} - onChange={this.handleMessageChange} - onFocus={this.handleMessageFocus} - onSelectionChange={this.handleMessageSelectionChange} - onTouchStart={this.handleInputTouchStart} - /> - - - - - - - ); - } -} - -export default connect((state: GlobalState, props) => ({ - auth: getAuth(state), - users: getActiveUsers(state), - safeAreaInsets: getSession(state).safeAreaInsets, - isAdmin: getIsAdmin(state), - isAnnouncementOnly: getIsActiveStreamAnnouncementOnly(props.narrow)(state), - isSubscribed: getIsActiveStreamSubscribed(props.narrow)(state), - canSend: canSendToActiveNarrow(props.narrow) && !getShowMessagePlaceholders(props.narrow)(state), - editMessage: getSession(state).editMessage, - draft: getDraftForActiveNarrow(props.narrow)(state), - lastMessageTopic: getLastMessageTopic(props.narrow)(state), -}))(ComposeBox); diff --git a/src/compose/ComposeBox.android.js b/src/compose/ComposeBox.js similarity index 100% rename from src/compose/ComposeBox.android.js rename to src/compose/ComposeBox.js