diff --git a/native/chat/settings/thread-settings-name.react.js b/native/chat/settings/thread-settings-name.react.js index 313fe6731..9c615b5aa 100644 --- a/native/chat/settings/thread-settings-name.react.js +++ b/native/chat/settings/thread-settings-name.react.js @@ -1,248 +1,245 @@ // @flow import invariant from 'invariant'; import { changeThreadSettingsActionTypes, changeThreadSettings, } from 'lib/actions/thread-actions'; import { createLoadingStatusSelector } from 'lib/selectors/loading-selectors'; import type { LoadingStatus } from 'lib/types/loading-types'; -import { loadingStatusPropType } from 'lib/types/loading-types'; import { type ThreadInfo, - threadInfoPropType, type ChangeThreadSettingsPayload, type UpdateThreadRequest, } from 'lib/types/thread-types'; -import type { DispatchActionPromise } from 'lib/utils/action-utils'; -import { connect } from 'lib/utils/redux-utils'; -import PropTypes from 'prop-types'; +import { + type DispatchActionPromise, + useServerCall, + useDispatchActionPromise, +} from 'lib/utils/action-utils'; import * as React from 'react'; import { Text, Alert, ActivityIndicator, TextInput, View } from 'react-native'; import EditSettingButton from '../../components/edit-setting-button.react'; -import type { AppState } from '../../redux/redux-setup'; -import { - type Colors, - colorsPropType, - colorsSelector, - styleSelector, -} from '../../themes/colors'; +import { useSelector } from '../../redux/redux-utils'; +import { type Colors, useStyles, useColors } from '../../themes/colors'; import type { LayoutEvent, ContentSizeChangeEvent, } from '../../types/react-native'; import SaveSettingButton from './save-setting-button.react'; +type BaseProps = {| + +threadInfo: ThreadInfo, + +nameEditValue: ?string, + +setNameEditValue: (value: ?string, callback?: () => void) => void, + +nameTextHeight: ?number, + +setNameTextHeight: (number: number) => void, + +canChangeSettings: boolean, +|}; type Props = {| - threadInfo: ThreadInfo, - nameEditValue: ?string, - setNameEditValue: (value: ?string, callback?: () => void) => void, - nameTextHeight: ?number, - setNameTextHeight: (number: number) => void, - canChangeSettings: boolean, + ...BaseProps, // Redux state - loadingStatus: LoadingStatus, - colors: Colors, - styles: typeof styles, + +loadingStatus: LoadingStatus, + +colors: Colors, + +styles: typeof unboundStyles, // Redux dispatch functions - dispatchActionPromise: DispatchActionPromise, + +dispatchActionPromise: DispatchActionPromise, // async functions that hit server APIs - changeThreadSettings: ( + +changeThreadSettings: ( update: UpdateThreadRequest, ) => Promise, |}; class ThreadSettingsName extends React.PureComponent { - static propTypes = { - threadInfo: threadInfoPropType.isRequired, - nameEditValue: PropTypes.string, - setNameEditValue: PropTypes.func.isRequired, - nameTextHeight: PropTypes.number, - setNameTextHeight: PropTypes.func.isRequired, - canChangeSettings: PropTypes.bool.isRequired, - loadingStatus: loadingStatusPropType.isRequired, - colors: colorsPropType.isRequired, - styles: PropTypes.objectOf(PropTypes.object).isRequired, - dispatchActionPromise: PropTypes.func.isRequired, - changeThreadSettings: PropTypes.func.isRequired, - }; textInput: ?React.ElementRef; render() { return ( Name {this.renderContent()} ); } renderContent() { if ( this.props.nameEditValue === null || this.props.nameEditValue === undefined ) { return ( {this.props.threadInfo.uiName} ); } let button; if (this.props.loadingStatus !== 'loading') { button = ; } else { button = ( ); } const textInputStyle = {}; if ( this.props.nameTextHeight !== undefined && this.props.nameTextHeight !== null ) { textInputStyle.height = this.props.nameTextHeight; } return ( {button} ); } textInputRef = (textInput: ?React.ElementRef) => { this.textInput = textInput; }; onLayoutText = (event: LayoutEvent) => { this.props.setNameTextHeight(event.nativeEvent.layout.height); }; onTextInputContentSizeChange = (event: ContentSizeChangeEvent) => { this.props.setNameTextHeight(event.nativeEvent.contentSize.height); }; threadEditName() { return this.props.threadInfo.name ? this.props.threadInfo.name : ''; } onPressEdit = () => { this.props.setNameEditValue(this.threadEditName()); }; onSubmit = () => { invariant( this.props.nameEditValue !== null && this.props.nameEditValue !== undefined, 'should be set', ); const name = this.props.nameEditValue.trim(); if (name === this.threadEditName()) { this.props.setNameEditValue(null); return; } const editNamePromise = this.editName(name); this.props.dispatchActionPromise( changeThreadSettingsActionTypes, editNamePromise, { customKeyName: `${changeThreadSettingsActionTypes.started}:name` }, ); editNamePromise.then(() => { this.props.setNameEditValue(null); }); }; async editName(newName: string) { try { return await this.props.changeThreadSettings({ threadID: this.props.threadInfo.id, changes: { name: newName }, }); } catch (e) { Alert.alert( 'Unknown error', 'Uhh... try again?', [{ text: 'OK', onPress: this.onErrorAcknowledged }], { cancelable: false }, ); throw e; } } onErrorAcknowledged = () => { this.props.setNameEditValue(this.threadEditName(), () => { invariant(this.textInput, 'textInput should be set'); this.textInput.focus(); }); }; } -const styles = { +const unboundStyles = { currentValue: { color: 'panelForegroundSecondaryLabel', flex: 1, fontFamily: 'Arial', fontSize: 16, margin: 0, paddingLeft: 4, paddingRight: 0, paddingVertical: 0, borderBottomColor: 'transparent', }, label: { color: 'panelForegroundTertiaryLabel', fontSize: 16, width: 96, }, row: { backgroundColor: 'panelForeground', flexDirection: 'row', paddingHorizontal: 24, paddingVertical: 8, }, }; -const stylesSelector = styleSelector(styles); const loadingStatusSelector = createLoadingStatusSelector( changeThreadSettingsActionTypes, `${changeThreadSettingsActionTypes.started}:name`, ); -export default connect( - (state: AppState) => ({ - loadingStatus: loadingStatusSelector(state), - colors: colorsSelector(state), - styles: stylesSelector(state), - }), - { changeThreadSettings }, -)(ThreadSettingsName); +export default React.memo(function ConnectedThreadSettingsName( + props: BaseProps, +) { + const styles = useStyles(unboundStyles); + const colors = useColors(); + const loadingStatus = useSelector(loadingStatusSelector); + + const dispatchActionPromise = useDispatchActionPromise(); + const callChangeThreadSettings = useServerCall(changeThreadSettings); + + return ( + + ); +});