[IMPROVEMENT] Unified header UX (#2234)
* Change drawer icon * Removed iOS variation * Patch to react-navigation-header-buttons... easier to patch then to overwrite its behaviour :( * Correctly position title * Header subtitle * Layout * Alignment * RoomView header * Renamed RoomHeaderLeft to LeftButtons * RoomView back button * Search icon on RoomView * Refactor * Fix header on tablet * Fix search messages close button on tablet * Search key command * Network status on RoomView header subtitle * Update tests * Scale content * SearchBox cancel color
This commit is contained in:
parent
2ec2a52f45
commit
5834ab5e22
|
@ -1,5 +1,3 @@
|
|||
import { isIOS, isAndroid } from '../utils/deviceInfo';
|
||||
|
||||
export const STATUS_COLORS = {
|
||||
online: '#2de0a5',
|
||||
busy: '#f5455c',
|
||||
|
@ -8,7 +6,7 @@ export const STATUS_COLORS = {
|
|||
};
|
||||
|
||||
export const SWITCH_TRACK_COLOR = {
|
||||
false: isAndroid ? '#f5455c' : null,
|
||||
false: '#f5455c',
|
||||
true: '#2de0a5'
|
||||
};
|
||||
|
||||
|
@ -34,11 +32,11 @@ export const themes = {
|
|||
separatorColor: '#cbcbcc',
|
||||
navbarBackground: '#ffffff',
|
||||
headerBorder: '#B2B2B2',
|
||||
headerBackground: isIOS ? '#f8f8f8' : '#2f343d',
|
||||
headerBackground: '#EEEFF1',
|
||||
headerSecondaryBackground: '#ffffff',
|
||||
headerTintColor: isAndroid ? '#ffffff' : '#1d74f5',
|
||||
headerTitleColor: isAndroid ? '#ffffff' : '#0d0e12',
|
||||
headerSecondaryText: isAndroid ? '#9ca2a8' : '#1d74f5',
|
||||
headerTintColor: '#6C727A',
|
||||
headerTitleColor: '#0C0D0F',
|
||||
headerSecondaryText: '#1d74f5',
|
||||
toastBackground: '#0C0D0F',
|
||||
videoBackground: '#1f2329',
|
||||
favoriteBackground: '#ffbb00',
|
||||
|
@ -63,7 +61,7 @@ export const themes = {
|
|||
chatComponentBackground: '#192132',
|
||||
auxiliaryBackground: '#07101e',
|
||||
bannerBackground: '#0e1f38',
|
||||
titleText: '#FFFFFF',
|
||||
titleText: '#f9f9f9',
|
||||
bodyText: '#e8ebed',
|
||||
backdropColor: '#000000',
|
||||
dangerColor: '#f5455c',
|
||||
|
@ -80,9 +78,9 @@ export const themes = {
|
|||
headerBorder: '#2F3A4B',
|
||||
headerBackground: '#0b182c',
|
||||
headerSecondaryBackground: '#0b182c',
|
||||
headerTintColor: isAndroid ? '#ffffff' : '#1d74f5',
|
||||
headerTitleColor: '#FFFFFF',
|
||||
headerSecondaryText: isAndroid ? '#9297a2' : '#1d74f5',
|
||||
headerTintColor: '#f9f9f9',
|
||||
headerTitleColor: '#f9f9f9',
|
||||
headerSecondaryText: '#9297a2',
|
||||
toastBackground: '#0C0D0F',
|
||||
videoBackground: '#1f2329',
|
||||
favoriteBackground: '#ffbb00',
|
||||
|
@ -124,9 +122,9 @@ export const themes = {
|
|||
headerBorder: '#323232',
|
||||
headerBackground: '#0d0d0d',
|
||||
headerSecondaryBackground: '#0d0d0d',
|
||||
headerTintColor: isAndroid ? '#ffffff' : '#1e9bfe',
|
||||
headerTintColor: '#f9f9f9',
|
||||
headerTitleColor: '#f9f9f9',
|
||||
headerSecondaryText: isAndroid ? '#b2b8c6' : '#1e9bfe',
|
||||
headerSecondaryText: '#b2b8c6',
|
||||
toastBackground: '#0C0D0F',
|
||||
videoBackground: '#1f2329',
|
||||
favoriteBackground: '#ffbb00',
|
||||
|
|
|
@ -17,7 +17,7 @@ const styles = StyleSheet.create({
|
|||
export const DisclosureImage = React.memo(({ theme }) => (
|
||||
<CustomIcon
|
||||
name='chevron-right'
|
||||
color={themes[theme].auxiliaryTintColor}
|
||||
color={themes[theme].auxiliaryText}
|
||||
size={20}
|
||||
/>
|
||||
));
|
||||
|
|
|
@ -20,6 +20,11 @@ export const getHeaderHeight = (isLandscape) => {
|
|||
return 56;
|
||||
};
|
||||
|
||||
export const getHeaderTitlePosition = insets => ({
|
||||
left: 60 + insets.left,
|
||||
right: 80 + insets.right
|
||||
});
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
height: headerHeight,
|
||||
|
|
|
@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
|||
import { HeaderButtons, HeaderButton, Item } from 'react-navigation-header-buttons';
|
||||
|
||||
import { CustomIcon } from '../lib/Icons';
|
||||
import { isIOS, isAndroid } from '../utils/deviceInfo';
|
||||
import { isIOS } from '../utils/deviceInfo';
|
||||
import { themes } from '../constants/colors';
|
||||
import I18n from '../i18n';
|
||||
import { withTheme } from '../theme';
|
||||
|
@ -15,11 +15,7 @@ const CustomHeaderButton = React.memo(withTheme(({ theme, ...props }) => (
|
|||
{...props}
|
||||
IconComponent={CustomIcon}
|
||||
iconSize={headerIconSize}
|
||||
color={
|
||||
isAndroid
|
||||
? themes[theme].headerTitleColor
|
||||
: themes[theme].headerTintColor
|
||||
}
|
||||
color={themes[theme].headerTintColor}
|
||||
/>
|
||||
)));
|
||||
|
||||
|
@ -32,7 +28,7 @@ export const CustomHeaderButtons = React.memo(props => (
|
|||
|
||||
export const DrawerButton = React.memo(({ navigation, testID, ...otherProps }) => (
|
||||
<CustomHeaderButtons left>
|
||||
<Item title='drawer' iconName='customize' onPress={navigation.toggleDrawer} testID={testID} {...otherProps} />
|
||||
<Item title='drawer' iconName='menu_hamburguer' onPress={navigation.toggleDrawer} testID={testID} {...otherProps} />
|
||||
</CustomHeaderButtons>
|
||||
));
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ const styles = StyleSheet.create({
|
|||
|
||||
const CancelButton = (onCancelPress, theme) => (
|
||||
<Touchable onPress={onCancelPress} style={styles.cancel}>
|
||||
<Text style={[styles.cancelText, { color: themes[theme].tintColor }]}>{I18n.t('Cancel')}</Text>
|
||||
<Text style={[styles.cancelText, { color: themes[theme].headerTintColor }]}>{I18n.t('Cancel')}</Text>
|
||||
</Touchable>
|
||||
);
|
||||
|
||||
|
|
|
@ -2,13 +2,12 @@ import React from 'react';
|
|||
import { StatusBar as StatusBarRN } from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { isIOS } from '../utils/deviceInfo';
|
||||
import { themes } from '../constants/colors';
|
||||
|
||||
const StatusBar = React.memo(({ theme, barStyle, backgroundColor }) => {
|
||||
if (!barStyle) {
|
||||
barStyle = 'light-content';
|
||||
if (theme === 'light' && isIOS) {
|
||||
if (theme === 'light') {
|
||||
barStyle = 'dark-content';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -543,6 +543,7 @@ export default {
|
|||
Video_call: 'Video call',
|
||||
View_Original: 'View Original',
|
||||
Voice_call: 'Voice call',
|
||||
Waiting_for_network: 'Waiting for network...',
|
||||
Websocket_disabled: 'Websocket is disabled for this server.\n{{contact}}',
|
||||
Welcome: 'Welcome',
|
||||
What_are_you_doing_right_now: 'What are you doing right now?',
|
||||
|
|
|
@ -479,6 +479,7 @@ export default {
|
|||
Verify_your_email_for_the_code_we_sent: 'Verifique em seu e-mail o código que enviamos',
|
||||
Video_call: 'Chamada de vídeo',
|
||||
Voice_call: 'Chamada de voz',
|
||||
Waiting_for_network: 'Aguardando rede...',
|
||||
Websocket_disabled: 'Websocket está desativado para esse servidor.\n{{contact}}',
|
||||
Welcome: 'Bem vindo',
|
||||
Whats_your_2fa: 'Qual seu código de autenticação?',
|
||||
|
|
|
@ -6,28 +6,20 @@ import {
|
|||
|
||||
import I18n from '../../../i18n';
|
||||
import sharedStyles from '../../Styles';
|
||||
import { isAndroid, isTablet } from '../../../utils/deviceInfo';
|
||||
import Icon from './Icon';
|
||||
import { themes } from '../../../constants/colors';
|
||||
import Markdown from '../../../containers/markdown';
|
||||
|
||||
const androidMarginLeft = isTablet ? 0 : 4;
|
||||
|
||||
const TITLE_SIZE = 16;
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
marginRight: isAndroid ? 15 : 5,
|
||||
marginLeft: isAndroid ? androidMarginLeft : -10,
|
||||
justifyContent: 'center'
|
||||
},
|
||||
titleContainer: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row'
|
||||
},
|
||||
threadContainer: {
|
||||
marginRight: isAndroid ? 20 : undefined
|
||||
},
|
||||
title: {
|
||||
...sharedStyles.textSemibold,
|
||||
fontSize: TITLE_SIZE
|
||||
|
@ -36,7 +28,6 @@ const styles = StyleSheet.create({
|
|||
alignItems: 'center'
|
||||
},
|
||||
subtitle: {
|
||||
marginRight: -16,
|
||||
...sharedStyles.textRegular,
|
||||
fontSize: 12
|
||||
},
|
||||
|
@ -87,12 +78,8 @@ SubTitle.propTypes = {
|
|||
};
|
||||
|
||||
const HeaderTitle = React.memo(({
|
||||
title, tmid, prid, scale, connecting, theme
|
||||
title, tmid, prid, scale, theme
|
||||
}) => {
|
||||
if (connecting) {
|
||||
title = I18n.t('Connecting');
|
||||
}
|
||||
|
||||
if (!tmid && !prid) {
|
||||
return (
|
||||
<Text
|
||||
|
@ -122,7 +109,6 @@ HeaderTitle.propTypes = {
|
|||
tmid: PropTypes.string,
|
||||
prid: PropTypes.string,
|
||||
scale: PropTypes.number,
|
||||
connecting: PropTypes.bool,
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
|
@ -147,7 +133,7 @@ const Header = React.memo(({
|
|||
style={styles.container}
|
||||
disabled={tmid}
|
||||
>
|
||||
<View style={[styles.titleContainer, tmid && styles.threadContainer]}>
|
||||
<View style={styles.titleContainer}>
|
||||
<Icon type={prid ? 'discussion' : type} status={status} roomUserId={roomUserId} theme={theme} />
|
||||
<HeaderTitle
|
||||
title={title}
|
||||
|
|
|
@ -5,7 +5,6 @@ import PropTypes from 'prop-types';
|
|||
import { STATUS_COLORS, themes } from '../../../constants/colors';
|
||||
import { CustomIcon } from '../../../lib/Icons';
|
||||
import Status from '../../../containers/Status/Status';
|
||||
import { isAndroid } from '../../../utils/deviceInfo';
|
||||
|
||||
const ICON_SIZE = 15;
|
||||
|
||||
|
@ -32,7 +31,7 @@ const Icon = React.memo(({
|
|||
if (type === 'l') {
|
||||
colorStyle = { color: STATUS_COLORS[status] };
|
||||
} else {
|
||||
colorStyle = { color: isAndroid && theme === 'light' ? themes[theme].buttonText : themes[theme].auxiliaryText };
|
||||
colorStyle = { color: themes[theme].auxiliaryText };
|
||||
}
|
||||
|
||||
let icon;
|
||||
|
|
|
@ -13,16 +13,21 @@ const styles = StyleSheet.create({
|
|||
}
|
||||
});
|
||||
|
||||
const RoomHeaderLeft = React.memo(({
|
||||
const LeftButtons = React.memo(({
|
||||
tmid, unreadsCount, navigation, baseUrl, userId, token, title, t, theme, goRoomActionsView, isMasterDetail
|
||||
}) => {
|
||||
if (!isMasterDetail || tmid) {
|
||||
const onPress = useCallback(() => navigation.goBack());
|
||||
const label = unreadsCount > 99 ? '+99' : unreadsCount || ' ';
|
||||
const labelLength = label.length ? label.length : 1;
|
||||
const marginLeft = -2 * labelLength;
|
||||
const fontSize = labelLength > 1 ? 14 : 17;
|
||||
return (
|
||||
<HeaderBackButton
|
||||
label={unreadsCount > 999 ? '+999' : unreadsCount || ' '}
|
||||
label={label}
|
||||
onPress={onPress}
|
||||
tintColor={themes[theme].headerTintColor}
|
||||
labelStyle={{ fontSize, marginLeft }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -44,7 +49,7 @@ const RoomHeaderLeft = React.memo(({
|
|||
return null;
|
||||
});
|
||||
|
||||
RoomHeaderLeft.propTypes = {
|
||||
LeftButtons.propTypes = {
|
||||
tmid: PropTypes.string,
|
||||
unreadsCount: PropTypes.number,
|
||||
navigation: PropTypes.object,
|
||||
|
@ -58,4 +63,4 @@ RoomHeaderLeft.propTypes = {
|
|||
isMasterDetail: PropTypes.bool
|
||||
};
|
||||
|
||||
export default RoomHeaderLeft;
|
||||
export default LeftButtons;
|
|
@ -68,6 +68,17 @@ class RightButtonsContainer extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
goSearchView = () => {
|
||||
const {
|
||||
rid, navigation, isMasterDetail
|
||||
} = this.props;
|
||||
if (isMasterDetail) {
|
||||
navigation.navigate('ModalStackNavigator', { screen: 'SearchMessagesView', params: { rid, showCloseModal: true } });
|
||||
} else {
|
||||
navigation.navigate('SearchMessagesView', { rid });
|
||||
}
|
||||
}
|
||||
|
||||
toggleFollowThread = () => {
|
||||
const { isFollowingThread } = this.state;
|
||||
const { toggleFollowThread } = this.props;
|
||||
|
@ -104,6 +115,12 @@ class RightButtonsContainer extends React.PureComponent {
|
|||
testID='room-view-header-threads'
|
||||
/>
|
||||
) : null}
|
||||
<Item
|
||||
title='search'
|
||||
iconName='magnifier'
|
||||
onPress={this.goSearchView}
|
||||
testID='room-view-search'
|
||||
/>
|
||||
</CustomHeaderButtons>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -4,10 +4,11 @@ import { connect } from 'react-redux';
|
|||
import equal from 'deep-equal';
|
||||
|
||||
import Header from './Header';
|
||||
import LeftButtons from './LeftButtons';
|
||||
import RightButtons from './RightButtons';
|
||||
import { withTheme } from '../../../theme';
|
||||
import RoomHeaderLeft from './RoomHeaderLeft';
|
||||
import { withDimensions } from '../../../dimensions';
|
||||
import I18n from '../../../i18n';
|
||||
|
||||
class RoomHeaderView extends Component {
|
||||
static propTypes = {
|
||||
|
@ -20,6 +21,7 @@ class RoomHeaderView extends Component {
|
|||
status: PropTypes.string,
|
||||
statusText: PropTypes.string,
|
||||
connecting: PropTypes.bool,
|
||||
connected: PropTypes.bool,
|
||||
theme: PropTypes.string,
|
||||
roomUserId: PropTypes.string,
|
||||
widthOffset: PropTypes.number,
|
||||
|
@ -30,7 +32,7 @@ class RoomHeaderView extends Component {
|
|||
|
||||
shouldComponentUpdate(nextProps) {
|
||||
const {
|
||||
type, title, subtitle, status, statusText, connecting, goRoomActionsView, usersTyping, theme, width, height
|
||||
type, title, subtitle, status, statusText, connecting, connected, goRoomActionsView, usersTyping, theme, width, height
|
||||
} = this.props;
|
||||
if (nextProps.theme !== theme) {
|
||||
return true;
|
||||
|
@ -53,6 +55,9 @@ class RoomHeaderView extends Component {
|
|||
if (nextProps.connecting !== connecting) {
|
||||
return true;
|
||||
}
|
||||
if (nextProps.connected !== connected) {
|
||||
return true;
|
||||
}
|
||||
if (nextProps.width !== width) {
|
||||
return true;
|
||||
}
|
||||
|
@ -70,9 +75,18 @@ class RoomHeaderView extends Component {
|
|||
|
||||
render() {
|
||||
const {
|
||||
title, subtitle, type, prid, tmid, widthOffset, status = 'offline', statusText, connecting, usersTyping, goRoomActionsView, roomUserId, theme, width, height
|
||||
title, subtitle: subtitleProp, type, prid, tmid, widthOffset, status = 'offline', statusText, connecting, connected, usersTyping, goRoomActionsView, roomUserId, theme, width, height
|
||||
} = this.props;
|
||||
|
||||
let subtitle;
|
||||
if (connecting) {
|
||||
subtitle = I18n.t('Connecting');
|
||||
} else if (!connected) {
|
||||
subtitle = I18n.t('Waiting_for_network');
|
||||
} else {
|
||||
subtitle = subtitleProp;
|
||||
}
|
||||
|
||||
return (
|
||||
<Header
|
||||
prid={prid}
|
||||
|
@ -108,7 +122,8 @@ const mapStateToProps = (state, ownProps) => {
|
|||
}
|
||||
|
||||
return {
|
||||
connecting: state.meteor.connecting,
|
||||
connecting: state.meteor.connecting || state.server.loading,
|
||||
connected: state.meteor.connected,
|
||||
usersTyping: state.usersTyping,
|
||||
status,
|
||||
statusText
|
||||
|
@ -117,4 +132,4 @@ const mapStateToProps = (state, ownProps) => {
|
|||
|
||||
export default connect(mapStateToProps)(withDimensions(withTheme(RoomHeaderView)));
|
||||
|
||||
export { RightButtons, RoomHeaderLeft };
|
||||
export { RightButtons, LeftButtons };
|
||||
|
|
|
@ -8,6 +8,7 @@ import moment from 'moment';
|
|||
import * as Haptics from 'expo-haptics';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import { withSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
import Touch from '../../utils/touch';
|
||||
import {
|
||||
|
@ -26,7 +27,7 @@ import styles from './styles';
|
|||
import log from '../../utils/log';
|
||||
import EventEmitter from '../../utils/events';
|
||||
import I18n from '../../i18n';
|
||||
import RoomHeaderView, { RightButtons, RoomHeaderLeft } from './Header';
|
||||
import RoomHeaderView, { RightButtons, LeftButtons } from './Header';
|
||||
import StatusBar from '../../containers/StatusBar';
|
||||
import Separator from './Separator';
|
||||
import { themes } from '../../constants/colors';
|
||||
|
@ -53,6 +54,7 @@ import Banner from './Banner';
|
|||
import Navigation from '../../lib/Navigation';
|
||||
import SafeAreaView from '../../containers/SafeAreaView';
|
||||
import { withDimensions } from '../../dimensions';
|
||||
import { getHeaderTitlePosition } from '../../containers/Header';
|
||||
|
||||
const stateAttrsUpdate = [
|
||||
'joined',
|
||||
|
@ -91,7 +93,8 @@ class RoomView extends React.Component {
|
|||
theme: PropTypes.string,
|
||||
replyBroadcast: PropTypes.func,
|
||||
width: PropTypes.number,
|
||||
height: PropTypes.number
|
||||
height: PropTypes.number,
|
||||
insets: PropTypes.object
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
|
@ -178,7 +181,7 @@ class RoomView extends React.Component {
|
|||
shouldComponentUpdate(nextProps, nextState) {
|
||||
const { state } = this;
|
||||
const { roomUpdate, member } = state;
|
||||
const { appState, theme } = this.props;
|
||||
const { appState, theme, insets } = this.props;
|
||||
if (theme !== nextProps.theme) {
|
||||
return true;
|
||||
}
|
||||
|
@ -192,12 +195,15 @@ class RoomView extends React.Component {
|
|||
if (stateUpdated) {
|
||||
return true;
|
||||
}
|
||||
if (!isEqual(nextProps.insets, insets)) {
|
||||
return true;
|
||||
}
|
||||
return roomAttrsUpdate.some(key => !isEqual(nextState.roomUpdate[key], roomUpdate[key]));
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
const { roomUpdate } = this.state;
|
||||
const { appState } = this.props;
|
||||
const { appState, insets } = this.props;
|
||||
|
||||
if (appState === 'foreground' && appState !== prevProps.appState && this.rid) {
|
||||
this.onForegroundInteraction = InteractionManager.runAfterInteractions(() => {
|
||||
|
@ -222,6 +228,9 @@ class RoomView extends React.Component {
|
|||
if (((roomUpdate.fname !== prevState.roomUpdate.fname) || (roomUpdate.name !== prevState.roomUpdate.name)) && !this.tmid) {
|
||||
this.setHeader();
|
||||
}
|
||||
if (insets.left !== prevProps.insets.left || insets.right !== prevProps.insets.right) {
|
||||
this.setHeader();
|
||||
}
|
||||
this.setReadOnly();
|
||||
}
|
||||
|
||||
|
@ -281,7 +290,7 @@ class RoomView extends React.Component {
|
|||
setHeader = () => {
|
||||
const { room, unreadsCount, roomUserId: stateRoomUserId } = this.state;
|
||||
const {
|
||||
navigation, route, isMasterDetail, theme, baseUrl, user
|
||||
navigation, route, isMasterDetail, theme, baseUrl, user, insets
|
||||
} = this.props;
|
||||
const rid = route.params?.rid;
|
||||
const prid = route.params?.prid;
|
||||
|
@ -299,9 +308,29 @@ class RoomView extends React.Component {
|
|||
if (!rid) {
|
||||
return;
|
||||
}
|
||||
const headerTitlePosition = getHeaderTitlePosition(insets);
|
||||
navigation.setOptions({
|
||||
headerShown: true,
|
||||
headerTitleAlign: 'left',
|
||||
headerTitleContainerStyle: {
|
||||
left: headerTitlePosition.left,
|
||||
right: headerTitlePosition.right
|
||||
},
|
||||
headerLeft: () => (
|
||||
<LeftButtons
|
||||
tmid={tmid}
|
||||
unreadsCount={unreadsCount}
|
||||
navigation={navigation}
|
||||
baseUrl={baseUrl}
|
||||
userId={userId}
|
||||
token={token}
|
||||
title={avatar}
|
||||
theme={theme}
|
||||
t={t}
|
||||
goRoomActionsView={this.goRoomActionsView}
|
||||
isMasterDetail={isMasterDetail}
|
||||
/>
|
||||
),
|
||||
headerTitle: () => (
|
||||
<RoomHeaderView
|
||||
rid={rid}
|
||||
|
@ -323,21 +352,6 @@ class RoomView extends React.Component {
|
|||
navigation={navigation}
|
||||
toggleFollowThread={this.toggleFollowThread}
|
||||
/>
|
||||
),
|
||||
headerLeft: () => (
|
||||
<RoomHeaderLeft
|
||||
tmid={tmid}
|
||||
unreadsCount={unreadsCount}
|
||||
navigation={navigation}
|
||||
baseUrl={baseUrl}
|
||||
userId={userId}
|
||||
token={token}
|
||||
title={avatar}
|
||||
theme={theme}
|
||||
t={t}
|
||||
goRoomActionsView={this.goRoomActionsView}
|
||||
isMasterDetail={isMasterDetail}
|
||||
/>
|
||||
)
|
||||
});
|
||||
}
|
||||
|
@ -1040,4 +1054,4 @@ const mapDispatchToProps = dispatch => ({
|
|||
replyBroadcast: message => dispatch(replyBroadcastAction(message))
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withDimensions(withTheme(RoomView)));
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withDimensions(withTheme(withSafeAreaInsets(RoomView))));
|
||||
|
|
|
@ -1,95 +0,0 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
Text, View, TouchableOpacity, StyleSheet
|
||||
} from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import I18n from '../../../i18n';
|
||||
import sharedStyles from '../../Styles';
|
||||
import { themes } from '../../../constants/colors';
|
||||
import { CustomIcon } from '../../../lib/Icons';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
button: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center'
|
||||
},
|
||||
title: {
|
||||
fontSize: 14,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
server: {
|
||||
fontSize: 12,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
disclosure: {
|
||||
marginLeft: 3,
|
||||
marginTop: 1,
|
||||
width: 12,
|
||||
height: 9
|
||||
},
|
||||
upsideDown: {
|
||||
transform: [{ scaleY: -1 }]
|
||||
}
|
||||
});
|
||||
|
||||
const HeaderTitle = React.memo(({ connecting, isFetching, theme }) => {
|
||||
let title = I18n.t('Messages');
|
||||
if (connecting) {
|
||||
title = I18n.t('Connecting');
|
||||
}
|
||||
if (isFetching) {
|
||||
title = I18n.t('Updating');
|
||||
}
|
||||
return <Text style={[styles.title, { color: themes[theme].headerTitleColor }]}>{title}</Text>;
|
||||
});
|
||||
|
||||
const Header = React.memo(({
|
||||
connecting, isFetching, serverName, showServerDropdown, onPress, theme
|
||||
}) => (
|
||||
<View style={styles.container}>
|
||||
<TouchableOpacity
|
||||
onPress={onPress}
|
||||
testID='rooms-list-header-server-dropdown-button'
|
||||
style={styles.container}
|
||||
// disabled={connecting || isFetching}
|
||||
>
|
||||
<HeaderTitle connecting={connecting} isFetching={isFetching} theme={theme} />
|
||||
<View style={styles.button}>
|
||||
<Text style={[styles.server, { color: themes[theme].headerTintColor }]} numberOfLines={1}>{serverName}</Text>
|
||||
<CustomIcon
|
||||
name='chevron-down'
|
||||
color={themes[theme].headerTintColor}
|
||||
style={[showServerDropdown && styles.upsideDown]}
|
||||
size={18}
|
||||
/>
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
));
|
||||
|
||||
Header.propTypes = {
|
||||
connecting: PropTypes.bool,
|
||||
isFetching: PropTypes.bool,
|
||||
serverName: PropTypes.string,
|
||||
theme: PropTypes.string,
|
||||
showServerDropdown: PropTypes.bool.isRequired,
|
||||
onPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
Header.defaultProps = {
|
||||
serverName: 'Rocket.Chat'
|
||||
};
|
||||
|
||||
HeaderTitle.propTypes = {
|
||||
connecting: PropTypes.bool,
|
||||
isFetching: PropTypes.bool,
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
||||
export default Header;
|
|
@ -9,26 +9,23 @@ import I18n from '../../../i18n';
|
|||
import sharedStyles from '../../Styles';
|
||||
import { themes } from '../../../constants/colors';
|
||||
import { CustomIcon } from '../../../lib/Icons';
|
||||
import { isTablet, isIOS } from '../../../utils/deviceInfo';
|
||||
import { useOrientation } from '../../../dimensions';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
justifyContent: 'center'
|
||||
justifyContent: 'center',
|
||||
marginLeft: isTablet ? 10 : 0
|
||||
},
|
||||
button: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
marginRight: 64
|
||||
alignItems: 'center'
|
||||
},
|
||||
server: {
|
||||
fontSize: 20,
|
||||
...sharedStyles.textRegular
|
||||
title: {
|
||||
...sharedStyles.textSemibold
|
||||
},
|
||||
serverSmall: {
|
||||
fontSize: 16
|
||||
},
|
||||
updating: {
|
||||
fontSize: 14,
|
||||
subtitle: {
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
upsideDown: {
|
||||
|
@ -37,41 +34,55 @@ const styles = StyleSheet.create({
|
|||
});
|
||||
|
||||
const Header = React.memo(({
|
||||
connecting, isFetching, serverName, showServerDropdown, showSearchHeader, theme, onSearchChangeText, onPress
|
||||
connecting, connected, isFetching, serverName, server, showServerDropdown, showSearchHeader, theme, onSearchChangeText, onPress
|
||||
}) => {
|
||||
const titleColorStyle = { color: themes[theme].headerTitleColor };
|
||||
const isLight = theme === 'light';
|
||||
const { isLandscape } = useOrientation();
|
||||
const scale = isIOS && isLandscape && !isTablet ? 0.8 : 1;
|
||||
const titleFontSize = 16 * scale;
|
||||
const subTitleFontSize = 12 * scale;
|
||||
|
||||
if (showSearchHeader) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TextInput
|
||||
autoFocus
|
||||
style={[styles.server, isLight && titleColorStyle]}
|
||||
style={[styles.title, isLight && titleColorStyle, { fontSize: titleFontSize }]}
|
||||
placeholder='Search'
|
||||
onChangeText={onSearchChangeText}
|
||||
theme={theme}
|
||||
testID='rooms-list-view-search-input'
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
let subtitle;
|
||||
if (connecting) {
|
||||
subtitle = I18n.t('Connecting');
|
||||
} else if (isFetching) {
|
||||
subtitle = I18n.t('Updating');
|
||||
} else if (!connected) {
|
||||
subtitle = I18n.t('Waiting_for_network');
|
||||
} else {
|
||||
subtitle = server?.replace(/(^\w+:|^)\/\//, '');
|
||||
}
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TouchableOpacity
|
||||
onPress={onPress}
|
||||
testID='rooms-list-header-server-dropdown-button'
|
||||
disabled={connecting || isFetching}
|
||||
>
|
||||
{connecting ? <Text style={[styles.updating, titleColorStyle]}>{I18n.t('Connecting')}</Text> : null}
|
||||
{isFetching ? <Text style={[styles.updating, titleColorStyle]}>{I18n.t('Updating')}</Text> : null}
|
||||
<View style={styles.button}>
|
||||
<Text style={[styles.server, isFetching && styles.serverSmall, titleColorStyle]} numberOfLines={1}>{serverName}</Text>
|
||||
<Text style={[styles.title, isFetching && styles.serverSmall, titleColorStyle, { fontSize: titleFontSize }]} numberOfLines={1}>{serverName}</Text>
|
||||
<CustomIcon
|
||||
name='chevron-down'
|
||||
color={themes[theme].headerTintColor}
|
||||
style={[showServerDropdown && styles.upsideDown]}
|
||||
style={[showServerDropdown && styles.upsideDown, { fontSize: subTitleFontSize }]}
|
||||
size={18}
|
||||
/>
|
||||
</View>
|
||||
{subtitle ? <Text style={[styles.subtitle, { color: themes[theme].auxiliaryText }]} numberOfLines={1}>{subtitle}</Text> : null}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
|
@ -83,8 +94,10 @@ Header.propTypes = {
|
|||
onPress: PropTypes.func.isRequired,
|
||||
onSearchChangeText: PropTypes.func.isRequired,
|
||||
connecting: PropTypes.bool,
|
||||
connected: PropTypes.bool,
|
||||
isFetching: PropTypes.bool,
|
||||
serverName: PropTypes.string,
|
||||
server: PropTypes.string,
|
||||
theme: PropTypes.string
|
||||
};
|
||||
|
|
@ -18,8 +18,10 @@ class RoomsListHeaderView extends PureComponent {
|
|||
showSearchHeader: PropTypes.bool,
|
||||
serverName: PropTypes.string,
|
||||
connecting: PropTypes.bool,
|
||||
connected: PropTypes.bool,
|
||||
isFetching: PropTypes.bool,
|
||||
theme: PropTypes.string,
|
||||
server: PropTypes.string,
|
||||
open: PropTypes.func,
|
||||
close: PropTypes.func,
|
||||
closeSort: PropTypes.func,
|
||||
|
@ -68,16 +70,18 @@ class RoomsListHeaderView extends PureComponent {
|
|||
|
||||
render() {
|
||||
const {
|
||||
serverName, showServerDropdown, showSearchHeader, connecting, isFetching, theme
|
||||
serverName, showServerDropdown, showSearchHeader, connecting, connected, isFetching, theme, server
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Header
|
||||
theme={theme}
|
||||
serverName={serverName}
|
||||
server={server}
|
||||
showServerDropdown={showServerDropdown}
|
||||
showSearchHeader={showSearchHeader}
|
||||
connecting={connecting}
|
||||
connected={connected}
|
||||
isFetching={isFetching}
|
||||
onPress={this.onPress}
|
||||
onSearchChangeText={this.onSearchChangeText}
|
||||
|
@ -91,8 +95,10 @@ const mapStateToProps = state => ({
|
|||
showSortDropdown: state.rooms.showSortDropdown,
|
||||
showSearchHeader: state.rooms.showSearchHeader,
|
||||
connecting: state.meteor.connecting || state.server.loading,
|
||||
connected: state.meteor.connected,
|
||||
isFetching: state.rooms.isFetching,
|
||||
serverName: state.settings.Site_Name
|
||||
serverName: state.settings.Site_Name,
|
||||
server: state.server.server
|
||||
});
|
||||
|
||||
const mapDispatchtoProps = dispatch => ({
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import SearchBox from '../../../containers/SearchBox';
|
||||
import { isIOS } from '../../../utils/deviceInfo';
|
||||
import { withTheme } from '../../../theme';
|
||||
|
||||
const SearchBar = React.memo(({
|
||||
theme, onChangeSearchText, inputRef, searching, onCancelSearchPress, onSearchFocus
|
||||
}) => {
|
||||
if (isIOS) {
|
||||
return (
|
||||
<SearchBox
|
||||
onChangeText={onChangeSearchText}
|
||||
testID='rooms-list-view-search'
|
||||
inputRef={inputRef}
|
||||
theme={theme}
|
||||
hasCancel={searching}
|
||||
onCancelPress={onCancelSearchPress}
|
||||
onFocus={onSearchFocus}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
SearchBar.propTypes = {
|
||||
theme: PropTypes.string,
|
||||
searching: PropTypes.bool,
|
||||
inputRef: PropTypes.func,
|
||||
onChangeSearchText: PropTypes.func,
|
||||
onCancelSearchPress: PropTypes.func,
|
||||
onSearchFocus: PropTypes.func
|
||||
};
|
||||
|
||||
export default withTheme(SearchBar);
|
|
@ -1,28 +1,16 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import SearchBar from './SearchBar';
|
||||
import Directory from './Directory';
|
||||
import Sort from './Sort';
|
||||
|
||||
const ListHeader = React.memo(({
|
||||
searching,
|
||||
sortBy,
|
||||
onChangeSearchText,
|
||||
toggleSort,
|
||||
goDirectory,
|
||||
inputRef,
|
||||
onCancelSearchPress,
|
||||
onSearchFocus
|
||||
goDirectory
|
||||
}) => (
|
||||
<>
|
||||
<SearchBar
|
||||
inputRef={inputRef}
|
||||
searching={searching}
|
||||
onChangeSearchText={onChangeSearchText}
|
||||
onCancelSearchPress={onCancelSearchPress}
|
||||
onSearchFocus={onSearchFocus}
|
||||
/>
|
||||
<Directory searching={searching} goDirectory={goDirectory} />
|
||||
<Sort searching={searching} sortBy={sortBy} toggleSort={toggleSort} />
|
||||
</>
|
||||
|
@ -31,12 +19,8 @@ const ListHeader = React.memo(({
|
|||
ListHeader.propTypes = {
|
||||
searching: PropTypes.bool,
|
||||
sortBy: PropTypes.string,
|
||||
onChangeSearchText: PropTypes.func,
|
||||
toggleSort: PropTypes.func,
|
||||
goDirectory: PropTypes.func,
|
||||
inputRef: PropTypes.func,
|
||||
onCancelSearchPress: PropTypes.func,
|
||||
onSearchFocus: PropTypes.func
|
||||
goDirectory: PropTypes.func
|
||||
};
|
||||
|
||||
export default ListHeader;
|
||||
|
|
|
@ -12,6 +12,7 @@ import { connect } from 'react-redux';
|
|||
import { isEqual, orderBy } from 'lodash';
|
||||
import Orientation from 'react-native-orientation-locker';
|
||||
import { Q } from '@nozbe/watermelondb';
|
||||
import { withSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
import database from '../../lib/database';
|
||||
import RocketChat from '../../lib/rocketchat';
|
||||
|
@ -30,7 +31,7 @@ import {
|
|||
} from '../../actions/rooms';
|
||||
import { appStart as appStartAction, ROOT_BACKGROUND } from '../../actions/app';
|
||||
import debounce from '../../utils/debounce';
|
||||
import { isIOS, isAndroid, isTablet } from '../../utils/deviceInfo';
|
||||
import { isIOS, isTablet } from '../../utils/deviceInfo';
|
||||
import RoomsListHeaderView from './Header';
|
||||
import {
|
||||
DrawerButton,
|
||||
|
@ -59,10 +60,9 @@ import { MAX_SIDEBAR_WIDTH } from '../../constants/tablet';
|
|||
import { getUserSelector } from '../../selectors/login';
|
||||
import { goRoom } from '../../utils/goRoom';
|
||||
import SafeAreaView from '../../containers/SafeAreaView';
|
||||
import Header from '../../containers/Header';
|
||||
import Header, { getHeaderTitlePosition } from '../../containers/Header';
|
||||
import { withDimensions } from '../../dimensions';
|
||||
|
||||
const SCROLL_OFFSET = 56;
|
||||
const INITIAL_NUM_TO_RENDER = isTablet ? 20 : 12;
|
||||
const CHATS_HEADER = 'Chats';
|
||||
const UNREAD_HEADER = 'Unread';
|
||||
|
@ -129,7 +129,8 @@ class RoomsListView extends React.Component {
|
|||
connected: PropTypes.bool,
|
||||
isMasterDetail: PropTypes.bool,
|
||||
rooms: PropTypes.array,
|
||||
width: PropTypes.number
|
||||
width: PropTypes.number,
|
||||
insets: PropTypes.object
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
|
@ -242,7 +243,7 @@ class RoomsListView extends React.Component {
|
|||
loading,
|
||||
search
|
||||
} = this.state;
|
||||
const { rooms, width } = this.props;
|
||||
const { rooms, width, insets } = this.props;
|
||||
if (nextState.loading !== loading) {
|
||||
return true;
|
||||
}
|
||||
|
@ -255,6 +256,9 @@ class RoomsListView extends React.Component {
|
|||
if (!isEqual(nextProps.rooms, rooms)) {
|
||||
return true;
|
||||
}
|
||||
if (!isEqual(nextProps.insets, insets)) {
|
||||
return true;
|
||||
}
|
||||
// If it's focused and there are changes, update
|
||||
if (chatsNotEqual) {
|
||||
this.shouldUpdate = false;
|
||||
|
@ -273,7 +277,8 @@ class RoomsListView extends React.Component {
|
|||
connected,
|
||||
roomsRequest,
|
||||
rooms,
|
||||
isMasterDetail
|
||||
isMasterDetail,
|
||||
insets
|
||||
} = this.props;
|
||||
const { item } = this.state;
|
||||
|
||||
|
@ -298,6 +303,9 @@ class RoomsListView extends React.Component {
|
|||
// eslint-disable-next-line react/no-did-update-set-state
|
||||
this.setState({ item: { rid: rooms[0] } });
|
||||
}
|
||||
if (insets.left !== prevProps.insets.left || insets.right !== prevProps.insets.right) {
|
||||
this.setHeader();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -318,9 +326,11 @@ class RoomsListView extends React.Component {
|
|||
|
||||
getHeader = () => {
|
||||
const { searching } = this.state;
|
||||
const { navigation, isMasterDetail } = this.props;
|
||||
const { navigation, isMasterDetail, insets } = this.props;
|
||||
const headerTitlePosition = getHeaderTitlePosition(insets);
|
||||
return {
|
||||
headerLeft: () => (searching && isAndroid ? (
|
||||
headerTitleAlign: 'left',
|
||||
headerLeft: () => (searching ? (
|
||||
<CustomHeaderButtons left>
|
||||
<Item
|
||||
title='cancel'
|
||||
|
@ -332,24 +342,31 @@ class RoomsListView extends React.Component {
|
|||
<DrawerButton
|
||||
navigation={navigation}
|
||||
testID='rooms-list-view-sidebar'
|
||||
onPress={isMasterDetail ? () => navigation.navigate('ModalStackNavigator', { screen: 'SettingsView' }) : () => navigation.toggleDrawer()}
|
||||
onPress={isMasterDetail
|
||||
? () => navigation.navigate('ModalStackNavigator', { screen: 'SettingsView' })
|
||||
: () => navigation.toggleDrawer()}
|
||||
/>
|
||||
)),
|
||||
headerTitle: () => <RoomsListHeaderView />,
|
||||
headerRight: () => (searching && isAndroid ? null : (
|
||||
headerTitleContainerStyle: {
|
||||
left: headerTitlePosition.left,
|
||||
right: headerTitlePosition.right
|
||||
},
|
||||
headerRight: () => (searching ? null : (
|
||||
<CustomHeaderButtons>
|
||||
{isAndroid ? (
|
||||
<Item
|
||||
title='new'
|
||||
iconName='new-chat'
|
||||
onPress={isMasterDetail
|
||||
? () => navigation.navigate('ModalStackNavigator', { screen: 'NewMessageView' })
|
||||
: () => navigation.navigate('NewMessageStackNavigator')}
|
||||
testID='rooms-list-view-create-channel'
|
||||
/>
|
||||
<Item
|
||||
title='search'
|
||||
iconName='magnifier'
|
||||
onPress={this.initSearching}
|
||||
/>
|
||||
) : null}
|
||||
<Item
|
||||
title='new'
|
||||
iconName='new-chat'
|
||||
onPress={isMasterDetail ? () => navigation.navigate('ModalStackNavigator', { screen: 'NewMessageView' }) : () => navigation.navigate('NewMessageStackNavigator')}
|
||||
testID='rooms-list-view-create-channel'
|
||||
testID='rooms-list-view-search'
|
||||
/>
|
||||
</CustomHeaderButtons>
|
||||
))
|
||||
|
@ -476,10 +493,8 @@ class RoomsListView extends React.Component {
|
|||
initSearching = () => {
|
||||
const { openSearchHeader } = this.props;
|
||||
this.internalSetState({ searching: true }, () => {
|
||||
if (isAndroid) {
|
||||
openSearchHeader();
|
||||
this.setHeader();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -493,18 +508,11 @@ class RoomsListView extends React.Component {
|
|||
|
||||
Keyboard.dismiss();
|
||||
|
||||
if (isIOS && this.inputRef) {
|
||||
this.inputRef.blur();
|
||||
this.inputRef.clear();
|
||||
}
|
||||
|
||||
this.setState({ searching: false, search: [] }, () => {
|
||||
if (isAndroid) {
|
||||
this.setHeader();
|
||||
closeSearchHeader();
|
||||
}
|
||||
setTimeout(() => {
|
||||
const offset = isAndroid ? 0 : SCROLL_OFFSET;
|
||||
const offset = 0;
|
||||
if (this.scroll.scrollTo) {
|
||||
this.scroll.scrollTo({ x: 0, y: offset, animated: true });
|
||||
} else if (this.scroll.scrollToOffset) {
|
||||
|
@ -564,7 +572,7 @@ class RoomsListView extends React.Component {
|
|||
toggleSort = () => {
|
||||
const { toggleSortDropdown } = this.props;
|
||||
|
||||
const offset = isAndroid ? 0 : SCROLL_OFFSET;
|
||||
const offset = 0;
|
||||
if (this.scroll.scrollTo) {
|
||||
this.scroll.scrollTo({ x: 0, y: offset, animated: true });
|
||||
} else if (this.scroll.scrollToOffset) {
|
||||
|
@ -714,8 +722,7 @@ class RoomsListView extends React.Component {
|
|||
if (handleCommandShowPreferences(event)) {
|
||||
navigation.navigate('SettingsView');
|
||||
} else if (handleCommandSearching(event)) {
|
||||
this.scroll.scrollToOffset({ animated: true, offset: 0 });
|
||||
this.inputRef.focus();
|
||||
this.initSearching();
|
||||
} else if (handleCommandSelectRoom(event)) {
|
||||
this.goRoomByIndex(input);
|
||||
} else if (handleCommandPreviousRoom(event)) {
|
||||
|
@ -744,19 +751,13 @@ class RoomsListView extends React.Component {
|
|||
|
||||
getScrollRef = ref => (this.scroll = ref);
|
||||
|
||||
getInputRef = ref => (this.inputRef = ref);
|
||||
|
||||
renderListHeader = () => {
|
||||
const { searching } = this.state;
|
||||
const { sortBy } = this.props;
|
||||
return (
|
||||
<ListHeader
|
||||
inputRef={this.getInputRef}
|
||||
searching={searching}
|
||||
sortBy={sortBy}
|
||||
onChangeSearchText={this.search}
|
||||
onCancelSearchPress={this.cancelSearch}
|
||||
onSearchFocus={this.initSearching}
|
||||
toggleSort={this.toggleSort}
|
||||
goDirectory={this.goDirectory}
|
||||
/>
|
||||
|
@ -869,7 +870,6 @@ class RoomsListView extends React.Component {
|
|||
ref={this.getScrollRef}
|
||||
data={searching ? search : chats}
|
||||
extraData={searching ? search : chats}
|
||||
contentOffset={isIOS ? { x: 0, y: SCROLL_OFFSET } : {}}
|
||||
keyExtractor={keyExtractor}
|
||||
style={[styles.list, { backgroundColor: themes[theme].backgroundColor }]}
|
||||
renderItem={this.renderItem}
|
||||
|
@ -953,4 +953,4 @@ const mapDispatchToProps = dispatch => ({
|
|||
closeServerDropdown: () => dispatch(closeServerDropdownAction())
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withDimensions(withTheme(RoomsListView)));
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withDimensions(withTheme(withSafeAreaInsets(RoomsListView))));
|
||||
|
|
|
@ -23,7 +23,7 @@ export default StyleSheet.create({
|
|||
sortToggleText: {
|
||||
fontSize: 16,
|
||||
flex: 1,
|
||||
marginLeft: 15,
|
||||
marginLeft: 12,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
dropdownContainer: {
|
||||
|
@ -50,16 +50,16 @@ export default StyleSheet.create({
|
|||
},
|
||||
sortSeparator: {
|
||||
height: StyleSheet.hairlineWidth,
|
||||
marginHorizontal: 15,
|
||||
marginHorizontal: 12,
|
||||
flex: 1
|
||||
},
|
||||
sortIcon: {
|
||||
width: 22,
|
||||
height: 22,
|
||||
marginHorizontal: 15
|
||||
marginHorizontal: 12
|
||||
},
|
||||
groupTitleContainer: {
|
||||
paddingHorizontal: 15,
|
||||
paddingHorizontal: 12,
|
||||
paddingTop: 17,
|
||||
paddingBottom: 10
|
||||
},
|
||||
|
@ -75,12 +75,12 @@ export default StyleSheet.create({
|
|||
},
|
||||
serverHeaderText: {
|
||||
fontSize: 16,
|
||||
marginLeft: 15,
|
||||
marginLeft: 12,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
serverHeaderAdd: {
|
||||
fontSize: 16,
|
||||
marginRight: 15,
|
||||
marginRight: 12,
|
||||
paddingVertical: 10,
|
||||
...sharedStyles.textRegular
|
||||
},
|
||||
|
@ -95,7 +95,7 @@ export default StyleSheet.create({
|
|||
serverIcon: {
|
||||
width: 42,
|
||||
height: 42,
|
||||
marginHorizontal: 15,
|
||||
marginHorizontal: 12,
|
||||
marginVertical: 13,
|
||||
borderRadius: 4,
|
||||
resizeMode: 'contain'
|
||||
|
@ -120,7 +120,7 @@ export default StyleSheet.create({
|
|||
directoryIcon: {
|
||||
width: 22,
|
||||
height: 22,
|
||||
marginHorizontal: 15
|
||||
marginHorizontal: 12
|
||||
},
|
||||
directoryText: {
|
||||
fontSize: 16,
|
||||
|
|
|
@ -72,6 +72,14 @@ async function sleep(ms) {
|
|||
return new Promise(res => setTimeout(res, ms));
|
||||
}
|
||||
|
||||
async function searchRoom(room) {
|
||||
await element(by.id('rooms-list-view-search')).tap();
|
||||
await expect(element(by.id('rooms-list-view-search-input'))).toExist();
|
||||
await waitFor(element(by.id('rooms-list-view-search-input'))).toExist().withTimeout(5000);
|
||||
await element(by.id('rooms-list-view-search-input')).typeText(room);
|
||||
await sleep(2000);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
navigateToWorkspace,
|
||||
navigateToLogin,
|
||||
|
@ -80,5 +88,6 @@ module.exports = {
|
|||
logout,
|
||||
createUser,
|
||||
tapBack,
|
||||
sleep
|
||||
sleep,
|
||||
searchRoom
|
||||
};
|
|
@ -3,7 +3,7 @@ const {
|
|||
} = require('detox');
|
||||
const OTP = require('otp.js');
|
||||
const GA = OTP.googleAuthenticator;
|
||||
const { navigateToLogin, login, tapBack, sleep, createUser } = require('../../helpers/app');
|
||||
const { navigateToLogin, login, tapBack, sleep, searchRoom } = require('../../helpers/app');
|
||||
const data = require('../../data');
|
||||
|
||||
describe('Broadcast room', () => {
|
||||
|
@ -73,9 +73,7 @@ describe('Broadcast room', () => {
|
|||
await sleep(1000);
|
||||
await element(by.id('two-factor-send')).tap();
|
||||
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(10000);
|
||||
await element(by.type('UIScrollView')).atIndex(1).scrollTo('top');
|
||||
await element(by.id('rooms-list-view-search')).typeText(`broadcast${ data.random }`);
|
||||
await sleep(2000);
|
||||
await searchRoom(`broadcast${ data.random }`);
|
||||
await waitFor(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist().withTimeout(60000);
|
||||
await expect(element(by.id(`rooms-list-view-item-broadcast${ data.random }`))).toExist();
|
||||
await element(by.id(`rooms-list-view-item-broadcast${ data.random }`)).tap();
|
||||
|
|
|
@ -2,7 +2,7 @@ const {
|
|||
device, expect, element, by, waitFor
|
||||
} = require('detox');
|
||||
const data = require('../../data');
|
||||
const { tapBack, sleep } = require('../../helpers/app');
|
||||
const { tapBack, sleep, searchRoom } = require('../../helpers/app');
|
||||
|
||||
const room = 'detox-public';
|
||||
|
||||
|
@ -16,9 +16,7 @@ async function mockMessage(message) {
|
|||
|
||||
async function navigateToRoom() {
|
||||
await sleep(2000);
|
||||
await element(by.type('UIScrollView')).atIndex(1).scrollTo('top');
|
||||
await element(by.id('rooms-list-view-search')).typeText(room);
|
||||
await sleep(2000);
|
||||
await searchRoom(room);
|
||||
await waitFor(element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0)).toBeVisible().withTimeout(60000);
|
||||
await element(by.id(`rooms-list-view-item-${ room }`)).atIndex(0).tap();
|
||||
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
const {
|
||||
device, expect, element, by, waitFor
|
||||
} = require('detox');
|
||||
const { logout, tapBack, sleep } = require('../../helpers/app');
|
||||
const { logout, tapBack, sleep, searchRoom } = require('../../helpers/app');
|
||||
|
||||
describe('Rooms list screen', () => {
|
||||
describe('Render', () => {
|
||||
|
@ -27,10 +27,7 @@ describe('Rooms list screen', () => {
|
|||
|
||||
describe('Usage', () => {
|
||||
it('should search room and navigate', async() => {
|
||||
await element(by.type('UIScrollView')).atIndex(1).scrollTo('top');
|
||||
await waitFor(element(by.id('rooms-list-view-search'))).toExist().withTimeout(2000);
|
||||
await element(by.id('rooms-list-view-search')).typeText('rocket.cat');
|
||||
await sleep(2000);
|
||||
await searchRoom('rocket.cat');
|
||||
await waitFor(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible().withTimeout(60000);
|
||||
await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toBeVisible();
|
||||
await element(by.id('rooms-list-view-item-rocket.cat')).tap();
|
||||
|
@ -41,7 +38,6 @@ describe('Rooms list screen', () => {
|
|||
await tapBack();
|
||||
await waitFor(element(by.id('rooms-list-view'))).toBeVisible().withTimeout(2000);
|
||||
await expect(element(by.id('rooms-list-view'))).toBeVisible();
|
||||
// await element(by.id('rooms-list-view-search')).typeText('');
|
||||
await sleep(2000);
|
||||
await waitFor(element(by.id('rooms-list-view-item-rocket.cat'))).toExist().withTimeout(60000);
|
||||
await expect(element(by.id('rooms-list-view-item-rocket.cat'))).toExist();
|
||||
|
|
|
@ -2,7 +2,7 @@ const {
|
|||
device, expect, element, by, waitFor
|
||||
} = require('detox');
|
||||
const data = require('../../data');
|
||||
const { tapBack, sleep } = require('../../helpers/app');
|
||||
const { tapBack, sleep, searchRoom } = require('../../helpers/app');
|
||||
|
||||
async function mockMessage(message) {
|
||||
await element(by.id('messagebox-input')).tap();
|
||||
|
@ -13,9 +13,7 @@ async function mockMessage(message) {
|
|||
};
|
||||
|
||||
async function navigateToRoom() {
|
||||
await element(by.type('UIScrollView')).atIndex(1).scrollTo('top');
|
||||
await element(by.id('rooms-list-view-search')).typeText(`private${ data.random }`);
|
||||
await sleep(2000);
|
||||
await searchRoom(`private${ data.random }`);
|
||||
await waitFor(element(by.id(`rooms-list-view-item-private${ data.random }`))).toExist().withTimeout(60000);
|
||||
await element(by.id(`rooms-list-view-item-private${ data.random }`)).tap();
|
||||
await waitFor(element(by.id('room-view'))).toBeVisible().withTimeout(5000);
|
||||
|
|
|
@ -2,7 +2,7 @@ const {
|
|||
device, expect, element, by, waitFor
|
||||
} = require('detox');
|
||||
const data = require('../../data');
|
||||
const { tapBack, sleep } = require('../../helpers/app');
|
||||
const { tapBack, sleep, searchRoom } = require('../../helpers/app');
|
||||
|
||||
const scrollDown = 200;
|
||||
|
||||
|
@ -13,10 +13,7 @@ async function navigateToRoomActions(type) {
|
|||
} else {
|
||||
room = `private${ data.random }`;
|
||||
}
|
||||
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(10000);
|
||||
await element(by.type('UIScrollView')).atIndex(1).scrollTo('top');
|
||||
await element(by.id('rooms-list-view-search')).typeText(room);
|
||||
await sleep(2000);
|
||||
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 waitFor(element(by.id('room-view'))).toExist().withTimeout(2000);
|
||||
|
|
|
@ -2,7 +2,7 @@ const {
|
|||
device, expect, element, by, waitFor
|
||||
} = require('detox');
|
||||
const data = require('../../data');
|
||||
const { tapBack, sleep } = require('../../helpers/app');
|
||||
const { tapBack, sleep, searchRoom } = require('../../helpers/app');
|
||||
|
||||
async function navigateToRoomInfo(type) {
|
||||
let room;
|
||||
|
@ -11,10 +11,7 @@ async function navigateToRoomInfo(type) {
|
|||
} else {
|
||||
room = `private${ data.random }`;
|
||||
}
|
||||
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(10000);
|
||||
await element(by.type('UIScrollView')).atIndex(1).swipe('down');
|
||||
await element(by.id('rooms-list-view-search')).typeText(room);
|
||||
await sleep(2000);
|
||||
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 waitFor(element(by.id('room-view'))).toExist().withTimeout(2000);
|
||||
|
@ -311,7 +308,6 @@ describe('Room info screen', () => {
|
|||
await expect(element(by.text('Yes, delete it!'))).toExist();
|
||||
await element(by.text('Yes, delete it!')).tap();
|
||||
await waitFor(element(by.id('rooms-list-view'))).toExist().withTimeout(10000);
|
||||
// await element(by.id('rooms-list-view-search')).typeText('');
|
||||
await sleep(2000);
|
||||
await waitFor(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible().withTimeout(60000);
|
||||
await expect(element(by.id(`rooms-list-view-item-${ room }`))).toBeNotVisible();
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
diff --git a/node_modules/react-navigation-header-buttons/src/HeaderButtons.js b/node_modules/react-navigation-header-buttons/src/HeaderButtons.js
|
||||
index 70ff376..01fba5e 100644
|
||||
--- a/node_modules/react-navigation-header-buttons/src/HeaderButtons.js
|
||||
+++ b/node_modules/react-navigation-header-buttons/src/HeaderButtons.js
|
||||
@@ -144,6 +144,6 @@ const styles = StyleSheet.create({
|
||||
}),
|
||||
},
|
||||
button: {
|
||||
- marginHorizontal: 11,
|
||||
+ marginHorizontal: 6
|
||||
},
|
||||
});
|
Loading…
Reference in New Issue