* start upgrade to RN 74 * migrate react-native-fs to expo-file-system * exclude expo-asset module * fix database manager remove directory * fix: android network helper * include expo on android * temporarily disable android dep lock * replace react-native-create-thumbnail with expo-video-thumnails * update patches file version * fix android build on 74 * create local library to replace MattermostManaged, Notifications and SplitView modules with new arch support * migrate app to use new mattermost-rnutils library * remove unused flipper class for android unsigned * fix mattermost-rnutils android foldedObserver lifecycle * use mattermost-rnutils on Android * use mattermost-rnutils on iOS * path react-native-navigation to not crash when activity is not NavigationActivity * create local library for android share extension with new arch support * Replace ShareModule with @mattermost/rnshare library * remove ShareModule from android native code * update react-intl * update nodejs to 20.x.x npm to 10.x.x and dev deps * update @gorhom/bottom-sheet * use MattermostShare conditionally based on the platform * update @react-native-camera-roll/camera-roll * remove unused react-native-calendars * fix metro config * fix terminate session race condition * remove unused analytics * replace react-native-device-info with expo-application and expo-device * update @react-native-clipboard/clipboard * update @react-native-community/datetimepicker * update @react-native-community/netinfo * update @sentry/react-native * update react-native-document-picker * update react-native-gesture-handler * update react-native-share * update react-native-svg and react-native-svg-transformer * update react-native-vector-icons * update babel * update react-native-shadow-2 * update semver * remove react-native-svg-transformer and convert svg files to svg components * fix @mattermost/rnshare new-arch build on android * remove react-native-create-thumbnail resolution in build.gradle * create @mattermost/hardware-keyboard library to replace hw-keyboard-events * fix hardware-keyboard library * fix rnutils library * create @mattermost/keyboard-tracker library * replace react-native-keyboard-tracking-view with @mattermost/keyboard-tracker * fix: rnutils to not crash on lateinit context * fix: rnutils delete database * revert changes to session_manager * Removed react-native-webview and added expo-web-browser instead With expo-web-browser we no longer need the webview for SSO login the SSO login is now done by using "custom Chrome tabs" on Android and ASWebAuthenticationSession on iOS * remove patch for react-native-keyboard-tracking-view * replace react-native-linear-gradient with expo-linear-gradient * replace react-native-in-app-review with expo-store-review * fix: shared group database directory on ios * replace react-native-fast-image with expo-image * remove unusued type def for react-native-keyboard-tracking-view * replace react-native-elements and react-native-button with @rneui * node version * update sizzling methods * fix tests using jest-expo * replace jail-monkey with expo-device * update babel deps * update typescript eslint * update rn and expo * react-native-document-picker @react-native-camera-roll/camera-roll @react-native-community/datetimepicker react-native-reanimated react-native-safe-area-context * update patches * update @sentry/react-native * upgrade react-native-navigation * update expo & expo-image * upgrade to working version of @sentry/react-native * update node, cocoapods, bundler, fastlane versions * @testing-library/react-native and eslint-plugin-jest * fix: FloatingTextInput causing a crash with reanimated * update sentry, localize, @types/lodash and uuid * fix floating text input label * update react-native-video * fix: cannot calculate shadow efficiently on some components * fix: reduce motion warning for bottomSheet * fix: shadow on YouTube component * update react-native-webrtc expo and @typescript-eslint * audit fix * fix swizzling bad merge * temp use of github dependency for @mattermost libraries * feedback review * feedback review * npm audit fix * update bundle deps * update @mattermost/react-native-turbo-log * update deps
302 lines
9.6 KiB
TypeScript
302 lines
9.6 KiB
TypeScript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||
// See LICENSE.txt for license information.
|
||
|
||
import {Button} from '@rneui/base';
|
||
import React, {useCallback, useMemo} from 'react';
|
||
import {useIntl} from 'react-intl';
|
||
import {View, Text, ScrollView} from 'react-native';
|
||
|
||
import CompassIcon from '@components/compass_icon';
|
||
import FormattedText from '@components/formatted_text';
|
||
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';
|
||
|
||
import SummaryReport, {SummaryReportType} from './summary_report';
|
||
|
||
import type {SearchResult, Result} from './invite';
|
||
|
||
const MAX_WIDTH_CONTENT = 480;
|
||
|
||
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
||
return {
|
||
container: {
|
||
display: 'flex',
|
||
flex: 1,
|
||
},
|
||
summary: {
|
||
display: 'flex',
|
||
flex: 1,
|
||
},
|
||
summaryContainer: {
|
||
display: 'flex',
|
||
flexGrow: 1,
|
||
flexDirection: 'row',
|
||
justifyContent: 'center',
|
||
alignItems: 'stretch',
|
||
marginTop: 20,
|
||
marginHorizontal: 20,
|
||
paddingBottom: 20,
|
||
},
|
||
summaryContent: {
|
||
flexGrow: 1,
|
||
flexDirection: 'column',
|
||
justifyContent: 'center',
|
||
maxWidth: MAX_WIDTH_CONTENT,
|
||
},
|
||
summarySvg: {
|
||
marginBottom: 20,
|
||
alignSelf: 'center',
|
||
},
|
||
summaryMessageText: {
|
||
color: theme.centerChannelColor,
|
||
...typography('Heading', 700, 'SemiBold'),
|
||
textAlign: 'center',
|
||
marginHorizontal: 32,
|
||
marginBottom: 24,
|
||
},
|
||
summaryErrorText: {
|
||
color: changeOpacity(theme.centerChannelColor, 0.72),
|
||
...typography('Body', 200, 'Regular'),
|
||
textAlign: 'center',
|
||
},
|
||
footer: {
|
||
display: 'flex',
|
||
flexDirection: 'row',
|
||
justifyContent: 'center',
|
||
borderTopWidth: 1,
|
||
borderTopColor: changeOpacity(theme.centerChannelColor, 0.16),
|
||
padding: 20,
|
||
},
|
||
summaryButtonContainer: {
|
||
flexGrow: 1,
|
||
flexDirection: 'row',
|
||
justifyContent: 'center',
|
||
},
|
||
summaryButtonTextContainer: {
|
||
display: 'flex',
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
height: 24,
|
||
},
|
||
summaryButtonIcon: {
|
||
marginRight: 7,
|
||
color: theme.buttonColor,
|
||
|
||
},
|
||
summaryButtonContainerStyle: {flexGrow: 1},
|
||
};
|
||
});
|
||
|
||
enum SummaryButtonType {
|
||
DONE = 'done',
|
||
RETRY = 'retry',
|
||
BACK = 'back',
|
||
}
|
||
|
||
type SummaryProps = {
|
||
result: Result;
|
||
selectedIds: {[id: string]: SearchResult};
|
||
error?: string;
|
||
testID: string;
|
||
onClose: () => void;
|
||
onRetry: () => void;
|
||
onBack: () => void;
|
||
}
|
||
|
||
export default function Summary({
|
||
result,
|
||
selectedIds,
|
||
error,
|
||
testID,
|
||
onClose,
|
||
onRetry,
|
||
onBack,
|
||
}: SummaryProps) {
|
||
const {formatMessage, locale} = useIntl();
|
||
const theme = useTheme();
|
||
const styles = getStyleSheet(theme);
|
||
|
||
const {sent, notSent} = result;
|
||
const sentCount = sent.length;
|
||
const notSentCount = notSent.length;
|
||
|
||
const styleSummaryMessageText = useMemo(() => {
|
||
const style = [];
|
||
|
||
style.push(styles.summaryMessageText);
|
||
|
||
if (error) {
|
||
style.push({marginBottom: 8});
|
||
}
|
||
|
||
return style;
|
||
}, [error, styles]);
|
||
|
||
let svg = <></>;
|
||
let message = '';
|
||
|
||
if (error) {
|
||
svg = <ErrorSvg/>;
|
||
message = formatMessage(
|
||
{
|
||
id: 'invite.summary.error',
|
||
defaultMessage: '{invitationsCount, plural, one {Invitation} other {Invitations}} could not be sent successfully',
|
||
},
|
||
{invitationsCount: sentCount + notSentCount},
|
||
);
|
||
} else if (!sentCount && notSentCount) {
|
||
svg = <ErrorSvg/>;
|
||
message = formatMessage(
|
||
{
|
||
id: 'invite.summary.not_sent',
|
||
defaultMessage: '{notSentCount, plural, one {Invitation wasn’t} other {Invitations weren’t}} sent',
|
||
},
|
||
{notSentCount},
|
||
);
|
||
} else if (sentCount && !notSentCount) {
|
||
svg = <SuccessSvg/>;
|
||
message = formatMessage(
|
||
{
|
||
id: 'invite.summary.sent',
|
||
defaultMessage: 'Your {sentCount, plural, one {invitation has} other {invitations have}} been sent',
|
||
},
|
||
{sentCount},
|
||
);
|
||
} else if (sentCount && notSentCount) {
|
||
svg = <AlertSvg/>;
|
||
message = formatMessage(
|
||
{
|
||
id: 'invite.summary.some_not_sent',
|
||
defaultMessage: '{notSentCount, plural, one {An invitation was} other {Some invitations were}} not sent',
|
||
},
|
||
{notSentCount},
|
||
);
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
return (
|
||
<Button
|
||
containerStyle={styles.summaryButtonContainerStyle}
|
||
buttonStyle={styleButtonBackground}
|
||
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
|
||
style={styles.container}
|
||
testID={testID}
|
||
>
|
||
<ScrollView
|
||
style={styles.summary}
|
||
contentContainerStyle={styles.summaryContainer}
|
||
testID='invite.summary'
|
||
>
|
||
<View style={styles.summaryContent}>
|
||
<View style={styles.summarySvg}>
|
||
{svg}
|
||
</View>
|
||
<Text style={styleSummaryMessageText}>
|
||
{message}
|
||
</Text>
|
||
{error ? (
|
||
<Text style={styles.summaryErrorText}>
|
||
{error}
|
||
</Text>
|
||
) : (
|
||
<>
|
||
{notSent.length > 0 && (
|
||
<SummaryReport
|
||
type={SummaryReportType.NOT_SENT}
|
||
invites={notSent}
|
||
selectedIds={selectedIds}
|
||
testID='invite.summary_report'
|
||
/>
|
||
)}
|
||
{sent.length > 0 && (
|
||
<SummaryReport
|
||
type={SummaryReportType.SENT}
|
||
invites={sent}
|
||
selectedIds={selectedIds}
|
||
testID='invite.summary_report'
|
||
/>
|
||
)}
|
||
</>
|
||
)}
|
||
</View>
|
||
</ScrollView>
|
||
<View style={styles.footer}>
|
||
<View style={styles.summaryButtonContainer}>
|
||
{error ? (
|
||
<>
|
||
{renderButton(SummaryButtonType.BACK)}
|
||
{renderButton(SummaryButtonType.RETRY)}
|
||
</>
|
||
) : (
|
||
renderButton(SummaryButtonType.DONE)
|
||
)}
|
||
</View>
|
||
</View>
|
||
</View>
|
||
);
|
||
}
|