[Gekidou MM-46365] fix reaction bar space and update bottom sheet styles (#6634)

* updated styles for reaction bar and made pick reaction pressable

* removed unused style import

* removed unused style import

* updated user avatars, user presence, thread options bottom sheets to new style also

* updated status bottom sheet, thread options, and some tweaks to the bottom sheet main file

* fixed a few minor styling issues

* used proper bottom inset in user presence bottom sheet

* various updates to bottom sheets

* used negative top position instead of negative margin

* updated camera type bottom sheet

* further refinements to bottom sheets

* updates to emoji bar and profile image picker

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
This commit is contained in:
Matthew Birtch
2022-12-23 07:08:51 -05:00
committed by GitHub
parent d063ecf8c7
commit 9753334ff2
26 changed files with 188 additions and 148 deletions

View File

@@ -283,7 +283,6 @@ const OptionItem = ({
}
</View>
);
if (Object.values(TouchableOptionTypes).includes(type)) {
return (
<TouchableOpacity onPress={onPress}>

View File

@@ -2,60 +2,36 @@
// See LICENSE.txt for license information.
import React from 'react';
import {useIntl} from 'react-intl';
import {View} from 'react-native';
import {CameraOptions} from 'react-native-image-picker';
import CompassIcon from '@components/compass_icon';
import FormattedText from '@components/formatted_text';
import TouchableWithFeedback from '@components/touchable_with_feedback';
import SlideUpPanelItem from '@components/slide_up_panel_item';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
import {dismissBottomSheet} from '@screens/navigation';
import {makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
type Props = {
onPress: (options: CameraOptions) => void;
}
const getStyle = makeStyleSheetFromTheme((theme: Theme) => ({
center: {
alignItems: 'center',
},
container: {
alignItems: 'center',
backgroundColor: theme.centerChannelBg,
height: 200,
paddingVertical: 10,
},
flex: {
flex: 1,
},
options: {
alignItems: 'center',
flex: 1,
flexDirection: 'row',
justifyContent: 'space-evenly',
width: '100%',
marginBottom: 50,
},
optionContainer: {
alignItems: 'flex-start',
},
title: {
color: theme.centerChannelColor,
fontSize: 18,
fontWeight: 'bold',
},
text: {
color: theme.centerChannelColor,
fontSize: 15,
...typography('Heading', 600, 'SemiBold'),
marginBottom: 8,
},
}));
const CameraType = ({onPress}: Props) => {
const theme = useTheme();
const isTablet = useIsTablet();
const style = getStyle(theme);
const intl = useIntl();
const onPhoto = async () => {
const options: CameraOptions = {
@@ -80,54 +56,26 @@ const CameraType = ({onPress}: Props) => {
};
return (
<View style={style.container}>
<View>
{!isTablet &&
<FormattedText
id='camera_type.title'
defaultMessage='Choose an action'
id='mobile.camera_type.title'
defaultMessage='Camera options'
style={style.title}
/>
}
<View style={style.options}>
<View style={style.flex}>
<TouchableWithFeedback
onPress={onPhoto}
testID='camera_type.photo'
>
<View style={style.center}>
<CompassIcon
color={theme.centerChannelColor}
name='camera-outline'
size={38}
/>
<FormattedText
id='camera_type.photo.option'
defaultMessage='Capture Photo'
style={style.text}
/>
</View>
</TouchableWithFeedback>
</View>
<View style={style.flex}>
<TouchableWithFeedback
onPress={onVideo}
testID='camera_type.video'
>
<View style={style.center}>
<CompassIcon
color={theme.centerChannelColor}
name='video-outline'
size={38}
/>
<FormattedText
id='camera_type.video.option'
defaultMessage='Record Video'
style={style.text}
/>
</View>
</TouchableWithFeedback>
</View>
</View>
<SlideUpPanelItem
icon='camera-outline'
onPress={onPhoto}
testID='camera_type.photo'
text={intl.formatMessage({id: 'camera_type.photo.option', defaultMessage: 'Capture Photo'})}
/>
<SlideUpPanelItem
icon='video-outline'
onPress={onVideo}
testID='camera_type.video'
text={intl.formatMessage({id: 'camera_type.video.option', defaultMessage: 'Record Video'})}
/>
</View>
);
};

View File

@@ -5,14 +5,18 @@ import React, {useCallback} from 'react';
import {useIntl} from 'react-intl';
import {Alert, StyleSheet} from 'react-native';
import {CameraOptions} from 'react-native-image-picker';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import CompassIcon from '@components/compass_icon';
import {ITEM_HEIGHT} from '@components/slide_up_panel_item';
import TouchableWithFeedback from '@components/touchable_with_feedback';
import {ICON_SIZE} from '@constants/post_draft';
import {useTheme} from '@context/theme';
import {TITLE_HEIGHT} from '@screens/bottom_sheet/content';
import {bottomSheet} from '@screens/navigation';
import {fileMaxWarning} from '@utils/file';
import PickerUtil from '@utils/file/file_picker';
import {bottomSheetSnapPoint} from '@utils/helpers';
import {changeOpacity} from '@utils/theme';
import CameraType from './camera_type';
@@ -36,6 +40,7 @@ export default function CameraQuickAction({
}: QuickActionAttachmentProps) {
const intl = useIntl();
const theme = useTheme();
const {bottom} = useSafeAreaInsets();
const handleButtonPress = useCallback((options: CameraOptions) => {
const picker = new PickerUtil(intl,
@@ -64,14 +69,15 @@ export default function CameraQuickAction({
return;
}
const snap = bottomSheetSnapPoint(2, ITEM_HEIGHT, bottom);
bottomSheet({
title: intl.formatMessage({id: 'camera_type.title', defaultMessage: 'Choose an action'}),
title: intl.formatMessage({id: 'mobile.camera_type.title', defaultMessage: 'Camera options'}),
renderContent,
snapPoints: [200, 10],
snapPoints: [TITLE_HEIGHT + snap, 10],
theme,
closeButtonId: 'camera-close-id',
});
}, [intl, theme, renderContent, maxFilesReached, maxFileCount]);
}, [intl, theme, renderContent, maxFilesReached, maxFileCount, bottom]);
const actionTestID = disabled ? `${testID}.disabled` : testID;
const color = disabled ? changeOpacity(theme.centerChannelColor, 0.16) : changeOpacity(theme.centerChannelColor, 0.64);

View File

@@ -30,7 +30,7 @@ const getStyle = makeStyleSheetFromTheme((theme: Theme) => ({
},
title: {
color: theme.centerChannelColor,
...typography('Body', 600, 'SemiBold'),
...typography('Heading', 600, 'SemiBold'),
},
betaContainer: {
backgroundColor: PostPriorityColors.IMPORTANT,

View File

@@ -4,11 +4,14 @@
import React, {useCallback} from 'react';
import {useIntl} from 'react-intl';
import {StyleProp, Text, TouchableOpacity, View, ViewStyle} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import FormattedText from '@components/formatted_text';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
import {TITLE_HEIGHT} from '@screens/bottom_sheet/content';
import {bottomSheet} from '@screens/navigation';
import {bottomSheetSnapPoint} from '@utils/helpers';
import {preventDoubleTap} from '@utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
@@ -19,6 +22,7 @@ import UsersList from './users_list';
import type UserModel from '@typings/database/models/servers/user';
const OVERFLOW_DISPLAY_LIMIT = 99;
const USER_ROW_HEIGHT = 40;
type Props = {
channelId: string;
@@ -88,9 +92,8 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
marginBottom: 12,
},
listHeaderText: {
color: changeOpacity(theme.centerChannelColor, 0.56),
...typography('Body', 75, 'SemiBold'),
textTransform: 'uppercase',
color: theme.centerChannelColor,
...typography('Heading', 600, 'SemiBold'),
},
};
});
@@ -99,6 +102,7 @@ const UserAvatarsStack = ({breakAt = 3, channelId, location, style: baseContaine
const theme = useTheme();
const intl = useIntl();
const isTablet = useIsTablet();
const {bottom} = useSafeAreaInsets();
const showParticipantsList = useCallback(preventDoubleTap(() => {
const renderContent = () => (
@@ -119,15 +123,16 @@ const UserAvatarsStack = ({breakAt = 3, channelId, location, style: baseContaine
/>
</>
);
const snap = bottomSheetSnapPoint(Math.min(users.length, 5), USER_ROW_HEIGHT, bottom);
bottomSheet({
closeButtonId: 'close-set-user-status',
renderContent,
initialSnapIndex: 1,
snapPoints: ['90%', '50%', 10],
snapPoints: ['90%', TITLE_HEIGHT + snap, 10],
title: intl.formatMessage({id: 'mobile.participants.header', defaultMessage: 'Thread Participants'}),
theme,
});
}), [isTablet, theme, users, channelId, location]);
}), [isTablet, theme, users, channelId, location, bottom]);
const displayUsers = users.slice(0, breakAt);
const overflowUsersCount = Math.min(users.length - displayUsers.length, OVERFLOW_DISPLAY_LIMIT);

View File

@@ -25,6 +25,7 @@ type AtMentionItemProps = {
showFullName: boolean;
testID?: string;
isCustomStatusEnabled: boolean;
pictureContainerStyle?: StyleProp<ViewStyle>;
}
const getName = (user: UserProfile | UserModel | undefined, showFullName: boolean, isCurrentUser: boolean, intl: IntlShape) => {
@@ -95,6 +96,7 @@ const UserItem = ({
showFullName,
testID,
isCustomStatusEnabled,
pictureContainerStyle,
}: AtMentionItemProps) => {
const theme = useTheme();
const style = getStyleFromTheme(theme);
@@ -116,7 +118,7 @@ const UserItem = ({
style={[style.row, containerStyle]}
testID={userItemTestId}
>
<View style={style.rowPicture}>
<View style={[style.rowPicture, pictureContainerStyle]}>
<ProfilePicture
author={user}
size={24}

View File

@@ -11,7 +11,7 @@ export const DEFAULT_EMOJIS = [
];
export const LARGE_ICON_SIZE = 32;
export const LARGE_CONTAINER_SIZE = 48;
export const REACTION_PICKER_HEIGHT = 72;
export const REACTION_PICKER_HEIGHT = 48;
export const SMALL_CONTAINER_SIZE = 44;
export const SMALL_ICON_BREAKPOINT = 410;
export const SMALL_ICON_SIZE = 28;

View File

@@ -26,8 +26,8 @@ const styles = StyleSheet.create({
icon_container: {
width: 24,
height: 24,
marginTop: 2,
marginRight: 8,
top: -1,
marginRight: 4,
},
});

View File

@@ -45,9 +45,10 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
},
separator: {
height: 1,
right: 16,
right: 20,
borderTopWidth: 1,
borderColor: changeOpacity(theme.centerChannelColor, 0.08),
marginBottom: 20,
},
};
});
@@ -83,7 +84,7 @@ const BottomSheetContent = ({buttonText, buttonIcon, children, disableButton, on
</>
{showButton && (
<>
<View style={[styles.separator, {width: separatorWidth, marginBottom: (isTablet ? TITLE_SEPARATOR_MARGIN_TABLET : TITLE_SEPARATOR_MARGIN)}]}/>
<View style={[styles.separator, {width: separatorWidth}]}/>
<Button
disabled={disableButton}
onPress={onPress}

View File

@@ -28,6 +28,7 @@ type SlideUpPanelProps = {
}
export const PADDING_TOP_MOBILE = 20;
export const PADDING_TOP_TABLET = 8;
const BottomSheet = ({closeButtonId, componentId, initialSnapIndex = 0, renderContent, snapPoints = ['90%', '50%', 50], testID}: SlideUpPanelProps) => {
const sheetRef = useRef<RNBottomSheet>(null);
@@ -100,7 +101,7 @@ const BottomSheet = ({closeButtonId, componentId, initialSnapIndex = 0, renderCo
const backdropStyle = useAnimatedStyle(() => ({
opacity: withTiming(backdropOpacity.value, {duration: 250, easing: Easing.inOut(Easing.linear)}),
backgroundColor: 'rgba(0, 0, 0, 0.5)',
backgroundColor: 'rgba(0, 0, 0, 0.6)',
}));
const renderBackdrop = () => {
@@ -126,9 +127,11 @@ const BottomSheet = ({closeButtonId, componentId, initialSnapIndex = 0, renderCo
<View
style={{
backgroundColor: theme.centerChannelBg,
borderColor: changeOpacity(theme.centerChannelColor, 0.16),
borderWidth: isTablet ? 0 : 1,
opacity: 1,
paddingHorizontal: 20,
paddingTop: isTablet ? 0 : PADDING_TOP_MOBILE,
paddingTop: isTablet ? PADDING_TOP_TABLET : PADDING_TOP_MOBILE,
height: '100%',
width: '100%',
alignSelf: 'center',
@@ -154,7 +157,7 @@ const BottomSheet = ({closeButtonId, componentId, initialSnapIndex = 0, renderCo
<RNBottomSheet
ref={sheetRef}
snapPoints={snapPoints}
borderRadius={10}
borderRadius={12}
initialSnap={snapPoints.length - 1}
renderContent={renderContainerContent}
onCloseEnd={handleCloseEnd}

View File

@@ -56,7 +56,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
const CustomStatusInput = ({emoji, isStatusSet, onChangeText, onClearHandle, onOpenEmojiPicker, text, theme}: Props) => {
const style = getStyleSheet(theme);
const intl = useIntl();
const placeholder = intl.formatMessage({id: 'custom_status.set_status', defaultMessage: 'Set a Status'});
const placeholder = intl.formatMessage({id: 'custom_status.set_status', defaultMessage: 'Set a custom status'});
return (
<>

View File

@@ -332,7 +332,7 @@ class CustomStatusModal extends NavigationComponent<Props, State> {
action={intl.formatMessage({id: 'mobile.custom_status.modal_confirm', defaultMessage: 'Done'})}
onPress={this.handleSetStatus}
testID='custom_status'
title={intl.formatMessage({id: 'mobile.routes.custom_status', defaultMessage: 'Set a Status'})}
title={intl.formatMessage({id: 'mobile.routes.custom_status', defaultMessage: 'Set a custom status'})}
/>
}
<SafeAreaView

View File

@@ -8,8 +8,11 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {Client} from '@client/rest';
import CompassIcon from '@components/compass_icon';
import FormattedText from '@components/formatted_text';
import {ITEM_HEIGHT} from '@components/slide_up_panel_item';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
import NetworkManager from '@managers/network_manager';
import PanelItem from '@screens/edit_profile/components/panel_item';
import {bottomSheet} from '@screens/navigation';
@@ -17,11 +20,11 @@ import PickerUtil from '@utils/file/file_picker';
import {bottomSheetSnapPoint} from '@utils/helpers';
import {preventDoubleTap} from '@utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
import type UserModel from '@typings/database/models/servers/user';
const hitSlop = {top: 100, bottom: 20, right: 20, left: 100};
const ACTION_HEIGHT = 55;
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
return {
@@ -38,6 +41,11 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
right: 0,
backgroundColor: theme.centerChannelBg,
},
title: {
...typography('Heading', 600, 'SemiBold'),
color: theme.centerChannelColor,
marginBottom: 8,
},
};
});
@@ -72,16 +80,24 @@ const ProfileImagePicker = ({
}: ImagePickerProps) => {
const theme = useTheme();
const intl = useIntl();
const insets = useSafeAreaInsets();
const {bottom} = useSafeAreaInsets();
const serverUrl = useServerUrl();
const pictureUtils = useMemo(() => new PickerUtil(intl, uploadFiles), [uploadFiles, intl]);
const canRemovePicture = hasPictureUrl(user, serverUrl);
const styles = getStyleSheet(theme);
const isTablet = useIsTablet();
const showFileAttachmentOptions = useCallback(preventDoubleTap(() => {
const renderContent = () => {
return (
<>
{!isTablet &&
<FormattedText
id='user.edit_profile.profile_photo.change_photo'
defaultMessage='Change profile photo'
style={styles.title}
/>
}
<PanelItem
pickerAction='takePhoto'
pictureUtils={pictureUtils}
@@ -106,16 +122,16 @@ const ProfileImagePicker = ({
};
const snapPointsCount = canRemovePicture ? 5 : 4;
const snapPoint = bottomSheetSnapPoint(snapPointsCount, ACTION_HEIGHT, insets.bottom);
const snapPoint = bottomSheetSnapPoint(snapPointsCount, ITEM_HEIGHT, bottom);
return bottomSheet({
closeButtonId: 'close-edit-profile',
renderContent,
snapPoints: [snapPoint, 10],
title: '',
title: 'Change profile photo',
theme,
});
}), [canRemovePicture, onRemoveProfileImage, insets, pictureUtils, theme]);
}), [canRemovePicture, onRemoveProfileImage, bottom, pictureUtils, theme]);
return (
<TouchableOpacity

View File

@@ -31,7 +31,7 @@ const CustomStatusText = ({isStatusSet, customStatus, testID}: CustomStatusTextP
text = (
<FormattedText
id='mobile.routes.custom_status'
defaultMessage='Set a Status'
defaultMessage='Set a custom status'
style={styles.text}
/>
);

View File

@@ -78,7 +78,7 @@ const CustomStatus = ({isTablet, currentUser}: CustomStatusProps) => {
if (isTablet) {
DeviceEventEmitter.emit(Events.ACCOUNT_SELECT_TABLET_VIEW, Screens.CUSTOM_STATUS);
} else {
showModal(Screens.CUSTOM_STATUS, intl.formatMessage({id: 'mobile.routes.custom_status', defaultMessage: 'Set a Status'}));
showModal(Screens.CUSTOM_STATUS, intl.formatMessage({id: 'mobile.routes.custom_status', defaultMessage: 'Set a custom status'}));
}
setShowRetryMessage(false);
}), [isTablet]);

View File

@@ -7,12 +7,15 @@ import {TouchableOpacity, View} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {setStatus} from '@actions/remote/user';
import FormattedText from '@components/formatted_text';
import SlideUpPanelItem, {ITEM_HEIGHT} from '@components/slide_up_panel_item';
import StatusLabel from '@components/status_label';
import UserStatusIndicator from '@components/user_status';
import General from '@constants/general';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
import {TITLE_HEIGHT} from '@screens/bottom_sheet/content';
import {bottomSheet, dismissBottomSheet, dismissModal} from '@screens/navigation';
import {bottomSheetSnapPoint} from '@utils/helpers';
import {preventDoubleTap} from '@utils/tap';
@@ -37,6 +40,13 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
spacer: {
marginLeft: 16,
},
listHeader: {
marginBottom: 12,
},
listHeaderText: {
color: theme.centerChannelColor,
...typography('Heading', 600, 'SemiBold'),
},
};
});
@@ -47,22 +57,33 @@ type Props = {
};
const UserStatus = ({currentUser}: Props) => {
const intl = useIntl();
const insets = useSafeAreaInsets();
const {bottom} = useSafeAreaInsets();
const serverUrl = useServerUrl();
const theme = useTheme();
const styles = getStyleSheet(theme);
const isTablet = useIsTablet();
const handleSetStatus = useCallback(preventDoubleTap(() => {
const renderContent = () => {
return (
<>
{!isTablet && (
<View style={styles.listHeader}>
<FormattedText
id='user_status.title'
defaultMessage={'Status'}
style={styles.listHeaderText}
/>
</View>
)}
<SlideUpPanelItem
icon='check-circle'
iconStyles={{color: theme.onlineIndicator}}
onPress={() => setUserStatus(ONLINE)}
testID='user_status.online.option'
text={intl.formatMessage({
id: 'mobile.set_status.online',
id: 'user_status.online',
defaultMessage: 'Online',
})}
textStyles={styles.label}
@@ -73,7 +94,7 @@ const UserStatus = ({currentUser}: Props) => {
onPress={() => setUserStatus(AWAY)}
testID='user_status.away.option'
text={intl.formatMessage({
id: 'mobile.set_status.away',
id: 'user_status.away',
defaultMessage: 'Away',
})}
textStyles={styles.label}
@@ -84,7 +105,7 @@ const UserStatus = ({currentUser}: Props) => {
onPress={() => setUserStatus(DND)}
testID='user_status.dnd.option'
text={intl.formatMessage({
id: 'mobile.set_status.dnd',
id: 'user_status.dnd',
defaultMessage: 'Do Not Disturb',
})}
textStyles={styles.label}
@@ -95,7 +116,7 @@ const UserStatus = ({currentUser}: Props) => {
onPress={() => setUserStatus(OFFLINE)}
testID='user_status.offline.option'
text={intl.formatMessage({
id: 'mobile.set_status.offline',
id: 'user_status.offline',
defaultMessage: 'Offline',
})}
textStyles={styles.label}
@@ -104,15 +125,15 @@ const UserStatus = ({currentUser}: Props) => {
);
};
const snapPoint = bottomSheetSnapPoint(4, ITEM_HEIGHT, insets.bottom);
const snapPoint = bottomSheetSnapPoint(4, ITEM_HEIGHT, bottom);
bottomSheet({
closeButtonId: 'close-set-user-status',
renderContent,
snapPoints: [snapPoint, 10],
title: intl.formatMessage({id: 'account.user_status.title', defaultMessage: 'User Presence'}),
snapPoints: [(snapPoint + TITLE_HEIGHT), 10],
title: intl.formatMessage({id: 'user_status.title', defaultMessage: 'Status'}),
theme,
});
}), [theme, insets]);
}), [theme, bottom]);
const updateStatus = useCallback((status: string) => {
const userStatus = {

View File

@@ -61,7 +61,7 @@ const ServerList = ({servers}: Props) => {
testID='server_list'
title={intl.formatMessage({id: 'your.servers', defaultMessage: 'Your servers'})}
>
<View style={styles.container}>
<View style={[styles.container, {marginTop: isTablet ? 12 : 0}]}>
<FlatList
data={servers}
renderItem={renderServer}

View File

@@ -59,6 +59,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({
badge: {
left: 18,
top: -5,
borderColor: theme.centerChannelBg,
},
button: {
borderRadius: 8,

View File

@@ -18,7 +18,7 @@ import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
divider: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
backgroundColor: changeOpacity(theme.centerChannelColor, 0.08),
height: 1,
},
};
@@ -110,7 +110,6 @@ const Filter = ({initialFilter, setFilter, title}: FilterProps) => {
showTitle={!isTablet}
testID='search.filters'
title={title}
titleSeparator={true}
>
<View>
<FlatList

View File

@@ -1,13 +1,13 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {View} from 'react-native';
import React, {useCallback, useMemo} from 'react';
import {Pressable, PressableStateCallbackType, View} from 'react-native';
import CompassIcon from '@components/compass_icon';
import {LARGE_ICON_SIZE} from '@constants/reaction_picker';
import {useTheme} from '@context/theme';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
return {
@@ -17,9 +17,13 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
justifyContent: 'center',
borderRadius: 4,
},
highlight: {
backgroundColor: changeOpacity(theme.buttonBg, 0.08),
borderRadius: 4,
},
icon: {
...typography('Body', 1000),
color: theme.centerChannelColor,
fontSize: LARGE_ICON_SIZE,
color: changeOpacity(theme.centerChannelColor, 0.56),
},
};
});
@@ -32,19 +36,31 @@ type PickReactionProps = {
const PickReaction = ({openEmojiPicker, width, height}: PickReactionProps) => {
const theme = useTheme();
const styles = getStyleSheet(theme);
const highlightedStyle = useCallback(({pressed}: PressableStateCallbackType) => (pressed ? styles.highlight : undefined), [styles.highlight]);
const pickReactionStyle = useMemo(() => [
styles.container,
{
width,
height,
},
], [width, height]);
return (
<View
style={[styles.container, {
width, height,
}]}
testID='post_options.reaction_bar.pick_reaction.button'
<Pressable
onPress={openEmojiPicker}
style={highlightedStyle}
>
<CompassIcon
onPress={openEmojiPicker}
name='emoticon-plus-outline'
style={styles.icon}
/>
</View>
<View
style={pickReactionStyle}
testID='post_options.reaction_bar.pick_reaction.button'
>
<CompassIcon
name='emoticon-plus-outline'
style={styles.icon}
/>
</View>
</Pressable>
);
};

View File

@@ -15,7 +15,8 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
fontWeight: 'bold',
},
highlight: {
backgroundColor: changeOpacity(theme.linkColor, 0.1),
backgroundColor: changeOpacity(theme.buttonBg, 0.08),
borderRadius: 4,
},
reactionContainer: {
backgroundColor: changeOpacity(theme.centerChannelColor, 0.04),

View File

@@ -18,6 +18,7 @@ import {
} from '@constants/reaction_picker';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import {useIsTablet} from '@hooks/device';
import {dismissBottomSheet, showModal} from '@screens/navigation';
import {makeStyleSheetFromTheme} from '@utils/theme';
@@ -37,6 +38,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
alignItems: 'center',
height: REACTION_PICKER_HEIGHT,
justifyContent: 'space-between',
marginBottom: 8,
},
};
});
@@ -49,6 +51,8 @@ const ReactionBar = ({recentEmojis = [], postId}: QuickReactionProps) => {
const isSmallDevice = width < SMALL_ICON_BREAKPOINT;
const styles = getStyleSheet(theme);
const isTablet = useIsTablet();
const handleEmojiPress = useCallback(async (emoji: string) => {
await dismissBottomSheet(Screens.POST_OPTIONS);
addReaction(serverUrl, postId, emoji);
@@ -74,7 +78,7 @@ const ReactionBar = ({recentEmojis = [], postId}: QuickReactionProps) => {
}
return (
<View style={styles.container}>
<View style={[styles.container, {marginTop: isTablet ? 12 : 0}]}>
{
recentEmojis.map((emoji) => {
return (

View File

@@ -5,6 +5,8 @@ import React, {useCallback, useEffect, useRef} from 'react';
import {ListRenderItemInfo, StyleSheet} from 'react-native';
import {FlatList} from 'react-native-gesture-handler';
import {useIsTablet} from '@hooks/device';
import Item from './item';
import type ReactionModel from '@typings/database/models/servers/reaction';
@@ -29,6 +31,7 @@ const style = StyleSheet.create({
});
const EmojiBar = ({emojiSelected, reactionsByName, setIndex, sortedReactions}: Props) => {
const isTablet = useIsTablet();
const listRef = useRef<FlatList<string>>(null);
const scrollToIndex = (index: number, animated = false) => {
@@ -81,7 +84,7 @@ const EmojiBar = ({emojiSelected, reactionsByName, setIndex, sortedReactions}: P
horizontal={true}
ref={listRef}
renderItem={renderItem}
style={style.container}
style={[style.container, {marginTop: isTablet ? 12 : 0}]}
onScrollToIndexFailed={onScrollToIndexFailed}
overScrollMode='never'
/>

View File

@@ -21,6 +21,11 @@ type Props = {
const style = StyleSheet.create({
container: {
marginBottom: 8,
paddingLeft: 0,
flexDirection: 'row',
},
picture: {
marginLeft: 0,
},
});
@@ -44,6 +49,7 @@ const Reactor = ({channelId, location, user}: Props) => {
<TouchableOpacity onPress={openUserProfile}>
<UserItem
containerStyle={style.container}
pictureContainerStyle={style.picture}
user={user}
testID='reactions.reactor_item'
/>

View File

@@ -2,8 +2,9 @@
// See LICENSE.txt for license information.
import {useManagedConfig} from '@mattermost/react-native-emm';
import React from 'react';
import React, {useMemo} from 'react';
import {View} from 'react-native';
import {useSafeAreaInsets} from 'react-native-safe-area-context';
import {CopyPermalinkOption, FollowThreadOption, ReplyOption, SaveOption} from '@components/common_post_options';
import FormattedText from '@components/formatted_text';
@@ -14,9 +15,12 @@ import {useIsTablet} from '@hooks/device';
import useNavButtonPressed from '@hooks/navigation_button_pressed';
import BottomSheet from '@screens/bottom_sheet';
import {dismissModal} from '@screens/navigation';
import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme';
import {bottomSheetSnapPoint} from '@utils/helpers';
import {makeStyleSheetFromTheme} from '@utils/theme';
import {typography} from '@utils/typography';
import {TITLE_HEIGHT} from '../bottom_sheet/content';
import MarkAsUnreadOption from './options/mark_as_unread_option';
import OpenInChannelOption from './options/open_in_channel_option';
@@ -35,12 +39,11 @@ type ThreadOptionsProps = {
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
return {
listHeader: {
marginBottom: 12,
marginBottom: 8,
},
listHeaderText: {
color: changeOpacity(theme.centerChannelColor, 0.56),
textTransform: 'uppercase',
...typography('Body', 75, 'SemiBold'),
color: theme.centerChannelColor,
...typography('Heading', 600, 'SemiBold'),
},
};
});
@@ -56,7 +59,7 @@ const ThreadOptions = ({
}: ThreadOptionsProps) => {
const theme = useTheme();
const isTablet = useIsTablet();
const {bottom} = useSafeAreaInsets();
const style = getStyleSheet(theme);
const close = () => {
@@ -103,6 +106,8 @@ const ThreadOptions = ({
);
}
const snapPoint = useMemo(() => TITLE_HEIGHT + bottomSheetSnapPoint(options.length, ITEM_HEIGHT, bottom), [bottom, options.length]);
const renderContent = () => (
<>
{!isTablet && (
@@ -124,7 +129,7 @@ const ThreadOptions = ({
closeButtonId={THREAD_OPTIONS_BUTTON}
componentId={Screens.THREAD_OPTIONS}
initialSnapIndex={0}
snapPoints={[((options.length + 2) * ITEM_HEIGHT), 10]}
snapPoints={[snapPoint, 10]}
testID='thread_options'
/>
);

View File

@@ -12,7 +12,6 @@
"account.logout": "Log out",
"account.logout_from": "Log out of {serverName}",
"account.settings": "Settings",
"account.user_status.title": "User Presence",
"account.your_profile": "Your Profile",
"alert.channel_deleted.description": "The channel {displayName} has been archived.",
"alert.channel_deleted.title": "Archived channel",
@@ -94,7 +93,6 @@
"browse_channels.showSharedChannels": "Show: Shared Channels",
"browse_channels.title": "Browse channels",
"camera_type.photo.option": "Capture Photo",
"camera_type.title": "Choose an action",
"camera_type.video.option": "Record Video",
"center_panel.archived.closeChannel": "Close Channel",
"channel_header.directchannel.you": "{displayName} (you)",
@@ -203,6 +201,7 @@
"combined_system_message.removed_from_team.two": "{firstUser} and {secondUser} were **removed from the team**.",
"combined_system_message.you": "You",
"connection_banner.connected": "Connection restored",
"connection_banner.connecting": "Connecting...",
"connection_banner.not_connected": "No internet connection",
"connection_banner.not_reachable": "The server is not reachable",
"create_direct_message.title": "Create Direct Message",
@@ -222,7 +221,7 @@
"custom_status.expiry.at": "at",
"custom_status.expiry.until": "Until",
"custom_status.failure_message": "Failed to update status. Try again",
"custom_status.set_status": "Set a Status",
"custom_status.set_status": "Set a custom status",
"custom_status.suggestions.in_a_meeting": "In a meeting",
"custom_status.suggestions.on_a_vacation": "On a vacation",
"custom_status.suggestions.out_for_lunch": "Out for lunch",
@@ -426,6 +425,7 @@
"mobile.calls_you": "(you)",
"mobile.camera_photo_permission_denied_description": "Take photos and upload them to your server or save them to your device. Open Settings to grant {applicationName} read and write access to your camera.",
"mobile.camera_photo_permission_denied_title": "{applicationName} would like to access your camera",
"mobile.camera_type.title": "Camera options",
"mobile.channel_info.alertNo": "No",
"mobile.channel_info.alertYes": "Yes",
"mobile.channel_list.recent": "Recent",
@@ -575,7 +575,7 @@
"mobile.reset_status.title_ooo": "Disable \"Out Of Office\"?",
"mobile.routes.code": "{language} Code",
"mobile.routes.code.noLanguage": "Code",
"mobile.routes.custom_status": "Set a Status",
"mobile.routes.custom_status": "Set a custom status",
"mobile.routes.table": "Table",
"mobile.routes.user_profile": "Profile",
"mobile.screen.settings": "Settings",
@@ -605,10 +605,7 @@
"mobile.server_url.invalid_format": "URL must start with http:// or https://",
"mobile.session_expired": "Please log in to continue receiving notifications. Sessions for {siteName} are configured to expire every {daysCount, number} {daysCount, plural, one {day} other {days}}.",
"mobile.session_expired.title": "Session Expired",
"mobile.set_status.away": "Away",
"mobile.set_status.dnd": "Do Not Disturb",
"mobile.set_status.offline": "Offline",
"mobile.set_status.online": "Online",
"mobile.storage_permission_denied_description": "Upload files to your server. Open Settings to grant {applicationName} Read and Write access to files on this device.",
"mobile.storage_permission_denied_title": "{applicationName} would like to access your files",
"mobile.suggestion.members": "Members",
@@ -917,8 +914,14 @@
"unsupported_server.message": "Your server, {serverDisplayName}, is running an unsupported server version. You may experience compatibility issues that cause crashes or severe bugs breaking core functionality of the app. Please contact your System Administrator to upgrade your Mattermost server.",
"unsupported_server.title": "Unsupported server version",
"user_profile.custom_status": "Custom Status",
"user_status.away": "Away",
"user_status.dnd": "Do Not Disturb",
"user_status.offline": "Offline",
"user_status.online": "Online",
"user_status.title": "Status",
"user.edit_profile.email.auth_service": "Login occurs through {service}. Email cannot be updated. Email address used for notifications is {email}.",
"user.edit_profile.email.web_client": "Email must be updated using a web client or desktop application.",
"user.edit_profile.profile_photo.change_photo": "Change profile photo",
"user.settings.general.email": "Email",
"user.settings.general.field_handled_externally": "Some fields below are handled through your login provider. If you want to change them, youll need to do so through your login provider.",
"user.settings.general.firstName": "First Name",
@@ -929,6 +932,7 @@
"user.settings.notifications.email_threads.description": "Notify me about all replies to threads I'm following",
"user.tutorial.long_press": "Long-press on an item to view a user's profile",
"video.download": "Download video",
"video.download_description": "This video must be downloaded to play it.",
"video.failed_description": "An error occurred while trying to play the video.",
"your.servers": "Your servers"
}