-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* [WIP] New Action Sheet * [NEW] Header Indicator * [IMPROVEMENT] Header Logic * [NEW] Use EventEmitter to show ActionSheet for while * [FIX] Animation * [IMPROVEMENT] Use provider * [FIX] Add callback * [FIX] Message Actions * [FIX] Add MessageActions icons * [NEW] MessageErrorActions * [IMPROVEMENT] OnClose * [FIX] Adjust height * [FIX] Close/Reopen * [CHORE] Remove react-native-action-sheet * [CHORE] Move ActionSheet * [FIX] Reply Message * [IMPROVEMENT] Hide ActionSheet logic * [WIP] Custom MessageActions Header * [IMPROVEMENT] MessageActions Header * [IMPROVEMENT] Enable Scroll * [FIX] Scroll on Android * Move to react-native-scroll-bottom-sheet * Stash * Refactor actions * Revert some changes * Trying react-native-modalize * Back to HOC * ActionSheet style * HOC Header * Reaction actionSheet * Fix messageBox actions * Fix add reaction * Change to flatListProps * fix modalize android scroll * Use react-native-scroll-bottom-sheet * [NEW] BottomSheet dismissable & [FIX] Android not opening * [NEW] Show emojis based on screen width * [WIP] Adjust to content height * [IMPROVEMENT] Responsible * [IMPROVEMENT] Use alert instead actionSheet at NewServerView * [FIX] Handle tablet cases * [IMPROVEMENT] Remove actionSheet of RoomMembersView * [IMPROVEMENT] Min snap distance when its portrait * [CHORE] Remove unused Components * [IMPROVEMENT] Remove duplicated add-reaction * [IMPROVEMENT] Refactor Icon Package * [IMPROVEMENT] Use new icons * [FIX] Select message at MessageActions before getOptions * [FIX] Custom header height * [CHORE] Remove patch & [FIX] Tablet bottom sheet * [FIX] Use ListItem height to BottomSheet Height * Some fixes * [FIX] Custom MessageActions header * [FIX] Android height adjust * [IMPROVEMENT] Item touchable & [FIX] Respect pin permission * [IMPROVEMENT] More than one snap point * some size fixes * improve code * hide horizontal scroll indicator * [FIX] Focus MessageBox on edit message * [FIX] Ripple color * [IMPROVEMENT] Backdrop must keep same opacity after 50% of the screen * [TEST] Change animation config * [IMPROVEMENT] BackHandler should close the ActionSheet * [CHORE] Add react-native-safe-area-context * [FIX] Provide a bottom padding at notch devices * [IMPROVEMENT] Improve backdrop input/output range * [FIX] Weird Android Snap behavior * [PATCH] React-native-scroll-bottom-sheet * [CI] Re-run build * [FIX] Landscape android * [IMPROVEMENT] Cover 50% of the screen at the landscape mode * [FIX] Adjust emoji content to width size * [IMPROVEMENT] Use hooks library * [IMPROVEMENT] Close the actionSheet when orientation change * deactivate safe-area-context for while * [REVERT] Re-add react-native-safe-area-context (3.0.2) * [IMPROVEMENT] Use focused background * [TESTS] E2E Tests updated to new BottomSheet * [NEW] Add cancel button * [FIX] Cancel button at android * [IMPROVEMENT] Use cancelable bottom sheet at room members view * [IMPROVEMENT] Use better function names * [IMPROVEMENT] Use getItemLayout * [FIX][TEMP] Animation * Review * Build * Header keyExtractor * Rename function * Tweak animation * Refactoring * useTheme * Refactoring * TestIDs * Refactor * Remove old lib Co-authored-by: Diego Mello <[email protected]>
- Loading branch information
1 parent
98ed84b
commit 893acdc
Showing
26 changed files
with
1,336 additions
and
827 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,214 @@ | ||
import React, { | ||
useRef, | ||
useState, | ||
useEffect, | ||
forwardRef, | ||
useImperativeHandle, | ||
useCallback, | ||
isValidElement | ||
} from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { Keyboard, Text } from 'react-native'; | ||
import { useSafeAreaInsets } from 'react-native-safe-area-context'; | ||
import { TapGestureHandler, State } from 'react-native-gesture-handler'; | ||
import ScrollBottomSheet from 'react-native-scroll-bottom-sheet'; | ||
import Animated, { | ||
Extrapolate, | ||
interpolate, | ||
Value, | ||
Easing | ||
} from 'react-native-reanimated'; | ||
import * as Haptics from 'expo-haptics'; | ||
import { | ||
useDimensions, | ||
useBackHandler, | ||
useDeviceOrientation | ||
} 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 { isTablet, isIOS } from '../../utils/deviceInfo'; | ||
import Separator from '../Separator'; | ||
import I18n from '../../i18n'; | ||
|
||
const getItemLayout = (data, index) => ({ length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index }); | ||
|
||
const HANDLE_HEIGHT = isIOS ? 40 : 56; | ||
const MAX_SNAP_HEIGHT = 16; | ||
const CANCEL_HEIGHT = 64; | ||
|
||
const ANIMATION_DURATION = 250; | ||
|
||
const ANIMATION_CONFIG = { | ||
duration: ANIMATION_DURATION, | ||
// https://easings.net/#easeInOutCubic | ||
easing: Easing.bezier(0.645, 0.045, 0.355, 1.0) | ||
}; | ||
|
||
const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => { | ||
const bottomSheetRef = useRef(); | ||
const [data, setData] = useState({}); | ||
const [isVisible, setVisible] = useState(false); | ||
const orientation = useDeviceOrientation(); | ||
const { height } = useDimensions().window; | ||
const insets = useSafeAreaInsets(); | ||
const { landscape } = orientation; | ||
|
||
const maxSnap = Math.max( | ||
( | ||
height | ||
// Items height | ||
- (ITEM_HEIGHT * (data?.options?.length || 0)) | ||
// Handle height | ||
- HANDLE_HEIGHT | ||
// Custom header height | ||
- (data?.headerHeight || 0) | ||
// Insets bottom height (Notch devices) | ||
- insets.bottom | ||
// Cancel button height | ||
- (data?.hasCancel ? CANCEL_HEIGHT : 0) | ||
), | ||
MAX_SNAP_HEIGHT | ||
); | ||
|
||
/* | ||
* if the action sheet cover more | ||
* than 60% of the whole screen | ||
* and it's not at the landscape mode | ||
* we'll provide more one snap | ||
* that point 50% of the whole screen | ||
*/ | ||
const snaps = (height - maxSnap > height * 0.6) && !landscape ? [maxSnap, height * 0.5, height] : [maxSnap, height]; | ||
const openedSnapIndex = snaps.length > 2 ? 1 : 0; | ||
const closedSnapIndex = snaps.length - 1; | ||
|
||
const toggleVisible = () => setVisible(!isVisible); | ||
|
||
const hide = () => { | ||
bottomSheetRef.current?.snapTo(closedSnapIndex); | ||
}; | ||
|
||
const show = (options) => { | ||
setData(options); | ||
toggleVisible(); | ||
}; | ||
|
||
const onBackdropPressed = ({ nativeEvent }) => { | ||
if (nativeEvent.oldState === State.ACTIVE) { | ||
hide(); | ||
} | ||
}; | ||
|
||
useBackHandler(() => { | ||
if (isVisible) { | ||
hide(); | ||
} | ||
return isVisible; | ||
}); | ||
|
||
useEffect(() => { | ||
if (isVisible) { | ||
Keyboard.dismiss(); | ||
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); | ||
bottomSheetRef.current?.snapTo(openedSnapIndex); | ||
} | ||
}, [isVisible]); | ||
|
||
// Hides action sheet when orientation changes | ||
useEffect(() => { | ||
setVisible(false); | ||
}, [orientation.landscape]); | ||
|
||
useImperativeHandle(ref, () => ({ | ||
showActionSheet: show, | ||
hideActionSheet: hide | ||
})); | ||
|
||
const renderHandle = useCallback(() => ( | ||
<> | ||
<Handle theme={theme} /> | ||
{isValidElement(data?.customHeader) ? data.customHeader : null} | ||
</> | ||
)); | ||
|
||
const renderFooter = useCallback(() => (data?.hasCancel ? ( | ||
<Button | ||
onPress={hide} | ||
style={[styles.button, { backgroundColor: themes[theme].auxiliaryBackground }]} | ||
theme={theme} | ||
> | ||
<Text style={[styles.text, { color: themes[theme].bodyText }]}> | ||
{I18n.t('Cancel')} | ||
</Text> | ||
</Button> | ||
) : null)); | ||
|
||
const renderSeparator = useCallback(() => <Separator theme={theme} style={styles.separator} />); | ||
|
||
const renderItem = useCallback(({ item }) => <Item item={item} hide={hide} theme={theme} />); | ||
|
||
const animatedPosition = React.useRef(new Value(0)); | ||
const opacity = interpolate(animatedPosition.current, { | ||
inputRange: [0, 1], | ||
outputRange: [0, 0.7], | ||
extrapolate: Extrapolate.CLAMP | ||
}); | ||
|
||
return ( | ||
<> | ||
{children} | ||
{isVisible && ( | ||
<> | ||
<TapGestureHandler onHandlerStateChange={onBackdropPressed}> | ||
<Animated.View | ||
testID='action-sheet-backdrop' | ||
style={[ | ||
styles.backdrop, | ||
{ | ||
backgroundColor: themes[theme].backdropColor, | ||
opacity | ||
} | ||
]} | ||
/> | ||
</TapGestureHandler> | ||
<ScrollBottomSheet | ||
testID='action-sheet' | ||
ref={bottomSheetRef} | ||
componentType='FlatList' | ||
snapPoints={snaps} | ||
initialSnapIndex={closedSnapIndex} | ||
renderHandle={renderHandle} | ||
onSettle={index => (index === closedSnapIndex) && toggleVisible()} | ||
animatedPosition={animatedPosition.current} | ||
containerStyle={[ | ||
styles.container, | ||
{ backgroundColor: themes[theme].focusedBackground }, | ||
(landscape || isTablet) && styles.bottomSheet | ||
]} | ||
animationConfig={ANIMATION_CONFIG} | ||
// FlatList props | ||
data={data?.options} | ||
renderItem={renderItem} | ||
keyExtractor={item => item.title} | ||
style={{ backgroundColor: themes[theme].focusedBackground }} | ||
contentContainerStyle={styles.content} | ||
ItemSeparatorComponent={renderSeparator} | ||
ListHeaderComponent={renderSeparator} | ||
ListFooterComponent={renderFooter} | ||
getItemLayout={getItemLayout} | ||
removeClippedSubviews={isIOS} | ||
/> | ||
</> | ||
)} | ||
</> | ||
); | ||
})); | ||
ActionSheet.propTypes = { | ||
children: PropTypes.node, | ||
theme: PropTypes.string | ||
}; | ||
|
||
export default ActionSheet; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { TouchableOpacity } from 'react-native'; | ||
|
||
import { isAndroid } from '../../utils/deviceInfo'; | ||
import Touch from '../../utils/touch'; | ||
|
||
// Taken from https://github.com/rgommezz/react-native-scroll-bottom-sheet#touchables | ||
export const Button = isAndroid ? Touch : TouchableOpacity; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { View } from 'react-native'; | ||
|
||
import styles from './styles'; | ||
import { themes } from '../../constants/colors'; | ||
|
||
export const Handle = React.memo(({ theme }) => ( | ||
<View style={[styles.handle, { backgroundColor: themes[theme].focusedBackground }]} testID='action-sheet-handle'> | ||
<View style={[styles.handleIndicator, { backgroundColor: themes[theme].auxiliaryText }]} /> | ||
</View> | ||
)); | ||
Handle.propTypes = { | ||
theme: PropTypes.string | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { Text } from 'react-native'; | ||
|
||
import { themes } from '../../constants/colors'; | ||
import { CustomIcon } from '../../lib/Icons'; | ||
import styles from './styles'; | ||
import { Button } from './Button'; | ||
|
||
export const Item = React.memo(({ item, hide, theme }) => { | ||
const onPress = () => { | ||
hide(); | ||
item?.onPress(); | ||
}; | ||
|
||
return ( | ||
<Button | ||
onPress={onPress} | ||
style={[styles.item, { backgroundColor: themes[theme].focusedBackground }]} | ||
theme={theme} | ||
> | ||
<CustomIcon name={item.icon} size={20} color={item.danger ? themes[theme].dangerColor : themes[theme].bodyText} /> | ||
<Text | ||
numberOfLines={1} | ||
style={[styles.title, { color: item.danger ? themes[theme].dangerColor : themes[theme].bodyText }]} | ||
> | ||
{item.title} | ||
</Text> | ||
</Button> | ||
); | ||
}); | ||
Item.propTypes = { | ||
item: PropTypes.shape({ | ||
title: PropTypes.string, | ||
icon: PropTypes.string, | ||
danger: PropTypes.bool, | ||
onPress: PropTypes.func | ||
}), | ||
hide: PropTypes.func, | ||
theme: PropTypes.string | ||
}; |
Oops, something went wrong.