bottomSheet snapPoint helper (#6169)

This commit is contained in:
Elias Nahum
2022-04-13 10:11:31 -04:00
committed by GitHub
parent 1ef9762565
commit 94b2a14313
13 changed files with 86 additions and 63 deletions

View File

@@ -2,7 +2,6 @@
// See LICENSE.txt for license information.
import React from 'react';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import {renderWithIntl} from '@test/intl-test-helper';
@@ -11,13 +10,11 @@ import Header from './header';
describe('components/channel_list/header', () => {
it('Channel List Header Component should match snapshot', () => {
const {toJSON} = renderWithIntl(
<SafeAreaProvider>
<Header
canCreateChannels={true}
canJoinChannels={true}
displayName={'Test!'}
/>
</SafeAreaProvider>,
<Header
canCreateChannels={true}
canJoinChannels={true}
displayName={'Test!'}
/>,
);
expect(toJSON()).toMatchSnapshot();

View File

@@ -3,7 +3,7 @@
import React, {useCallback, useEffect} from 'react';
import {useIntl} from 'react-intl';
import {Platform, Text, View} from 'react-native';
import {Text, View} from 'react-native';
import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
@@ -14,6 +14,7 @@ import {useServerDisplayName} from '@context/server';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
import {bottomSheet} from '@screens/navigation';
import {bottomSheetSnapPoint} from '@utils/helpers';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
@@ -101,7 +102,7 @@ const ChannelListHeader = ({canCreateChannels, canJoinChannels, displayName, ico
bottomSheet({
closeButtonId,
renderContent,
snapPoints: [((items + Platform.select({android: 1, default: 0})) * ITEM_HEIGHT) + (insets.bottom * 2), 10],
snapPoints: [bottomSheetSnapPoint(items, ITEM_HEIGHT, insets.bottom), 10],
theme,
title: intl.formatMessage({id: 'home.header.plus_menu', defaultMessage: 'Options'}),
});

View File

@@ -3,7 +3,6 @@
import React from 'react';
import {useIntl} from 'react-intl';
import {View} from 'react-native';
import SlideUpPanelItem from '@components/slide_up_panel_item';
@@ -36,14 +35,12 @@ const PlusMenuItem = ({pickerAction, onPress}: PlusMenuItemProps) => {
};
const itemType = menuItems[pickerAction];
return (
<View>
<SlideUpPanelItem
text={itemType.text}
icon={itemType.icon}
onPress={onPress}
testID={itemType.testID}
/>
</View>
<SlideUpPanelItem
text={itemType.text}
icon={itemType.icon}
onPress={onPress}
testID={itemType.testID}
/>
);
};

View File

@@ -3,7 +3,6 @@
import Database from '@nozbe/watermelondb/Database';
import React from 'react';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import {SYSTEM_IDENTIFIERS} from '@constants/database';
import ServerDataOperator from '@database/operator/server_data_operator';
@@ -31,14 +30,12 @@ describe('components/channel_list', () => {
it('should render', () => {
const wrapper = renderWithEverything(
<SafeAreaProvider>
<ChannelsList
isTablet={false}
teamsCount={1}
currentTeamId={TestHelper.basicTeam!.id}
channelsCount={1}
/>
</SafeAreaProvider>,
<ChannelsList
isTablet={false}
teamsCount={1}
currentTeamId={TestHelper.basicTeam!.id}
channelsCount={1}
/>,
{database},
);
expect(wrapper.toJSON()).toBeTruthy();
@@ -51,14 +48,12 @@ describe('components/channel_list', () => {
});
const wrapper = renderWithEverything(
<SafeAreaProvider>
<ChannelsList
isTablet={false}
teamsCount={0}
currentTeamId='TestHelper.basicTeam!.id'
channelsCount={1}
/>
</SafeAreaProvider>,
<ChannelsList
isTablet={false}
teamsCount={0}
currentTeamId='TestHelper.basicTeam!.id'
channelsCount={1}
/>,
{database},
);
@@ -72,14 +67,12 @@ describe('components/channel_list', () => {
it('should render channels error', () => {
const wrapper = renderWithEverything(
<SafeAreaProvider>
<ChannelsList
isTablet={false}
teamsCount={1}
currentTeamId={TestHelper.basicTeam!.id}
channelsCount={0}
/>
</SafeAreaProvider>,
<ChannelsList
isTablet={false}
teamsCount={1}
currentTeamId={TestHelper.basicTeam!.id}
channelsCount={0}
/>,
{database},
);
expect(wrapper.toJSON()).toMatchSnapshot();

View File

@@ -7,6 +7,7 @@ import Clipboard from '@react-native-community/clipboard';
import React, {useCallback, useMemo} from 'react';
import {useIntl} from 'react-intl';
import {GestureResponderEvent, StyleProp, StyleSheet, Text, TextStyle, View} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import CompassIcon from '@components/compass_icon';
import SlideUpPanelItem, {ITEM_HEIGHT} from '@components/slide_up_panel_item';
@@ -14,6 +15,7 @@ import {MM_TABLES} from '@constants/database';
import {useTheme} from '@context/theme';
import UserModel from '@database/models/server/user';
import {bottomSheet, dismissBottomSheet, showModal} from '@screens/navigation';
import {bottomSheetSnapPoint} from '@utils/helpers';
import {displayUsername, getUsersByUsername} from '@utils/user';
import type UserModelType from '@typings/database/models/servers/user';
@@ -54,6 +56,7 @@ const AtMention = ({
const intl = useIntl();
const managedConfig = useManagedConfig<ManagedConfig>();
const theme = useTheme();
const insets = useSafeAreaInsets();
const user = useMemo(() => {
const usersByUsername = getUsersByUsername(users);
let mn = mentionName.toLowerCase();
@@ -146,12 +149,12 @@ const AtMention = ({
bottomSheet({
closeButtonId: 'close-at-mention',
renderContent,
snapPoints: [3 * ITEM_HEIGHT, 10],
snapPoints: [bottomSheetSnapPoint(2, ITEM_HEIGHT, insets.bottom), 10],
title: intl.formatMessage({id: 'post.options.title', defaultMessage: 'Options'}),
theme,
});
}
}, [managedConfig, intl, theme]);
}, [managedConfig, intl, theme, insets]);
const mentionTextStyle = [];

View File

@@ -6,6 +6,7 @@ import Clipboard from '@react-native-community/clipboard';
import React, {useCallback} from 'react';
import {useIntl} from 'react-intl';
import {Keyboard, StyleSheet, Text, TextStyle, TouchableOpacity, View} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import FormattedText from '@components/formatted_text';
import SlideUpPanelItem, {ITEM_HEIGHT} from '@components/slide_up_panel_item';
@@ -13,6 +14,7 @@ import SyntaxHighlighter from '@components/syntax_highlight';
import {Screens} from '@constants';
import {useTheme} from '@context/theme';
import {bottomSheet, dismissBottomSheet, goToScreen} from '@screens/navigation';
import {bottomSheetSnapPoint} from '@utils/helpers';
import {getHighlightLanguageFromNameOrAlias, getHighlightLanguageName} from '@utils/markdown';
import {preventDoubleTap} from '@utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
@@ -66,6 +68,7 @@ const MarkdownCodeBlock = ({language = '', content, textStyle}: MarkdownCodeBloc
const intl = useIntl();
const managedConfig = useManagedConfig<ManagedConfig>();
const theme = useTheme();
const insets = useSafeAreaInsets();
const style = getStyleSheet(theme);
const handlePress = preventDoubleTap(() => {
@@ -134,12 +137,12 @@ const MarkdownCodeBlock = ({language = '', content, textStyle}: MarkdownCodeBloc
bottomSheet({
closeButtonId: 'close-code-block',
renderContent,
snapPoints: [3 * ITEM_HEIGHT, 10],
snapPoints: [bottomSheetSnapPoint(2, ITEM_HEIGHT, insets.bottom), 10],
title: intl.formatMessage({id: 'post.options.title', defaultMessage: 'Options'}),
theme,
});
}
}, [managedConfig, intl, theme]);
}, [managedConfig, intl, insets, theme]);
const trimContent = (text: string) => {
const lines = text.split('\n');

View File

@@ -7,6 +7,7 @@ import React, {useCallback, useMemo, useRef, useState} from 'react';
import {useIntl} from 'react-intl';
import {Alert, Platform, StyleProp, Text, TextStyle, TouchableWithoutFeedback, View} from 'react-native';
import Animated from 'react-native-reanimated';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {SvgUri} from 'react-native-svg';
import parseUrl from 'url-parse';
@@ -24,6 +25,7 @@ import {bottomSheet, dismissBottomSheet} from '@screens/navigation';
import {lookupMimeType} from '@utils/file';
import {fileToGalleryItem, openGalleryAtIndex} from '@utils/gallery';
import {generateId} from '@utils/general';
import {bottomSheetSnapPoint} from '@utils/helpers';
import {calculateDimensions, getViewPortWidth, isGifTooLarge} from '@utils/images';
import {getMarkdownImageSize} from '@utils/markdown';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
@@ -69,6 +71,7 @@ const MarkdownImage = ({
}: MarkdownImageProps) => {
const intl = useIntl();
const isTablet = useIsTablet();
const insets = useSafeAreaInsets();
const theme = useTheme();
const style = getStyleSheet(theme);
const managedConfig = useManagedConfig<ManagedConfig>();
@@ -180,12 +183,12 @@ const MarkdownImage = ({
bottomSheet({
closeButtonId: 'close-mardown-image',
renderContent,
snapPoints: [3 * ITEM_HEIGHT, 10],
snapPoints: [bottomSheetSnapPoint(2, ITEM_HEIGHT, insets.bottom), 10],
title: intl.formatMessage({id: 'post.options.title', defaultMessage: 'Options'}),
theme,
});
}
}, [managedConfig, intl, theme]);
}, [managedConfig, intl, insets, theme]);
const handleOnError = useCallback(() => {
setFailed(true);

View File

@@ -6,6 +6,7 @@ import Clipboard from '@react-native-community/clipboard';
import React, {Children, ReactElement, useCallback} from 'react';
import {useIntl} from 'react-intl';
import {Alert, StyleSheet, Text, View} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import urlParse from 'url-parse';
import {switchToChannelByName} from '@actions/remote/channel';
@@ -16,6 +17,7 @@ import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {bottomSheet, dismissAllModals, dismissBottomSheet, popToRoot} from '@screens/navigation';
import {errorBadChannel} from '@utils/draft';
import {bottomSheetSnapPoint} from '@utils/helpers';
import {preventDoubleTap} from '@utils/tap';
import {matchDeepLink, normalizeProtocol, tryOpenURL} from '@utils/url';
@@ -36,6 +38,7 @@ const style = StyleSheet.create({
const MarkdownLink = ({children, experimentalNormalizeMarkdownLinks, href, siteURL}: MarkdownLinkProps) => {
const intl = useIntl();
const insets = useSafeAreaInsets();
const managedConfig = useManagedConfig<ManagedConfig>();
const serverUrl = useServerUrl();
const theme = useTheme();
@@ -147,12 +150,12 @@ const MarkdownLink = ({children, experimentalNormalizeMarkdownLinks, href, siteU
bottomSheet({
closeButtonId: 'close-mardown-link',
renderContent,
snapPoints: [3 * ITEM_HEIGHT, 10],
snapPoints: [bottomSheetSnapPoint(2, ITEM_HEIGHT, insets.bottom), 10],
title: intl.formatMessage({id: 'post.options.title', defaultMessage: 'Options'}),
theme,
});
}
}, [managedConfig, intl, theme]);
}, [managedConfig, intl, insets, theme]);
const renderChildren = experimentalNormalizeMarkdownLinks ? parseChildren() : children;

View File

@@ -4,12 +4,14 @@
import React, {useCallback} from 'react';
import {useIntl} from 'react-intl';
import {StyleSheet, TouchableOpacity, View} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {removePost} from '@actions/local/post';
import CompassIcon from '@components/compass_icon';
import SlideUpPanelItem, {ITEM_HEIGHT} from '@components/slide_up_panel_item';
import {useServerUrl} from '@context/server';
import {bottomSheet, dismissBottomSheet} from '@screens/navigation';
import {bottomSheetSnapPoint} from '@utils/helpers';
import type PostModel from '@typings/database/models/servers/post';
@@ -33,7 +35,9 @@ const retryPost = (serverUrl: string, post: PostModel) => post;
const Failed = ({post, theme}: FailedProps) => {
const intl = useIntl();
const insets = useSafeAreaInsets();
const serverUrl = useServerUrl();
const onPress = useCallback(() => {
const renderContent = () => {
return (
@@ -67,11 +71,11 @@ const Failed = ({post, theme}: FailedProps) => {
bottomSheet({
closeButtonId: 'close-post-failed',
renderContent,
snapPoints: [3 * ITEM_HEIGHT, 10],
snapPoints: [bottomSheetSnapPoint(2, ITEM_HEIGHT, insets.bottom), 10],
title: intl.formatMessage({id: 'post.options.title', defaultMessage: 'Options'}),
theme,
});
}, []);
}, [insets]);
return (
<TouchableOpacity

View File

@@ -4,6 +4,7 @@
import React, {useCallback, useMemo} from 'react';
import {useIntl} from 'react-intl';
import {TouchableOpacity} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {Client} from '@client/rest';
import CompassIcon from '@components/compass_icon';
@@ -13,6 +14,7 @@ import NetworkManager from '@init/network_manager';
import PanelItem from '@screens/edit_profile/components/panel_item';
import {bottomSheet} from '@screens/navigation';
import PickerUtil from '@utils/file/file_picker';
import {bottomSheetSnapPoint} from '@utils/helpers';
import {preventDoubleTap} from '@utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
@@ -70,6 +72,7 @@ const ProfileImagePicker = ({
}: ImagePickerProps) => {
const theme = useTheme();
const intl = useIntl();
const insets = useSafeAreaInsets();
const serverUrl = useServerUrl();
const pictureUtils = useMemo(() => new PickerUtil(intl, uploadFiles), [uploadFiles, intl]);
const canRemovePicture = hasPictureUrl(user, serverUrl);
@@ -102,16 +105,17 @@ const ProfileImagePicker = ({
);
};
const snapPoints = canRemovePicture ? 5 : 4;
const snapPointsCount = canRemovePicture ? 5 : 4;
const snapPoint = bottomSheetSnapPoint(snapPointsCount, ACTION_HEIGHT, insets.bottom);
return bottomSheet({
closeButtonId: 'close-edit-profile',
renderContent,
snapPoints: [(snapPoints * ACTION_HEIGHT), 10],
snapPoints: [snapPoint, 10],
title: '',
theme,
});
}), [canRemovePicture, onRemoveProfileImage, pictureUtils, theme]);
}), [canRemovePicture, onRemoveProfileImage, insets, pictureUtils, theme]);
return (
<TouchableOpacity

View File

@@ -4,6 +4,7 @@
import React, {useCallback} from 'react';
import {useIntl} from 'react-intl';
import {TextStyle} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {setStatus} from '@actions/remote/user';
import MenuItem from '@components/menu_item';
@@ -13,6 +14,7 @@ import UserStatusIndicator from '@components/user_status';
import General from '@constants/general';
import {useServerUrl} from '@context/server';
import {bottomSheet, dismissBottomSheet, dismissModal} from '@screens/navigation';
import {bottomSheetSnapPoint} from '@utils/helpers';
import {preventDoubleTap} from '@utils/tap';
import {changeOpacity} from '@utils/theme';
import {confirmOutOfOfficeDisabled} from '@utils/user';
@@ -29,6 +31,7 @@ const {OUT_OF_OFFICE, OFFLINE, AWAY, ONLINE, DND} = General;
const UserStatus = ({currentUser, style, theme}: Props) => {
const intl = useIntl();
const insets = useSafeAreaInsets();
const serverUrl = useServerUrl();
const handleSetStatus = useCallback(preventDoubleTap(() => {
@@ -83,14 +86,15 @@ const UserStatus = ({currentUser, style, theme}: Props) => {
);
};
const snapPoint = bottomSheetSnapPoint(4, ITEM_HEIGHT, insets.bottom);
bottomSheet({
closeButtonId: 'close-set-user-status',
renderContent,
snapPoints: [(5 * ITEM_HEIGHT) + 10, 10],
snapPoints: [snapPoint, 10],
title: intl.formatMessage({id: 'account.user_status.title', defaultMessage: 'User Presence'}),
theme,
});
}), [theme]);
}), [theme, insets]);
const updateStatus = useCallback((status: string) => {
const userStatus = {

View File

@@ -2,7 +2,7 @@
// See LICENSE.txt for license information.
import moment, {Moment} from 'moment-timezone';
import {NativeModules} from 'react-native';
import {NativeModules, Platform} from 'react-native';
import {Device} from '@constants';
import {CUSTOM_STATUS_TIME_PICKER_INTERVALS_IN_MINUTES} from '@constants/custom_status';
@@ -142,3 +142,7 @@ export async function isTablet() {
}
export const pluckUnique = (key: string) => (array: Array<{[key: string]: unknown}>) => Array.from(new Set(array.map((obj) => obj[key])));
export function bottomSheetSnapPoint(itemsCount: number, itemHeight: number, bottomInset = 0) {
return ((itemsCount + Platform.select({android: 1, default: 0})) * itemHeight) + (bottomInset * 2.5);
}

View File

@@ -5,6 +5,7 @@ import DatabaseProvider from '@nozbe/watermelondb/DatabaseProvider';
import {render} from '@testing-library/react-native';
import React, {ReactElement} from 'react';
import {createIntl, IntlProvider} from 'react-intl';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import {ThemeContext, getDefaultThemeByAppearance} from '@context/theme';
import {getTranslations} from '@i18n';
@@ -25,7 +26,9 @@ export function renderWithIntl(ui: ReactElement, {locale = 'en', ...renderOption
locale={locale}
messages={getTranslations(locale)}
>
{children}
<SafeAreaProvider>
{children}
</SafeAreaProvider>
</IntlProvider>
);
}
@@ -41,7 +44,9 @@ export function renderWithIntlAndTheme(ui: ReactElement, {locale = 'en', ...rend
messages={getTranslations(locale)}
>
<ThemeContext.Provider value={getDefaultThemeByAppearance()}>
{children}
<SafeAreaProvider>
{children}
</SafeAreaProvider>
</ThemeContext.Provider>
</IntlProvider>
);
@@ -63,7 +68,9 @@ export function renderWithEverything(ui: ReactElement, {locale = 'en', database,
messages={getTranslations(locale)}
>
<ThemeContext.Provider value={getDefaultThemeByAppearance()}>
{children}
<SafeAreaProvider>
{children}
</SafeAreaProvider>
</ThemeContext.Provider>
</IntlProvider>
</DatabaseProvider>