diff --git a/app/screens/invite/invite.tsx b/app/screens/invite/invite.tsx index ea8e7423a..6adbd54ff 100644 --- a/app/screens/invite/invite.tsx +++ b/app/screens/invite/invite.tsx @@ -3,7 +3,7 @@ import React, {useCallback, useEffect, useState, useRef} from 'react'; import {IntlShape, useIntl} from 'react-intl'; -import {Keyboard, View, LayoutChangeEvent, Platform} from 'react-native'; +import {Keyboard, View, LayoutChangeEvent} from 'react-native'; import {OptionsTopBarButton} from 'react-native-navigation'; import {SafeAreaView} from 'react-native-safe-area-context'; @@ -28,33 +28,15 @@ import Summary from './summary'; import type {NavButtons} from '@typings/screens/navigation'; const CLOSE_BUTTON_ID = 'close-invite'; -const BACK_BUTTON_ID = 'back-invite'; const SEND_BUTTON_ID = 'send-invite'; -const SEARCH_TIMEOUT_MILLISECONDS = 200; +const TIMEOUT_MILLISECONDS = 200; const DEFAULT_RESULT = {sent: [], notSent: []}; -const makeLeftButton = (theme: Theme, type: LeftButtonType): OptionsTopBarButton => ( - (type === LeftButtonType.BACK) ? ( - { - id: BACK_BUTTON_ID, - icon: CompassIcon.getImageSourceSync( - Platform.select({ - ios: 'arrow-back-ios', - default: 'arrow-left', - }), - 24, - theme.sidebarHeaderTextColor, - ), - testID: 'invite.back.button', - } - ) : ( - { - id: CLOSE_BUTTON_ID, - icon: CompassIcon.getImageSourceSync('close', 24, theme.sidebarHeaderTextColor), - testID: 'invite.close.button', - } - ) -); +const makeLeftButton = (theme: Theme): OptionsTopBarButton => ({ + id: CLOSE_BUTTON_ID, + icon: CompassIcon.getImageSourceSync('close', 24, theme.sidebarHeaderTextColor), + testID: 'invite.close.button', +}); const makeRightButton = (theme: Theme, formatMessage: IntlShape['formatMessage'], enabled: boolean): OptionsTopBarButton => ({ id: SEND_BUTTON_ID, @@ -105,11 +87,6 @@ enum Stage { LOADING = 'loading', } -enum LeftButtonType { - CLOSE = 'close', - BACK = 'back', -} - type InviteProps = { componentId: string; teamId: string; @@ -171,13 +148,12 @@ export default function Invite({ }, [serverUrl, teamId]); const handleReset = () => { + setStage(Stage.LOADING); + setSendError(''); setTerm(''); setSearchResults([]); - setSelectedIds({}); - setLoading(false); setResult(DEFAULT_RESULT); setStage(Stage.SELECTION); - setSendError(''); }; const handleClearSearch = useCallback(() => { @@ -196,7 +172,7 @@ export default function Invite({ searchTimeoutId.current = setTimeout(async () => { await searchUsers(text); setLoading(false); - }, SEARCH_TIMEOUT_MILLISECONDS); + }, TIMEOUT_MILLISECONDS); }, [searchUsers]); const handleSelectItem = useCallback((item: SearchResult) => { @@ -213,6 +189,15 @@ export default function Invite({ handleClearSearch(); }, [selectedIds, handleClearSearch]); + const handleRetry = () => { + setSendError(''); + setStage(Stage.LOADING); + + setTimeout(() => { + handleSend(); + }, TIMEOUT_MILLISECONDS); + }; + const handleSendError = () => { setSendError(formatMessage({id: 'invite.send_error', defaultMessage: 'Something went wrong while trying to send invitations. Please check your network connection and try again.'})); setResult(DEFAULT_RESULT); @@ -327,12 +312,11 @@ export default function Invite({ }; useNavButtonPressed(CLOSE_BUTTON_ID, componentId, closeModal, [closeModal]); - useNavButtonPressed(BACK_BUTTON_ID, componentId, handleReset, [handleReset]); useNavButtonPressed(SEND_BUTTON_ID, componentId, handleSend, [handleSend]); useEffect(() => { const buttons: NavButtons = { - leftButtons: [makeLeftButton(theme, stage === Stage.RESULT && sendError ? LeftButtonType.BACK : LeftButtonType.CLOSE)], + leftButtons: [makeLeftButton(theme)], rightButtons: stage === Stage.SELECTION ? [makeRightButton(theme, formatMessage, selectedCount > 0)] : [], }; @@ -379,7 +363,8 @@ export default function Invite({ selectedIds={selectedIds} error={sendError} onClose={closeModal} - onRetry={handleReset} + onRetry={handleRetry} + onBack={handleReset} testID='invite.screen.summary' /> ); diff --git a/app/screens/invite/summary.tsx b/app/screens/invite/summary.tsx index 630704e14..8342c0579 100644 --- a/app/screens/invite/summary.tsx +++ b/app/screens/invite/summary.tsx @@ -12,6 +12,7 @@ import AlertSvg from '@components/illustrations/alert'; import ErrorSvg from '@components/illustrations/error'; import SuccessSvg from '@components/illustrations/success'; import {useTheme} from '@context/theme'; +import {t} from '@i18n'; import {buttonBackgroundStyle, buttonTextStyle} from '@utils/buttonStyles'; import {makeStyleSheetFromTheme, changeOpacity} from '@utils/theme'; import {typography} from '@utils/typography'; @@ -67,6 +68,8 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { }, summaryButtonContainer: { flexGrow: 1, + flexDirection: 'row', + justifyContent: 'center', maxWidth: MAX_WIDTH_CONTENT, }, summaryButtonTextContainer: { @@ -83,6 +86,12 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => { }; }); +enum SummaryButtonType { + DONE = 'done', + RETRY = 'retry', + BACK = 'back', +} + type SummaryProps = { result: Result; selectedIds: {[id: string]: SearchResult}; @@ -90,6 +99,7 @@ type SummaryProps = { testID: string; onClose: () => void; onRetry: () => void; + onBack: () => void; } export default function Summary({ @@ -99,8 +109,9 @@ export default function Summary({ testID, onClose, onRetry, + onBack, }: SummaryProps) { - const {formatMessage} = useIntl(); + const {formatMessage, locale} = useIntl(); const theme = useTheme(); const styles = getStyleSheet(theme); @@ -108,8 +119,6 @@ export default function Summary({ const sentCount = sent.length; const notSentCount = notSent.length; - const styleButtonText = buttonTextStyle(theme, 'lg', 'primary'); - const styleButtonBackground = buttonBackgroundStyle(theme, 'lg', 'primary'); const styleSummaryMessageText = useMemo(() => { const style = []; @@ -163,14 +172,67 @@ export default function Summary({ ); } - const handleOnPressButton = useCallback(() => { - if (error) { - onRetry(); - return; + const renderButton = useCallback((type: SummaryButtonType) => { + let onPress; + let iconName = ''; + let text; + let styleButtonText = buttonTextStyle(theme, 'lg', 'primary'); + let styleButtonBackground = buttonBackgroundStyle(theme, 'lg', 'primary'); + const styleButtonIcon = []; + styleButtonIcon.push(styles.summaryButtonIcon); + + switch (type) { + case SummaryButtonType.BACK: + onPress = onBack; + iconName = 'chevron-left'; + text = { + id: t('invite.summary.back'), + defaultMessage: 'Go back', + }; + styleButtonText = buttonTextStyle(theme, 'lg', 'tertiary'); + styleButtonBackground = [buttonBackgroundStyle(theme, 'lg', 'tertiary'), {marginRight: 8}]; + styleButtonIcon.push({color: theme.buttonBg}); + break; + case SummaryButtonType.RETRY: + onPress = onRetry; + iconName = 'refresh'; + text = { + id: t('invite.summary.try_again'), + defaultMessage: 'Try again', + }; + break; + case SummaryButtonType.DONE: + default: + onPress = onClose; + text = { + id: t('invite.summary.done'), + defaultMessage: 'Done', + }; + break; } - onClose(); - }, [error, onRetry, onClose]); + return ( + + ); + }, [theme, locale, onClose, onRetry, onBack]); return ( - + {error ? ( + <> + {renderButton(SummaryButtonType.BACK)} + {renderButton(SummaryButtonType.RETRY)} + + ) : ( + renderButton(SummaryButtonType.DONE) + )}