[IMPROVEMENT] List Component (#2506)

* List.Item

* section

* Start removing theme as prop

* Remove StatusBar theme prop

* SafeAreaView theme prop

* Minor fixes

* List.Container

* Add translateTitle and translateSubtitle props

* Storybook

* Show action indicator

* Header

* Info

* Theme stories

* FlatList

* DisplayName

* Fix settings

* FlatList tweaks

* ThemeView

* Screen Lock Config

* DefaultBrowserView

* PickerView and User Prefs

* Notification Prefs

* StatusView

* Auto Translate

* InviteUsersEdit

* Visitor

* Minor fixes

* Remove Separator

* Remove iteminfo

* Font scale

* Legal

* Jitsi and e2e

* Block

* search, star, etc

* auto translate and notifications

* RoomInfo

* Refactor RoomActions

* lint

* Remove DisclosureIndicator

* padding horizontal 12

* Detox

* Tests

* Address review comments

* Fix vertical scroll

Co-authored-by: Djorkaeff Alexandre <djorkaeff.unb@gmail.com>
This commit is contained in:
Diego Mello 2020-10-30 10:59:44 -03:00 committed by GitHub
parent 46e3db97e8
commit 52850cbccc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
103 changed files with 35860 additions and 1904 deletions

View File

@ -1,4 +1,4 @@
export default {
activateKeepAwake: () => '',
deactivateKeepAwake: () => ''
};
const activateKeepAwake = () => '';
const deactivateKeepAwake = () => '';
export { activateKeepAwake, deactivateKeepAwake };

File diff suppressed because it is too large Load Diff

View File

@ -27,7 +27,7 @@ import { Button } from './Button';
import { themes } from '../../constants/colors';
import styles, { ITEM_HEIGHT } from './styles';
import { isTablet, isIOS } from '../../utils/deviceInfo';
import Separator from '../Separator';
import * as List from '../List';
import I18n from '../../i18n';
import { useOrientation, useDimensions } from '../../dimensions';
@ -142,8 +142,6 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
</Button>
) : null));
const renderSeparator = useCallback(() => <Separator theme={theme} style={styles.separator} />);
const renderItem = useCallback(({ item }) => <Item item={item} hide={hide} theme={theme} />);
const animatedPosition = React.useRef(new Value(0));
@ -191,8 +189,8 @@ const ActionSheet = React.memo(forwardRef(({ children, theme }, ref) => {
keyExtractor={item => item.title}
style={{ backgroundColor: themes[theme].focusedBackground }}
contentContainerStyle={styles.content}
ItemSeparatorComponent={renderSeparator}
ListHeaderComponent={renderSeparator}
ItemSeparatorComponent={List.Separator}
ListHeaderComponent={List.Separator}
ListFooterComponent={renderFooter}
getItemLayout={getItemLayout}
removeClippedSubviews={isIOS}

View File

@ -1,37 +0,0 @@
import React from 'react';
import { View, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import { themes } from '../constants/colors';
import { CustomIcon } from '../lib/Icons';
const styles = StyleSheet.create({
disclosureContainer: {
marginLeft: 6,
marginRight: 9,
alignItems: 'center',
justifyContent: 'center'
}
});
export const DisclosureImage = React.memo(({ theme }) => (
<CustomIcon
name='chevron-right'
color={themes[theme].auxiliaryText}
size={20}
/>
));
DisclosureImage.propTypes = {
theme: PropTypes.string
};
const DisclosureIndicator = React.memo(({ theme }) => (
<View style={styles.disclosureContainer}>
<DisclosureImage theme={theme} />
</View>
));
DisclosureIndicator.propTypes = {
theme: PropTypes.string
};
export default DisclosureIndicator;

View File

@ -31,14 +31,14 @@ const FormContainer = ({
contentContainerStyle={sharedStyles.container}
keyboardVerticalOffset={128}
>
<StatusBar theme={theme} />
<StatusBar />
<ScrollView
style={sharedStyles.container}
contentContainerStyle={[sharedStyles.containerScrollView, styles.scrollView]}
{...scrollPersistTaps}
{...props}
>
<SafeAreaView testID={testID} theme={theme} style={{ backgroundColor: themes[theme].backgroundColor }}>
<SafeAreaView testID={testID} style={{ backgroundColor: themes[theme].backgroundColor }}>
{children}
<AppVersion theme={theme} />
</SafeAreaView>

View File

@ -1,29 +0,0 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import sharedStyles from '../views/Styles';
import { themes } from '../constants/colors';
const styles = StyleSheet.create({
infoContainer: {
padding: 15
},
infoText: {
fontSize: 14,
...sharedStyles.textRegular
}
});
const ItemInfo = React.memo(({ info, theme }) => (
<View style={[styles.infoContainer, { backgroundColor: themes[theme].auxiliaryBackground }]}>
<Text style={[styles.infoText, { color: themes[theme].infoText }]}>{info}</Text>
</View>
));
ItemInfo.propTypes = {
info: PropTypes.string,
theme: PropTypes.string
};
export default ItemInfo;

View File

@ -0,0 +1,30 @@
import React from 'react';
import { ScrollView, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import { withTheme } from '../../theme';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
const styles = StyleSheet.create({
container: {
paddingVertical: 16
}
});
const ListContainer = React.memo(({ children, ...props }) => (
<ScrollView
contentContainerStyle={styles.container}
scrollIndicatorInsets={{ right: 1 }} // https://github.com/facebook/react-native/issues/26610#issuecomment-539843444
{...scrollPersistTaps}
{...props}
>
{children}
</ScrollView>
));
ListContainer.propTypes = {
children: PropTypes.array.isRequired
};
ListContainer.displayName = 'List.Container';
export default withTheme(ListContainer);

View File

@ -0,0 +1,40 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import sharedStyles from '../../views/Styles';
import { themes } from '../../constants/colors';
import I18n from '../../i18n';
import { withTheme } from '../../theme';
import { PADDING_HORIZONTAL } from './constants';
const styles = StyleSheet.create({
container: {
paddingBottom: 12,
paddingHorizontal: PADDING_HORIZONTAL
},
title: {
fontSize: 16,
...sharedStyles.textRegular
}
});
const ListHeader = React.memo(({ title, theme, translateTitle }) => (
<View style={styles.container}>
<Text style={[styles.title, { color: themes[theme].infoText }]} numberOfLines={1}>{translateTitle ? I18n.t(title) : title}</Text>
</View>
));
ListHeader.propTypes = {
title: PropTypes.string,
theme: PropTypes.string,
translateTitle: PropTypes.bool
};
ListHeader.defaultProps = {
translateTitle: true
};
ListHeader.displayName = 'List.Header';
export default withTheme(ListHeader);

View File

@ -0,0 +1,34 @@
import React from 'react';
import { View, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import { themes } from '../../constants/colors';
import { CustomIcon } from '../../lib/Icons';
import { withTheme } from '../../theme';
const styles = StyleSheet.create({
icon: {
alignItems: 'center',
justifyContent: 'center'
}
});
const ListIcon = React.memo(({ theme, name, color }) => (
<View style={styles.icon}>
<CustomIcon
name={name}
color={color ?? themes[theme].auxiliaryText}
size={20}
/>
</View>
));
ListIcon.propTypes = {
theme: PropTypes.string,
name: PropTypes.string,
color: PropTypes.string
};
ListIcon.displayName = 'List.Icon';
export default withTheme(ListIcon);

View File

@ -0,0 +1,40 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import sharedStyles from '../../views/Styles';
import { themes } from '../../constants/colors';
import { withTheme } from '../../theme';
import { PADDING_HORIZONTAL } from './constants';
import I18n from '../../i18n';
const styles = StyleSheet.create({
container: {
paddingTop: 8,
paddingHorizontal: PADDING_HORIZONTAL
},
text: {
fontSize: 14,
...sharedStyles.textRegular
}
});
const ListInfo = React.memo(({ info, translateInfo, theme }) => (
<View style={styles.container}>
<Text style={[styles.text, { color: themes[theme].infoText }]}>{translateInfo ? I18n.t(info) : info}</Text>
</View>
));
ListInfo.propTypes = {
info: PropTypes.string,
theme: PropTypes.string,
translateInfo: PropTypes.bool
};
ListInfo.defaultProps = {
translateInfo: true
};
ListInfo.displayName = 'List.Info';
export default withTheme(ListInfo);

View File

@ -0,0 +1,137 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import Touch from '../../utils/touch';
import { themes } from '../../constants/colors';
import sharedStyles from '../../views/Styles';
import { withTheme } from '../../theme';
import I18n from '../../i18n';
import { Icon } from '.';
import { BASE_HEIGHT, PADDING_HORIZONTAL } from './constants';
import { withDimensions } from '../../dimensions';
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: PADDING_HORIZONTAL
},
leftContainer: {
paddingRight: PADDING_HORIZONTAL
},
rightContainer: {
paddingLeft: PADDING_HORIZONTAL
},
disabled: {
opacity: 0.3
},
textContainer: {
flex: 1,
justifyContent: 'center'
},
title: {
fontSize: 16,
...sharedStyles.textRegular
},
subtitle: {
fontSize: 14,
...sharedStyles.textRegular
}
});
const Content = React.memo(({
title, subtitle, disabled, testID, left, right, color, theme, translateTitle, translateSubtitle, showActionIndicator, fontScale
}) => (
<View style={[styles.container, disabled && styles.disabled, { height: BASE_HEIGHT * fontScale }]} testID={testID}>
{left
? (
<View style={styles.leftContainer}>
{left()}
</View>
)
: null}
<View style={styles.textContainer}>
<Text style={[styles.title, { color: color || themes[theme].titleText }]} numberOfLines={1}>{translateTitle ? I18n.t(title) : title}</Text>
{subtitle
? <Text style={[styles.subtitle, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>{translateSubtitle ? I18n.t(subtitle) : subtitle}</Text>
: null
}
</View>
{right || showActionIndicator
? (
<View style={styles.rightContainer}>
{right ? right() : null}
{showActionIndicator ? <Icon name='chevron-right' /> : null}
</View>
)
: null}
</View>
));
const Button = React.memo(({
onPress, ...props
}) => (
<Touch
onPress={() => onPress(props.title)}
style={{ backgroundColor: themes[props.theme].backgroundColor }}
enabled={!props.disabled}
theme={props.theme}
>
<Content {...props} />
</Touch>
));
const ListItem = React.memo(({ ...props }) => {
if (props.onPress) {
return <Button {...props} />;
}
return (
<View style={{ backgroundColor: themes[props.theme].backgroundColor }}>
<Content {...props} />
</View>
);
});
ListItem.propTypes = {
onPress: PropTypes.func,
theme: PropTypes.string
};
ListItem.displayName = 'List.Item';
Content.propTypes = {
title: PropTypes.string.isRequired,
subtitle: PropTypes.string,
left: PropTypes.func,
right: PropTypes.func,
disabled: PropTypes.bool,
testID: PropTypes.string,
theme: PropTypes.string,
color: PropTypes.string,
translateTitle: PropTypes.bool,
translateSubtitle: PropTypes.bool,
showActionIndicator: PropTypes.bool,
fontScale: PropTypes.number
};
Content.defaultProps = {
translateTitle: true,
translateSubtitle: true,
showActionIndicator: false
};
Button.propTypes = {
title: PropTypes.string,
onPress: PropTypes.func,
disabled: PropTypes.bool,
theme: PropTypes.string
};
Button.defaultProps = {
disabled: false
};
export default withTheme(withDimensions(ListItem));

View File

@ -0,0 +1,28 @@
import React from 'react';
import { View, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import { withTheme } from '../../theme';
import { Header } from '.';
const styles = StyleSheet.create({
container: {
marginVertical: 16
}
});
const ListSection = React.memo(({ children, title, translateTitle }) => (
<View style={styles.container}>
{title ? <Header {...{ title, translateTitle }} /> : null}
{children}
</View>
));
ListSection.propTypes = {
children: PropTypes.array.isRequired,
title: PropTypes.string,
translateTitle: PropTypes.bool
};
ListSection.displayName = 'List.Section';
export default withTheme(ListSection);

View File

@ -2,7 +2,8 @@ import React from 'react';
import { View, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import { themes } from '../constants/colors';
import { themes } from '../../constants/colors';
import { withTheme } from '../../theme';
const styles = StyleSheet.create({
separator: {
@ -11,7 +12,7 @@ const styles = StyleSheet.create({
});
const Separator = React.memo(({ style, theme }) => (
const ListSeparator = React.memo(({ style, theme }) => (
<View
style={[
styles.separator,
@ -21,9 +22,11 @@ const Separator = React.memo(({ style, theme }) => (
/>
));
Separator.propTypes = {
ListSeparator.propTypes = {
style: PropTypes.object,
theme: PropTypes.string
};
export default Separator;
ListSeparator.displayName = 'List.Separator';
export default withTheme(ListSeparator);

View File

@ -0,0 +1,2 @@
export const PADDING_HORIZONTAL = 12;
export const BASE_HEIGHT = 46;

View File

@ -0,0 +1,8 @@
export { default as Container } from './ListContainer';
export { default as Item } from './ListItem';
export { default as Section } from './ListSection';
export { default as Icon } from './ListIcon';
export { default as Separator } from './ListSeparator';
export { default as Header } from './ListHeader';
export { default as Info } from './ListInfo';
export * from './styles';

View File

@ -0,0 +1,7 @@
import { StyleSheet } from 'react-native';
export const styles = StyleSheet.create({
contentContainerStyleFlatList: {
paddingVertical: 32
}
});

View File

@ -1,102 +0,0 @@
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import Touch from '../utils/touch';
import { themes } from '../constants/colors';
import sharedStyles from '../views/Styles';
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
height: 46,
paddingHorizontal: 15
},
disabled: {
opacity: 0.3
},
textContainer: {
flex: 1,
justifyContent: 'center'
},
title: {
fontSize: 16,
...sharedStyles.textRegular
},
subtitle: {
fontSize: 14,
...sharedStyles.textRegular
}
});
const Content = React.memo(({
title, subtitle, disabled, testID, left, right, color, theme
}) => (
<View style={[styles.container, disabled && styles.disabled]} testID={testID}>
{left ? left() : null}
<View style={styles.textContainer}>
<Text style={[styles.title, { color: color || themes[theme].titleText }]}>{title}</Text>
{subtitle
? <Text style={[styles.subtitle, { color: themes[theme].bodyText }]}>{subtitle}</Text>
: null
}
</View>
{right ? right() : null}
</View>
));
const Button = React.memo(({
onPress, ...props
}) => (
<Touch
onPress={() => onPress(props.title)}
style={{ backgroundColor: themes[props.theme].backgroundColor }}
enabled={!props.disabled}
theme={props.theme}
>
<Content {...props} />
</Touch>
));
const Item = React.memo(({ ...props }) => {
if (props.onPress) {
return <Button {...props} />;
}
return (
<View style={{ backgroundColor: themes[props.theme].backgroundColor }}>
<Content {...props} />
</View>
);
});
Item.propTypes = {
onPress: PropTypes.func,
theme: PropTypes.string
};
Content.propTypes = {
title: PropTypes.string.isRequired,
subtitle: PropTypes.string,
left: PropTypes.func,
right: PropTypes.func,
disabled: PropTypes.bool,
testID: PropTypes.string,
theme: PropTypes.string,
color: PropTypes.string
};
Button.propTypes = {
title: PropTypes.string,
onPress: PropTypes.func,
disabled: PropTypes.bool,
theme: PropTypes.string
};
Button.defaultProps = {
disabled: false
};
export default Item;

View File

@ -99,7 +99,7 @@ const ModalContent = React.memo(({
}) => {
if (message && message.reactions) {
return (
<SafeAreaView theme={props.theme} style={styles.safeArea}>
<SafeAreaView style={styles.safeArea}>
<Touchable onPress={onClose}>
<View style={styles.titleContainer}>
<CustomIcon

View File

@ -3,6 +3,7 @@ import { StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import { SafeAreaView as SafeAreaContext } from 'react-native-safe-area-context';
import { themes } from '../constants/colors';
import { withTheme } from '../theme';
const styles = StyleSheet.create({
view: {
@ -31,4 +32,4 @@ SafeAreaView.propTypes = {
children: PropTypes.element
};
export default SafeAreaView;
export default withTheme(SafeAreaView);

View File

@ -3,6 +3,7 @@ import { StatusBar as StatusBarRN } from 'react-native';
import PropTypes from 'prop-types';
import { themes } from '../constants/colors';
import { withTheme } from '../theme';
const StatusBar = React.memo(({ theme, barStyle, backgroundColor }) => {
if (!barStyle) {
@ -20,4 +21,4 @@ StatusBar.propTypes = {
backgroundColor: PropTypes.string
};
export default StatusBar;
export default withTheme(StatusBar);

View File

@ -1,8 +1,7 @@
import React from 'react';
import { StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import Separator from '../Separator';
import * as List from '../List';
const styles = StyleSheet.create({
separator: {
@ -12,7 +11,4 @@ const styles = StyleSheet.create({
}
});
export const Divider = ({ theme }) => <Separator style={styles.separator} theme={theme} />;
Divider.propTypes = {
theme: PropTypes.string
};
export const Divider = () => <List.Separator style={styles.separator} />;

View File

@ -4,9 +4,8 @@ import PropTypes from 'prop-types';
import Touchable from 'react-native-platform-touchable';
import FastImage from '@rocket.chat/react-native-fast-image';
import Separator from '../../Separator';
import Check from '../../Check';
import * as List from '../../List';
import { textParser } from '../utils';
import { themes } from '../../../constants/colors';
@ -48,7 +47,7 @@ const Items = ({
style={[styles.items, { backgroundColor: themes[theme].backgroundColor }]}
contentContainerStyle={[styles.itemContent, { backgroundColor: themes[theme].backgroundColor }]}
keyboardShouldPersistTaps='always'
ItemSeparatorComponent={() => <Separator theme={theme} />}
ItemSeparatorComponent={List.Separator}
keyExtractor={keyExtractor}
renderItem={({ item }) => <Item item={item} onSelect={onSelect} theme={theme} selected={selected.find(s => s === item.value)} />}
/>

View File

@ -5,10 +5,10 @@ import Popover from 'react-native-popover-view';
import Touchable from 'react-native-platform-touchable';
import { CustomIcon } from '../../lib/Icons';
import Separator from '../Separator';
import ActivityIndicator from '../ActivityIndicator';
import { themes } from '../../constants/colors';
import { BUTTON_HIT_SLOP } from '../message/utils';
import * as List from '../List';
const keyExtractor = item => item.value;
@ -50,7 +50,7 @@ const Options = ({
data={options}
renderItem={({ item }) => <Option option={item} onOptionPress={onOptionPress} parser={parser} theme={theme} />}
keyExtractor={keyExtractor}
ItemSeparatorComponent={() => <Separator theme={theme} />}
ItemSeparatorComponent={List.Separator}
/>
);
Options.propTypes = {

View File

@ -5,7 +5,6 @@ import PropTypes from 'prop-types';
import shortnameToUnicode from '../../utils/shortnameToUnicode';
import { CustomIcon } from '../../lib/Icons';
import DisclosureIndicator from '../DisclosureIndicator';
import styles from './styles';
import { themes } from '../../constants/colors';
import I18n from '../../i18n';
@ -33,7 +32,13 @@ const RepliedThread = React.memo(({
<View style={styles.repliedThread} testID={`message-thread-replied-on-${ msg }`}>
<CustomIcon name='threads' size={20} style={styles.repliedThreadIcon} color={themes[theme].tintColor} />
<Text style={[styles.repliedThreadName, { color: themes[theme].tintColor }]} numberOfLines={1}>{msg}</Text>
<DisclosureIndicator theme={theme} />
<View style={styles.repliedThreadDisclosure}>
<CustomIcon
name='chevron-right'
color={themes[theme].auxiliaryText}
size={20}
/>
</View>
</View>
);
}, (prevProps, nextProps) => {

View File

@ -164,6 +164,12 @@ export default StyleSheet.create({
flex: 1,
...sharedStyles.textRegular
},
repliedThreadDisclosure: {
marginLeft: 4,
marginRight: 4,
alignItems: 'center',
justifyContent: 'center'
},
readReceipt: {
lineHeight: 20
},

View File

@ -128,8 +128,8 @@ class QueueListView extends React.Component {
render() {
const { queued, theme } = this.props;
return (
<SafeAreaView testID='queue-list-view' theme={theme} style={{ backgroundColor: themes[theme].backgroundColor }}>
<StatusBar theme={theme} />
<SafeAreaView testID='queue-list-view' style={{ backgroundColor: themes[theme].backgroundColor }}>
<StatusBar />
<FlatList
ref={this.getScrollRef}
data={queued}

View File

@ -100,7 +100,6 @@ export default {
announcement: 'Ankündigung',
Announcement: 'Ankündigung',
Apply_Your_Certificate: 'Wende dein Zertifikat an',
Applying_a_theme_will_change_how_the_app_looks: 'Das Erscheinungsbild festzulegen wird das Aussehen der Anwendung ändern.',
ARCHIVE: 'ARCHIV',
archive: 'Archiv',
are_typing: 'tippen',
@ -180,7 +179,7 @@ export default {
deleting_room: 'lösche Raum',
description: 'Beschreibung',
Description: 'Beschreibung',
DESKTOP_OPTIONS: 'Desktop-Einstellungen',
Desktop_Options: 'Desktop-Einstellungen',
Directory: 'Verzeichnis',
Direct_Messages: 'Direkte Nachrichten',
Disable_notifications: 'Benachrichtigungen deaktiveren',
@ -198,9 +197,8 @@ export default {
Edit_Status: 'Status ändern',
Edit_Invite: 'Einladung bearbeiten',
Email_or_password_field_is_empty: 'Das E-Mail- oder Passwortfeld ist leer',
Email: 'Email',
EMAIL: 'EMAIL',
email: 'Email',
Email: 'E-mail',
email: 'E-mail',
Empty_title: 'leerer Titel',
Enable_Auto_Translate: 'Automatische Übersetzung aktivieren',
Enable_notifications: 'Benachrichtigungen aktivieren',
@ -247,7 +245,7 @@ export default {
Message_HideType_room_unarchived: 'Raum nicht mehr archiviert',
IP: 'IP',
In_app: 'In-App-Browser',
IN_APP_AND_DESKTOP: 'IN-APP UND DESKTOP',
In_App_And_Desktop: 'In-app und Desktop',
In_App_and_Desktop_Alert_info: 'Zeigt ein Banner oben am Bildschirm, wenn die App geöffnet ist und eine Benachrichtigung auf dem Desktop.',
Invisible: 'Unsichtbar',
Invite: 'Einladen',
@ -373,7 +371,7 @@ export default {
Profile: 'Profil',
Public_Channel: 'Öffentlicher Kanal',
Public: 'Öffentlich',
PUSH_NOTIFICATIONS: 'Push-Benachrichtigungen',
Push_Notifications: 'Push-Benachrichtigungen',
Push_Notifications_Alert_Info: 'Diese Benachrichtigungen werden Ihnen zugestellt, wenn die App nicht geöffnet ist.',
Quote: 'Zitat',
Reactions_are_disabled: 'Reaktionen sind deaktiviert',

View File

@ -100,7 +100,6 @@ export default {
announcement: 'announcement',
Announcement: 'Announcement',
Apply_Your_Certificate: 'Apply Your Certificate',
Applying_a_theme_will_change_how_the_app_looks: 'Applying a theme will change how the app looks.',
ARCHIVE: 'ARCHIVE',
archive: 'archive',
are_typing: 'are typing',
@ -184,8 +183,8 @@ export default {
deleting_room: 'deleting room',
description: 'description',
Description: 'Description',
DESKTOP_OPTIONS: 'DESKTOP OPTIONS',
DESKTOP_NOTIFICATIONS: 'DESKTOP NOTIFICATIONS',
Desktop_Options: 'Desktop Options',
Desktop_Notifications: 'Desktop Notifications',
Desktop_Alert_info: 'These notifications are delivered in desktop',
Directory: 'Directory',
Direct_Messages: 'Direct Messages',
@ -212,8 +211,7 @@ export default {
Email_Notification_Mode_All: 'Every Mention/DM',
Email_Notification_Mode_Disabled: 'Disabled',
Email_or_password_field_is_empty: 'Email or password field is empty',
Email: 'Email',
EMAIL: 'EMAIL',
Email: 'E-mail',
email: 'e-mail',
Empty_title: 'Empty title',
Enable_Auto_Translate: 'Enable Auto-Translate',
@ -270,7 +268,7 @@ export default {
I_Saved_My_E2E_Password: 'I Saved My E2E Password',
IP: 'IP',
In_app: 'In-app',
IN_APP_AND_DESKTOP: 'IN-APP AND DESKTOP',
In_App_And_Desktop: 'In-app and Desktop',
In_App_and_Desktop_Alert_info: 'Displays a banner at the top of the screen when app is open, and displays a notification on desktop',
Invisible: 'Invisible',
Invite: 'Invite',
@ -398,7 +396,7 @@ export default {
Profile: 'Profile',
Public_Channel: 'Public Channel',
Public: 'Public',
PUSH_NOTIFICATIONS: 'PUSH NOTIFICATIONS',
Push_Notifications: 'Push Notifications',
Push_Notifications_Alert_Info: 'These notifications are delivered to you when the app is not open',
Quote: 'Quote',
Reactions_are_disabled: 'Reactions are disabled',

View File

@ -95,7 +95,6 @@ export default {
announcement: 'anuncio',
Announcement: 'Anuncio',
Apply_Your_Certificate: 'Applica tu Certificación',
Applying_a_theme_will_change_how_the_app_looks: 'Aplicando un tema modificará el aspecto de la App.',
ARCHIVE: 'FICHERO',
archive: 'Fichero',
are_typing: 'escribiendo',
@ -160,7 +159,7 @@ export default {
deleting_room: 'eliminando sala',
description: 'descripción',
Description: 'Descripción',
DESKTOP_OPTIONS: 'OPCIONES DE ESCRITORIO',
Desktop_Options: 'Opciones De Escritorio',
Directory: 'Directorio',
Direct_Messages: 'Mensajes directo',
Disable_notifications: 'Desactivar notificaciones',
@ -172,8 +171,7 @@ export default {
edited: 'editado',
Edit: 'Editar',
Email_or_password_field_is_empty: 'El email o la contraseña están vacios',
Email: 'Email',
EMAIL: 'EMAIL',
Email: 'E-mail',
email: 'e-mail',
Enable_Auto_Translate: 'Permitir Auto-Translate',
Enable_notifications: 'Permitir notificaciones',
@ -197,7 +195,7 @@ export default {
Has_joined_the_channel: 'Se ha unido al canal',
Has_joined_the_conversation: 'Se ha unido a la conversación',
Has_left_the_channel: 'Ha dejado el canal',
IN_APP_AND_DESKTOP: 'IN-APP AND DESKTOP',
In_App_And_Desktop: 'In-app and Desktop',
In_App_and_Desktop_Alert_info: 'Muestra un banner en la parte superior de la pantalla cuando la aplicación está abierta y muestra una notificación en el escritorio',
Invisible: 'Invisible',
Invite: 'Invitar',
@ -290,7 +288,7 @@ export default {
Profile: 'Perfil',
Public_Channel: 'Canal público',
Public: 'Público',
PUSH_NOTIFICATIONS: 'PUSH NOTIFICATIONS',
Push_Notifications: 'Push Notifications',
Push_Notifications_Alert_Info: 'Estas notificaciones se le entregan cuando la aplicación no está abierta',
Quote: 'Citar',
Reactions_are_disabled: 'Las reacciones están desactivadas',

View File

@ -100,7 +100,6 @@ export default {
announcement: 'annonce',
Announcement: 'Annonce',
Apply_Your_Certificate: 'Valider le Certificat',
Applying_a_theme_will_change_how_the_app_looks: 'Valider un thème va modifier l\'affichage de l\'application.',
ARCHIVE: 'ARCHIVER',
archive: 'archiver',
are_typing: 'sont en train d\'écrire',
@ -180,7 +179,7 @@ export default {
deleting_room: 'effacement de la salle',
description: 'la description',
Description: 'La description',
DESKTOP_OPTIONS: 'DESKTOP OPTIONS',
Desktop_Options: 'Desktop Options',
Directory: 'Répertoire',
Direct_Messages: 'Messages directs',
Disable_notifications: 'Désactiver les notifications',
@ -199,7 +198,6 @@ export default {
Edit_Invite: 'Modifier l\'invitation',
Email_or_password_field_is_empty: 'Le champ e-mail ou mot de passe est vide',
Email: 'E-mail',
EMAIL: 'EMAIL',
email: 'e-mail',
Empty_title: 'Titre vide',
Enable_Auto_Translate: 'Activer la traduction-auto',
@ -247,7 +245,7 @@ export default {
Message_HideType_room_unarchived: 'Salon Désarchivé',
IP: 'IP',
In_app: 'In-app',
IN_APP_AND_DESKTOP: 'IN-APP ET BUREAU',
In_App_And_Desktop: 'In-app et Bureau',
In_App_and_Desktop_Alert_info: 'Affiche une bannière en haut de l\'écran lorsque l\'application est ouverte et affiche une notification sur le bureau',
Invisible: 'Invisible',
Invite: 'Inviter',
@ -373,7 +371,7 @@ export default {
Profile: 'Profil',
Public_Channel: 'Canal Public',
Public: 'Public',
PUSH_NOTIFICATIONS: 'NOTIFICATIONS PUSH',
Push_Notifications: 'Notifications Push',
Push_Notifications_Alert_Info: 'Ces notifications vous sont livrées lorsque l\'application n\'est pas ouverte',
Quote: 'Citation',
Reactions_are_disabled: 'Les réactions sont désactivées',

View File

@ -100,7 +100,6 @@ export default {
announcement: 'annuncio',
Announcement: 'Annuncio',
Apply_Your_Certificate: 'Applica il tuo certificato',
Applying_a_theme_will_change_how_the_app_looks: 'Applicare un tema cambierà l\'aspetto dell\'app.',
ARCHIVE: 'ARCHIVIO',
archive: 'archivio',
are_typing: 'stanno scrivendo',
@ -184,8 +183,8 @@ export default {
deleting_room: 'cancellazione stanza',
description: 'descrizione',
Description: 'Descrizione',
DESKTOP_OPTIONS: 'OPZIONI DESKTOP',
DESKTOP_NOTIFICATIONS: 'NOTIFICHE DESKTOP',
Desktop_Options: 'Opzioni Desktop',
Desktop_Notifications: 'Notifiche Desktop',
Desktop_Alert_info: 'Queste notifiche vengono inviate sul client desktop',
Directory: 'Rubrica',
Direct_Messages: 'Messaggi diretti',
@ -213,7 +212,6 @@ export default {
Email_Notification_Mode_Disabled: 'Disabilitato',
Email_or_password_field_is_empty: 'Il campo e-mail o password sono vuoti',
Email: 'E-mail',
EMAIL: 'E-MAIL',
email: 'e-mail',
Empty_title: 'Titolo vuoto',
Enable_Auto_Translate: 'Abilita traduzione automatica',
@ -270,7 +268,7 @@ export default {
I_Saved_My_E2E_Password: 'Ho salvato la mia Password E2E',
IP: 'Indirizzo IP',
In_app: 'In-app',
IN_APP_AND_DESKTOP: 'IN-APP E DESKTOP',
In_App_And_Desktop: 'In-app e Desktop',
In_App_and_Desktop_Alert_info: 'Mostra una notifica in cima allo schermo quando l\'app è aperta, e mostra una notifica sul desktop',
Invisible: 'Invisibile',
Invite: 'Invita',
@ -398,7 +396,7 @@ export default {
Profile: 'Profilo',
Public_Channel: 'Canale pubblico',
Public: 'Pubblico',
PUSH_NOTIFICATIONS: 'NOTIFICHE PUSH',
Push_Notifications: 'Notifiche Push',
Push_Notifications_Alert_Info: 'Queste notifiche ti vengono recapitate quando l\'app non è aperta',
Quote: 'Cita',
Reactions_are_disabled: 'Le reazioni sono disabilitate',

View File

@ -110,8 +110,6 @@ export default {
announcement: 'アナウンス',
Announcement: 'アナウンス',
Apply_Your_Certificate: '証明書を適用する',
Applying_a_theme_will_change_how_the_app_looks:
'テーマを変更すると見た目が変わります',
ARCHIVE: 'アーカイブ',
archive: 'アーカイブ',
are_typing: 'が入力中',
@ -183,7 +181,7 @@ export default {
deleting_room: 'ルームを削除',
description: '概要',
Description: '概要',
DESKTOP_OPTIONS: 'デスクトップオプション',
Desktop_Options: 'デスクトップオプション',
Directory: 'ディレクトリ',
Direct_Messages: 'ダイレクトメッセージ',
Disable_notifications: '通知を無効化',
@ -198,7 +196,6 @@ export default {
Edit_Invite: '編集に招待',
Email_or_password_field_is_empty: 'メールアドレスかパスワードの入力欄が空です',
Email: 'メールアドレス',
EMAIL: 'メールアドレス',
email: 'メールアドレス',
Enable_Auto_Translate: '自動翻訳を有効にする',
Enable_markdown: 'マークダウンを有効にする',
@ -227,7 +224,7 @@ export default {
Has_joined_the_channel: 'はチャンネルに参加しました',
Has_joined_the_conversation: 'は会話に参加しました',
Has_left_the_channel: 'はチャンネルを退出しました',
IN_APP_AND_DESKTOP: 'アプリ内とデスクトップ',
In_App_And_Desktop: 'アプリ内とデスクトップ',
In_App_and_Desktop_Alert_info:
'アプリを表示中にはバナーを上部に表示し、デスクトップには通知を送ります。',
Invisible: '状態を隠す',
@ -332,7 +329,7 @@ export default {
Profile: 'プロフィール',
Public_Channel: 'パブリックチャンネル',
Public: 'パブリック',
PUSH_NOTIFICATIONS: 'プッシュ通知',
Push_Notifications: 'プッシュ通知',
Push_Notifications_Alert_Info:
'通知はアプリを開いていない時に送られます。',
Quote: '引用',

View File

@ -96,7 +96,6 @@ export default {
announcement: 'aankondiging',
Announcement: 'Aankondiging',
Apply_Your_Certificate: 'Gebruik je certificaat',
Applying_a_theme_will_change_how_the_app_looks: 'Een thema toepassen verandert de looks van de app.',
ARCHIVE: 'ARCHIVEER',
archive: 'archiveer',
are_typing: 'zijn aan het typen',
@ -162,7 +161,7 @@ export default {
deleting_room: 'kamer legen',
description: 'beschrijving',
Description: 'Beschrijving',
DESKTOP_OPTIONS: 'DESKTOP OPTIES',
Desktop_Options: 'Desktop Opties',
Directory: 'Map',
Direct_Messages: 'Directe berichten',
Disable_notifications: 'Zet notificaties uit',
@ -175,8 +174,7 @@ export default {
Edit: 'Bewerk',
Edit_Invite: 'Bewerk uitnodiging',
Email_or_password_field_is_empty: 'Email of wachtwoord veld is leeg',
Email: 'Email',
EMAIL: 'EMAIL',
Email: 'E-mail',
email: 'e-mail',
Enable_Auto_Translate: 'Zet Auto-Translate aan',
Enable_notifications: 'Zet notifications aan',
@ -202,7 +200,7 @@ export default {
Has_joined_the_channel: 'Is bij het kanaal gekomen',
Has_joined_the_conversation: 'Neemt deel aan het gesprek',
Has_left_the_channel: 'Heeft het kanaal verlaten',
IN_APP_AND_DESKTOP: 'IN-APP EN DESKTOP',
In_App_And_Desktop: 'In-app en Desktop',
In_App_and_Desktop_Alert_info: 'Laat een banner bovenaan het scherm zien als de app open is en geeft een notificatie op de desktop',
Invisible: 'Onzichtbaar',
Invite: 'Nodig uit',
@ -301,7 +299,7 @@ export default {
Profile: 'Profiel',
Public_Channel: 'Publiek kanaal',
Public: 'Publiek',
PUSH_NOTIFICATIONS: 'PUSHNOTIFICATIES',
Push_Notifications: 'Pushnotificaties',
Push_Notifications_Alert_Info: 'Deze notificaties krijg je als de app niet geopend is',
Quote: 'Quote',
Reactions_are_disabled: 'Reacties zijn uitgeschakeld',

View File

@ -102,7 +102,6 @@ export default {
and: 'e',
announcement: 'anúncio',
Announcement: 'Anúncio',
Applying_a_theme_will_change_how_the_app_looks: 'Aplicar um tema mudará a aparência do app.',
ARCHIVE: 'ARQUIVAR',
archive: 'arquivar',
are_typing: 'estão digitando',
@ -178,8 +177,8 @@ export default {
DELETE: 'EXCLUIR',
deleting_room: 'excluindo sala',
Direct_Messages: 'Mensagens Diretas',
DESKTOP_OPTIONS: 'OPÇÕES DE ÁREA DE TRABALHO',
DESKTOP_NOTIFICATIONS: 'NOTIFICAÇÕES DE ÁREA DE TRABALHO',
Desktop_Options: 'Opções De Área De Trabalho',
Desktop_Notifications: 'Notificações da Área de Trabalho',
Desktop_Alert_info: 'Essas notificações são entregues a você na área de trabalho',
Directory: 'Diretório',
description: 'descrição',
@ -204,7 +203,7 @@ export default {
End_to_end_encrypted_room: 'Sala criptografada de ponta a ponta',
end_to_end_encryption: 'criptografia de ponta a ponta',
Email_or_password_field_is_empty: 'Email ou senha estão vazios',
Email: 'Email',
Email: 'E-mail',
email: 'e-mail',
Empty_title: 'Título vazio',
Email_Notification_Mode_All: 'Cada Menção / Mensagem Direta',
@ -372,6 +371,7 @@ export default {
Profile: 'Perfil',
Public_Channel: 'Canal Público',
Public: 'Público',
Push_Notifications: 'Notificações Push',
Push_Notifications_Alert_Info: 'Essas notificações são entregues a você quando o aplicativo não está aberto',
Quote: 'Citar',
Reactions_are_disabled: 'Reagir está desabilitado',

View File

@ -154,7 +154,7 @@ export default {
DELETE: 'УДАЛИТЬ',
description: 'описание',
Description: 'Описание',
DESKTOP_OPTIONS: 'ПАРАМЕТРЫ РАБОЧЕГО СТОЛА',
Desktop_Options: 'Параметры Рабочего Стола',
Directory: 'Директория',
Direct_Messages: 'Личные сообщения',
Disable_notifications: 'Отключить уведомления',
@ -166,8 +166,7 @@ export default {
edited: 'отредактировано',
Edit: 'Редактировать',
Email_or_password_field_is_empty: 'Поле электронной почты или пароля пусты',
Email: 'Email',
EMAIL: 'EMAIL',
Email: 'E-mail',
email: 'e-mail',
Enable_Auto_Translate: 'Включить автоперевод',
Enable_notifications: 'Включить уведомления',
@ -192,7 +191,7 @@ export default {
Has_joined_the_channel: 'Присоединился к каналу',
Has_joined_the_conversation: 'Присоединился к беседе',
Has_left_the_channel: 'Покинул канал',
IN_APP_AND_DESKTOP: 'В приложении и на десктопе',
In_App_And_Desktop: 'В приложении и на десктопе',
In_App_and_Desktop_Alert_info: 'Отображает баннер в верхней части экрана, когда приложение открыто, и отображает уведомление на рабочем столе.',
Invisible: 'Невидимый',
Invite: 'Приглашение',
@ -283,7 +282,7 @@ export default {
Profile: 'Профиль',
Public_Channel: 'Публичный канал',
Public: 'Публичный',
PUSH_NOTIFICATIONS: 'PUSH УВЕДОМЛЕНИЯ',
Push_Notifications: 'Push Уведомления',
Push_Notifications_Alert_Info: 'Эти уведомления доставляются вам, когда приложение не открыто',
Quote: 'Цитата',
Reactions_are_disabled: 'Реакции отключены',

View File

@ -100,7 +100,6 @@ export default {
announcement: '公告',
Announcement: '公告',
Apply_Your_Certificate: '使用自己的凭证',
Applying_a_theme_will_change_how_the_app_looks: '套用主题将会改变 App 的外观',
ARCHIVE: '封存',
archive: '封存',
are_typing: '正在输入',
@ -184,8 +183,8 @@ export default {
deleting_room: '正在删除聊天室',
description: '描述',
Description: '描述',
DESKTOP_OPTIONS: '桌面选项',
DESKTOP_NOTIFICATIONS: '桌面通知',
Desktop_Options: '桌面选项',
Desktop_Notifications: '桌面通知',
Desktop_Alert_info: '这些通知将发送至桌面',
Directory: '目录',
Direct_Messages: '私訊',
@ -213,7 +212,6 @@ export default {
Email_Notification_Mode_Disabled: '禁用',
Email_or_password_field_is_empty: '邮件或密码字段为空',
Email: '邮箱',
EMAIL: 'EMAIL',
email: '邮箱',
Empty_title: '空白标题',
Enable_Auto_Translate: '开启自动翻译',
@ -270,7 +268,7 @@ export default {
I_Saved_My_E2E_Password: '保存我的 E2E 密码',
IP: 'IP',
In_app: 'App 内',
IN_APP_AND_DESKTOP: 'App 内及桌面',
In_App_And_Desktop: 'App 内及桌面',
In_App_and_Desktop_Alert_info: '打开应用程序时,在屏幕顶部显示横幅,并在桌面上显示通知',
Invisible: '隐身',
Invite: '邀请',
@ -397,7 +395,7 @@ export default {
Profile: '个人资料',
Public_Channel: '公共频道',
Public: '公共',
PUSH_NOTIFICATIONS: '推送通知',
Push_Notifications: '推送通知',
Push_Notifications_Alert_Info: '这些通知将在未开启 App 时发送给您',
Quote: '引用',
Reactions_are_disabled: '表情貼被禁用',

View File

@ -100,7 +100,6 @@ export default {
announcement: '公告',
Announcement: '公告',
Apply_Your_Certificate: '使用自己的憑證',
Applying_a_theme_will_change_how_the_app_looks: '套用主題將會改變 App 的外觀',
ARCHIVE: '封存',
archive: '封存',
are_typing: '正在輸入',
@ -184,8 +183,8 @@ export default {
deleting_room: '正在刪除聊天室',
description: '描述',
Description: '描述',
DESKTOP_OPTIONS: '桌面選項',
DESKTOP_NOTIFICATIONS: '桌面通知',
Desktop_Options: '桌面選項',
Desktop_Notifications: '桌面通知',
Desktop_Alert_info: '這些通知將發送至桌面',
Directory: '目錄',
Direct_Messages: '私訊',
@ -213,7 +212,6 @@ export default {
Email_Notification_Mode_Disabled: '禁用',
Email_or_password_field_is_empty: '電子郵件或密碼字段為空',
Email: '電子郵件',
EMAIL: 'EMAIL',
email: '電子郵件',
Empty_title: '空白標題',
Enable_Auto_Translate: '開啟自動翻譯',
@ -270,7 +268,7 @@ export default {
I_Saved_My_E2E_Password: '儲存我的 E2E 密碼',
IP: 'IP',
In_app: 'App 內',
IN_APP_AND_DESKTOP: 'App 內及桌面',
In_App_And_Desktop: 'App 內及桌面',
In_App_and_Desktop_Alert_info: '當在應用程序打開時,螢幕頂端顯示橫幅,並在桌面上顯示通知',
Invisible: '隱身',
Invite: '邀請',
@ -397,7 +395,7 @@ export default {
Profile: '個人資料',
Public_Channel: '公共頻道',
Public: '公共',
PUSH_NOTIFICATIONS: '推送通知',
Push_Notifications: '推送通知',
Push_Notifications_Alert_Info: '這些通知將在未開啟 App 時發送給您',
Quote: '引用',
Reactions_are_disabled: '表情貼被禁用',

View File

@ -68,7 +68,9 @@ export default class Root extends React.Component {
if (!isFDroidBuild) {
this.initCrashReport();
}
const { width, height, scale } = Dimensions.get('window');
const {
width, height, scale, fontScale
} = Dimensions.get('window');
this.state = {
theme: defaultTheme(),
themePreferences: {
@ -77,7 +79,8 @@ export default class Root extends React.Component {
},
width,
height,
scale
scale,
fontScale
};
if (isTablet) {
this.initTablet();
@ -134,8 +137,14 @@ export default class Root extends React.Component {
};
// Dimensions update fires twice
onDimensionsChange = debounce(({ window: { width, height, scale } }) => {
this.setDimensions({ width, height, scale });
onDimensionsChange = debounce(({
window: {
width, height, scale, fontScale
}
}) => {
this.setDimensions({
width, height, scale, fontScale
});
this.setMasterDetail(width);
})
@ -148,8 +157,12 @@ export default class Root extends React.Component {
});
}
setDimensions = ({ width, height, scale }) => {
this.setState({ width, height, scale });
setDimensions = ({
width, height, scale, fontScale
}) => {
this.setState({
width, height, scale, fontScale
});
}
initTablet = () => {
@ -176,7 +189,7 @@ export default class Root extends React.Component {
render() {
const {
themePreferences, theme, width, height, scale
themePreferences, theme, width, height, scale, fontScale
} = this.state;
return (
<SafeAreaProvider initialMetrics={initialWindowMetrics}>
@ -194,6 +207,7 @@ export default class Root extends React.Component {
width,
height,
scale,
fontScale,
setDimensions: this.setDimensions
}}
>

View File

@ -117,7 +117,9 @@ App.propTypes = {
class Root extends React.Component {
constructor(props) {
super(props);
const { width, height, scale } = Dimensions.get('screen');
const {
width, height, scale, fontScale
} = Dimensions.get('screen');
this.state = {
theme: defaultTheme(),
themePreferences: {
@ -127,7 +129,8 @@ class Root extends React.Component {
root: '',
width,
height,
scale
scale,
fontScale
};
this.init();
}
@ -166,18 +169,28 @@ class Root extends React.Component {
}
// Dimensions update fires twice
onDimensionsChange = debounce(({ window: { width, height, scale } }) => {
this.setDimensions({ width, height, scale });
onDimensionsChange = debounce(({
window: {
width, height, scale, fontScale
}
}) => {
this.setDimensions({
width, height, scale, fontScale
});
this.setMasterDetail(width);
})
setDimensions = ({ width, height, scale }) => {
this.setState({ width, height, scale });
setDimensions = ({
width, height, scale, fontScale
}) => {
this.setState({
width, height, scale, fontScale
});
}
render() {
const {
theme, root, width, height, scale
theme, root, width, height, scale, fontScale
} = this.state;
const navTheme = navigationTheme(theme);
return (
@ -189,6 +202,7 @@ class Root extends React.Component {
width,
height,
scale,
fontScale,
setDimensions: this.setDimensions
}}
>

View File

@ -18,18 +18,17 @@ class AdminPanelView extends React.Component {
static propTypes = {
baseUrl: PropTypes.string,
token: PropTypes.string,
theme: PropTypes.string
token: PropTypes.string
}
render() {
const { baseUrl, token, theme } = this.props;
const { baseUrl, token } = this.props;
if (!baseUrl) {
return null;
}
return (
<SafeAreaView theme={theme}>
<StatusBar theme={theme} />
<SafeAreaView>
<StatusBar />
<WebView
// https://github.com/react-native-community/react-native-webview/issues/1311
onMessage={() => {}}

View File

@ -28,7 +28,7 @@ const styles = StyleSheet.create({
const AuthLoadingView = React.memo(({ theme, text }) => (
<View style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]}>
<StatusBar theme={theme} />
<StatusBar />
{text && (
<>
<ActivityIndicator color={themes[theme].auxiliaryText} size='large' />

View File

@ -155,7 +155,7 @@ class AuthenticationWebView extends React.PureComponent {
return (
<>
<StatusBar theme={theme} />
<StatusBar />
<WebView
source={{ uri: url }}
userAgent={userAgent}

View File

@ -1,53 +1,22 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
FlatList, Switch, View, StyleSheet, ScrollView
} from 'react-native';
import { FlatList, Switch, StyleSheet } from 'react-native';
import RocketChat from '../../lib/rocketchat';
import I18n from '../../i18n';
import StatusBar from '../../containers/StatusBar';
import { CustomIcon } from '../../lib/Icons';
import sharedStyles from '../Styles';
import ListItem from '../../containers/ListItem';
import Separator from '../../containers/Separator';
import * as List from '../../containers/List';
import { SWITCH_TRACK_COLOR, themes } from '../../constants/colors';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import { withTheme } from '../../theme';
import SafeAreaView from '../../containers/SafeAreaView';
import { logEvent, events } from '../../utils/log';
const styles = StyleSheet.create({
contentContainerStyle: {
borderTopWidth: StyleSheet.hairlineWidth,
marginTop: 10,
paddingBottom: 30
},
flatListContainerStyle: {
borderBottomWidth: StyleSheet.hairlineWidth
},
sectionSeparator: {
...sharedStyles.separatorVertical,
height: 10
list: {
paddingTop: 16
}
});
const SectionSeparator = React.memo(({ theme }) => (
<View
style={[
styles.sectionSeparator,
{
backgroundColor: themes[theme].auxiliaryBackground,
borderColor: themes[theme].separatorColor
}
]}
/>
));
SectionSeparator.propTypes = {
theme: PropTypes.string
};
class AutoTranslateView extends React.Component {
static navigationOptions = () => ({
title: I18n.t('Auto_Translate')
@ -55,8 +24,7 @@ class AutoTranslateView extends React.Component {
static propTypes = {
route: PropTypes.object,
theme: PropTypes.string,
navigation: PropTypes.object
theme: PropTypes.string
}
constructor(props) {
@ -135,14 +103,9 @@ class AutoTranslateView extends React.Component {
}
}
renderSeparator = () => {
const { theme } = this.props;
return <Separator theme={theme} />;
}
renderIcon = () => {
const { theme } = this.props;
return <CustomIcon name='check' size={20} style={{ color: themes[theme].tintColor }} />;
return <List.Icon name='check' style={{ color: themes[theme].tintColor }} />;
}
renderSwitch = () => {
@ -158,54 +121,46 @@ class AutoTranslateView extends React.Component {
renderItem = ({ item }) => {
const { selectedLanguage } = this.state;
const { theme } = this.props;
const { language, name } = item;
const isSelected = selectedLanguage === language;
return (
<ListItem
<List.Item
title={name || language}
onPress={() => this.saveAutoTranslateLanguage(language)}
testID={`auto-translate-view-${ language }`}
right={isSelected ? this.renderIcon : null}
theme={theme}
translateTitle={false}
/>
);
}
render() {
const { languages } = this.state;
const { theme } = this.props;
return (
<SafeAreaView testID='auto-translate-view' theme={theme}>
<StatusBar theme={theme} />
<ScrollView
{...scrollPersistTaps}
contentContainerStyle={[
styles.contentContainerStyle,
{
backgroundColor: themes[theme].auxiliaryBackground,
borderColor: themes[theme].separatorColor
}
]}
testID='auto-translate-view-list'
>
<ListItem
title={I18n.t('Enable_Auto_Translate')}
testID='auto-translate-view-switch'
right={() => this.renderSwitch()}
theme={theme}
/>
<SectionSeparator theme={theme} />
<SafeAreaView testID='auto-translate-view'>
<StatusBar />
<List.Container testID='auto-translate-view-list'>
<List.Section>
<List.Separator />
<List.Item
title='Enable_Auto_Translate'
testID='auto-translate-view-switch'
right={() => this.renderSwitch()}
/>
<List.Separator />
</List.Section>
<FlatList
data={languages}
extraData={this.state}
keyExtractor={item => item.language}
renderItem={this.renderItem}
ItemSeparatorComponent={this.renderSeparator}
contentContainerStyle={[styles.flatListContainerStyle, { borderColor: themes[theme].separatorColor }]}
ItemSeparatorComponent={List.Separator}
ListFooterComponent={List.Separator}
ListHeaderComponent={List.Separator}
contentContainerStyle={[List.styles.contentContainerStyleFlatList, styles.list]}
/>
</ScrollView>
</List.Container>
</SafeAreaView>
);
}

View File

@ -323,8 +323,8 @@ class CreateChannelView extends React.Component {
contentContainerStyle={[sharedStyles.container, styles.container]}
keyboardVerticalOffset={128}
>
<StatusBar theme={theme} />
<SafeAreaView testID='create-channel-view' theme={theme}>
<StatusBar />
<SafeAreaView testID='create-channel-view'>
<ScrollView {...scrollPersistTaps}>
<View style={[sharedStyles.separatorVertical, { borderColor: themes[theme].separatorColor }]}>
<TextInput

View File

@ -151,8 +151,8 @@ class CreateChannelView extends React.Component {
contentContainerStyle={styles.container}
keyboardVerticalOffset={128}
>
<StatusBar theme={theme} />
<SafeAreaView testID='create-discussion-view' style={styles.container} theme={theme}>
<StatusBar />
<SafeAreaView testID='create-discussion-view' style={styles.container}>
<ScrollView {...scrollPersistTaps}>
<Text style={[styles.description, { color: themes[theme].auxiliaryText }]}>{I18n.t('Discussion_Desc')}</Text>
<SelectChannel

View File

@ -1,17 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
StyleSheet, FlatList, View, Text, Linking
} from 'react-native';
import { FlatList, Linking } from 'react-native';
import I18n from '../i18n';
import { withTheme } from '../theme';
import { themes } from '../constants/colors';
import sharedStyles from './Styles';
import StatusBar from '../containers/StatusBar';
import Separator from '../containers/Separator';
import ListItem from '../containers/ListItem';
import { CustomIcon } from '../lib/Icons';
import * as List from '../containers/List';
import { DEFAULT_BROWSER_KEY } from '../utils/openLink';
import { isIOS } from '../utils/deviceInfo';
import SafeAreaView from '../containers/SafeAreaView';
@ -44,21 +39,6 @@ const BROWSERS = [
}
];
const styles = StyleSheet.create({
list: {
paddingBottom: 18
},
info: {
paddingTop: 25,
paddingBottom: 18,
paddingHorizontal: 16
},
infoText: {
fontSize: 16,
...sharedStyles.textRegular
}
});
class DefaultBrowserView extends React.Component {
static navigationOptions = () => ({
title: I18n.t('Default_browser')
@ -120,59 +100,44 @@ class DefaultBrowserView extends React.Component {
}
}
renderSeparator = () => {
const { theme } = this.props;
return <Separator theme={theme} />;
}
renderIcon = () => {
const { theme } = this.props;
return <CustomIcon name='check' size={20} color={themes[theme].tintColor} />;
return <List.Icon name='check' color={themes[theme].tintColor} />;
}
renderItem = ({ item }) => {
const { theme } = this.props;
const { title, value } = item;
return (
<ListItem
<List.Item
title={I18n.t(title, { defaultValue: title })}
onPress={() => this.changeDefaultBrowser(value)}
testID={`default-browser-view-${ title }`}
right={this.isSelected(value) ? this.renderIcon : null}
theme={theme}
translateTitle={false}
/>
);
}
renderHeader = () => {
const { theme } = this.props;
return (
<>
<View style={styles.info}>
<Text style={[styles.infoText, { color: themes[theme].infoText }]}>{I18n.t('Choose_where_you_want_links_be_opened')}</Text>
</View>
{this.renderSeparator()}
</>
);
}
renderHeader = () => (
<>
<List.Header title='Choose_where_you_want_links_be_opened' />
<List.Separator />
</>
)
render() {
const { supported } = this.state;
const { theme } = this.props;
return (
<SafeAreaView testID='default-browser-view' theme={theme}>
<StatusBar theme={theme} />
<SafeAreaView testID='default-browser-view'>
<StatusBar />
<FlatList
data={DEFAULT_BROWSERS.concat(supported)}
keyExtractor={item => item.value}
contentContainerStyle={[
styles.list,
{ borderColor: themes[theme].separatorColor }
]}
contentContainerStyle={List.styles.contentContainerStyleFlatList}
renderItem={this.renderItem}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderSeparator}
ItemSeparatorComponent={this.renderSeparator}
ListFooterComponent={List.Separator}
ItemSeparatorComponent={List.Separator}
/>
</SafeAreaView>
);

View File

@ -240,9 +240,8 @@ class DirectoryView extends React.Component {
<SafeAreaView
style={{ backgroundColor: themes[theme].backgroundColor }}
testID='directory-view'
theme={theme}
>
<StatusBar theme={theme} />
<StatusBar />
<FlatList
data={data}
style={styles.list}

View File

@ -62,9 +62,9 @@ class E2EEnterYourPasswordView extends React.Component {
contentContainerStyle={sharedStyles.container}
keyboardVerticalOffset={128}
>
<StatusBar theme={theme} />
<StatusBar />
<ScrollView {...scrollPersistTaps} style={sharedStyles.container} contentContainerStyle={[sharedStyles.containerScrollView, styles.scrollView]}>
<SafeAreaView theme={theme} style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]}>
<SafeAreaView style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]}>
<TextInput
inputRef={(e) => { this.passwordInput = e; }}
placeholder={I18n.t('Password')}

View File

@ -43,7 +43,6 @@ class E2EHowItWorksView extends React.Component {
<SafeAreaView
style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]}
testID='e2e-how-it-works-view'
theme={theme}
>
<Markdown
msg={I18n.t('E2E_How_It_Works_info1')}

View File

@ -126,8 +126,8 @@ class E2ESaveYourPasswordView extends React.Component {
const { theme } = this.props;
return (
<SafeAreaView theme={theme} style={{ backgroundColor: themes[theme].backgroundColor }}>
<StatusBar theme={theme} />
<SafeAreaView style={{ backgroundColor: themes[theme].backgroundColor }}>
<StatusBar />
<ScrollView {...scrollPersistTaps} style={sharedStyles.container} contentContainerStyle={sharedStyles.containerScrollView}>
<View style={[styles.container, { backgroundColor: themes[theme].backgroundColor }]}>
<Text style={[styles.warning, { color: themes[theme].dangerColor }]}>{I18n.t('Save_Your_Encryption_Password_warning')}</Text>

View File

@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ScrollView, View } from 'react-native';
import { View } from 'react-native';
import { connect } from 'react-redux';
import RNPickerSelect from 'react-native-picker-select';
@ -8,15 +8,13 @@ import {
inviteLinksSetParams as inviteLinksSetParamsAction,
inviteLinksCreate as inviteLinksCreateAction
} from '../../actions/inviteLinks';
import ListItem from '../../containers/ListItem';
import * as List from '../../containers/List';
import styles from './styles';
import Button from '../../containers/Button';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import I18n from '../../i18n';
import StatusBar from '../../containers/StatusBar';
import { themes } from '../../constants/colors';
import { withTheme } from '../../theme';
import Separator from '../../containers/Separator';
import SafeAreaView from '../../containers/SafeAreaView';
import { logEvent, events } from '../../utils/log';
@ -62,7 +60,6 @@ class InviteUsersView extends React.Component {
navigation: PropTypes.object,
route: PropTypes.object,
theme: PropTypes.string,
timeDateFormat: PropTypes.string,
createInviteLink: PropTypes.func,
inviteLinksSetParams: PropTypes.func
}
@ -110,29 +107,23 @@ class InviteUsersView extends React.Component {
render() {
const { theme } = this.props;
return (
<SafeAreaView style={{ backgroundColor: themes[theme].backgroundColor }} theme={theme}>
<ScrollView
{...scrollPersistTaps}
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
contentContainerStyle={styles.contentContainer}
showsVerticalScrollIndicator={false}
>
<StatusBar theme={theme} />
<Separator theme={theme} />
<ListItem
title={I18n.t('Expiration_Days')}
right={() => this.renderPicker('days', 'Never')}
theme={theme}
/>
<Separator theme={theme} />
<ListItem
title={I18n.t('Max_number_of_uses')}
right={() => this.renderPicker('maxUses', 'No_limit')}
theme={theme}
/>
<Separator theme={theme} />
<SafeAreaView>
<List.Container>
<StatusBar />
<List.Section>
<List.Separator />
<List.Item
title='Expiration_Days'
right={() => this.renderPicker('days', 'Never')}
/>
<List.Separator />
<List.Item
title='Max_number_of_uses'
right={() => this.renderPicker('maxUses', 'No_limit')}
/>
<List.Separator />
</List.Section>
<View style={styles.innerContainer}>
<View style={[styles.divider, { backgroundColor: themes[theme].separatorColor }]} />
<Button
title={I18n.t('Generate_New_Link')}
type='primary'
@ -140,7 +131,7 @@ class InviteUsersView extends React.Component {
theme={theme}
/>
</View>
</ScrollView>
</List.Container>
</SafeAreaView>
);
}

View File

@ -1,36 +1,12 @@
import { StyleSheet } from 'react-native';
import { PADDING_HORIZONTAL } from '../../containers/List/constants';
import sharedStyles from '../Styles';
export default StyleSheet.create({
innerContainer: {
paddingHorizontal: 20
},
divider: {
width: '100%',
height: StyleSheet.hairlineWidth,
marginVertical: 20
},
sectionSeparatorBorder: {
height: 10
},
marginBottom: {
height: 30
},
contentContainer: {
marginVertical: 10
},
infoText: {
...sharedStyles.textRegular,
fontSize: 13,
paddingHorizontal: 15,
paddingVertical: 10
},
sectionTitle: {
...sharedStyles.separatorBottom,
paddingHorizontal: 15,
paddingVertical: 10,
fontSize: 14
paddingHorizontal: PADDING_HORIZONTAL,
paddingTop: 16
},
viewContainer: {
justifyContent: 'center'

View File

@ -102,14 +102,14 @@ class InviteUsersView extends React.Component {
theme, invite
} = this.props;
return (
<SafeAreaView style={{ backgroundColor: themes[theme].backgroundColor }} theme={theme}>
<SafeAreaView style={{ backgroundColor: themes[theme].backgroundColor }}>
<ScrollView
{...scrollPersistTaps}
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
contentContainerStyle={styles.contentContainer}
showsVerticalScrollIndicator={false}
>
<StatusBar theme={theme} />
<StatusBar />
<View style={styles.innerContainer}>
<RCTextInput
label={I18n.t('Invite_Link')}

View File

@ -1,8 +1,9 @@
import { StyleSheet } from 'react-native';
import { PADDING_HORIZONTAL } from '../../containers/List/constants';
export default StyleSheet.create({
innerContainer: {
padding: 20,
padding: PADDING_HORIZONTAL,
paddingBottom: 0
},
divider: {

View File

@ -9,10 +9,7 @@ import { showErrorAlert } from '../../utils/info';
import log, { logEvent, events } from '../../utils/log';
import { setUser as setUserAction } from '../../actions/login';
import StatusBar from '../../containers/StatusBar';
import { CustomIcon } from '../../lib/Icons';
import sharedStyles from '../Styles';
import ListItem from '../../containers/ListItem';
import Separator from '../../containers/Separator';
import * as List from '../../containers/List';
import { themes } from '../../constants/colors';
import { withTheme } from '../../theme';
import { appStart as appStartAction, ROOT_LOADING, ROOT_INSIDE } from '../../actions/app';
@ -108,50 +105,39 @@ class LanguageView extends React.Component {
}
}
renderSeparator = () => {
const { theme } = this.props;
return <Separator theme={theme} />;
}
renderIcon = () => {
const { theme } = this.props;
return <CustomIcon name='check' size={20} style={{ color: themes[theme].tintColor }} />;
return <List.Icon name='check' color={themes[theme].tintColor} />;
}
renderItem = ({ item }) => {
const { value, label } = item;
const { language } = this.state;
const { theme } = this.props;
const isSelected = language === value;
return (
<ListItem
<List.Item
title={label}
onPress={() => this.submit(value)}
testID={`language-view-${ value }`}
right={isSelected ? this.renderIcon : null}
theme={theme}
translateTitle={false}
/>
);
}
render() {
const { theme } = this.props;
return (
<SafeAreaView testID='language-view' theme={theme}>
<StatusBar theme={theme} />
<SafeAreaView testID='language-view'>
<StatusBar />
<FlatList
data={LANGUAGES}
keyExtractor={item => item.value}
contentContainerStyle={[
sharedStyles.listContentContainer,
{
backgroundColor: themes[theme].auxiliaryBackground,
borderColor: themes[theme].separatorColor
}
]}
ListHeaderComponent={List.Separator}
ListFooterComponent={List.Separator}
contentContainerStyle={List.styles.contentContainerStyleFlatList}
renderItem={this.renderItem}
ItemSeparatorComponent={this.renderSeparator}
ItemSeparatorComponent={List.Separator}
/>
</SafeAreaView>
);

View File

@ -1,51 +1,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Text, ScrollView, View, StyleSheet
} from 'react-native';
import { connect } from 'react-redux';
import Touch from '../utils/touch';
import sharedStyles from './Styles';
import scrollPersistTaps from '../utils/scrollPersistTaps';
import I18n from '../i18n';
import DisclosureIndicator from '../containers/DisclosureIndicator';
import StatusBar from '../containers/StatusBar';
import { themes } from '../constants/colors';
import openLink from '../utils/openLink';
import { withTheme } from '../theme';
import SafeAreaView from '../containers/SafeAreaView';
const styles = StyleSheet.create({
scroll: {
marginTop: 35,
borderTopWidth: StyleSheet.hairlineWidth,
borderBottomWidth: StyleSheet.hairlineWidth
},
separator: {
height: StyleSheet.hairlineWidth,
width: '100%',
marginLeft: 20
},
item: {
width: '100%',
height: 48,
paddingLeft: 20,
paddingRight: 10,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between'
},
text: {
...sharedStyles.textMedium,
fontSize: 18
}
});
const Separator = ({ theme }) => <View style={[styles.separator, { backgroundColor: themes[theme].separatorColor }]} />;
Separator.propTypes = {
theme: PropTypes.string
};
import * as List from '../containers/List';
class LegalView extends React.Component {
static propTypes = {
@ -61,40 +23,29 @@ class LegalView extends React.Component {
openLink(`${ server }/${ route }`, theme);
}
renderItem = ({ text, route, testID }) => {
const { theme } = this.props;
return (
<Touch
style={[styles.item, { backgroundColor: themes[theme].backgroundColor }]}
onPress={() => this.onPressItem({ route })}
testID={testID}
theme={theme}
>
<Text style={[styles.text, { color: themes[theme].titleText }]}>{I18n.t(text)}</Text>
<DisclosureIndicator theme={theme} />
</Touch>
);
}
render() {
const { theme } = this.props;
return (
<SafeAreaView testID='legal-view' theme={theme}>
<StatusBar theme={theme} />
<ScrollView
contentContainerStyle={[
styles.scroll,
{
backgroundColor: themes[theme].backgroundColor,
borderColor: themes[theme].separatorColor
}
]}
{...scrollPersistTaps}
>
{this.renderItem({ text: 'Terms_of_Service', route: 'terms-of-service', testID: 'legal-terms-button' })}
<Separator theme={theme} />
{this.renderItem({ text: 'Privacy_Policy', route: 'privacy-policy', testID: 'legal-privacy-button' })}
</ScrollView>
<SafeAreaView testID='legal-view'>
<StatusBar />
<List.Container>
<List.Section>
<List.Separator />
<List.Item
title='Terms_of_Service'
onPress={() => this.onPressItem({ route: 'terms-of-service' })}
testID='legal-terms-button'
showActionIndicator
/>
<List.Separator />
<List.Item
title='Privacy_Policy'
onPress={() => this.onPressItem({ route: 'privacy-policy' })}
testID='legal-privacy-button'
showActionIndicator
/>
<List.Separator />
</List.Section>
</List.Container>
</SafeAreaView>
);
}

View File

@ -151,7 +151,7 @@ const LivechatEditView = ({
keyboardVerticalOffset={128}
>
<ScrollView {...scrollPersistTaps} style={styles.container}>
<SafeAreaView theme={theme}>
<SafeAreaView>
<Title
title={visitor?.username}
theme={theme}

View File

@ -296,9 +296,8 @@ class MessagesView extends React.Component {
<SafeAreaView
style={{ backgroundColor: themes[theme].backgroundColor }}
testID={this.content.testID}
theme={theme}
>
<StatusBar theme={theme} />
<StatusBar />
<FlatList
data={messages}
renderItem={this.renderItem}

View File

@ -261,11 +261,10 @@ class NewMessageView extends React.Component {
);
}
render = () => {
const { theme } = this.props;
render() {
return (
<SafeAreaView testID='new-message-view' theme={theme}>
<StatusBar theme={theme} />
<SafeAreaView testID='new-message-view'>
<StatusBar />
{this.renderList()}
</SafeAreaView>
);

View File

@ -3,9 +3,9 @@ import { View, FlatList, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import TextInput from '../../../containers/TextInput';
import * as List from '../../../containers/List';
import { themes } from '../../../constants/colors';
import Item from './Item';
import Separator from '../../../containers/Separator';
const styles = StyleSheet.create({
container: {
@ -64,7 +64,7 @@ const ServerInput = ({
<FlatList
data={serversHistory}
renderItem={({ item }) => <Item item={item} theme={theme} onPress={() => onPressServerHistory(item)} onDelete={onDelete} />}
ItemSeparatorComponent={() => <Separator theme={theme} />}
ItemSeparatorComponent={List.Separator}
keyExtractor={item => item.id}
/>
</View>

View File

@ -1,29 +0,0 @@
import React from 'react';
import {
Text
} from 'react-native';
import PropTypes from 'prop-types';
import styles from './styles';
import { themes } from '../../constants/colors';
const Info = React.memo(({ info, theme }) => (
<Text
style={[
styles.infoText,
{
color: themes[theme].infoText,
backgroundColor: themes[theme].auxiliaryBackground
}
]}
>
{info}
</Text>
));
Info.propTypes = {
info: PropTypes.string,
theme: PropTypes.string
};
export default Info;

View File

@ -1,23 +0,0 @@
import React from 'react';
import {
View
} from 'react-native';
import PropTypes from 'prop-types';
import styles from './styles';
import { themes } from '../../constants/colors';
const SectionSeparator = React.memo(({ theme }) => (
<View
style={[
styles.sectionSeparatorBorder,
{ backgroundColor: themes[theme].auxiliaryBackground }
]}
/>
));
SectionSeparator.propTypes = {
theme: PropTypes.string
};
export default SectionSeparator;

View File

@ -1,29 +0,0 @@
import React from 'react';
import {
Text
} from 'react-native';
import PropTypes from 'prop-types';
import styles from './styles';
import { themes } from '../../constants/colors';
const SectionTitle = React.memo(({ title, theme }) => (
<Text
style={[
styles.sectionTitle,
{
backgroundColor: themes[theme].auxiliaryBackground,
color: themes[theme].infoText
}
]}
>
{title}
</Text>
));
SectionTitle.propTypes = {
title: PropTypes.string,
theme: PropTypes.string
};
export default SectionTitle;

View File

@ -1,26 +1,26 @@
import React from 'react';
import {
View, ScrollView, Switch, Text
} from 'react-native';
import { Switch, Text, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import database from '../../lib/database';
import { SWITCH_TRACK_COLOR, themes } from '../../constants/colors';
import StatusBar from '../../containers/StatusBar';
import ListItem from '../../containers/ListItem';
import Separator from '../../containers/Separator';
import * as List from '../../containers/List';
import I18n from '../../i18n';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import styles from './styles';
import RocketChat from '../../lib/rocketchat';
import { withTheme } from '../../theme';
import protectedFunction from '../../lib/methods/helpers/protectedFunction';
import SafeAreaView from '../../containers/SafeAreaView';
import log, { events, logEvent } from '../../utils/log';
import SectionTitle from './SectionTitle';
import SectionSeparator from './SectionSeparator';
import Info from './Info';
import { OPTIONS } from './options';
import sharedStyles from '../Styles';
const styles = StyleSheet.create({
pickerText: {
...sharedStyles.textRegular,
fontSize: 16
}
});
class NotificationPreferencesView extends React.Component {
static navigationOptions = () => ({
@ -132,120 +132,103 @@ class NotificationPreferencesView extends React.Component {
render() {
const { room } = this.state;
const { theme } = this.props;
return (
<SafeAreaView testID='notification-preference-view' theme={theme}>
<StatusBar theme={theme} />
<ScrollView
{...scrollPersistTaps}
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
contentContainerStyle={styles.contentContainer}
testID='notification-preference-view-list'
>
<Separator theme={theme} />
<ListItem
title={I18n.t('Receive_Notification')}
testID='notification-preference-view-receive-notification'
right={() => this.renderSwitch('disableNotifications')}
theme={theme}
/>
<Separator theme={theme} />
<Info info={I18n.t('Receive_notifications_from', { name: room.name })} theme={theme} />
<SectionSeparator theme={theme} />
<SafeAreaView testID='notification-preference-view'>
<StatusBar />
<List.Container testID='notification-preference-view-list'>
<List.Section>
<List.Separator />
<List.Item
title='Receive_Notification'
testID='notification-preference-view-receive-notification'
right={() => this.renderSwitch('disableNotifications')}
/>
<List.Separator />
<List.Info info={I18n.t('Receive_notifications_from', { name: room.name })} translateInfo={false} />
</List.Section>
<Separator theme={theme} />
<ListItem
title={I18n.t('Receive_Group_Mentions')}
testID='notification-preference-view-group-mentions'
right={() => this.renderSwitch('muteGroupMentions')}
theme={theme}
/>
<Separator theme={theme} />
<Info info={I18n.t('Receive_Group_Mentions_Info')} theme={theme} />
<List.Section>
<List.Separator />
<List.Item
title='Receive_Group_Mentions'
testID='notification-preference-view-group-mentions'
right={() => this.renderSwitch('muteGroupMentions')}
/>
<List.Separator />
<List.Info info='Receive_Group_Mentions_Info' />
</List.Section>
<SectionSeparator theme={theme} />
<Separator theme={theme} />
<ListItem
title={I18n.t('Show_Unread_Counter')}
testID='notification-preference-view-unread-count'
right={() => this.renderSwitch('hideUnreadStatus')}
theme={theme}
/>
<Separator theme={theme} />
<Info info={I18n.t('Show_Unread_Counter_Info')} theme={theme} />
<List.Section>
<List.Separator />
<List.Item
title='Show_Unread_Counter'
testID='notification-preference-view-unread-count'
right={() => this.renderSwitch('hideUnreadStatus')}
/>
<List.Separator />
<List.Info info='Show_Unread_Counter_Info' />
</List.Section>
<SectionSeparator theme={theme} />
<SectionTitle title={I18n.t('IN_APP_AND_DESKTOP')} theme={theme} />
<Separator theme={theme} />
<List.Section title='In_App_And_Desktop'>
<List.Separator />
<List.Item
title='Alert'
testID='notification-preference-view-alert'
onPress={title => this.pickerSelection(title, 'desktopNotifications')}
right={() => this.renderPickerOption('desktopNotifications')}
/>
<List.Separator />
<List.Info info='In_App_and_Desktop_Alert_info' />
</List.Section>
<ListItem
title={I18n.t('Alert')}
testID='notification-preference-view-alert'
onPress={title => this.pickerSelection(title, 'desktopNotifications')}
right={() => this.renderPickerOption('desktopNotifications')}
theme={theme}
/>
<Separator theme={theme} />
<Info info={I18n.t('In_App_and_Desktop_Alert_info')} theme={theme} />
<List.Section title='Push_Notifications'>
<List.Separator />
<List.Item
title='Alert'
testID='notification-preference-view-push-notification'
onPress={title => this.pickerSelection(title, 'mobilePushNotifications')}
right={() => this.renderPickerOption('mobilePushNotifications')}
/>
<List.Separator />
<List.Info info='Push_Notifications_Alert_Info' />
</List.Section>
<SectionSeparator theme={theme} />
<SectionTitle title={I18n.t('PUSH_NOTIFICATIONS')} theme={theme} />
<Separator theme={theme} />
<List.Section title='Desktop_Options'>
<List.Separator />
<List.Item
title='Audio'
testID='notification-preference-view-audio'
onPress={title => this.pickerSelection(title, 'audioNotifications')}
right={() => this.renderPickerOption('audioNotifications')}
/>
<List.Separator />
<List.Item
title='Sound'
testID='notification-preference-view-sound'
onPress={title => this.pickerSelection(title, 'audioNotificationValue')}
right={() => this.renderPickerOption('audioNotificationValue')}
/>
<List.Separator />
<List.Item
title='Notification_Duration'
testID='notification-preference-view-notification-duration'
onPress={title => this.pickerSelection(title, 'desktopNotificationDuration')}
right={() => this.renderPickerOption('desktopNotificationDuration')}
/>
<List.Separator />
</List.Section>
<ListItem
title={I18n.t('Alert')}
testID='notification-preference-view-push-notification'
onPress={title => this.pickerSelection(title, 'mobilePushNotifications')}
right={() => this.renderPickerOption('mobilePushNotifications')}
theme={theme}
/>
<Separator theme={theme} />
<Info info={I18n.t('Push_Notifications_Alert_Info')} theme={theme} />
<SectionSeparator theme={theme} />
<SectionTitle title={I18n.t('DESKTOP_OPTIONS')} theme={theme} />
<Separator theme={theme} />
<ListItem
title={I18n.t('Audio')}
testID='notification-preference-view-audio'
onPress={title => this.pickerSelection(title, 'audioNotifications')}
right={() => this.renderPickerOption('audioNotifications')}
theme={theme}
/>
<Separator theme={theme} />
<ListItem
title={I18n.t('Sound')}
testID='notification-preference-view-sound'
onPress={title => this.pickerSelection(title, 'audioNotificationValue')}
right={() => this.renderPickerOption('audioNotificationValue')}
theme={theme}
/>
<Separator theme={theme} />
<ListItem
title={I18n.t('Notification_Duration')}
testID='notification-preference-view-notification-duration'
onPress={title => this.pickerSelection(title, 'desktopNotificationDuration')}
right={() => this.renderPickerOption('desktopNotificationDuration')}
theme={theme}
/>
<Separator theme={theme} />
<SectionSeparator theme={theme} />
<SectionTitle title={I18n.t('EMAIL')} theme={theme} />
<Separator theme={theme} />
<ListItem
title={I18n.t('Alert')}
testID='notification-preference-view-email-alert'
onPress={title => this.pickerSelection(title, 'emailNotifications')}
right={() => this.renderPickerOption('emailNotifications')}
theme={theme}
/>
<Separator theme={theme} />
<View style={[styles.marginBottom, { backgroundColor: themes[theme].auxiliaryBackground }]} />
</ScrollView>
<List.Section title='Email'>
<List.Separator />
<List.Item
title='Alert'
testID='notification-preference-view-email-alert'
onPress={title => this.pickerSelection(title, 'emailNotifications')}
right={() => this.renderPickerOption('emailNotifications')}
/>
<List.Separator />
</List.Section>
</List.Container>
</SafeAreaView>
);
}

View File

@ -1,31 +0,0 @@
import { StyleSheet } from 'react-native';
import sharedStyles from '../Styles';
export default StyleSheet.create({
sectionSeparatorBorder: {
height: 10
},
marginBottom: {
height: 30
},
contentContainer: {
marginVertical: 10
},
infoText: {
...sharedStyles.textRegular,
fontSize: 13,
paddingHorizontal: 15,
paddingVertical: 10
},
sectionTitle: {
...sharedStyles.separatorBottom,
paddingHorizontal: 15,
paddingVertical: 10,
fontSize: 14
},
pickerText: {
...sharedStyles.textRegular,
fontSize: 16
}
});

View File

@ -10,15 +10,11 @@ import { themes } from '../constants/colors';
import debounce from '../utils/debounce';
import sharedStyles from './Styles';
import ListItem from '../containers/ListItem';
import Check from '../containers/Check';
import Separator from '../containers/Separator';
import * as List from '../containers/List';
import SearchBox from '../containers/SearchBox';
import SafeAreaView from '../containers/SafeAreaView';
const styles = StyleSheet.create({
check: {
marginHorizontal: 0
},
search: {
width: '100%',
height: 56
@ -28,10 +24,6 @@ const styles = StyleSheet.create({
paddingVertical: 56,
...sharedStyles.textAlignCenter,
...sharedStyles.textSemibold
},
withoutBorder: {
borderBottomWidth: 0,
borderTopWidth: 0
}
});
@ -41,11 +33,11 @@ const Item = React.memo(({
onItemPress,
theme
}) => (
<ListItem
<List.Item
title={I18n.t(item.label, { defaultValue: item.label, second: item?.second })}
right={selected && (() => <Check theme={theme} style={styles.check} />)}
right={selected && (() => <List.Icon name='check' color={themes[theme].tintColor} />)}
onPress={onItemPress}
theme={theme}
translateTitle={false}
/>
));
Item.propTypes = {
@ -109,7 +101,7 @@ class PickerView extends React.PureComponent {
const { theme } = this.props;
return (
<>
<SafeAreaView>
{this.renderSearch()}
<FlatList
data={data}
@ -122,19 +114,13 @@ class PickerView extends React.PureComponent {
onItemPress={() => this.onChangeValue(item.value)}
/>
)}
ItemSeparatorComponent={() => <Separator theme={theme} />}
ItemSeparatorComponent={List.Separator}
ListHeaderComponent={List.Separator}
ListFooterComponent={List.Separator}
ListEmptyComponent={() => <Text style={[styles.noResult, { color: themes[theme].titleText }]}>{I18n.t('No_results_found')}</Text>}
contentContainerStyle={[
sharedStyles.listContentContainer,
{
backgroundColor: themes[theme].auxiliaryBackground,
borderColor: themes[theme].separatorColor
},
!data.length && styles.withoutBorder
]}
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
contentContainerStyle={[List.styles.contentContainerStyleFlatList]}
/>
</>
</SafeAreaView>
);
}
}

View File

@ -469,8 +469,8 @@ class ProfileView extends React.Component {
contentContainerStyle={sharedStyles.container}
keyboardVerticalOffset={128}
>
<StatusBar theme={theme} />
<SafeAreaView testID='profile-view' theme={theme}>
<StatusBar />
<SafeAreaView testID='profile-view'>
<ScrollView
contentContainerStyle={sharedStyles.containerScrollView}
testID='profile-view-list'

View File

@ -135,8 +135,8 @@ class ReadReceiptView extends React.Component {
}
return (
<SafeAreaView testID='read-receipt-view' theme={theme}>
<StatusBar theme={theme} />
<SafeAreaView testID='read-receipt-view'>
<StatusBar />
{loading
? <ActivityIndicator theme={theme} />
: (

View File

@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
View, SectionList, Text, Alert, Share, Switch
View, Text, Alert, Share, Switch
} from 'react-native';
import { connect } from 'react-redux';
import _ from 'lodash';
@ -13,24 +13,22 @@ import styles from './styles';
import sharedStyles from '../Styles';
import Avatar from '../../containers/Avatar';
import Status from '../../containers/Status';
import * as List from '../../containers/List';
import RocketChat from '../../lib/rocketchat';
import log, { logEvent, events } from '../../utils/log';
import RoomTypeIcon from '../../containers/RoomTypeIcon';
import I18n from '../../i18n';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import { CustomIcon } from '../../lib/Icons';
import DisclosureIndicator from '../../containers/DisclosureIndicator';
import StatusBar from '../../containers/StatusBar';
import { themes, SWITCH_TRACK_COLOR } from '../../constants/colors';
import { withTheme } from '../../theme';
import { CloseModalButton } from '../../containers/HeaderButton';
import { getUserSelector } from '../../selectors/login';
import Markdown from '../../containers/markdown';
import { showConfirmationAlert, showErrorAlert } from '../../utils/info';
import SafeAreaView from '../../containers/SafeAreaView';
import { E2E_ROOM_TYPES } from '../../lib/encryption/constants';
import protectedFunction from '../../lib/methods/helpers/protectedFunction';
import database from '../../lib/database';
import { withDimensions } from '../../dimensions';
class RoomActionsView extends React.Component {
static navigationOptions = ({ navigation, isMasterDetail }) => {
@ -44,19 +42,15 @@ class RoomActionsView extends React.Component {
}
static propTypes = {
baseUrl: PropTypes.string,
navigation: PropTypes.object,
route: PropTypes.object,
user: PropTypes.shape({
id: PropTypes.string,
token: PropTypes.string
}),
leaveRoom: PropTypes.func,
jitsiEnabled: PropTypes.bool,
e2eEnabled: PropTypes.bool,
setLoadingInvite: PropTypes.func,
closeRoom: PropTypes.func,
theme: PropTypes.string
theme: PropTypes.string,
fontScale: PropTypes.number
}
constructor(props) {
@ -240,281 +234,6 @@ class RoomActionsView extends React.Component {
}
}
get sections() {
const {
room, member, membersCount, canViewMembers, canAddUser, canInviteUser, joined, canAutoTranslate, canForwardGuest, canReturnQueue, canEdit
} = this.state;
const { jitsiEnabled, e2eEnabled } = this.props;
const {
rid, t, blocker, encrypted
} = room;
const isGroupChat = RocketChat.isGroupChat(room);
const notificationsAction = {
icon: 'notification',
name: I18n.t('Notifications'),
route: 'NotificationPrefView',
params: { rid, room },
testID: 'room-actions-notifications',
right: this.renderDisclosure
};
const jitsiActions = jitsiEnabled ? [
{
icon: 'phone',
name: I18n.t('Voice_call'),
event: () => RocketChat.callJitsi(rid, true),
testID: 'room-actions-voice',
right: this.renderDisclosure
},
{
icon: 'camera',
name: I18n.t('Video_call'),
event: () => RocketChat.callJitsi(rid),
testID: 'room-actions-video',
right: this.renderDisclosure
}
] : [];
const sections = [{
data: [{
icon: 'star',
name: I18n.t('Room_Info'),
route: 'RoomInfoView',
// forward room only if room isn't joined
params: {
rid, t, room, member
},
disabled: isGroupChat,
testID: 'room-actions-info'
}],
renderItem: this.renderRoomInfo
}, {
data: jitsiActions,
renderItem: this.renderItem
}, {
data: [
{
icon: 'attach',
name: I18n.t('Files'),
route: 'MessagesView',
params: { rid, t, name: 'Files' },
testID: 'room-actions-files',
right: this.renderDisclosure
},
{
icon: 'mention',
name: I18n.t('Mentions'),
route: 'MessagesView',
params: { rid, t, name: 'Mentions' },
testID: 'room-actions-mentioned',
right: this.renderDisclosure
},
{
icon: 'star',
name: I18n.t('Starred'),
route: 'MessagesView',
params: { rid, t, name: 'Starred' },
testID: 'room-actions-starred',
right: this.renderDisclosure
},
{
icon: 'search',
name: I18n.t('Search'),
route: 'SearchMessagesView',
params: { rid, encrypted },
testID: 'room-actions-search',
right: this.renderDisclosure
},
{
icon: 'share',
name: I18n.t('Share'),
event: this.handleShare,
testID: 'room-actions-share',
right: this.renderDisclosure
},
{
icon: 'pin',
name: I18n.t('Pinned'),
route: 'MessagesView',
params: { rid, t, name: 'Pinned' },
testID: 'room-actions-pinned',
right: this.renderDisclosure
}
],
renderItem: this.renderItem
}];
if (canAutoTranslate) {
sections[2].data.push({
icon: 'language',
name: I18n.t('Auto_Translate'),
route: 'AutoTranslateView',
params: { rid, room },
testID: 'room-actions-auto-translate',
right: this.renderDisclosure
});
}
if (isGroupChat) {
sections[2].data.unshift({
icon: 'team',
name: I18n.t('Members'),
description: membersCount > 0 ? `${ membersCount } ${ I18n.t('members') }` : null,
route: 'RoomMembersView',
params: { rid, room },
testID: 'room-actions-members',
right: this.renderDisclosure
});
}
if (t === 'd' && !isGroupChat) {
sections.push({
data: [
{
icon: 'ban',
name: I18n.t(`${ blocker ? 'Unblock' : 'Block' }_user`),
type: 'danger',
event: this.toggleBlockUser,
testID: 'room-actions-block-user',
right: this.renderDisclosure
}
],
renderItem: this.renderItem
});
sections[2].data.push(notificationsAction);
} else if (t === 'c' || t === 'p') {
const actions = [];
if (canViewMembers) {
actions.push({
icon: 'team',
name: I18n.t('Members'),
description: membersCount > 0 ? `${ membersCount } ${ I18n.t('members') }` : null,
route: 'RoomMembersView',
params: { rid, room },
testID: 'room-actions-members',
right: this.renderDisclosure
});
}
if (canAddUser) {
actions.push({
icon: 'add',
name: I18n.t('Add_users'),
route: 'SelectedUsersView',
params: {
rid,
title: I18n.t('Add_users'),
nextAction: this.addUser
},
testID: 'room-actions-add-user',
right: this.renderDisclosure
});
}
if (canInviteUser) {
actions.push({
icon: 'user-add',
name: I18n.t('Invite_users'),
route: 'InviteUsersView',
params: {
rid
},
testID: 'room-actions-invite-user',
right: this.renderDisclosure
});
}
sections[2].data = [...actions, ...sections[2].data];
if (joined) {
sections[2].data.push(notificationsAction);
sections.push({
data: [
{
icon: 'logout',
name: I18n.t('Leave_channel'),
type: 'danger',
event: this.leaveChannel,
testID: 'room-actions-leave-channel',
right: this.renderDisclosure
}
],
renderItem: this.renderItem
});
}
} else if (t === 'l') {
sections[2].data = [];
if (!this.isOmnichannelPreview) {
sections[2].data.push({
icon: 'close',
name: I18n.t('Close'),
event: this.closeLivechat,
right: this.renderDisclosure
});
if (canForwardGuest) {
sections[2].data.push({
icon: 'user-forward',
name: I18n.t('Forward'),
route: 'ForwardLivechatView',
params: { rid },
right: this.renderDisclosure
});
}
if (canReturnQueue) {
sections[2].data.push({
icon: 'undo',
name: I18n.t('Return'),
event: this.returnLivechat,
right: this.renderDisclosure
});
}
sections[2].data.push({
icon: 'history',
name: I18n.t('Navigation_history'),
route: 'VisitorNavigationView',
params: { rid },
right: this.renderDisclosure
});
}
sections.push({
data: [notificationsAction],
renderItem: this.renderItem
});
}
// If can edit this room
// If this room type can be Encrypted
// If e2e is enabled for this server
if (canEdit && E2E_ROOM_TYPES[t] && e2eEnabled) {
sections.splice(2, 0, {
data: [{
icon: 'encrypted',
name: I18n.t('Encrypted'),
testID: 'room-actions-encrypt',
right: this.renderEncryptedSwitch
}],
renderItem: this.renderItem
});
}
return sections;
}
renderDisclosure = () => {
const { theme } = this.props;
return <DisclosureIndicator theme={theme} />;
}
renderSeparator = () => {
const { theme } = this.props;
return <View style={[styles.separator, { backgroundColor: themes[theme].separatorColor }]} />;
}
renderEncryptedSwitch = () => {
const { room } = this.state;
const { encrypted } = room;
@ -523,7 +242,6 @@ class RoomActionsView extends React.Component {
value={encrypted}
trackColor={SWITCH_TRACK_COLOR}
onValueChange={this.toggleEncrypted}
style={styles.encryptedSwitch}
/>
);
}
@ -667,128 +385,467 @@ class RoomActionsView extends React.Component {
);
}
renderRoomInfo = ({ item }) => {
renderRoomInfo = () => {
const { room, member } = this.state;
const {
name,
t,
rid,
topic
rid, name, t, topic
} = room;
const { theme } = this.props;
const { theme, fontScale } = this.props;
const avatar = RocketChat.getRoomAvatar(room);
const isGroupChat = RocketChat.isGroupChat(room);
return (
this.renderTouchableItem((
<>
<Avatar
text={avatar}
style={styles.avatar}
size={50}
type={t}
rid={rid}
>
{t === 'd' && member._id ? <Status style={sharedStyles.status} id={member._id} /> : null }
</Avatar>
<View style={[styles.roomTitleContainer, item.disabled && styles.roomTitlePadding]}>
{room.t === 'd'
? <Text style={[styles.roomTitle, { color: themes[theme].titleText }]} numberOfLines={1}>{room.fname}</Text>
: (
<View style={styles.roomTitleRow}>
<RoomTypeIcon type={room.prid ? 'discussion' : room.t} status={room.visitor?.status} theme={theme} />
<Text style={[styles.roomTitle, { color: themes[theme].titleText }]} numberOfLines={1}>{RocketChat.getRoomTitle(room)}</Text>
</View>
)
<List.Section>
<List.Separator />
<Touch
onPress={() => this.onPressTouchable({
route: 'RoomInfoView',
// forward room only if room isn't joined
params: {
rid, t, room, member
}
<Markdown
preview
msg={t === 'd' ? `@${ name }` : topic}
style={[styles.roomDescription, { color: themes[theme].auxiliaryText }]}
numberOfLines={1}
theme={theme}
/>
{room.t === 'd' && <Markdown msg={member.statusText} style={[styles.roomDescription, { color: themes[theme].auxiliaryText }]} preview theme={theme} numberOfLines={1} />}
})}
style={{ backgroundColor: themes[theme].backgroundColor }}
accessibilityLabel={I18n.t('Room_Info')}
accessibilityTraits='button'
enabled={!isGroupChat}
testID='room-actions-info'
theme={theme}
>
<View style={[styles.roomInfoContainer, { height: 72 * fontScale }]}>
<Avatar
text={avatar}
style={styles.avatar}
size={50}
type={t}
rid={rid}
>
{t === 'd' && member._id ? <Status style={sharedStyles.status} id={member._id} /> : null }
</Avatar>
<View style={styles.roomTitleContainer}>
{room.t === 'd'
? <Text style={[styles.roomTitle, { color: themes[theme].titleText }]} numberOfLines={1}>{room.fname}</Text>
: (
<View style={styles.roomTitleRow}>
<RoomTypeIcon type={room.prid ? 'discussion' : room.t} status={room.visitor?.status} theme={theme} />
<Text style={[styles.roomTitle, { color: themes[theme].titleText }]} numberOfLines={1}>{RocketChat.getRoomTitle(room)}</Text>
</View>
)
}
<Markdown
preview
msg={t === 'd' ? `@${ name }` : topic}
style={[styles.roomDescription, { color: themes[theme].auxiliaryText }]}
numberOfLines={1}
theme={theme}
/>
{room.t === 'd' && <Markdown msg={member.statusText} style={[styles.roomDescription, { color: themes[theme].auxiliaryText }]} preview theme={theme} numberOfLines={1} />}
</View>
{isGroupChat ? null : <List.Icon name='chevron-right' />}
</View>
{!item.disabled && <DisclosureIndicator theme={theme} />}
</>
), item)
</Touch>
<List.Separator />
</List.Section>
);
}
renderTouchableItem = (subview, item) => {
const { theme } = this.props;
return (
<Touch
onPress={() => this.onPressTouchable(item)}
style={{ backgroundColor: themes[theme].backgroundColor }}
accessibilityLabel={item.name}
accessibilityTraits='button'
enabled={!item.disabled}
testID={item.testID}
theme={theme}
>
<View style={styles.sectionItem}>
{subview}
</View>
</Touch>
);
}
renderItem = ({ item }) => {
const { theme } = this.props;
const colorDanger = { color: themes[theme].dangerColor };
const subview = item.type === 'danger' ? (
<>
<CustomIcon name={item.icon} size={24} style={[styles.sectionItemIcon, colorDanger]} />
<Text style={[styles.sectionItemName, colorDanger]}>{ item.name }</Text>
</>
) : (
<>
<CustomIcon name={item.icon} size={24} style={[styles.sectionItemIcon, { color: themes[theme].bodyText }]} />
<Text style={[styles.sectionItemName, { color: themes[theme].bodyText }]}>{ item.name }</Text>
{item.description ? <Text style={[styles.sectionItemDescription, { color: themes[theme].auxiliaryText }]}>{ item.description }</Text> : null}
{item?.right?.()}
</>
);
return this.renderTouchableItem(subview, item);
}
renderSectionSeparator = (data) => {
const { theme } = this.props;
if (data.trailingItem) {
return <View style={[styles.sectionSeparator, data.leadingSection && styles.sectionSeparatorBorder, { backgroundColor: themes[theme].auxiliaryBackground, borderColor: themes[theme].separatorColor }]} />;
renderJitsi = () => {
const { room } = this.state;
const { jitsiEnabled } = this.props;
if (!jitsiEnabled) {
return null;
}
if (!data.trailingSection) {
return <View style={[styles.sectionSeparatorBorder, { backgroundColor: themes[theme].auxiliaryBackground, borderColor: themes[theme].separatorColor }]} />;
return (
<List.Section>
<List.Separator />
<List.Item
title='Voice_call'
onPress={() => RocketChat.callJitsi(room?.rid, true)}
testID='room-actions-voice'
left={() => <List.Icon name='phone' />}
showActionIndicator
/>
<List.Separator />
<List.Item
title='Video_call'
onPress={() => RocketChat.callJitsi(room?.rid)}
testID='room-actions-video'
left={() => <List.Icon name='camera' />}
showActionIndicator
/>
<List.Separator />
</List.Section>
);
}
renderE2EEncryption = () => {
const {
room, canEdit
} = this.state;
const { e2eEnabled } = this.props;
// If can edit this room
// If this room type can be Encrypted
// If e2e is enabled for this server
if (canEdit && E2E_ROOM_TYPES[room?.t] && e2eEnabled) {
return (
<List.Section>
<List.Separator />
<List.Item
title='Encrypted'
testID='room-actions-encrypt'
left={() => <List.Icon name='encrypted' />}
right={this.renderEncryptedSwitch}
/>
<List.Separator />
</List.Section>
);
}
return null;
}
render() {
renderLastSection = () => {
const { room, joined } = this.state;
const { theme } = this.props;
const { t, blocker } = room;
if (!joined || t === 'l') {
return null;
}
if (t === 'd') {
return (
<List.Section>
<List.Separator />
<List.Item
title={`${ blocker ? 'Unblock' : 'Block' }_user`}
onPress={() => this.onPressTouchable({
event: this.toggleBlockUser
})}
testID='room-actions-block-user'
left={() => <List.Icon name='ban' color={themes[theme].dangerColor} />}
showActionIndicator
color={themes[theme].dangerColor}
/>
<List.Separator />
</List.Section>
);
}
if (t === 'p' || t === 'c') {
return (
<List.Section>
<List.Separator />
<List.Item
title='Leave_channel'
onPress={() => this.onPressTouchable({
event: this.leaveChannel
})}
testID='room-actions-leave-channel'
left={() => <List.Icon name='logout' color={themes[theme].dangerColor} />}
showActionIndicator
color={themes[theme].dangerColor}
/>
<List.Separator />
</List.Section>
);
}
}
render() {
const {
room, membersCount, canViewMembers, canAddUser, canInviteUser, joined, canAutoTranslate, canForwardGuest, canReturnQueue
} = this.state;
const {
rid, t, encrypted
} = room;
const isGroupChat = RocketChat.isGroupChat(room);
return (
<SafeAreaView testID='room-actions-view' theme={theme}>
<StatusBar theme={theme} />
<SectionList
contentContainerStyle={[styles.contentContainer, { backgroundColor: themes[theme].auxiliaryBackground }]}
style={[styles.container, { backgroundColor: themes[theme].auxiliaryBackground }]}
stickySectionHeadersEnabled={false}
sections={this.sections}
SectionSeparatorComponent={this.renderSectionSeparator}
ItemSeparatorComponent={this.renderSeparator}
keyExtractor={item => item.name}
testID='room-actions-list'
{...scrollPersistTaps}
/>
<SafeAreaView testID='room-actions-view'>
<StatusBar />
<List.Container>
{this.renderRoomInfo()}
{this.renderJitsi()}
{this.renderE2EEncryption()}
<List.Section>
<List.Separator />
{(['c', 'p'].includes(t) && canViewMembers) || isGroupChat
? (
<>
<List.Item
title='Members'
subtitle={membersCount > 0 ? `${ membersCount } ${ I18n.t('members') }` : null}
onPress={() => this.onPressTouchable({ route: 'RoomMembersView', params: { rid, room } })}
testID='room-actions-members'
left={() => <List.Icon name='team' />}
showActionIndicator
translateSubtitle={false}
/>
<List.Separator />
</>
)
: null}
{['c', 'p'].includes(t) && canAddUser
? (
<>
<List.Item
title='Add_users'
onPress={() => this.onPressTouchable({
route: 'SelectedUsersView',
params: {
rid,
title: I18n.t('Add_users'),
nextAction: this.addUser
}
})}
testID='room-actions-add-user'
left={() => <List.Icon name='add' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
{['c', 'p'].includes(t) && canInviteUser
? (
<>
<List.Item
title='Invite_users'
onPress={() => this.onPressTouchable({
route: 'InviteUsersView',
params: { rid }
})}
testID='room-actions-invite-user'
left={() => <List.Icon name='user-add' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
{['c', 'p', 'd'].includes(t)
? (
<>
<List.Item
title='Files'
onPress={() => this.onPressTouchable({
route: 'MessagesView',
params: { rid, t, name: 'Files' }
})}
testID='room-actions-files'
left={() => <List.Icon name='attach' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
{['c', 'p', 'd'].includes(t)
? (
<>
<List.Item
title='Mentions'
onPress={() => this.onPressTouchable({
route: 'MessagesView',
params: { rid, t, name: 'Mentions' }
})}
testID='room-actions-mentioned'
left={() => <List.Icon name='mention' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
{['c', 'p', 'd'].includes(t)
? (
<>
<List.Item
title='Starred'
onPress={() => this.onPressTouchable({
route: 'MessagesView',
params: { rid, t, name: 'Starred' }
})}
testID='room-actions-starred'
left={() => <List.Icon name='star' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
{['c', 'p', 'd'].includes(t)
? (
<>
<List.Item
title='Search'
onPress={() => this.onPressTouchable({
route: 'SearchMessagesView',
params: { rid, encrypted }
})}
testID='room-actions-search'
left={() => <List.Icon name='search' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
{['c', 'p', 'd'].includes(t)
? (
<>
<List.Item
title='Share'
onPress={() => this.onPressTouchable({
event: this.handleShare
})}
testID='room-actions-share'
left={() => <List.Icon name='share' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
{['c', 'p', 'd'].includes(t)
? (
<>
<List.Item
title='Pinned'
onPress={() => this.onPressTouchable({
route: 'MessagesView',
params: { rid, t, name: 'Pinned' }
})}
testID='room-actions-pinned'
left={() => <List.Icon name='pin' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
{['c', 'p', 'd'].includes(t) && canAutoTranslate
? (
<>
<List.Item
title='Auto_Translate'
onPress={() => this.onPressTouchable({
route: 'AutoTranslateView',
params: { rid, room }
})}
testID='room-actions-auto-translate'
left={() => <List.Icon name='language' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
{['c', 'p', 'd'].includes(t) && joined
? (
<>
<List.Item
title='Notifications'
onPress={() => this.onPressTouchable({
route: 'NotificationPrefView',
params: { rid, room }
})}
testID='room-actions-notifications'
left={() => <List.Icon name='notification' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
{['l'].includes(t) && !this.isOmnichannelPreview
? (
<>
<List.Item
title='Close'
onPress={() => this.onPressTouchable({
event: this.closeLivechat
})}
left={() => <List.Icon name='close' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
{['l'].includes(t) && !this.isOmnichannelPreview && canForwardGuest
? (
<>
<List.Item
title='Forward'
onPress={() => this.onPressTouchable({
route: 'ForwardLivechatView',
params: { rid }
})}
left={() => <List.Icon name='user-forward' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
{['l'].includes(t) && !this.isOmnichannelPreview && canReturnQueue
? (
<>
<List.Item
title='Return'
onPress={() => this.onPressTouchable({
event: this.returnLivechat
})}
left={() => <List.Icon name='undo' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
{['l'].includes(t) && !this.isOmnichannelPreview
? (
<>
<List.Item
title='Navigation_history'
onPress={() => this.onPressTouchable({
route: 'VisitorNavigationView',
params: { rid }
})}
left={() => <List.Icon name='history' />}
showActionIndicator
/>
<List.Separator />
</>
)
: null}
</List.Section>
{this.renderLastSection()}
</List.Container>
</SafeAreaView>
);
}
}
const mapStateToProps = state => ({
user: getUserSelector(state),
baseUrl: state.server.server,
jitsiEnabled: state.settings.Jitsi_Enabled || false,
e2eEnabled: state.settings.E2E_Enable || false
});
@ -799,4 +856,4 @@ const mapDispatchToProps = dispatch => ({
setLoadingInvite: loading => dispatch(setLoadingAction(loading))
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(RoomActionsView));
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(withDimensions(RoomActionsView)));

View File

@ -1,51 +1,20 @@
import { StyleSheet } from 'react-native';
import { PADDING_HORIZONTAL } from '../../containers/List/constants';
import sharedStyles from '../Styles';
export default StyleSheet.create({
contentContainer: {
paddingBottom: 30
},
container: {
flex: 1
},
sectionItem: {
paddingVertical: 11,
roomInfoContainer: {
paddingHorizontal: PADDING_HORIZONTAL,
flexDirection: 'row',
alignItems: 'center'
},
sectionItemIcon: {
width: 56,
textAlign: 'center'
},
sectionItemName: {
flex: 1,
fontSize: 14,
...sharedStyles.textRegular
},
sectionItemDescription: {
fontSize: 14,
...sharedStyles.textRegular
},
separator: {
height: StyleSheet.hairlineWidth
},
sectionSeparator: {
borderBottomWidth: StyleSheet.hairlineWidth,
height: 36
},
sectionSeparatorBorder: {
borderTopWidth: StyleSheet.hairlineWidth
},
avatar: {
marginHorizontal: 16
marginRight: PADDING_HORIZONTAL
},
roomTitleContainer: {
flex: 1
},
roomTitlePadding: {
paddingRight: 16
},
roomTitle: {
fontSize: 16,
...sharedStyles.textMedium
@ -58,8 +27,5 @@ export default StyleSheet.create({
paddingRight: 16,
flexDirection: 'row',
alignItems: 'center'
},
encryptedSwitch: {
marginHorizontal: 16
}
});

View File

@ -418,10 +418,9 @@ class RoomInfoEditView extends React.Component {
contentContainerStyle={sharedStyles.container}
keyboardVerticalOffset={128}
>
<StatusBar theme={theme} />
<StatusBar />
<SafeAreaView
testID='room-info-edit-view'
theme={theme}
style={{ backgroundColor: themes[theme].backgroundColor }}
>
<ScrollView

View File

@ -350,10 +350,9 @@ class RoomInfoView extends React.Component {
const { theme } = this.props;
return (
<ScrollView style={[styles.scroll, { backgroundColor: themes[theme].backgroundColor }]}>
<StatusBar theme={theme} />
<StatusBar />
<SafeAreaView
style={{ backgroundColor: themes[theme].backgroundColor }}
theme={theme}
testID='room-info-view'
>
<View style={[styles.avatarContainer, this.isDirect && styles.avatarContainerDirectRoom, { backgroundColor: themes[theme].auxiliaryBackground }]}>

View File

@ -250,8 +250,8 @@ class RoomMembersView extends React.Component {
} = this.state;
const { theme } = this.props;
return (
<SafeAreaView testID='room-members-view' theme={theme}>
<StatusBar theme={theme} />
<SafeAreaView testID='room-members-view'>
<StatusBar />
<FlatList
data={filtering ? membersFiltered : members}
renderItem={this.renderItem}

View File

@ -1020,9 +1020,8 @@ class RoomView extends React.Component {
<SafeAreaView
style={{ backgroundColor: themes[theme].backgroundColor }}
testID='room-view'
theme={theme}
>
<StatusBar theme={theme} />
<StatusBar />
<Banner
rid={rid}
title={I18n.t('Announcement')}

View File

@ -1008,8 +1008,8 @@ class RoomsListView extends React.Component {
} = this.props;
return (
<SafeAreaView testID='rooms-list-view' theme={theme} style={{ backgroundColor: themes[theme].backgroundColor }}>
<StatusBar theme={theme} />
<SafeAreaView testID='rooms-list-view' style={{ backgroundColor: themes[theme].backgroundColor }}>
<StatusBar />
{this.renderHeader()}
{this.renderScroll()}
{showSortDropdown ? (

View File

@ -1,32 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import { StyleSheet, Switch, ScrollView } from 'react-native';
import { Switch } from 'react-native';
import { connect } from 'react-redux';
import I18n from '../i18n';
import { withTheme } from '../theme';
import { themes, SWITCH_TRACK_COLOR } from '../constants/colors';
import StatusBar from '../containers/StatusBar';
import Separator from '../containers/Separator';
import ListItem from '../containers/ListItem';
import ItemInfo from '../containers/ItemInfo';
import { CustomIcon } from '../lib/Icons';
import * as List from '../containers/List';
import database from '../lib/database';
import { supportedBiometryLabel, changePasscode, checkHasPasscode } from '../utils/localAuthentication';
import { DisclosureImage } from '../containers/DisclosureIndicator';
import { DEFAULT_AUTO_LOCK } from '../constants/localAuthentication';
import SafeAreaView from '../containers/SafeAreaView';
import { events, logEvent } from '../utils/log';
const styles = StyleSheet.create({
listPadding: {
paddingVertical: 36
},
emptySpace: {
marginTop: 36
}
});
const DEFAULT_BIOMETRY = false;
class ScreenLockConfigView extends React.Component {
@ -161,29 +148,23 @@ class ScreenLockConfigView extends React.Component {
this.setState({ autoLockTime }, () => this.save());
}
renderSeparator = () => {
const { theme } = this.props;
return <Separator theme={theme} />;
}
renderIcon = () => {
const { theme } = this.props;
return <CustomIcon name='check' size={20} color={themes[theme].tintColor} />;
return <List.Icon name='check' color={themes[theme].tintColor} />;
}
renderItem = ({ item }) => {
const { theme } = this.props;
const { title, value, disabled } = item;
return (
<>
<ListItem
<List.Item
title={title}
onPress={() => this.changeAutoLockTime(value)}
right={this.isSelected(value) ? this.renderIcon : null}
theme={theme}
disabled={disabled}
translateTitle={false}
/>
<Separator theme={theme} />
<List.Separator />
</>
);
}
@ -214,7 +195,7 @@ class ScreenLockConfigView extends React.Component {
renderAutoLockItems = () => {
const { autoLock, autoLockTime } = this.state;
const { theme, Force_Screen_Lock_After, Force_Screen_Lock } = this.props;
const { Force_Screen_Lock_After, Force_Screen_Lock } = this.props;
if (!autoLock) {
return null;
}
@ -233,75 +214,62 @@ class ScreenLockConfigView extends React.Component {
});
}
return (
<>
<Separator style={styles.emptySpace} theme={theme} />
<List.Section>
<List.Separator />
{items.map(item => this.renderItem({ item }))}
</>
</List.Section>
);
}
renderDisclosure = () => {
const { theme } = this.props;
return <DisclosureImage theme={theme} />;
}
renderBiometry = () => {
const { autoLock, biometryLabel } = this.state;
const { theme } = this.props;
if (!autoLock || !biometryLabel) {
return null;
}
return (
<>
<Separator theme={theme} />
<ListItem
<List.Section>
<List.Separator />
<List.Item
title={I18n.t('Local_authentication_unlock_with_label', { label: biometryLabel })}
right={() => this.renderBiometrySwitch()}
theme={theme}
translateTitle={false}
/>
<Separator theme={theme} />
</>
<List.Separator />
</List.Section>
);
}
render() {
const { autoLock } = this.state;
const { theme } = this.props;
return (
<SafeAreaView theme={theme}>
<StatusBar theme={theme} />
<ScrollView
keyExtractor={item => item.value}
contentContainerStyle={styles.listPadding}
>
<Separator theme={theme} />
<ListItem
title={I18n.t('Local_authentication_unlock_option')}
right={() => this.renderAutoLockSwitch()}
theme={theme}
/>
{autoLock
? (
<>
<Separator theme={theme} />
<ListItem
title={I18n.t('Local_authentication_change_passcode')}
theme={theme}
right={this.renderDisclosure}
onPress={this.changePasscode}
/>
</>
)
: null
}
<Separator theme={theme} />
<ItemInfo
info={I18n.t('Local_authentication_info')}
theme={theme}
/>
<SafeAreaView>
<StatusBar />
<List.Container>
<List.Section>
<List.Separator />
<List.Item
title='Local_authentication_unlock_option'
right={() => this.renderAutoLockSwitch()}
/>
{autoLock
? (
<>
<List.Separator />
<List.Item
title='Local_authentication_change_passcode'
onPress={this.changePasscode}
showActionIndicator
/>
</>
)
: null
}
<List.Separator />
<List.Info info='Local_authentication_info' />
</List.Section>
{this.renderBiometry()}
{this.renderAutoLockItems()}
</ScrollView>
</List.Container>
</SafeAreaView>
);
}

View File

@ -183,8 +183,8 @@ class SearchMessagesView extends React.Component {
render() {
const { theme } = this.props;
return (
<SafeAreaView style={{ backgroundColor: themes[theme].backgroundColor }} testID='search-messages-view' theme={theme}>
<StatusBar theme={theme} />
<SafeAreaView style={{ backgroundColor: themes[theme].backgroundColor }} testID='search-messages-view'>
<StatusBar />
<View style={styles.searchContainer}>
<RCTextInput
autoFocus

View File

@ -83,8 +83,8 @@ class SelectServerView extends React.Component {
const { servers } = this.state;
const { theme } = this.props;
return (
<SafeAreaView theme={theme}>
<StatusBar theme={theme} />
<SafeAreaView>
<StatusBar />
<View style={[styles.list, { borderColor: themes[theme].separatorColor }]}>
<FlatList
data={servers}

View File

@ -307,10 +307,10 @@ class SelectedUsersView extends React.Component {
}
render = () => {
const { loading, theme } = this.props;
const { loading } = this.props;
return (
<SafeAreaView testID='select-users-view' theme={theme}>
<StatusBar theme={theme} />
<SafeAreaView testID='select-users-view'>
<StatusBar />
{this.renderList()}
<Loading visible={loading} />
</SafeAreaView>

View File

@ -104,9 +104,9 @@ class SetUsernameView extends React.Component {
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
contentContainerStyle={sharedStyles.container}
>
<StatusBar theme={theme} />
<StatusBar />
<ScrollView {...scrollPersistTaps} contentContainerStyle={sharedStyles.containerScrollView}>
<SafeAreaView testID='set-username-view' theme={theme}>
<SafeAreaView testID='set-username-view'>
<Text
style={[
sharedStyles.loginTitle,

View File

@ -1,6 +1,6 @@
import React from 'react';
import {
View, Linking, ScrollView, Switch, Share, Clipboard
Linking, Switch, Share, Clipboard
} from 'react-native';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
@ -14,19 +14,14 @@ import { toggleCrashReport as toggleCrashReportAction, toggleAnalyticsEvents as
import { SWITCH_TRACK_COLOR, themes } from '../../constants/colors';
import { DrawerButton, CloseModalButton } from '../../containers/HeaderButton';
import StatusBar from '../../containers/StatusBar';
import ListItem from '../../containers/ListItem';
import ItemInfo from '../../containers/ItemInfo';
import { DisclosureImage } from '../../containers/DisclosureIndicator';
import Separator from '../../containers/Separator';
import * as List from '../../containers/List';
import I18n from '../../i18n';
import RocketChat, { CRASH_REPORT_KEY, ANALYTICS_EVENTS_KEY } from '../../lib/rocketchat';
import {
getReadableVersion, getDeviceModel, isAndroid
} from '../../utils/deviceInfo';
import openLink from '../../utils/openLink';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import { showErrorAlert, showConfirmationAlert } from '../../utils/info';
import styles from './styles';
import {
loggerConfig, analytics, logEvent, events
} from '../../utils/log';
@ -44,22 +39,6 @@ import database from '../../lib/database';
import { isFDroidBuild } from '../../constants/environment';
import { getUserSelector } from '../../selectors/login';
const SectionSeparator = React.memo(({ theme }) => (
<View
style={[
styles.sectionSeparatorBorder,
{
borderColor: themes[theme].separatorColor,
backgroundColor: themes[theme].auxiliaryBackground
}
]}
/>
));
SectionSeparator.propTypes = {
theme: PropTypes.string
};
class SettingsView extends React.Component {
static navigationOptions = ({ navigation, isMasterDetail }) => ({
headerLeft: () => (isMasterDetail ? (
@ -223,11 +202,6 @@ class SettingsView extends React.Component {
openLink(LICENSE_LINK, theme);
}
renderDisclosure = () => {
const { theme } = this.props;
return <DisclosureImage theme={theme} />;
}
renderCrashReportSwitch = () => {
const { allowCrashReport } = this.props;
return (
@ -253,170 +227,153 @@ class SettingsView extends React.Component {
render() {
const { server, isMasterDetail, theme } = this.props;
return (
<SafeAreaView testID='settings-view' theme={theme}>
<StatusBar theme={theme} />
<ScrollView
{...scrollPersistTaps}
contentContainerStyle={styles.listPadding}
showsVerticalScrollIndicator={false}
testID='settings-view-list'
>
<SafeAreaView testID='settings-view'>
<StatusBar />
<List.Container testID='settings-view-list'>
{isMasterDetail ? (
<>
<Separator theme={theme} />
<SidebarView theme={theme} />
<SectionSeparator theme={theme} />
<ListItem
title={I18n.t('Profile')}
onPress={() => this.navigateToScreen('ProfileView')}
showActionIndicator
testID='settings-profile'
right={this.renderDisclosure}
theme={theme}
/>
<List.Section>
<List.Separator />
<SidebarView />
<List.Separator />
</List.Section>
<List.Section>
<List.Separator />
<List.Item
title='Profile'
onPress={() => this.navigateToScreen('ProfileView')}
showActionIndicator
testID='settings-profile'
/>
<List.Separator />
</List.Section>
</>
) : null}
<Separator theme={theme} />
<ListItem
title={I18n.t('Contact_us')}
onPress={this.sendEmail}
showActionIndicator
testID='settings-view-contact'
right={this.renderDisclosure}
theme={theme}
/>
<Separator theme={theme} />
<ListItem
title={I18n.t('Language')}
onPress={() => this.navigateToScreen('LanguageView')}
showActionIndicator
testID='settings-view-language'
right={this.renderDisclosure}
theme={theme}
/>
<Separator theme={theme} />
{!isFDroidBuild ? (
<>
<ListItem
title={I18n.t('Review_this_app')}
showActionIndicator
onPress={onReviewPress}
testID='settings-view-review-app'
right={this.renderDisclosure}
theme={theme}
/>
</>
) : null}
<Separator theme={theme} />
<ListItem
title={I18n.t('Share_this_app')}
showActionIndicator
onPress={this.shareApp}
testID='settings-view-share-app'
right={this.renderDisclosure}
theme={theme}
/>
<Separator theme={theme} />
<ListItem
title={I18n.t('Default_browser')}
showActionIndicator
onPress={() => this.navigateToScreen('DefaultBrowserView')}
testID='settings-view-default-browser'
right={this.renderDisclosure}
theme={theme}
/>
<Separator theme={theme} />
<ListItem
title={I18n.t('Theme')}
showActionIndicator
onPress={() => this.navigateToScreen('ThemeView')}
testID='settings-view-theme'
right={this.renderDisclosure}
theme={theme}
/>
<Separator theme={theme} />
<ListItem
title={I18n.t('Screen_lock')}
showActionIndicator
onPress={() => this.navigateToScreen('ScreenLockConfigView')}
right={this.renderDisclosure}
theme={theme}
/>
<List.Section>
<List.Separator />
<List.Item
title='Contact_us'
onPress={this.sendEmail}
showActionIndicator
testID='settings-view-contact'
/>
<List.Separator />
<List.Item
title='Language'
onPress={() => this.navigateToScreen('LanguageView')}
showActionIndicator
testID='settings-view-language'
/>
<List.Separator />
{!isFDroidBuild ? (
<>
<List.Item
title='Review_this_app'
showActionIndicator
onPress={onReviewPress}
testID='settings-view-review-app'
/>
</>
) : null}
<List.Separator />
<List.Item
title='Share_this_app'
showActionIndicator
onPress={this.shareApp}
testID='settings-view-share-app'
/>
<List.Separator />
<List.Item
title='Default_browser'
showActionIndicator
onPress={() => this.navigateToScreen('DefaultBrowserView')}
testID='settings-view-default-browser'
/>
<List.Separator />
<List.Item
title='Theme'
showActionIndicator
onPress={() => this.navigateToScreen('ThemeView')}
testID='settings-view-theme'
/>
<List.Separator />
<List.Item
title='Screen_lock'
showActionIndicator
onPress={() => this.navigateToScreen('ScreenLockConfigView')}
/>
<List.Separator />
</List.Section>
<SectionSeparator theme={theme} />
<ListItem
title={I18n.t('License')}
onPress={this.onPressLicense}
showActionIndicator
testID='settings-view-license'
right={this.renderDisclosure}
theme={theme}
/>
<Separator theme={theme} />
<ListItem
title={I18n.t('Version_no', { version: getReadableVersion })}
onPress={this.copyAppVersion}
testID='settings-view-version'
theme={theme}
/>
<Separator theme={theme} />
<ListItem
title={I18n.t('Server_version', { version: server.version })}
onPress={this.copyServerVersion}
subtitle={`${ server.server.split('//')[1] }`}
testID='settings-view-server-version'
theme={theme}
/>
<SectionSeparator theme={theme} />
<List.Section>
<List.Separator />
<List.Item
title='License'
onPress={this.onPressLicense}
showActionIndicator
testID='settings-view-license'
/>
<List.Separator />
<List.Item
title={I18n.t('Version_no', { version: getReadableVersion })}
onPress={this.copyAppVersion}
testID='settings-view-version'
translateTitle={false}
/>
<List.Separator />
<List.Item
title={I18n.t('Server_version', { version: server.version })}
onPress={this.copyServerVersion}
subtitle={`${ server.server.split('//')[1] }`}
testID='settings-view-server-version'
translateTitle={false}
translateSubtitle={false}
/>
<List.Separator />
</List.Section>
{!isFDroidBuild ? (
<>
<ListItem
title={I18n.t('Log_analytics_events')}
testID='settings-view-analytics-events'
right={() => this.renderAnalyticsEventsSwitch()}
theme={theme}
/>
<Separator theme={theme} />
<ListItem
title={I18n.t('Send_crash_report')}
testID='settings-view-crash-report'
right={() => this.renderCrashReportSwitch()}
theme={theme}
/>
<Separator theme={theme} />
<ItemInfo
info={I18n.t('Crash_report_disclaimer')}
theme={theme}
/>
<Separator theme={theme} />
<List.Section>
<List.Separator />
<List.Item
title='Log_analytics_events'
testID='settings-view-analytics-events'
right={() => this.renderAnalyticsEventsSwitch()}
/>
<List.Separator />
<List.Item
title='Send_crash_report'
testID='settings-view-crash-report'
right={() => this.renderCrashReportSwitch()}
/>
<List.Separator />
<List.Info info='Crash_report_disclaimer' />
</List.Section>
</>
) : null}
<ListItem
title={I18n.t('Clear_cache')}
testID='settings-clear-cache'
onPress={this.handleClearCache}
right={this.renderDisclosure}
color={themes[theme].dangerColor}
theme={theme}
/>
<Separator theme={theme} />
<ListItem
title={I18n.t('Logout')}
testID='settings-logout'
onPress={this.handleLogout}
right={this.renderDisclosure}
color={themes[theme].dangerColor}
theme={theme}
/>
<Separator theme={theme} />
</ScrollView>
<List.Section>
<List.Separator />
<List.Item
title='Clear_cache'
testID='settings-clear-cache'
onPress={this.handleClearCache}
showActionIndicator
color={themes[theme].dangerColor}
/>
<List.Separator />
<List.Item
title='Logout'
testID='settings-logout'
onPress={this.handleLogout}
showActionIndicator
color={themes[theme].dangerColor}
/>
<List.Separator />
</List.Section>
</List.Container>
</SafeAreaView>
);
}

View File

@ -1,13 +0,0 @@
import { StyleSheet } from 'react-native';
import sharedStyles from '../Styles';
export default StyleSheet.create({
sectionSeparatorBorder: {
...sharedStyles.separatorVertical,
height: 36
},
listPadding: {
paddingVertical: 36
}
});

View File

@ -458,10 +458,9 @@ class ShareListView extends React.Component {
}
render() {
const { theme } = this.props;
return (
<SafeAreaView theme={theme}>
<StatusBar theme={theme} />
<SafeAreaView>
<StatusBar />
{this.renderContent()}
</SafeAreaView>
);

View File

@ -324,7 +324,6 @@ class ShareView extends Component {
return (
<SafeAreaView
style={{ backgroundColor: themes[theme].backgroundColor }}
theme={theme}
>
<StatusBar barStyle='light-content' backgroundColor={themes[theme].previewBackground} />
{this.renderContent()}

View File

@ -215,7 +215,7 @@ class Sidebar extends Component {
return null;
}
return (
<SafeAreaView testID='sidebar-view' style={{ backgroundColor: themes[theme].focusedBackground }} vertical={isMasterDetail} theme={theme}>
<SafeAreaView testID='sidebar-view' style={{ backgroundColor: themes[theme].focusedBackground }} vertical={isMasterDetail}>
<ScrollView
style={[
styles.container,

View File

@ -4,8 +4,7 @@ import { FlatList, StyleSheet } from 'react-native';
import { connect } from 'react-redux';
import I18n from '../i18n';
import Separator from '../containers/Separator';
import ListItem from '../containers/ListItem';
import * as List from '../containers/List';
import Status from '../containers/Status/Status';
import TextInput from '../containers/TextInput';
import EventEmitter from '../utils/events';
@ -14,7 +13,6 @@ import RocketChat from '../lib/rocketchat';
import log, { logEvent, events } from '../utils/log';
import { LISTENER } from '../containers/Toast';
import { themes } from '../constants/colors';
import { withTheme } from '../theme';
import { getUserSelector } from '../selectors/login';
import { CustomHeaderButtons, Item, CancelModalButton } from '../containers/HeaderButton';
@ -37,9 +35,6 @@ const STATUS = [{
}];
const styles = StyleSheet.create({
status: {
marginRight: 16
},
inputContainer: {
marginTop: 32,
marginBottom: 32
@ -129,11 +124,6 @@ class StatusView extends React.Component {
this.setState({ loading: false });
}
renderSeparator = () => {
const { theme } = this.props;
return <Separator theme={theme} />;
}
renderHeader = () => {
const { statusText } = this.state;
const { user, theme } = this.props;
@ -157,18 +147,18 @@ class StatusView extends React.Component {
placeholder={I18n.t('What_are_you_doing_right_now')}
testID='status-view-input'
/>
<Separator theme={theme} />
<List.Separator />
</>
);
}
renderItem = ({ item }) => {
const { statusText } = this.state;
const { theme, user } = this.props;
const { user } = this.props;
const { id, name } = item;
return (
<ListItem
title={I18n.t(name)}
<List.Item
title={name}
onPress={async() => {
logEvent(events[`STATUS_${ item.id.toUpperCase() }`]);
if (user.status !== item.id) {
@ -184,25 +174,22 @@ class StatusView extends React.Component {
}
}}
testID={`status-view-${ id }`}
left={() => <Status style={styles.status} size={12} status={item.id} />}
theme={theme}
left={() => <Status size={12} status={item.id} />}
/>
);
}
render() {
const { loading } = this.state;
const { theme } = this.props;
return (
<SafeAreaView testID='status-view' theme={theme}>
<SafeAreaView testID='status-view'>
<FlatList
data={STATUS}
keyExtractor={item => item.id}
contentContainerStyle={{ borderColor: themes[theme].separatorColor }}
renderItem={this.renderItem}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={() => <Separator theme={theme} />}
ItemSeparatorComponent={this.renderSeparator}
ListFooterComponent={List.Separator}
ItemSeparatorComponent={List.Separator}
/>
<Loading visible={loading} />
</SafeAreaView>

View File

@ -121,11 +121,6 @@ export default StyleSheet.create({
inputLastChild: {
marginBottom: 15
},
listContentContainer: {
borderTopWidth: StyleSheet.hairlineWidth,
borderBottomWidth: StyleSheet.hairlineWidth,
marginVertical: 36
},
notchLandscapeContainer: {
marginTop: -34,
paddingHorizontal: 30

View File

@ -1,17 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
FlatList, Text, View, StyleSheet
} from 'react-native';
import I18n from '../i18n';
import { withTheme } from '../theme';
import { themes } from '../constants/colors';
import sharedStyles from './Styles';
import StatusBar from '../containers/StatusBar';
import Separator from '../containers/Separator';
import ListItem from '../containers/ListItem';
import { CustomIcon } from '../lib/Icons';
import * as List from '../containers/List';
import { THEME_PREFERENCES_KEY } from '../lib/rocketchat';
import { supportSystemTheme } from '../utils/deviceInfo';
import SafeAreaView from '../containers/SafeAreaView';
@ -39,8 +33,6 @@ const THEMES = [
}, {
label: 'Dark',
value: 'dark',
separator: true,
header: 'Dark_level',
group: DARK_GROUP
}, {
label: 'Black',
@ -53,20 +45,8 @@ if (supportSystemTheme()) {
THEMES.unshift(SYSTEM_THEME);
}
const styles = StyleSheet.create({
list: {
paddingBottom: 18
},
info: {
paddingTop: 25,
paddingBottom: 18,
paddingHorizontal: 16
},
infoText: {
fontSize: 16,
...sharedStyles.textRegular
}
});
const themeGroup = THEMES.filter(item => item.group === THEME_GROUP);
const darkGroup = THEMES.filter(item => item.group === DARK_GROUP);
class ThemeView extends React.Component {
static navigationOptions = () => ({
@ -114,74 +94,44 @@ class ThemeView extends React.Component {
await UserPreferences.setMapAsync(THEME_PREFERENCES_KEY, newTheme);
};
renderSeparator = () => {
const { theme } = this.props;
return <Separator theme={theme} />;
}
renderIcon = () => {
const { theme } = this.props;
return <CustomIcon name='check' size={20} color={themes[theme].tintColor} />;
return <List.Icon name='check' color={themes[theme].tintColor} />;
}
renderItem = ({ item, index }) => {
const { theme } = this.props;
renderItem = ({ item }) => {
const { label, value } = item;
const isFirst = index === 0;
return (
<>
{item.separator || isFirst ? this.renderSectionHeader(item.header) : null}
<ListItem
title={I18n.t(label)}
<List.Item
title={label}
onPress={() => this.onClick(item)}
testID={`theme-view-${ value }`}
right={this.isSelected(item) ? this.renderIcon : null}
theme={theme}
/>
<List.Separator />
</>
);
}
renderSectionHeader = (header = 'Theme') => {
const { theme } = this.props;
return (
<>
<View style={styles.info}>
<Text style={[styles.infoText, { color: themes[theme].infoText }]}>{I18n.t(header)}</Text>
</View>
{this.renderSeparator()}
</>
);
}
renderFooter = () => {
const { theme } = this.props;
return (
<View style={[styles.info, sharedStyles.separatorTop, { borderColor: themes[theme].separatorColor }]}>
<Text style={{ color: themes[theme].infoText }}>
{I18n.t('Applying_a_theme_will_change_how_the_app_looks')}
</Text>
</View>
);
}
render() {
const { theme } = this.props;
return (
<SafeAreaView testID='theme-view' theme={theme}>
<StatusBar theme={theme} />
<FlatList
data={THEMES}
keyExtractor={item => item.value + item.group}
contentContainerStyle={[
styles.list,
{ borderColor: themes[theme].separatorColor }
]}
renderItem={this.renderItem}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
ItemSeparatorComponent={this.renderSeparator}
/>
<SafeAreaView testID='theme-view'>
<StatusBar />
<List.Container>
<List.Section title='Theme'>
<List.Separator />
{
themeGroup.map(item => this.renderItem({ item }))
}
</List.Section>
<List.Section title='Dark_level'>
<List.Separator />
{
darkGroup.map(item => this.renderItem({ item }))
}
</List.Section>
</List.Container>
</SafeAreaView>
);
}

View File

@ -334,8 +334,8 @@ class ThreadMessagesView extends React.Component {
}
return (
<SafeAreaView testID='thread-messages-view' theme={theme}>
<StatusBar theme={theme} />
<SafeAreaView testID='thread-messages-view'>
<StatusBar />
<FlatList
data={messages}
extraData={this.state}

View File

@ -1,29 +0,0 @@
import React from 'react';
import {
Text
} from 'react-native';
import PropTypes from 'prop-types';
import styles from './styles';
import { themes } from '../../constants/colors';
const Info = React.memo(({ info, theme }) => (
<Text
style={[
styles.infoText,
{
color: themes[theme].infoText,
backgroundColor: themes[theme].auxiliaryBackground
}
]}
>
{info}
</Text>
));
Info.propTypes = {
info: PropTypes.string,
theme: PropTypes.string
};
export default Info;

View File

@ -1,23 +0,0 @@
import React from 'react';
import {
View
} from 'react-native';
import PropTypes from 'prop-types';
import styles from './styles';
import { themes } from '../../constants/colors';
const SectionSeparator = React.memo(({ theme }) => (
<View
style={[
styles.sectionSeparatorBorder,
{ backgroundColor: themes[theme].auxiliaryBackground }
]}
/>
));
SectionSeparator.propTypes = {
theme: PropTypes.string
};
export default SectionSeparator;

View File

@ -1,29 +0,0 @@
import React from 'react';
import {
Text
} from 'react-native';
import PropTypes from 'prop-types';
import styles from './styles';
import { themes } from '../../constants/colors';
const SectionTitle = React.memo(({ title, theme }) => (
<Text
style={[
styles.sectionTitle,
{
backgroundColor: themes[theme].auxiliaryBackground,
color: themes[theme].infoText
}
]}
>
{title}
</Text>
));
SectionTitle.propTypes = {
title: PropTypes.string,
theme: PropTypes.string
};
export default SectionTitle;

View File

@ -1,27 +1,26 @@
import React from 'react';
import {
View, ScrollView, Text
} from 'react-native';
import { Text, StyleSheet } from 'react-native';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { themes } from '../../constants/colors';
import StatusBar from '../../containers/StatusBar';
import ListItem from '../../containers/ListItem';
import Separator from '../../containers/Separator';
import * as List from '../../containers/List';
import I18n from '../../i18n';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import styles from './styles';
import RocketChat from '../../lib/rocketchat';
import { withTheme } from '../../theme';
import SafeAreaView from '../../containers/SafeAreaView';
import SectionTitle from './SectionTitle';
import SectionSeparator from './SectionSeparator';
import Info from './Info';
import { OPTIONS } from './options';
import ActivityIndicator from '../../containers/ActivityIndicator';
import { DisclosureImage } from '../../containers/DisclosureIndicator';
import { getUserSelector } from '../../selectors/login';
import sharedStyles from '../Styles';
const styles = StyleSheet.create({
pickerText: {
...sharedStyles.textRegular,
fontSize: 16
}
});
class UserNotificationPreferencesView extends React.Component {
static navigationOptions = () => ({
@ -30,7 +29,6 @@ class UserNotificationPreferencesView extends React.Component {
static propTypes = {
navigation: PropTypes.object,
route: PropTypes.object,
theme: PropTypes.string,
user: PropTypes.shape({
id: PropTypes.string
@ -90,72 +88,55 @@ class UserNotificationPreferencesView extends React.Component {
this.setState({ preferences: settings.preferences });
}
renderDisclosure = () => {
const { theme } = this.props;
return <DisclosureImage theme={theme} />;
}
render() {
const { theme } = this.props;
const { loading } = this.state;
return (
<SafeAreaView testID='user-notification-preference-view' theme={theme}>
<StatusBar theme={theme} />
<ScrollView
{...scrollPersistTaps}
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
contentContainerStyle={styles.contentContainer}
testID='user-notification-preference-view-list'
>
<SafeAreaView testID='user-notification-preference-view'>
<StatusBar />
<List.Container>
{loading
? (
<>
<SectionSeparator theme={theme} />
<SectionTitle title={I18n.t('DESKTOP_NOTIFICATIONS')} theme={theme} />
<List.Section title='Desktop_Notifications'>
<List.Separator />
<List.Item
title='Alert'
testID='user-notification-preference-view-alert'
onPress={title => this.pickerSelection(title, 'desktopNotifications')}
right={() => this.renderPickerOption('desktopNotifications')}
/>
<List.Separator />
<List.Info info='Desktop_Alert_info' />
</List.Section>
<ListItem
title={I18n.t('Alert')}
testID='user-notification-preference-view-alert'
onPress={title => this.pickerSelection(title, 'desktopNotifications')}
right={() => this.renderPickerOption('desktopNotifications')}
theme={theme}
/>
<Separator theme={theme} />
<Info info={I18n.t('Desktop_Alert_info')} theme={theme} />
<List.Section title='Push_Notifications'>
<List.Separator />
<List.Item
title='Alert'
testID='user-notification-preference-view-push-notification'
onPress={title => this.pickerSelection(title, 'mobileNotifications')}
right={() => this.renderPickerOption('mobileNotifications')}
/>
<List.Separator />
<List.Info info='Push_Notifications_Alert_Info' />
</List.Section>
<SectionSeparator theme={theme} />
<SectionTitle title={I18n.t('PUSH_NOTIFICATIONS')} theme={theme} />
<Separator theme={theme} />
<ListItem
title={I18n.t('Alert')}
testID='user-notification-preference-view-push-notification'
onPress={title => this.pickerSelection(title, 'mobileNotifications')}
right={() => this.renderPickerOption('mobileNotifications')}
theme={theme}
/>
<Separator theme={theme} />
<Info info={I18n.t('Push_Notifications_Alert_Info')} theme={theme} />
<SectionSeparator theme={theme} />
<SectionTitle title={I18n.t('EMAIL')} theme={theme} />
<Separator theme={theme} />
<ListItem
title={I18n.t('Alert')}
testID='user-notification-preference-view-email-alert'
onPress={title => this.pickerSelection(title, 'emailNotificationMode')}
right={() => this.renderPickerOption('emailNotificationMode')}
theme={theme}
/>
<Separator theme={theme} />
<Info info={I18n.t('You_need_to_verifiy_your_email_address_to_get_notications')} theme={theme} />
<List.Section title='Email'>
<List.Separator />
<List.Item
title='Alert'
testID='user-notification-preference-view-email-alert'
onPress={title => this.pickerSelection(title, 'emailNotificationMode')}
right={() => this.renderPickerOption('emailNotificationMode')}
/>
<List.Separator />
<List.Info info='You_need_to_verifiy_your_email_address_to_get_notications' />
</List.Section>
</>
) : <ActivityIndicator theme={theme} />
}
<View style={[styles.marginBottom, { backgroundColor: themes[theme].auxiliaryBackground }]} />
</ScrollView>
</List.Container>
</SafeAreaView>
);
}

View File

@ -1,31 +0,0 @@
import { StyleSheet } from 'react-native';
import sharedStyles from '../Styles';
export default StyleSheet.create({
sectionSeparatorBorder: {
height: 10
},
marginBottom: {
height: 30
},
contentContainer: {
marginVertical: 10
},
infoText: {
...sharedStyles.textRegular,
fontSize: 13,
paddingHorizontal: 15,
paddingVertical: 10
},
sectionTitle: {
...sharedStyles.separatorBottom,
paddingHorizontal: 15,
paddingVertical: 10,
fontSize: 14
},
pickerText: {
...sharedStyles.textRegular,
fontSize: 16
}
});

View File

@ -1,18 +1,13 @@
import React from 'react';
import { ScrollView } from 'react-native';
import PropTypes from 'prop-types';
import I18n from '../../i18n';
import {
logEvent, events
} from '../../utils/log';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import Separator from '../../containers/Separator';
import SafeAreaView from '../../containers/SafeAreaView';
import StatusBar from '../../containers/StatusBar';
import ListItem from '../../containers/ListItem';
import { DisclosureImage } from '../../containers/DisclosureIndicator';
import { withTheme } from '../../theme';
import * as List from '../../containers/List';
class UserPreferencesView extends React.Component {
static navigationOptions = () => ({
@ -20,13 +15,7 @@ class UserPreferencesView extends React.Component {
});
static propTypes = {
navigation: PropTypes.object,
theme: PropTypes.string
}
renderDisclosure = () => {
const { theme } = this.props;
return <DisclosureImage theme={theme} />;
navigation: PropTypes.object
}
navigateToScreen = (screen, params) => {
@ -36,30 +25,24 @@ class UserPreferencesView extends React.Component {
}
render() {
const { theme } = this.props;
return (
<SafeAreaView testID='preferences-view' theme={theme}>
<StatusBar theme={theme} />
<ScrollView
{...scrollPersistTaps}
contentContainerStyle={{ paddingVertical: 36 }}
showsVerticalScrollIndicator={false}
testID='preferences-view-list'
>
<ListItem
title={I18n.t('Notifications')}
onPress={() => this.navigateToScreen('UserNotificationPrefView')}
showActionIndicator
testID='preferences-view-notifications'
right={this.renderDisclosure}
theme={theme}
/>
<Separator theme={theme} />
</ScrollView>
<SafeAreaView testID='preferences-view'>
<StatusBar />
<List.Container>
<List.Section>
<List.Separator />
<List.Item
title={I18n.t('Notifications')}
onPress={() => this.navigateToScreen('UserNotificationPrefView')}
showActionIndicator
testID='preferences-view-notifications'
/>
<List.Separator />
</List.Section>
</List.Container>
</SafeAreaView>
);
}
}
export default withTheme(UserPreferencesView);
export default UserPreferencesView;

View File

@ -5,12 +5,12 @@ import PropTypes from 'prop-types';
import { withTheme } from '../theme';
import RocketChat from '../lib/rocketchat';
import { themes } from '../constants/colors';
import Separator from '../containers/Separator';
import openLink from '../utils/openLink';
import I18n from '../i18n';
import debounce from '../utils/debounce';
import sharedStyles from './Styles';
import ListItem from '../containers/ListItem';
import * as List from '../containers/List';
import SafeAreaView from '../containers/SafeAreaView';
const styles = StyleSheet.create({
noResult: {
@ -18,23 +18,18 @@ const styles = StyleSheet.create({
paddingVertical: 56,
...sharedStyles.textAlignCenter,
...sharedStyles.textSemibold
},
withoutBorder: {
borderBottomWidth: 0,
borderTopWidth: 0
}
});
const Item = ({ item, theme }) => (
<ListItem
const Item = ({ item }) => (
<List.Item
title={item.navigation?.page?.title || I18n.t('Empty_title')}
onPress={() => openLink(item.navigation?.page?.location?.href)}
theme={theme}
translateTitle={false}
/>
);
Item.propTypes = {
item: PropTypes.object,
theme: PropTypes.string
item: PropTypes.object
};
const VisitorNavigationView = ({ route, theme }) => {
@ -67,24 +62,20 @@ const VisitorNavigationView = ({ route, theme }) => {
}, 300);
return (
<FlatList
data={pages}
renderItem={({ item }) => <Item item={item} theme={theme} />}
ItemSeparatorComponent={() => <Separator theme={theme} />}
contentContainerStyle={[
sharedStyles.listContentContainer,
{
backgroundColor: themes[theme].auxiliaryBackground,
borderColor: themes[theme].separatorColor
},
!pages.length && styles.withoutBorder
]}
style={{ backgroundColor: themes[theme].auxiliaryBackground }}
ListEmptyComponent={() => <Text style={[styles.noResult, { color: themes[theme].titleText }]}>{I18n.t('No_results_found')}</Text>}
keyExtractor={item => item}
onEndReached={onEndReached}
onEndReachedThreshold={5}
/>
<SafeAreaView>
<FlatList
data={pages}
renderItem={({ item }) => <Item item={item} theme={theme} />}
ItemSeparatorComponent={List.Separator}
ListFooterComponent={List.Separator}
ListHeaderComponent={List.Separator}
contentContainerStyle={List.styles.contentContainerStyleFlatList}
ListEmptyComponent={() => <Text style={[styles.noResult, { color: themes[theme].titleText }]}>{I18n.t('No_results_found')}</Text>}
keyExtractor={item => item}
onEndReached={onEndReached}
onEndReachedThreshold={5}
/>
</SafeAreaView>
);
};
VisitorNavigationView.propTypes = {

View File

@ -37,7 +37,7 @@ describe('Status screen', () => {
describe('Usage', async () => {
it('should change status', async () => {
await element(by.id('status-view-busy')).tap();
await expect(element(by.id('status-view-current-busy'))).toExist();
await waitFor(element(by.id('status-view-current-busy'))).toExist().withTimeout(2000);
});
it('should change status text', async () => {

View File

@ -15,7 +15,7 @@ async function navigateToRoomInfo(type) {
}
await searchRoom(room);
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toExist().withTimeout(60000);
await element(by.id(`rooms-list-view-item-${ room }`)).tap();
await element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0).tap();
await waitFor(element(by.id('room-view'))).toExist().withTimeout(2000);
await element(by.id('room-view-header-actions')).tap();
await waitFor(element(by.id('room-actions-view'))).toExist().withTimeout(5000);

View File

@ -5,7 +5,7 @@ import RNBootSplash from 'react-native-bootsplash';
import 'react-native-gesture-handler';
// eslint-disable-next-line no-undef
jest.mock('react-native/Libraries/Components/Touchable/TouchableOpacity', () => jest.fn(() => null));
// jest.mock('react-native/Libraries/Components/Touchable/TouchableOpacity', () => jest.fn(() => null));
RNBootSplash.hide();

196
storybook/stories/List.js Normal file
View File

@ -0,0 +1,196 @@
/* eslint-disable import/no-extraneous-dependencies, import/no-unresolved, import/extensions, react/prop-types */
import React from 'react';
import { FlatList } from 'react-native';
import { storiesOf } from '@storybook/react-native';
import * as List from '../../app/containers/List';
import SafeAreaView from '../../app/containers/SafeAreaView';
import { longText } from '../utils';
import { ThemeContext } from '../../app/theme';
import { DimensionsContext } from '../../app/dimensions';
const stories = storiesOf('List', module);
stories.add('title and subtitle', () => (
<List.Container>
<List.Separator />
<List.Item title='Chats' />
<List.Separator />
<List.Item title='Chats' subtitle='All' />
<List.Separator />
<List.Item title={longText} subtitle={longText} translateTitle={false} translateSubtitle={false} testID='test-id' />
<List.Separator />
</List.Container>
));
stories.add('pressable', () => (
<List.Container>
<List.Separator />
<List.Item title='Press me' onPress={() => alert('Hi there!')} translateTitle={false} />
<List.Separator />
<List.Item title={'I\'m disabled'} onPress={() => alert('Hi there!')} disabled translateTitle={false} />
<List.Separator />
</List.Container>
));
stories.add('header', () => (
<List.Container>
<List.Header title='Chats' />
<List.Header title={longText} translateTitle={false} />
</List.Container>
));
stories.add('icon', () => (
<List.Container>
<List.Icon name='emoji' />
</List.Container>
));
stories.add('separator', () => (
<List.Container>
<List.Separator />
</List.Container>
));
stories.add('with section and info', () => (
<SafeAreaView>
<List.Container>
<List.Section>
<List.Separator />
<List.Item title='Section Item' translateTitle={false} />
<List.Separator />
<List.Item title='Section Item' translateTitle={false} />
<List.Separator />
</List.Section>
<List.Section>
<List.Separator />
<List.Item title='Section Item' translateTitle={false} />
<List.Separator />
<List.Item title='Section Item' translateTitle={false} />
<List.Separator />
</List.Section>
<List.Section title='Chats'>
<List.Separator />
<List.Item title='Section Item' translateTitle={false} />
<List.Separator />
<List.Item title='Section Item' translateTitle={false} />
<List.Separator />
<List.Info info='Chats' />
</List.Section>
<List.Section title={longText} translateTitle={false}>
<List.Separator />
<List.Item title='Section Item' translateTitle={false} />
<List.Separator />
<List.Item title='Section Item' translateTitle={false} />
<List.Separator />
<List.Info info={longText} translateInfo={false} />
</List.Section>
</List.Container>
</SafeAreaView>
));
stories.add('with icon', () => (
<List.Container>
<List.Separator />
<List.Item title='Icon Left' translateTitle={false} left={() => <List.Icon name='emoji' />} />
<List.Separator />
<List.Item title='Icon Right' translateTitle={false} right={() => <List.Icon name='emoji' />} />
<List.Separator />
<List.Item
title={longText}
subtitle={longText}
translateTitle={false}
translateSubtitle={false}
left={() => <List.Icon name='emoji' />}
right={() => <List.Icon name='emoji' />}
/>
<List.Separator />
<List.Item title='Show Action Indicator' translateTitle={false} showActionIndicator />
<List.Separator />
</List.Container>
));
stories.add('with custom color', () => (
<List.Container>
<List.Separator />
<List.Item title='Chats' color='red' />
<List.Separator />
</List.Container>
));
const ListItemFull = ({ ...props }) => (
<List.Item
title='Chats'
subtitle='All'
onPress={() => alert('Hi')}
left={() => <List.Icon name='emoji' />}
right={() => <List.Icon name='emoji' />}
{...props}
/>
);
const ListFull = () => (
<SafeAreaView>
<List.Container>
<List.Section title='Chats'>
<List.Separator />
<ListItemFull />
<List.Separator />
<ListItemFull disabled />
<List.Separator />
<List.Info info='Chats' />
</List.Section>
<List.Section title='Chats'>
<List.Separator />
<ListItemFull />
<List.Separator />
<ListItemFull disabled />
<List.Separator />
<List.Info info='Chats' />
</List.Section>
</List.Container>
</SafeAreaView>
);
const ThemeStory = ({ theme }) => (
<ThemeContext.Provider
value={{ theme }}
>
<ListFull />
</ThemeContext.Provider>
);
stories.add('with dark theme', () => <ThemeStory theme='dark' />);
stories.add('with black theme', () => <ThemeStory theme='black' />);
const FontStory = ({ fontScale }) => (
<DimensionsContext.Provider
value={{ fontScale }}
>
<ListFull />
</DimensionsContext.Provider>
);
/**
* It's going to test height only.
* Font scale on text and icons is applied based on OS setting
*/
stories.add('with small font', () => <FontStory fontScale={0.8} />);
stories.add('with bigger font', () => <FontStory fontScale={1.5} />);
stories.add('with FlatList', () => (
<SafeAreaView>
<FlatList
data={[...Array(30).keys()]}
contentContainerStyle={List.styles.contentContainerStyleFlatList}
renderItem={({ item }) => <List.Item title={item} translateTitle={false} />}
ListHeaderComponent={List.Separator}
ListFooterComponent={List.Separator}
ItemSeparatorComponent={List.Separator}
keyExtractor={item => item}
/>
</SafeAreaView>
));

Some files were not shown because too many files have changed in this diff Show More