diff --git a/app/components/announcement_banner/expanded_announcement_banner.tsx b/app/components/announcement_banner/expanded_announcement_banner.tsx index 50e8defe4..439e33bd1 100644 --- a/app/components/announcement_banner/expanded_announcement_banner.tsx +++ b/app/components/announcement_banner/expanded_announcement_banner.tsx @@ -2,14 +2,13 @@ // See LICENSE.txt for license information. import {BottomSheetScrollView} from '@gorhom/bottom-sheet'; -import {Button} from '@rneui/base'; import React, {useCallback, useMemo} from 'react'; import {useIntl} from 'react-intl'; import {ScrollView, Text, View} from 'react-native'; import {useSafeAreaInsets} from 'react-native-safe-area-context'; import {dismissAnnouncement} from '@actions/local/systems'; -import FormattedText from '@components/formatted_text'; +import Button from '@components/button'; import Markdown from '@components/markdown'; import {Screens} from '@constants'; import {useServerUrl} from '@context/server'; @@ -17,7 +16,6 @@ import {useTheme} from '@context/theme'; import {useBottomSheetListsFix} from '@hooks/bottom_sheet_lists_fix'; import {useIsTablet} from '@hooks/device'; import {dismissBottomSheet} from '@screens/navigation'; -import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; import {getMarkdownTextStyles, getMarkdownBlockStyles} from '@utils/markdown'; import {makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; @@ -46,6 +44,9 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { color: theme.centerChannelColor, ...typography('Heading', 600, 'SemiBold'), }, + dismissButtonContainer: { + marginTop: 10, + }, }; }); @@ -69,20 +70,7 @@ const ExpandedAnnouncementBanner = ({ const dismissBanner = useCallback(() => { dismissAnnouncement(serverUrl, bannerText); close(); - }, [bannerText]); - - const buttonStyles = useMemo(() => { - return { - okay: { - button: buttonBackgroundStyle(theme, 'lg', 'primary'), - text: buttonTextStyle(theme, 'lg', 'primary'), - }, - dismiss: { - button: [{marginTop: 10}, buttonBackgroundStyle(theme, 'lg', 'link')], - text: buttonTextStyle(theme, 'lg', 'link'), - }, - }; - }, [theme]); + }, [bannerText, serverUrl]); const containerStyle = useMemo(() => { return [style.container, {marginBottom: insets.bottom + 10}]; @@ -118,26 +106,21 @@ const ExpandedAnnouncementBanner = ({ /> + size='lg' + theme={theme} + /> {allowDismissal && ( - + )} ); diff --git a/app/components/button/index.tsx b/app/components/button/index.tsx index aebc41aa9..dfb9fdce8 100644 --- a/app/components/button/index.tsx +++ b/app/components/button/index.tsx @@ -6,8 +6,8 @@ import React, {useMemo, type ReactNode} from 'react'; import {type StyleProp, StyleSheet, Text, type TextStyle, View, type ViewStyle, type Insets} from 'react-native'; import CompassIcon from '@components/compass_icon'; +import Loading from '@components/loading'; import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; -import {changeOpacity} from '@utils/theme'; type Props = { theme: Theme; @@ -16,16 +16,17 @@ type Props = { textStyle?: StyleProp; size?: ButtonSize; emphasis?: ButtonEmphasis; - buttonType?: ButtonType; - buttonState?: ButtonState; testID?: string; - onPress: () => void; + onPress?: () => void; text: string; iconComponent?: ReactNode; disabled?: boolean; hitSlop?: Insets; isIconOnTheRight?: boolean; iconName?: string; + showLoader?: boolean; + isInverted?: boolean; + isDestructive?: boolean; }; const styles = StyleSheet.create({ @@ -50,8 +51,6 @@ const Button = ({ textStyle, size = 'm', emphasis, - buttonType, - buttonState, onPress, text, testID, @@ -60,24 +59,44 @@ const Button = ({ iconComponent, disabled, hitSlop, + showLoader = false, + isInverted = false, + isDestructive = false, }: Props) => { - const bgStyle = useMemo(() => [ - buttonBackgroundStyle(theme, size, emphasis, buttonType, buttonState), - backgroundStyle, - ], [theme, backgroundStyle, size, emphasis, buttonType, buttonState]); + let buttonType: ButtonType = 'default'; + if (isDestructive) { + buttonType = 'destructive'; + } else if (isInverted) { + buttonType = 'inverted'; + } - const txtStyle = useMemo(() => [ + const bgStyle = useMemo(() => [ + buttonBackgroundStyle(theme, size, emphasis, buttonType), + backgroundStyle, + ], [theme, backgroundStyle, size, emphasis, buttonType]); + + const bgDisabledStyle = useMemo(() => [ + buttonBackgroundStyle(theme, size, emphasis, 'disabled'), + backgroundStyle, + ], [theme, backgroundStyle, size, emphasis]); + + const txtStyle = useMemo(() => StyleSheet.flatten([ buttonTextStyle(theme, size, emphasis, buttonType), textStyle, - ], [theme, textStyle, size, emphasis, buttonType]); + ]), [theme, textStyle, size, emphasis, buttonType]); - let buttonStyle = StyleSheet.flatten(bgStyle); - if (disabled) { - buttonStyle = { - ...buttonStyle, - backgroundColor: changeOpacity(buttonStyle.backgroundColor! as string, 0.4), - }; - } + const txtDisabledStyle = useMemo(() => StyleSheet.flatten([ + buttonTextStyle(theme, size, emphasis, 'disabled'), + textStyle, + ]), [theme, textStyle, size, emphasis]); + + const txtStyleToUse = disabled ? txtDisabledStyle : txtStyle; + + const loadingComponent = ( + + ); let icon: ReactNode; @@ -90,7 +109,7 @@ const Button = ({ @@ -99,8 +118,9 @@ const Button = ({ return ( + {showLoader && loadingComponent} {!isIconOnTheRight && icon} {text} diff --git a/app/components/loading/index.tsx b/app/components/loading/index.tsx index f85d28bb2..8080c59b0 100644 --- a/app/components/loading/index.tsx +++ b/app/components/loading/index.tsx @@ -2,14 +2,14 @@ // See LICENSE.txt for license information. import React from 'react'; -import {ActivityIndicator, type StyleProp, View, type ViewStyle, Text, type TextStyle} from 'react-native'; +import {ActivityIndicator, type StyleProp, View, type ViewStyle, Text, type TextStyle, type ColorValue} from 'react-native'; import {useTheme} from '@context/theme'; type LoadingProps = { containerStyle?: StyleProp; size?: number | 'small' | 'large'; - color?: string; + color?: ColorValue; themeColor?: keyof Theme; footerText?: string; footerTextStyles?: TextStyle; diff --git a/app/components/loading_error/__snapshots__/index.test.tsx.snap b/app/components/loading_error/__snapshots__/index.test.tsx.snap index 7bcee9d50..6b65f1227 100644 --- a/app/components/loading_error/__snapshots__/index.test.tsx.snap +++ b/app/components/loading_error/__snapshots__/index.test.tsx.snap @@ -73,61 +73,106 @@ exports[`Loading Error should match snapshot 1`] = ` Error description - - Retry - + + + + + Retry + + + + + `; diff --git a/app/components/loading_error/index.tsx b/app/components/loading_error/index.tsx index d5b613178..a7bee50de 100644 --- a/app/components/loading_error/index.tsx +++ b/app/components/loading_error/index.tsx @@ -1,14 +1,14 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useMemo} from 'react'; +import React from 'react'; +import {useIntl} from 'react-intl'; import {Text, View} from 'react-native'; +import Button from '@components/button'; import CompassIcon from '@components/compass_icon'; import Loading from '@components/loading'; -import TouchableWithFeedback from '@components/touchable_with_feedback'; import {useTheme} from '@context/theme'; -import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; @@ -49,14 +49,15 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ textAlign: 'center', marginTop: 4, }, + buttonContainer: { + marginTop: 24, + }, })); const LoadingError = ({loading, message, onRetry, title}: Props) => { const theme = useTheme(); + const intl = useIntl(); const styles = getStyleSheet(theme); - const buttonStyle = useMemo(() => { - return [{marginTop: 24}, buttonBackgroundStyle(theme, 'lg', 'primary', 'inverted')]; - }, [theme]); if (loading) { return ( @@ -81,13 +82,14 @@ const LoadingError = ({loading, message, onRetry, title}: Props) => { {message} - - {'Retry'} - + + + theme={theme} + size='lg' + text={o.label || ''} + /> ))} diff --git a/app/screens/bottom_sheet/button.tsx b/app/screens/bottom_sheet/button.tsx index 2672d0ee9..5796e9783 100644 --- a/app/screens/bottom_sheet/button.tsx +++ b/app/screens/bottom_sheet/button.tsx @@ -2,18 +2,16 @@ // See LICENSE.txt for license information. import React from 'react'; -import {type GestureResponderEvent, Platform, Text, useWindowDimensions, View} from 'react-native'; +import {Platform, useWindowDimensions, View} from 'react-native'; -import CompassIcon from '@components/compass_icon'; -import TouchableWithFeedback from '@components/touchable_with_feedback'; +import Button from '@components/button'; import {useTheme} from '@context/theme'; import {useIsTablet} from '@hooks/device'; -import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; type Props = { disabled?: boolean; - onPress?: (e: GestureResponderEvent) => void; + onPress?: () => void; icon?: string; testID?: string; text?: string; @@ -21,22 +19,12 @@ type Props = { const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { return { - button: { - display: 'flex', - flexDirection: 'row', - }, buttonContainer: { paddingHorizontal: 20, }, container: { backgroundColor: theme.centerChannelBg, }, - iconContainer: { - width: 24, - height: 24, - top: -1, - marginRight: 4, - }, separator: { height: 1, right: 20, @@ -56,40 +44,22 @@ function BottomSheetButton({disabled = false, onPress, icon, testID, text}: Prop const styles = getStyleSheet(theme); const separatorWidth = Math.max(dimensions.width, 450); - const buttonType = disabled ? 'disabled' : 'default'; - const styleButtonText = buttonTextStyle(theme, 'lg', 'primary', buttonType); - const styleButtonBackground = buttonBackgroundStyle(theme, 'lg', 'primary', buttonType); - - const iconColor = disabled ? changeOpacity(theme.centerChannelColor, 0.32) : theme.buttonColor; return ( - - {icon && ( - - - - )} - {text && ( - {text} - )} - - - + theme={theme} + size='lg' + disabled={disabled} + /> + ); } diff --git a/app/screens/bottom_sheet/content.tsx b/app/screens/bottom_sheet/content.tsx index e143e1dac..183fc742a 100644 --- a/app/screens/bottom_sheet/content.tsx +++ b/app/screens/bottom_sheet/content.tsx @@ -2,7 +2,7 @@ // See LICENSE.txt for license information. import React from 'react'; -import {type GestureResponderEvent, Text, useWindowDimensions, View} from 'react-native'; +import {Text, useWindowDimensions, View} from 'react-native'; import {useTheme} from '@context/theme'; import {useIsTablet} from '@hooks/device'; @@ -15,7 +15,7 @@ type Props = { buttonText?: string; children: React.ReactNode; disableButton?: boolean; - onPress?: (e: GestureResponderEvent) => void; + onPress?: () => void; showButton: boolean; showTitle: boolean; testID?: string; diff --git a/app/screens/channel_bookmark/index.tsx b/app/screens/channel_bookmark/index.tsx index c40c5c79a..2e160d1e7 100644 --- a/app/screens/channel_bookmark/index.tsx +++ b/app/screens/channel_bookmark/index.tsx @@ -303,7 +303,6 @@ const ChannelBookmarkAddOrEdit = ({ {canDeleteBookmarks && + ); }; diff --git a/app/screens/forgot_password/index.tsx b/app/screens/forgot_password/index.tsx index dc3700aea..a1c155641 100644 --- a/app/screens/forgot_password/index.tsx +++ b/app/screens/forgot_password/index.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Button} from '@rneui/base'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {useIntl} from 'react-intl'; import {Keyboard, Platform, Text, useWindowDimensions, View} from 'react-native'; @@ -11,6 +10,7 @@ import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-nati import {SafeAreaView} from 'react-native-safe-area-context'; import {sendPasswordResetEmail} from '@actions/remote/session'; +import Button from '@components/button'; import FloatingTextInput from '@components/floating_text_input_label'; import FormattedText from '@components/formatted_text'; import {Screens} from '@constants'; @@ -18,7 +18,6 @@ import useAndroidHardwareBackHandler from '@hooks/android_back_handler'; import {useAvoidKeyboard} from '@hooks/device'; import SecurityManager from '@managers/security_manager'; import Background from '@screens/background'; -import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; import {isEmail} from '@utils/helpers'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; @@ -65,7 +64,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ justifyContent: 'center', paddingHorizontal: 24, }, - returnButton: { + returnButtonContainer: { marginTop: 32, }, subheader: { @@ -158,17 +157,15 @@ const ForgotPassword = ({componentId, serverUrl, theme}: Props) => { {email} - + ); } @@ -221,19 +218,16 @@ const ForgotPassword = ({componentId, serverUrl, theme}: Props) => { theme={theme} value={email} /> - + diff --git a/app/screens/home/channel_list/categories_list/__snapshots__/index.test.tsx.snap b/app/screens/home/channel_list/categories_list/__snapshots__/index.test.tsx.snap index 82644406d..1ab6c7494 100644 --- a/app/screens/home/channel_list/categories_list/__snapshots__/index.test.tsx.snap +++ b/app/screens/home/channel_list/categories_list/__snapshots__/index.test.tsx.snap @@ -256,61 +256,106 @@ exports[`components/categories_list should render channels error 1`] = ` There was a problem loading content for this team. - - Retry - + + + + + Retry + + + + + @@ -643,61 +688,106 @@ exports[`components/categories_list should render team error 1`] = ` There was a problem loading content for this server. - - Retry - + + + + + Retry + + + + + diff --git a/app/screens/home/channel_list/categories_list/categories/unreads/empty_state/index.tsx b/app/screens/home/channel_list/categories_list/categories/unreads/empty_state/index.tsx index 68d865a5c..e4dd0019b 100644 --- a/app/screens/home/channel_list/categories_list/categories/unreads/empty_state/index.tsx +++ b/app/screens/home/channel_list/categories_list/categories/unreads/empty_state/index.tsx @@ -1,14 +1,14 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useMemo} from 'react'; +import React from 'react'; +import {useIntl} from 'react-intl'; import {View} from 'react-native'; import {showUnreadChannelsOnly} from '@actions/local/channel'; +import Button from '@components/button'; import FormattedText from '@components/formatted_text'; -import TouchableWithFeedback from '@components/touchable_with_feedback'; import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; -import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; @@ -19,7 +19,7 @@ type Props = { } const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ - button: { + buttonContainer: { marginTop: 24, }, container: { @@ -45,13 +45,11 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ })); function EmptyUnreads({onlyUnreads}: Props) { + const intl = useIntl(); const theme = useTheme(); const serverUrl = useServerUrl(); const styles = getStyleSheet(theme); - const buttonStyle = useMemo(() => [buttonBackgroundStyle(theme, 'lg', 'tertiary', 'inverted'), styles.button], - [theme]); - const onPress = () => { showUnreadChannelsOnly(serverUrl, !onlyUnreads); }; @@ -71,17 +69,16 @@ function EmptyUnreads({onlyUnreads}: Props) { style={styles.paragraph} testID='unreads.empty.paragraph' /> - - + + + + ); - }, [buttonDisabled, loginId, password, isLoading, theme]); + }, [styles.loginButtonContainer, buttonDisabled, onLogin, intl, isLoading, theme]); const endAdornment = ( {(emailEnabled || usernameEnabled) && config.PasswordEnableForgotLink !== 'false' && ( - + )} {renderProceedButton} diff --git a/app/screens/login/sso_options.tsx b/app/screens/login/sso_options.tsx index 9ec3bc321..37f3fd4b2 100644 --- a/app/screens/login/sso_options.tsx +++ b/app/screens/login/sso_options.tsx @@ -1,15 +1,12 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Button} from '@rneui/base'; import React from 'react'; import {useIntl} from 'react-intl'; -import {Image, type ImageSourcePropType, Text, View} from 'react-native'; +import {Image, type ImageSourcePropType, StyleSheet, View} from 'react-native'; -import CompassIcon from '@components/compass_icon'; +import Button from '@components/button'; import {Sso} from '@constants'; -import {buttonBackgroundStyle} from '@utils/buttonStyles'; -import {makeStyleSheetFromTheme, changeOpacity} from '@utils/theme'; type SsoInfo = { text: string; @@ -26,8 +23,6 @@ type Props = { const SsoOptions = ({goToSso, ssoOnly, ssoOptions, theme}: Props) => { const {formatMessage} = useIntl(); - const styles = getStyleSheet(theme); - const styleButtonBackground = buttonBackgroundStyle(theme, 'lg', 'primary'); const getSsoButtonOptions = ((ssoType: string): SsoInfo => { const sso: SsoInfo = {} as SsoInfo; @@ -63,12 +58,8 @@ const SsoOptions = ({goToSso, ssoOnly, ssoOptions, theme}: Props) => { (ssoType: string) => ssoOptions[ssoType].enabled, ); - let styleViewContainer; - let styleButtonContainer; - if (enabledSSOs.length === 2 && !ssoOnly) { - styleViewContainer = styles.containerAsRow; - styleButtonContainer = styles.buttonContainer; - } + const styleViewContainer = enabledSSOs.length === 2 && !ssoOnly ? styles.containerAsRow : undefined; + const styleButtonWrapper = enabledSSOs.length === 2 && !ssoOnly ? styles.buttonWrapper : undefined; const componentArray = []; for (const ssoType of enabledSSOs) { @@ -78,38 +69,24 @@ const SsoOptions = ({goToSso, ssoOnly, ssoOptions, theme}: Props) => { }; componentArray.push( - , + , ); } @@ -120,41 +97,23 @@ const SsoOptions = ({goToSso, ssoOnly, ssoOptions, theme}: Props) => { ); }; -const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ +const styles = StyleSheet.create({ container: { marginVertical: 24, + gap: 8, }, containerAsRow: { flexDirection: 'row', alignItems: 'center', }, - buttonContainer: { - width: '48%', - marginRight: 8, - }, - button: { - marginVertical: 4, - backgroundColor: 'transparent', - borderWidth: 1, - borderColor: changeOpacity(theme.centerChannelColor, 0.16), - }, - buttonTextContainer: { - color: theme.centerChannelColor, - flexDirection: 'row', - marginLeft: 9, - }, - buttonText: { - color: theme.centerChannelColor, - fontFamily: 'OpenSans-SemiBold', - fontSize: 16, - lineHeight: 18, - top: 2, + buttonWrapper: { + flex: 1, }, logoStyle: { height: 18, marginRight: 5, width: 18, }, -})); +}); export default SsoOptions; diff --git a/app/screens/mfa/index.tsx b/app/screens/mfa/index.tsx index b59dc772e..b0c9cd15a 100644 --- a/app/screens/mfa/index.tsx +++ b/app/screens/mfa/index.tsx @@ -1,7 +1,6 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Button} from '@rneui/base'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {useIntl} from 'react-intl'; import {Keyboard, Platform, useWindowDimensions, View} from 'react-native'; @@ -11,18 +10,16 @@ import Animated, {useAnimatedStyle, useSharedValue, withTiming} from 'react-nati import {SafeAreaView} from 'react-native-safe-area-context'; import {login} from '@actions/remote/session'; +import Button from '@components/button'; import FloatingTextInput from '@components/floating_text_input_label'; import FormattedText from '@components/formatted_text'; -import Loading from '@components/loading'; import useAndroidHardwareBackHandler from '@hooks/android_back_handler'; import {useAvoidKeyboard} from '@hooks/device'; -import {t} from '@i18n'; +import {usePreventDoubleTap} from '@hooks/utils'; import SecurityManager from '@managers/security_manager'; import Background from '@screens/background'; import {popTopScreen} from '@screens/navigation'; -import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; import {getErrorMessage} from '@utils/errors'; -import {preventDoubleTap} from '@utils/tap'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; @@ -52,9 +49,6 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ justifyContent: 'center', marginTop: Platform.select({android: 56}), }, - error: { - marginTop: 64, - }, flex: { flex: 1, }, @@ -72,16 +66,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ paddingHorizontal: 24, height: '100%', }, - loading: { - height: 20, - width: 20, - }, - loadingContainerStyle: { - marginRight: 10, - padding: 0, - top: -2, - }, - proceedButton: { + proceedButtonContainer: { marginTop: 32, }, shield: { @@ -114,12 +99,12 @@ const MFA = ({componentId, config, goToHome, license, loginId, password, serverD setError(''); }, []); - const submit = useCallback(preventDoubleTap(async () => { + const submit = usePreventDoubleTap(useCallback(async () => { Keyboard.dismiss(); if (!token) { setError( formatMessage({ - id: t('login_mfa.tokenReq'), + id: 'login_mfa.tokenReq', defaultMessage: 'Please enter an MFA token', }), ); @@ -133,7 +118,7 @@ const MFA = ({componentId, config, goToHome, license, loginId, password, serverD return; } goToHome(result.error); - }), [token]); + }, [config, formatMessage, goToHome, intl, license, loginId, password, serverDisplayName, serverUrl, token])); const transform = useAnimatedStyle(() => { const duration = Platform.OS === 'android' ? 250 : 350; @@ -224,25 +209,18 @@ const MFA = ({componentId, config, goToHome, license, loginId, password, serverD theme={theme} value={token} /> - + diff --git a/app/screens/onboarding/footer_buttons.tsx b/app/screens/onboarding/footer_buttons.tsx index 2ca85d637..501cdc422 100644 --- a/app/screens/onboarding/footer_buttons.tsx +++ b/app/screens/onboarding/footer_buttons.tsx @@ -2,14 +2,13 @@ // See LICENSE.txt for license information. import React from 'react'; -import {Pressable, useWindowDimensions, View} from 'react-native'; +import {Pressable, StyleSheet, useWindowDimensions, View} from 'react-native'; import Animated, {Extrapolate, interpolate, useAnimatedStyle} from 'react-native-reanimated'; import CompassIcon from '@components/compass_icon'; import FormattedText from '@components/formatted_text'; import {ONBOARDING_CONTENT_MAX_WIDTH} from '@screens/onboarding/slide'; import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; -import {makeStyleSheetFromTheme} from '@utils/theme'; type Props = { theme: Theme; @@ -19,24 +18,20 @@ type Props = { scrollX: Animated.SharedValue; }; -const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ +const styles = StyleSheet.create({ button: { marginTop: 5, flex: 1, justifyContent: 'center', alignItems: 'center', }, - rowIcon: { - color: theme.buttonColor, - fontSize: 12, - marginLeft: 5, - marginTop: 4.5, - }, nextButtonText: { flexDirection: 'row', position: 'absolute', justifyContent: 'center', + alignItems: 'center', width: 120, + gap: 5, }, footerButtonsContainer: { flexDirection: 'column', @@ -46,7 +41,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ width: '100%', alignItems: 'center', }, -})); +}); const AnimatedButton = Animated.createAnimatedComponent(Pressable); const BUTTON_SIZE = 120; @@ -60,7 +55,6 @@ const FooterButtons = ({ }: Props) => { const {width} = useWindowDimensions(); const buttonWidth = Math.min(width * 0.8, ONBOARDING_CONTENT_MAX_WIDTH); - const styles = getStyleSheet(theme); // keep in mind penultimate and ultimate slides to run buttons text/opacity/size animations const penultimateSlide = lastSlideIndex - 1; @@ -114,6 +108,8 @@ const FooterButtons = ({ return {opacity: interpolatedScale}; }); + const textStyles = StyleSheet.flatten(buttonTextStyle(theme, 'lg', 'primary', 'default')); + const nextButtonText = ( ); diff --git a/app/screens/permalink/permalink.tsx b/app/screens/permalink/permalink.tsx index 8f0815e37..210098967 100644 --- a/app/screens/permalink/permalink.tsx +++ b/app/screens/permalink/permalink.tsx @@ -2,6 +2,7 @@ // See LICENSE.txt for license information. import React, {useCallback, useEffect, useMemo, useState} from 'react'; +import {useIntl} from 'react-intl'; import {Alert, Text, TouchableOpacity, View} from 'react-native'; import Animated from 'react-native-reanimated'; import {type Edge, SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context'; @@ -10,6 +11,7 @@ import {getPosts} from '@actions/local/post'; import {fetchChannelById, joinChannel, switchToChannelById} from '@actions/remote/channel'; import {fetchPostById, fetchPostsAround, fetchPostThread} from '@actions/remote/post'; import {addCurrentUserToTeam, fetchTeamByName, removeCurrentUserFromTeam} from '@actions/remote/team'; +import Button from '@components/button'; import CompassIcon from '@components/compass_icon'; import FormattedText from '@components/formatted_text'; import Loading from '@components/loading'; @@ -21,12 +23,11 @@ import {useTheme} from '@context/theme'; import DatabaseManager from '@database/manager'; import useAndroidHardwareBackHandler from '@hooks/android_back_handler'; import {useIsTablet} from '@hooks/device'; +import {usePreventDoubleTap} from '@hooks/utils'; import SecurityManager from '@managers/security_manager'; import {getChannelById, getMyChannel} from '@queries/servers/channel'; import {dismissModal} from '@screens/navigation'; -import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; import {closePermalink} from '@utils/permalink'; -import {preventDoubleTap} from '@utils/tap'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {secureGetFromRecord} from '@utils/types'; import {typography} from '@utils/typography'; @@ -105,11 +106,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => ({ alignItems: 'center', }, footer: { - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'row', padding: 20, - width: '100%', borderBottomLeftRadius: 12, borderBottomRightRadius: 12, borderTopWidth: 1, @@ -139,6 +136,7 @@ function Permalink({ isTeamMember, currentTeamId, }: Props) { + const intl = useIntl(); const [posts, setPosts] = useState([]); const [loading, setLoading] = useState(true); const theme = useTheme(); @@ -273,17 +271,17 @@ function Permalink({ } dismissModal({componentId: Screens.PERMALINK}); closePermalink(); - }, [error]); + }, [error?.joinedTeam, error?.teamId, serverUrl]); useAndroidHardwareBackHandler(Screens.PERMALINK, handleClose); - const handlePress = useCallback(preventDoubleTap(() => { + const handlePress = usePreventDoubleTap(useCallback(() => { if (channel) { switchToChannelById(serverUrl, channel.id, channel.teamId); } - }), [channel?.id, channel?.teamId]); + }, [channel, serverUrl])); - const handleJoin = useCallback(preventDoubleTap(async () => { + const handleJoin = usePreventDoubleTap(useCallback(async () => { setLoading(true); setError(undefined); if (error?.teamId && error.channelId) { @@ -296,7 +294,7 @@ function Permalink({ } setChannelId(error.channelId); } - }), [error, serverUrl]); + }, [error, serverUrl])); let content; if (loading) { @@ -334,18 +332,13 @@ function Permalink({ /> - - - + /> ); diff --git a/app/screens/permalink/permalink_error.tsx b/app/screens/permalink/permalink_error.tsx index f0eea6939..b88e0643e 100644 --- a/app/screens/permalink/permalink_error.tsx +++ b/app/screens/permalink/permalink_error.tsx @@ -2,16 +2,15 @@ // See LICENSE.txt for license information. import React from 'react'; import {useIntl} from 'react-intl'; -import {Text, TouchableOpacity, View} from 'react-native'; +import {Text, View} from 'react-native'; -import FormattedText from '@components/formatted_text'; +import Button from '@components/button'; import JoinPrivateChannel from '@components/illustrations/join_private_channel'; import JoinPublicChannel from '@components/illustrations/join_public_channel'; import MessageNotViewable from '@components/illustrations/message_not_viewable'; import Markdown from '@components/markdown'; import {Screens} from '@constants'; import {useTheme} from '@context/theme'; -import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; import {getMarkdownBlockStyles, getMarkdownTextStyles} from '@utils/markdown'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; @@ -49,6 +48,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { borderTopWidth: 1, borderTopColor: changeOpacity(theme.centerChannelColor, 0.16), padding: 20, + gap: 8, }, }; @@ -63,9 +63,6 @@ function PermalinkError({ const style = getStyleSheet(theme); const intl = useIntl(); - const buttonStylePrimary = buttonBackgroundStyle(theme, 'lg', 'primary'); - const buttonTextStylePrimary = buttonTextStyle(theme, 'lg', 'primary'); - if (error.notExist || error.unreachable) { const title = intl.formatMessage({id: 'permalink.error.access.title', defaultMessage: 'Message not viewable'}); const text = intl.formatMessage({id: 'permalink.error.access.text', defaultMessage: 'The message you are trying to view is in a channel you don’t have access to or has been deleted.'}); @@ -77,25 +74,17 @@ function PermalinkError({ {text} - - - + /> ); } - const buttonStyleTertiary = buttonBackgroundStyle(theme, 'lg', 'tertiary'); - const buttonTextStyleTertiary = buttonTextStyle(theme, 'lg', 'tertiary'); - const isPrivate = error.privateChannel || error.privateTeam; let image; let title; @@ -143,23 +132,19 @@ function PermalinkError({ /> - - {button} - - + + ); }; diff --git a/app/screens/settings/notifications/send_test_notification_notice/__snapshots__/send_test_notification_notice.test.tsx.snap b/app/screens/settings/notifications/send_test_notification_notice/__snapshots__/send_test_notification_notice.test.tsx.snap index 9d69e64be..d225fb772 100644 --- a/app/screens/settings/notifications/send_test_notification_notice/__snapshots__/send_test_notification_notice.test.tsx.snap +++ b/app/screens/settings/notifications/send_test_notification_notice/__snapshots__/send_test_notification_notice.test.tsx.snap @@ -187,22 +187,13 @@ exports[`SendTestNotificationNotice should match snapshot 1`] = ` Send a test notification @@ -298,22 +289,13 @@ exports[`SendTestNotificationNotice should match snapshot 1`] = ` Troubleshooting docs diff --git a/app/screens/sso/components/auth_error.tsx b/app/screens/sso/components/auth_error.tsx index 897ac7d3e..f4fa86d47 100644 --- a/app/screens/sso/components/auth_error.tsx +++ b/app/screens/sso/components/auth_error.tsx @@ -1,12 +1,12 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import {Button} from '@rneui/base'; import React from 'react'; +import {useIntl} from 'react-intl'; import {Text, View} from 'react-native'; +import Button from '@components/button'; import FormattedText from '@components/formatted_text'; -import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; import {changeOpacity, makeStyleSheetFromTheme} from '@utils/theme'; import {typography} from '@utils/typography'; @@ -18,7 +18,7 @@ interface AuthErrorProps { const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { return { - button: { + buttonContainer: { marginTop: 25, }, errorText: { @@ -44,6 +44,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { }); const AuthError = ({error, retry, theme}: AuthErrorProps) => { + const intl = useIntl(); const style = getStyleSheet(theme); return ( @@ -57,17 +58,15 @@ const AuthError = ({error, retry, theme}: AuthErrorProps) => { {`${error}.`} - + ); }; diff --git a/app/screens/user_profile/options.tsx b/app/screens/user_profile/options.tsx index 0bd421f5c..e1bc34301 100644 --- a/app/screens/user_profile/options.tsx +++ b/app/screens/user_profile/options.tsx @@ -1,19 +1,17 @@ // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. -import React, {useCallback, useMemo} from 'react'; +import React, {useCallback} from 'react'; import {useIntl} from 'react-intl'; -import {DeviceEventEmitter, StyleSheet, TouchableOpacity, View} from 'react-native'; +import {DeviceEventEmitter, StyleSheet, View} from 'react-native'; import {createDirectChannel, switchToChannelById} from '@actions/remote/channel'; -import CompassIcon from '@components/compass_icon'; -import FormattedText from '@components/formatted_text'; +import Button from '@components/button'; import OptionBox, {OPTIONS_HEIGHT} from '@components/option_box'; import {Events, Screens} from '@constants'; import {useServerUrl} from '@context/server'; import {useTheme} from '@context/theme'; import {dismissBottomSheet} from '@screens/navigation'; -import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; import type {AvailableScreens} from '@typings/screens/navigation'; @@ -45,9 +43,7 @@ const styles = StyleSheet.create({ width: '100%', }, singleContainer: { - flexDirection: 'row', marginBottom: 20, - width: '100%', }, }); @@ -56,10 +52,6 @@ const UserProfileOptions = ({location, type, userId, username}: Props) => { const theme = useTheme(); const serverUrl = useServerUrl(); - const buttonStyle = useMemo(() => { - return buttonBackgroundStyle(theme, 'lg', 'tertiary', 'default'); - }, [theme]); - const mentionUser = useCallback(async () => { await dismissBottomSheet(Screens.USER_PROFILE); DeviceEventEmitter.emit(Events.SEND_TO_POST_DRAFT, {location, text: `@${username}`}); @@ -80,7 +72,7 @@ const UserProfileOptions = ({location, type, userId, username}: Props) => { iconName='send' onPress={openChannel} testID='user_profile_options.send_message.option' - text={intl.formatMessage({id: 'channel_info.send_mesasge', defaultMessage: 'Send message'})} + text={intl.formatMessage({id: 'channel_info.send_a_mesasge', defaultMessage: 'Send message'})} /> { return ( - - - - + size='lg' + emphasis='tertiary' + theme={theme} + text={intl.formatMessage({id: 'channel_info.send_mesasge', defaultMessage: 'Send message'})} + iconName='send' + /> ); }; diff --git a/app/screens/user_profile/user_profile.tsx b/app/screens/user_profile/user_profile.tsx index bd8e3fa17..032dae7a3 100644 --- a/app/screens/user_profile/user_profile.tsx +++ b/app/screens/user_profile/user_profile.tsx @@ -158,11 +158,17 @@ const UserProfile = ({ '90%', ]; }, [ - headerText, showUserProfileOptions, showCustomStatus, - showNickname, showPosition, showLocalTime, - manageMode, bottom, showOptions, - canChangeMemberRoles, canManageAndRemoveMembers, - enableCustomAttributes, + headerText, + showUserProfileOptions, + showCustomStatus, + showNickname, + showPosition, + showLocalTime, + manageMode, + bottom, + showOptions, + canChangeMemberRoles, + canManageAndRemoveMembers, ]); useEffect(() => { diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index cca58db66..f7c837cd3 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -464,6 +464,7 @@ "load_channels_error.title": "Couldn't load {teamDisplayName}", "load_teams_error.message": "There was a problem loading content for this server.", "load_teams_error.title": "Couldn't load {serverName}", + "loading_error.retry": "Retry", "login_mfa.enterToken": "To complete the sign in process, please enter the code from your mobile device's authenticator app.", "login_mfa.token": "Enter MFA Token", "login_mfa.tokenReq": "Please enter an MFA token",