[Gekidou MM-46229] Add hideAndLock, showAndUnlock callbacks for Search (#6677)

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
Jason Frerich
2022-10-27 22:04:26 -05:00
committed by GitHub
parent 7cf2bff658
commit c627fb8df9
17 changed files with 292 additions and 230 deletions

View File

@@ -4,9 +4,11 @@
import React, {useMemo} from 'react';
import {Platform, Text, View} from 'react-native';
import Animated, {useAnimatedStyle, withTiming} from 'react-native-reanimated';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import CompassIcon from '@components/compass_icon';
import TouchableWithFeedback from '@components/touchable_with_feedback';
import ViewConstants from '@constants/view';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
@@ -24,18 +26,18 @@ type Props = {
defaultHeight: number;
hasSearch: boolean;
isLargeTitle: boolean;
largeHeight: number;
heightOffset: number;
leftComponent?: React.ReactElement;
onBackPress?: () => void;
onTitlePress?: () => void;
rightButtons?: HeaderRightButton[];
scrollValue?: Animated.SharedValue<number>;
lockValue?: Animated.SharedValue<number | null>;
showBackButton?: boolean;
subtitle?: string;
subtitleCompanion?: React.ReactElement;
theme: Theme;
title?: string;
top: number;
}
const hitSlop = {top: 20, bottom: 20, left: 20, right: 20};
@@ -127,20 +129,21 @@ const Header = ({
defaultHeight,
hasSearch,
isLargeTitle,
largeHeight,
heightOffset,
leftComponent,
onBackPress,
onTitlePress,
rightButtons,
scrollValue,
lockValue,
showBackButton = true,
subtitle,
subtitleCompanion,
theme,
title,
top,
}: Props) => {
const styles = getStyleSheet(theme);
const insets = useSafeAreaInsets();
const opacity = useAnimatedStyle(() => {
if (!isLargeTitle) {
@@ -151,8 +154,7 @@ const Header = ({
return {opacity: 0};
}
const largeTitleLabelHeight = 60;
const barHeight = (largeHeight - defaultHeight) - largeTitleLabelHeight;
const barHeight = heightOffset - ViewConstants.LARGE_HEADER_TITLE_HEIGHT;
const val = (scrollValue?.value ?? 0);
const showDuration = 200;
const hideDuration = 50;
@@ -161,11 +163,15 @@ const Header = ({
return {
opacity: withTiming(opacityValue, {duration}),
};
}, [defaultHeight, largeHeight, isLargeTitle, hasSearch]);
}, [heightOffset, isLargeTitle, hasSearch]);
const containerStyle = useMemo(() => {
return [styles.container, {height: defaultHeight + top, paddingTop: top}];
}, [defaultHeight, theme]);
const containerAnimatedStyle = useAnimatedStyle(() => ({
height: defaultHeight,
paddingTop: insets.top,
}), [defaultHeight, lockValue]);
const containerStyle = useMemo(() => (
[styles.container, containerAnimatedStyle]), [styles, containerAnimatedStyle]);
const additionalTitleStyle = useMemo(() => ({
marginLeft: Platform.select({android: showBackButton && !leftComponent ? 20 : 0}),

View File

@@ -2,11 +2,12 @@
// See LICENSE.txt for license information.
import React from 'react';
import Animated, {useAnimatedStyle} from 'react-native-reanimated';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import Animated, {useAnimatedStyle, useDerivedValue} from 'react-native-reanimated';
import {SEARCH_INPUT_HEIGHT, SEARCH_INPUT_MARGIN} from '@constants/view';
import {useTheme} from '@context/theme';
import useHeaderHeight, {MAX_OVERSCROLL} from '@hooks/header';
import {clamp} from '@utils/gallery';
import {makeStyleSheetFromTheme} from '@utils/theme';
import Header, {HeaderRightButton} from './header';
@@ -23,6 +24,7 @@ type Props = SearchProps & {
onTitlePress?: () => void;
rightButtons?: HeaderRightButton[];
scrollValue?: Animated.SharedValue<number>;
lockValue?: Animated.SharedValue<number | null>;
hideHeader?: () => void;
showBackButton?: boolean;
subtitle?: string;
@@ -47,6 +49,7 @@ const NavigationHeader = ({
onTitlePress,
rightButtons,
scrollValue,
lockValue,
showBackButton,
subtitle,
subtitleCompanion,
@@ -55,21 +58,37 @@ const NavigationHeader = ({
...searchProps
}: Props) => {
const theme = useTheme();
const insets = useSafeAreaInsets();
const styles = getStyleSheet(theme);
const {largeHeight, defaultHeight} = useHeaderHeight();
const {largeHeight, defaultHeight, headerOffset} = useHeaderHeight();
const containerHeight = useAnimatedStyle(() => {
const minHeight = defaultHeight + insets.top;
const minHeight = defaultHeight;
const value = -(scrollValue?.value || 0);
const height = ((isLargeTitle ? largeHeight : defaultHeight)) + value + insets.top;
const calculatedHeight = (isLargeTitle ? largeHeight : defaultHeight) + value;
const height = lockValue?.value ? lockValue.value : calculatedHeight;
return {
height: Math.max(height, minHeight),
minHeight,
maxHeight: largeHeight + insets.top + MAX_OVERSCROLL,
maxHeight: largeHeight + MAX_OVERSCROLL,
};
});
const minScrollValue = useDerivedValue(() => scrollValue?.value || 0, [scrollValue]);
const translateY = useDerivedValue(() => (
lockValue?.value ? -lockValue.value : Math.min(-minScrollValue.value, headerOffset)
), [lockValue, minScrollValue, headerOffset]);
const searchTopStyle = useAnimatedStyle(() => {
const margin = clamp(-minScrollValue.value, -headerOffset, headerOffset);
const marginTop = (lockValue?.value ? -lockValue?.value : margin) - SEARCH_INPUT_HEIGHT - SEARCH_INPUT_MARGIN;
return {marginTop};
}, [lockValue, headerOffset, scrollValue]);
const heightOffset = useDerivedValue(() => (
lockValue?.value ? lockValue.value : headerOffset
), [lockValue, headerOffset]);
return (
<>
<Animated.View style={[styles.container, containerHeight]}>
@@ -77,40 +96,36 @@ const NavigationHeader = ({
defaultHeight={defaultHeight}
hasSearch={hasSearch}
isLargeTitle={isLargeTitle}
largeHeight={largeHeight}
heightOffset={heightOffset.value}
leftComponent={leftComponent}
onBackPress={onBackPress}
onTitlePress={onTitlePress}
rightButtons={rightButtons}
lockValue={lockValue}
scrollValue={scrollValue}
showBackButton={showBackButton}
subtitle={subtitle}
subtitleCompanion={subtitleCompanion}
theme={theme}
title={title}
top={insets.top}
/>
{isLargeTitle &&
<NavigationHeaderLargeTitle
defaultHeight={defaultHeight}
heightOffset={heightOffset.value}
hasSearch={hasSearch}
largeHeight={largeHeight}
scrollValue={scrollValue}
subtitle={subtitle}
theme={theme}
title={title}
translateY={translateY}
/>
}
{hasSearch &&
<NavigationSearch
{...searchProps}
defaultHeight={defaultHeight}
largeHeight={largeHeight}
scrollValue={scrollValue}
hideHeader={hideHeader}
theme={theme}
top={0}
/>
<NavigationSearch
{...searchProps}
hideHeader={hideHeader}
theme={theme}
topStyle={searchTopStyle}
/>
}
</Animated.View>
</>

View File

@@ -9,13 +9,12 @@ import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
type Props = {
defaultHeight: number;
heightOffset: number;
hasSearch: boolean;
largeHeight: number;
scrollValue?: Animated.SharedValue<number>;
subtitle?: string;
theme: Theme;
title: string;
translateY: Animated.DerivedValue<number>;
}
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
@@ -34,33 +33,29 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
}));
const NavigationHeaderLargeTitle = ({
defaultHeight,
largeHeight,
heightOffset,
hasSearch,
scrollValue,
subtitle,
theme,
title,
translateY,
}: Props) => {
const styles = getStyleSheet(theme);
const transform = useAnimatedStyle(() => {
const value = scrollValue?.value || 0;
return {
transform: [{translateY: Math.min(-value, largeHeight - defaultHeight)}],
};
});
const transform = useAnimatedStyle(() => (
{transform: [{translateY: translateY.value}]}
), [translateY?.value]);
const containerStyle = useMemo(() => {
return [{height: largeHeight - defaultHeight}, styles.container];
}, [defaultHeight, largeHeight, theme]);
return [{height: heightOffset}, styles.container];
}, [heightOffset, theme]);
return (
<Animated.View style={[containerStyle, transform]}>
<Text
ellipsizeMode='tail'
numberOfLines={1}
style={styles.heading}
style={[styles.heading]}
testID='navigation.large_header.title'
>
{title}

View File

@@ -2,35 +2,26 @@
// See LICENSE.txt for license information.
import React, {useCallback, useEffect, useMemo} from 'react';
import {DeviceEventEmitter, Keyboard, NativeSyntheticEvent, Platform, TextInputFocusEventData} from 'react-native';
import Animated, {useAnimatedStyle} from 'react-native-reanimated';
import {DeviceEventEmitter, Keyboard, NativeSyntheticEvent, Platform, TextInputFocusEventData, ViewStyle} from 'react-native';
import Animated, {AnimatedStyleProp} from 'react-native-reanimated';
import Search, {SearchProps} from '@components/search';
import {Events} from '@constants';
import {HEADER_SEARCH_HEIGHT} from '@constants/view';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
type Props = SearchProps & {
defaultHeight: number;
largeHeight: number;
scrollValue?: Animated.SharedValue<number>;
topStyle: AnimatedStyleProp<ViewStyle>;
hideHeader?: () => void;
theme: Theme;
top: number;
}
const INITIAL_TOP = -45;
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
container: {
backgroundColor: theme.sidebarBg,
height: HEADER_SEARCH_HEIGHT,
justifyContent: 'center',
paddingHorizontal: 20,
width: '100%',
zIndex: 10,
top: INITIAL_TOP,
},
inputContainerStyle: {
backgroundColor: changeOpacity(theme.sidebarText, 0.12),
@@ -41,11 +32,9 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
}));
const NavigationSearch = ({
defaultHeight,
largeHeight,
scrollValue,
hideHeader,
theme,
topStyle,
...searchProps
}: Props) => {
const styles = getStyleSheet(theme);
@@ -58,12 +47,6 @@ const NavigationSearch = ({
color: theme.sidebarText,
}), [theme]);
const searchTop = useAnimatedStyle(() => {
const value = scrollValue?.value || 0;
const min = (largeHeight - defaultHeight);
return {marginTop: Math.min(-Math.min((value), min), min)};
}, [largeHeight, defaultHeight]);
const onFocus = useCallback((e: NativeSyntheticEvent<TextInputFocusEventData>) => {
hideHeader?.();
searchProps.onFocus?.(e);
@@ -89,7 +72,7 @@ const NavigationSearch = ({
}, []);
return (
<Animated.View style={[styles.container, searchTop]}>
<Animated.View style={[styles.container, topStyle]}>
<Search
{...searchProps}
cancelButtonProps={cancelButtonProps}

View File

@@ -75,7 +75,7 @@ function PostDraft({
ios: (keyboardHeight ? keyboardHeight - keyboardAdjustment : (postInputTop + insetsAdjustment)),
default: postInputTop + insetsAdjustment,
});
const autocompleteAvailableSpace = containerHeight - autocompletePosition - (isChannelScreen ? headerHeight + insets.top : 0);
const autocompleteAvailableSpace = containerHeight - autocompletePosition - (isChannelScreen ? headerHeight : 0);
const [animatedAutocompletePosition, animatedAutocompleteAvailableSpace] = useAutocompleteDefaultAnimatedValues(autocompletePosition, autocompleteAvailableSpace);