// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. 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'; export type HeaderRightButton = { borderless?: boolean; buttonType?: 'native' | 'opacity' | 'highlight'; color?: string; iconName: string; count?: number | string; onPress: () => void; rippleRadius?: number; testID?: string; } type Props = { defaultHeight: number; hasSearch: boolean; isLargeTitle: boolean; heightOffset: number; leftComponent?: React.ReactElement; onBackPress?: () => void; onTitlePress?: () => void; rightButtons?: HeaderRightButton[]; scrollValue?: Animated.SharedValue; showBackButton?: boolean; subtitle?: string; subtitleCompanion?: React.ReactElement; theme: Theme; title?: string; titleCompanion?: React.ReactElement; } const hitSlop = {top: 20, bottom: 20, left: 20, right: 20}; const rightButtonHitSlop = {top: 20, bottom: 5, left: 5, right: 5}; const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ centered: { alignItems: Platform.select({android: 'flex-start', ios: 'center'}), }, container: { alignItems: 'center', backgroundColor: theme.sidebarBg, flexDirection: 'row', justifyContent: 'flex-start', paddingHorizontal: 16, zIndex: 10, }, subtitleContainer: { flexDirection: 'row', justifyContent: Platform.select({android: 'flex-start', ios: 'center'}), left: Platform.select({ios: undefined, default: 3}), }, subtitle: { color: changeOpacity(theme.sidebarHeaderTextColor, 0.72), ...typography('Body', 75), lineHeight: 12, marginBottom: 8, marginTop: 2, height: 13, }, titleContainer: { alignItems: Platform.select({android: 'flex-start', ios: 'center'}), justifyContent: 'center', flex: 3, height: '100%', ...Platform.select({ ios: { flex: undefined, width: '100%', position: 'absolute', left: 16, bottom: 0, zIndex: 1, }, }), }, leftAction: { alignItems: 'center', flexDirection: 'row', }, leftContainer: { height: '100%', justifyContent: 'center', ...Platform.select({ ios: { paddingLeft: 16, zIndex: 5, position: 'absolute', bottom: 0, }, }), }, rightContainer: { alignItems: 'center', flexDirection: 'row', height: '100%', justifyContent: 'flex-end', ...Platform.select({ ios: { right: 16, bottom: 0, position: 'absolute', zIndex: 2, }, }), }, rightButtonContainer: { flexDirection: 'row', alignItems: 'center', gap: 6, }, rightIcon: { padding: 5, }, title: { color: theme.sidebarHeaderTextColor, ...typography('Heading', 300), }, titleRow: { flexDirection: 'row', alignItems: 'center', gap: 4, }, })); const Header = ({ defaultHeight, hasSearch, isLargeTitle, heightOffset, leftComponent, onBackPress, onTitlePress, rightButtons, scrollValue, showBackButton = true, subtitle, subtitleCompanion, theme, title, titleCompanion, }: Props) => { const styles = getStyleSheet(theme); const insets = useSafeAreaInsets(); const opacity = useAnimatedStyle(() => { if (!isLargeTitle) { return {opacity: 1}; } if (hasSearch) { return {opacity: 0}; } const barHeight = heightOffset - ViewConstants.LARGE_HEADER_TITLE_HEIGHT; const val = (scrollValue?.value || 0); const showDuration = 200; const hideDuration = 50; const duration = val >= barHeight ? showDuration : hideDuration; const opacityValue = val >= barHeight ? 1 : 0; return { opacity: withTiming(opacityValue, {duration}), }; }, [heightOffset, isLargeTitle, hasSearch]); const containerAnimatedStyle = useAnimatedStyle(() => ({ height: defaultHeight, paddingTop: insets.top, }), [defaultHeight]); const containerStyle = useMemo(() => ( [styles.container, containerAnimatedStyle]), [styles, containerAnimatedStyle]); const additionalTitleStyle = useMemo(() => { return { marginLeft: Platform.select({android: showBackButton && !leftComponent ? 20 : 0}), paddingHorizontal: Platform.select({ ios: rightButtons?.length === 2 ? 90 : 60, android: 8, }), }; }, [leftComponent, showBackButton, rightButtons]); return ( {showBackButton && {leftComponent} } {!hasSearch && {title} {titleCompanion} } {!isLargeTitle && Boolean(subtitle || subtitleCompanion) && {subtitle} {subtitleCompanion} } {Boolean(rightButtons?.length) && rightButtons?.map((r) => ( {Boolean(r.count) && ( {r.count} )} )) } ); }; export default React.memo(Header);