From 2fb7d917a7e18b5de3cd906a242a5a7c9e2f5946 Mon Sep 17 00:00:00 2001 From: Gleidson Daniel Silva Date: Tue, 22 Mar 2022 17:44:27 -0300 Subject: [PATCH] Chore: Evaluate ActionSheet - TypeScript (#3927) --- app/containers/ActionSheet/ActionSheet.tsx | 77 ++++++++++------------ app/containers/ActionSheet/Handle.tsx | 14 ++-- app/containers/ActionSheet/Item.tsx | 15 +++-- app/containers/ActionSheet/Provider.tsx | 30 +++++---- app/dimensions.tsx | 12 ++-- app/views/RoomMembersView/index.tsx | 2 +- 6 files changed, 78 insertions(+), 72 deletions(-) diff --git a/app/containers/ActionSheet/ActionSheet.tsx b/app/containers/ActionSheet/ActionSheet.tsx index 11414150..79303a59 100644 --- a/app/containers/ActionSheet/ActionSheet.tsx +++ b/app/containers/ActionSheet/ActionSheet.tsx @@ -1,30 +1,29 @@ +import { useBackHandler } from '@react-native-community/hooks'; +import * as Haptics from 'expo-haptics'; import React, { forwardRef, isValidElement, useEffect, useImperativeHandle, useRef, useState } from 'react'; import { Keyboard, Text } from 'react-native'; +import { HandlerStateChangeEventPayload, State, TapGestureHandler } from 'react-native-gesture-handler'; +import Animated, { Easing, Extrapolate, interpolateNode, Value } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { State, TapGestureHandler } from 'react-native-gesture-handler'; import ScrollBottomSheet from 'react-native-scroll-bottom-sheet'; -import Animated, { Easing, Extrapolate, Value, interpolateNode } from 'react-native-reanimated'; -import * as Haptics from 'expo-haptics'; -import { useBackHandler } from '@react-native-community/hooks'; -import { Item } from './Item'; -import { Handle } from './Handle'; -import { Button } from './Button'; import { themes } from '../../constants/colors'; -import styles, { ITEM_HEIGHT } from './styles'; +import { useDimensions, useOrientation } from '../../dimensions'; +import I18n from '../../i18n'; +import { useTheme } from '../../theme'; import { isIOS, isTablet } from '../../utils/deviceInfo'; import * as List from '../List'; -import I18n from '../../i18n'; -import { IDimensionsContextProps, useDimensions, useOrientation } from '../../dimensions'; +import { Button } from './Button'; +import { Handle } from './Handle'; +import { IActionSheetItem, Item } from './Item'; +import { TActionSheetOptions, TActionSheetOptionsItem } from './Provider'; +import styles, { ITEM_HEIGHT } from './styles'; -interface IActionSheetData { - options: any; - headerHeight?: number; - hasCancel?: boolean; - customHeader: any; -} - -const getItemLayout = (data: any, index: number) => ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index }); +const getItemLayout = (data: TActionSheetOptionsItem[] | null | undefined, index: number) => ({ + length: ITEM_HEIGHT, + offset: ITEM_HEIGHT * index, + index +}); const HANDLE_HEIGHT = isIOS ? 40 : 56; const MAX_SNAP_HEIGHT = 16; @@ -39,16 +38,17 @@ const ANIMATION_CONFIG = { }; const ActionSheet = React.memo( - forwardRef(({ children, theme }: { children: JSX.Element; theme: string }, ref) => { - const bottomSheetRef: any = useRef(); - const [data, setData] = useState({} as IActionSheetData); + forwardRef(({ children }: { children: React.ReactElement }, ref) => { + const { theme } = useTheme(); + const bottomSheetRef = useRef>(null); + const [data, setData] = useState({} as TActionSheetOptions); const [isVisible, setVisible] = useState(false); - const { height }: Partial = useDimensions(); + const { height } = useDimensions(); const { isLandscape } = useOrientation(); const insets = useSafeAreaInsets(); const maxSnap = Math.max( - height! - + height - // Items height ITEM_HEIGHT * (data?.options?.length || 0) - // Handle height @@ -69,7 +69,7 @@ const ActionSheet = React.memo( * we'll provide more one snap * that point 50% of the whole screen */ - const snaps: any = height! - maxSnap > height! * 0.6 && !isLandscape ? [maxSnap, height! * 0.5, height] : [maxSnap, height]; + const snaps = height - maxSnap > height * 0.6 && !isLandscape ? [maxSnap, height * 0.5, height] : [maxSnap, height]; const openedSnapIndex = snaps.length > 2 ? 1 : 0; const closedSnapIndex = snaps.length - 1; @@ -79,12 +79,12 @@ const ActionSheet = React.memo( bottomSheetRef.current?.snapTo(closedSnapIndex); }; - const show = (options: any) => { + const show = (options: TActionSheetOptions) => { setData(options); toggleVisible(); }; - const onBackdropPressed = ({ nativeEvent }: any) => { + const onBackdropPressed = ({ nativeEvent }: { nativeEvent: HandlerStateChangeEventPayload }) => { if (nativeEvent.oldState === State.ACTIVE) { hide(); } @@ -117,7 +117,7 @@ const ActionSheet = React.memo( const renderHandle = () => ( <> - + {isValidElement(data?.customHeader) ? data.customHeader : null} ); @@ -127,21 +127,23 @@ const ActionSheet = React.memo( ) : null; - const renderItem = ({ item }: any) => ; + const renderItem = ({ item }: { item: IActionSheetItem['item'] }) => ; const animatedPosition = React.useRef(new Value(0)); - // TODO: Similar to https://github.com/wcandillon/react-native-redash/issues/307#issuecomment-827442320 const opacity = interpolateNode(animatedPosition.current, { inputRange: [0, 1], outputRange: [0, themes[theme].backdropOpacity], extrapolate: Extrapolate.CLAMP - }) as any; + }) as any; // The function's return differs from the expected type of opacity, however this problem is something related to lib, maybe when updating the types will be fixed. + + const bottomSheet = isLandscape || isTablet ? styles.bottomSheet : {}; return ( <> @@ -160,7 +162,7 @@ const ActionSheet = React.memo( ]} /> - testID='action-sheet' ref={bottomSheetRef} componentType='FlatList' @@ -169,18 +171,11 @@ const ActionSheet = React.memo( renderHandle={renderHandle} onSettle={index => index === closedSnapIndex && toggleVisible()} animatedPosition={animatedPosition.current} - containerStyle={ - [ - styles.container, - { backgroundColor: themes[theme].focusedBackground }, - (isLandscape || isTablet) && styles.bottomSheet - ] as any - } + containerStyle={{ ...styles.container, ...bottomSheet, backgroundColor: themes[theme].focusedBackground }} animationConfig={ANIMATION_CONFIG} - // FlatList props - data={data?.options} + data={data.options} renderItem={renderItem} - keyExtractor={(item: any) => item.title} + keyExtractor={item => item.title} style={{ backgroundColor: themes[theme].focusedBackground }} contentContainerStyle={styles.content} ItemSeparatorComponent={List.Separator} diff --git a/app/containers/ActionSheet/Handle.tsx b/app/containers/ActionSheet/Handle.tsx index d95262d1..1b2b6a62 100644 --- a/app/containers/ActionSheet/Handle.tsx +++ b/app/containers/ActionSheet/Handle.tsx @@ -3,9 +3,13 @@ import { View } from 'react-native'; import styles from './styles'; import { themes } from '../../constants/colors'; +import { useTheme } from '../../theme'; -export const Handle = React.memo(({ theme }: { theme: string }) => ( - - - -)); +export const Handle = React.memo(() => { + const { theme } = useTheme(); + return ( + + + + ); +}); diff --git a/app/containers/ActionSheet/Item.tsx b/app/containers/ActionSheet/Item.tsx index 47a3de5a..9c281f8c 100644 --- a/app/containers/ActionSheet/Item.tsx +++ b/app/containers/ActionSheet/Item.tsx @@ -3,23 +3,24 @@ import { Text, View } from 'react-native'; import { themes } from '../../constants/colors'; import { CustomIcon } from '../../lib/Icons'; +import { useTheme } from '../../theme'; import { Button } from './Button'; import styles from './styles'; -interface IActionSheetItem { +export interface IActionSheetItem { item: { title: string; icon: string; - danger: boolean; - testID: string; - onPress(): void; - right: Function; + danger?: boolean; + testID?: string; + onPress: () => void; + right?: Function; }; - theme: string; hide(): void; } -export const Item = React.memo(({ item, hide, theme }: IActionSheetItem) => { +export const Item = React.memo(({ item, hide }: IActionSheetItem) => { + const { theme } = useTheme(); const onPress = () => { hide(); item?.onPress(); diff --git a/app/containers/ActionSheet/Provider.tsx b/app/containers/ActionSheet/Provider.tsx index 8e786b05..11224226 100644 --- a/app/containers/ActionSheet/Provider.tsx +++ b/app/containers/ActionSheet/Provider.tsx @@ -1,14 +1,21 @@ import React, { ForwardedRef, forwardRef, useContext, useRef } from 'react'; import ActionSheet from './ActionSheet'; -import { useTheme } from '../../theme'; +export type TActionSheetOptionsItem = { title: string; icon: string; onPress: () => void }; + +export type TActionSheetOptions = { + options: TActionSheetOptionsItem[]; + headerHeight: number; + customHeader: React.ReactElement | null; + hasCancel?: boolean; +}; interface IActionSheetProvider { - Provider: any; - Consumer: any; + showActionSheet: (item: TActionSheetOptions) => void; + hideActionSheet: () => void; } -const context: IActionSheetProvider = React.createContext({ +const context = React.createContext({ showActionSheet: () => {}, hideActionSheet: () => {} }); @@ -17,17 +24,16 @@ export const useActionSheet = () => useContext(context); const { Provider, Consumer } = context; -export const withActionSheet = (Component: any): any => - forwardRef((props: any, ref: ForwardedRef) => ( - {(contexts: any) => } +export const withActionSheet = (Component: React.ComponentType): typeof Component => + forwardRef((props: typeof React.Component, ref: ForwardedRef) => ( + {(contexts: IActionSheetProvider) => } )); -export const ActionSheetProvider = React.memo(({ children }: { children: JSX.Element | JSX.Element[] }) => { - const ref: ForwardedRef = useRef(); - const { theme }: any = useTheme(); +export const ActionSheetProvider = React.memo(({ children }: { children: React.ReactElement | React.ReactElement[] }) => { + const ref: ForwardedRef = useRef(null); const getContext = () => ({ - showActionSheet: (options: any) => { + showActionSheet: (options: TActionSheetOptions) => { ref.current?.showActionSheet(options); }, hideActionSheet: () => { @@ -37,7 +43,7 @@ export const ActionSheetProvider = React.memo(({ children }: { children: JSX.Ele return ( - + <>{children} diff --git a/app/dimensions.tsx b/app/dimensions.tsx index ddf95d6d..1c2ce628 100644 --- a/app/dimensions.tsx +++ b/app/dimensions.tsx @@ -6,10 +6,10 @@ import { TNavigationOptions } from './definitions/navigationTypes'; export interface IDimensionsContextProps { width: number; - height?: number; - scale: number; - fontScale: number; - setDimensions: ({ + height: number; + scale?: number; + fontScale?: number; + setDimensions?: ({ width, height, scale, @@ -22,7 +22,7 @@ export interface IDimensionsContextProps { }) => void; } -export const DimensionsContext = React.createContext>(Dimensions.get('window')); +export const DimensionsContext = React.createContext(Dimensions.get('window')); export function withDimensions(Component: React.ComponentType & TNavigationOptions): typeof Component { const DimensionsComponent = (props: T) => ( @@ -37,7 +37,7 @@ export const useDimensions = () => React.useContext(DimensionsContext); export const useOrientation = () => { const { width, height } = React.useContext(DimensionsContext); - const isPortrait = height! > width!; + const isPortrait = height > width; return { isPortrait, isLandscape: !isPortrait diff --git a/app/views/RoomMembersView/index.tsx b/app/views/RoomMembersView/index.tsx index 2ad9a734..43395d91 100644 --- a/app/views/RoomMembersView/index.tsx +++ b/app/views/RoomMembersView/index.tsx @@ -666,4 +666,4 @@ const mapStateToProps = (state: IApplicationState) => ({ viewAllTeamsPermission: state.permissions['view-all-teams'] }); -export default connect(mapStateToProps)(withActionSheet(withTheme(RoomMembersView))); +export default connect(mapStateToProps)(withTheme(withActionSheet(RoomMembersView)));