MM-42835_Invite People - add email+user invites
This commit is contained in:
parent
5816406040
commit
0b86c5129c
2 changed files with 101 additions and 72 deletions
|
|
@ -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'
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<Button
|
||||
containerStyle={[styleButtonBackground, {flexGrow: 1}]}
|
||||
onPress={onPress}
|
||||
testID={`invite.summary_button.${SummaryButtonType.RETRY}`}
|
||||
>
|
||||
<View style={styles.summaryButtonTextContainer}>
|
||||
{iconName && (
|
||||
<CompassIcon
|
||||
name={iconName}
|
||||
size={24}
|
||||
style={styleButtonIcon}
|
||||
/>
|
||||
)}
|
||||
<FormattedText
|
||||
{...text}
|
||||
style={styleButtonText}
|
||||
/>
|
||||
</View>
|
||||
</Button>
|
||||
);
|
||||
}, [theme, locale, onClose, onRetry, onBack]);
|
||||
|
||||
return (
|
||||
<View
|
||||
|
|
@ -211,32 +273,14 @@ export default function Summary({
|
|||
</ScrollView>
|
||||
<View style={styles.footer}>
|
||||
<View style={styles.summaryButtonContainer}>
|
||||
<Button
|
||||
containerStyle={styleButtonBackground}
|
||||
onPress={handleOnPressButton}
|
||||
testID='invite.summary_button'
|
||||
>
|
||||
{error ? (
|
||||
<View style={styles.summaryButtonTextContainer}>
|
||||
<CompassIcon
|
||||
name='refresh'
|
||||
size={24}
|
||||
style={styles.summaryButtonIcon}
|
||||
/>
|
||||
<FormattedText
|
||||
id='invite.summary.try_again'
|
||||
defaultMessage='Try again'
|
||||
style={styleButtonText}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<FormattedText
|
||||
id='invite.summary.done'
|
||||
defaultMessage='Done'
|
||||
style={styleButtonText}
|
||||
/>
|
||||
)}
|
||||
</Button>
|
||||
{error ? (
|
||||
<>
|
||||
{renderButton(SummaryButtonType.BACK)}
|
||||
{renderButton(SummaryButtonType.RETRY)}
|
||||
</>
|
||||
) : (
|
||||
renderButton(SummaryButtonType.DONE)
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
|||
Loading…
Reference in a new issue