diff --git a/native/chat/settings/color-selector-modal.react.js b/native/chat/settings/color-selector-modal.react.js index cb2e2491b..3ed750693 100644 --- a/native/chat/settings/color-selector-modal.react.js +++ b/native/chat/settings/color-selector-modal.react.js @@ -1,185 +1,183 @@ // @flow import * as React from 'react'; import { TouchableHighlight, Alert } from 'react-native'; import Icon from 'react-native-vector-icons/FontAwesome'; import { changeThreadSettingsActionTypes, changeThreadSettings, } from 'lib/actions/thread-actions'; import { type ThreadInfo, type ChangeThreadSettingsPayload, type UpdateThreadRequest, } from 'lib/types/thread-types'; import type { DispatchActionPromise } from 'lib/utils/action-utils'; import { useServerCall, useDispatchActionPromise, } from 'lib/utils/action-utils'; import ColorSelector from '../../components/color-selector.react'; import Modal from '../../components/modal.react'; import type { RootNavigationProp } from '../../navigation/root-navigator.react'; import type { NavigationRoute } from '../../navigation/route-names'; import { useSelector } from '../../redux/redux-utils'; import { type Colors, useStyles, useColors } from '../../themes/colors'; export type ColorSelectorModalParams = { +presentedFrom: string, +color: string, +threadInfo: ThreadInfo, +setColor: (color: string) => void, }; type BaseProps = { +navigation: RootNavigationProp<'ColorSelectorModal'>, +route: NavigationRoute<'ColorSelectorModal'>, }; type Props = { ...BaseProps, // Redux state +colors: Colors, +styles: typeof unboundStyles, +windowWidth: number, // Redux dispatch functions +dispatchActionPromise: DispatchActionPromise, // async functions that hit server APIs +changeThreadSettings: ( request: UpdateThreadRequest, ) => Promise, }; function ColorSelectorModal(props: Props): React.Node { const { changeThreadSettings: updateThreadSettings, dispatchActionPromise, windowWidth, } = props; const { threadInfo, setColor } = props.route.params; const close = props.navigation.goBackOnce; const onErrorAcknowledged = React.useCallback(() => { setColor(threadInfo.color); }, [setColor, threadInfo.color]); const editColor = React.useCallback( async (newColor: string) => { const threadID = threadInfo.id; try { return await updateThreadSettings({ threadID, changes: { color: newColor }, }); } catch (e) { Alert.alert( 'Unknown error', 'Uhh... try again?', [{ text: 'OK', onPress: onErrorAcknowledged }], { cancelable: false }, ); throw e; } }, [onErrorAcknowledged, threadInfo.id, updateThreadSettings], ); const onColorSelected = React.useCallback( (color: string) => { const colorEditValue = color.substr(1); setColor(colorEditValue); close(); dispatchActionPromise( changeThreadSettingsActionTypes, editColor(colorEditValue), { customKeyName: `${changeThreadSettingsActionTypes.started}:color` }, ); }, [setColor, close, dispatchActionPromise, editColor], ); const { colorSelectorContainer, closeButton, closeButtonIcon } = props.styles; // Based on the assumption we are always in portrait, // and consequently width is the lowest dimensions const modalStyle = React.useMemo( () => [colorSelectorContainer, { height: 0.75 * windowWidth }], [colorSelectorContainer, windowWidth], ); const { modalIosHighlightUnderlay } = props.colors; const { color } = props.route.params; return ( ); } const unboundStyles = { closeButton: { borderRadius: 3, height: 18, position: 'absolute', right: 5, top: 5, width: 18, }, closeButtonIcon: { color: 'modalBackgroundSecondaryLabel', left: 3, position: 'absolute', }, colorSelector: { bottom: 10, left: 10, position: 'absolute', right: 10, top: 10, }, colorSelectorContainer: { backgroundColor: 'modalBackground', - borderColor: 'modalForegroundBorder', borderRadius: 5, - borderWidth: 2, flex: 0, marginHorizontal: 15, marginVertical: 20, }, }; const ConnectedColorSelectorModal: React.ComponentType = React.memo( function ConnectedColorSelectorModal(props: BaseProps) { const styles = useStyles(unboundStyles); const colors = useColors(); const windowWidth = useSelector(state => state.dimensions.width); const dispatchActionPromise = useDispatchActionPromise(); const callChangeThreadSettings = useServerCall(changeThreadSettings); return ( ); }, ); export default ConnectedColorSelectorModal; diff --git a/native/components/modal.react.js b/native/components/modal.react.js index 82f392e9f..94af3f67c 100644 --- a/native/components/modal.react.js +++ b/native/components/modal.react.js @@ -1,61 +1,63 @@ // @flow import { useNavigation } from '@react-navigation/native'; import * as React from 'react'; import { View, TouchableWithoutFeedback, StyleSheet } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useStyles } from '../themes/colors'; import type { ViewStyle } from '../types/styles'; import KeyboardAvoidingView from './keyboard-avoiding-view.react'; type Props = $ReadOnly<{ +children: React.Node, +containerStyle?: ViewStyle, +modalStyle?: ViewStyle, +safeAreaEdges?: $ReadOnlyArray<'top' | 'right' | 'bottom' | 'left'>, }>; function Modal(props: Props): React.Node { const navigation = useNavigation(); const close = React.useCallback(() => { if (navigation.isFocused()) { navigation.goBack(); } }, [navigation]); const styles = useStyles(unboundStyles); const { containerStyle, modalStyle, children, safeAreaEdges } = props; return ( {children} ); } const unboundStyles = { container: { flex: 1, justifyContent: 'center', overflow: 'visible', }, modal: { backgroundColor: 'modalBackground', + borderColor: 'modalForegroundBorder', + borderWidth: 2, borderRadius: 5, flex: 1, justifyContent: 'center', marginBottom: 30, marginHorizontal: 15, marginTop: 100, padding: 12, }, }; export default Modal;