diff --git a/app/constants/colors.js b/app/constants/colors.js
index 7b9d5676..0b0a8ae7 100644
--- a/app/constants/colors.js
+++ b/app/constants/colors.js
@@ -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',
diff --git a/app/containers/DisclosureIndicator.js b/app/containers/DisclosureIndicator.js
index 9d574de6..e33dbe81 100644
--- a/app/containers/DisclosureIndicator.js
+++ b/app/containers/DisclosureIndicator.js
@@ -17,7 +17,7 @@ const styles = StyleSheet.create({
export const DisclosureImage = React.memo(({ theme }) => (
));
diff --git a/app/containers/Header/index.js b/app/containers/Header/index.js
index 249b832e..2137a71a 100644
--- a/app/containers/Header/index.js
+++ b/app/containers/Header/index.js
@@ -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,
diff --git a/app/containers/HeaderButton.js b/app/containers/HeaderButton.js
index 3ac44d45..e712a7e1 100644
--- a/app/containers/HeaderButton.js
+++ b/app/containers/HeaderButton.js
@@ -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 }) => (
-
+
));
diff --git a/app/containers/SearchBox.js b/app/containers/SearchBox.js
index 7c3e337e..89c51def 100644
--- a/app/containers/SearchBox.js
+++ b/app/containers/SearchBox.js
@@ -47,7 +47,7 @@ const styles = StyleSheet.create({
const CancelButton = (onCancelPress, theme) => (
- {I18n.t('Cancel')}
+ {I18n.t('Cancel')}
);
diff --git a/app/containers/StatusBar.js b/app/containers/StatusBar.js
index cc9bd73c..8add422f 100644
--- a/app/containers/StatusBar.js
+++ b/app/containers/StatusBar.js
@@ -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';
}
}
diff --git a/app/i18n/locales/en.js b/app/i18n/locales/en.js
index c973245b..7237b3b2 100644
--- a/app/i18n/locales/en.js
+++ b/app/i18n/locales/en.js
@@ -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?',
diff --git a/app/i18n/locales/pt-BR.js b/app/i18n/locales/pt-BR.js
index 8055e7ea..14c76d81 100644
--- a/app/i18n/locales/pt-BR.js
+++ b/app/i18n/locales/pt-BR.js
@@ -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?',
diff --git a/app/views/RoomView/Header/Header.js b/app/views/RoomView/Header/Header.js
index 58792de2..9022514a 100644
--- a/app/views/RoomView/Header/Header.js
+++ b/app/views/RoomView/Header/Header.js
@@ -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 (
-
+
{
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 (
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;
diff --git a/app/views/RoomView/Header/RightButtons.js b/app/views/RoomView/Header/RightButtons.js
index caec40d2..201866fe 100644
--- a/app/views/RoomView/Header/RightButtons.js
+++ b/app/views/RoomView/Header/RightButtons.js
@@ -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}
+
);
}
diff --git a/app/views/RoomView/Header/index.js b/app/views/RoomView/Header/index.js
index e94b58ca..61b4eb1c 100644
--- a/app/views/RoomView/Header/index.js
+++ b/app/views/RoomView/Header/index.js
@@ -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 (
{
}
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 };
diff --git a/app/views/RoomView/index.js b/app/views/RoomView/index.js
index 62553d36..a4a7549f 100644
--- a/app/views/RoomView/index.js
+++ b/app/views/RoomView/index.js
@@ -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: () => (
+
+ ),
headerTitle: () => (
- ),
- headerLeft: () => (
-
)
});
}
@@ -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))));
diff --git a/app/views/RoomsListView/Header/Header.ios.js b/app/views/RoomsListView/Header/Header.ios.js
deleted file mode 100644
index e45d8bae..00000000
--- a/app/views/RoomsListView/Header/Header.ios.js
+++ /dev/null
@@ -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 {title};
-});
-
-const Header = React.memo(({
- connecting, isFetching, serverName, showServerDropdown, onPress, theme
-}) => (
-
-
-
-
- {serverName}
-
-
-
-
-));
-
-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;
diff --git a/app/views/RoomsListView/Header/Header.android.js b/app/views/RoomsListView/Header/Header.js
similarity index 54%
rename from app/views/RoomsListView/Header/Header.android.js
rename to app/views/RoomsListView/Header/Header.js
index d1d4fb74..839048bc 100644
--- a/app/views/RoomsListView/Header/Header.android.js
+++ b/app/views/RoomsListView/Header/Header.js
@@ -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 (
);
}
+ 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 (
- {connecting ? {I18n.t('Connecting')} : null}
- {isFetching ? {I18n.t('Updating')} : null}
- {serverName}
+ {serverName}
+ {subtitle ? {subtitle} : null}
);
@@ -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
};
diff --git a/app/views/RoomsListView/Header/index.js b/app/views/RoomsListView/Header/index.js
index badcb59a..12a57dc0 100644
--- a/app/views/RoomsListView/Header/index.js
+++ b/app/views/RoomsListView/Header/index.js
@@ -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 (
({
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 => ({
diff --git a/app/views/RoomsListView/ListHeader/SearchBar.js b/app/views/RoomsListView/ListHeader/SearchBar.js
deleted file mode 100644
index 6446d4cf..00000000
--- a/app/views/RoomsListView/ListHeader/SearchBar.js
+++ /dev/null
@@ -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 (
-
- );
- }
- 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);
diff --git a/app/views/RoomsListView/ListHeader/index.js b/app/views/RoomsListView/ListHeader/index.js
index 63d20ca9..dec38b50 100644
--- a/app/views/RoomsListView/ListHeader/index.js
+++ b/app/views/RoomsListView/ListHeader/index.js
@@ -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
}) => (
<>
-
>
@@ -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;
diff --git a/app/views/RoomsListView/index.js b/app/views/RoomsListView/index.js
index 39b035e6..aa5038a4 100644
--- a/app/views/RoomsListView/index.js
+++ b/app/views/RoomsListView/index.js
@@ -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 ? (
- navigation.navigate('ModalStackNavigator', { screen: 'SettingsView' }) : () => navigation.toggleDrawer()}
+ onPress={isMasterDetail
+ ? () => navigation.navigate('ModalStackNavigator', { screen: 'SettingsView' })
+ : () => navigation.toggleDrawer()}
/>
)),
headerTitle: () => ,
- headerRight: () => (searching && isAndroid ? null : (
+ headerTitleContainerStyle: {
+ left: headerTitlePosition.left,
+ right: headerTitlePosition.right
+ },
+ headerRight: () => (searching ? null : (
- {isAndroid ? (
-
- ) : null}
- navigation.navigate('ModalStackNavigator', { screen: 'NewMessageView' }) : () => navigation.navigate('NewMessageStackNavigator')}
+ onPress={isMasterDetail
+ ? () => navigation.navigate('ModalStackNavigator', { screen: 'NewMessageView' })
+ : () => navigation.navigate('NewMessageStackNavigator')}
testID='rooms-list-view-create-channel'
/>
+
))
};
@@ -476,10 +493,8 @@ class RoomsListView extends React.Component {
initSearching = () => {
const { openSearchHeader } = this.props;
this.internalSetState({ searching: true }, () => {
- if (isAndroid) {
- openSearchHeader();
- this.setHeader();
- }
+ 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();
- }
+ 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 (
@@ -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))));
diff --git a/app/views/RoomsListView/styles.js b/app/views/RoomsListView/styles.js
index 9deb03dc..70f2bb6d 100644
--- a/app/views/RoomsListView/styles.js
+++ b/app/views/RoomsListView/styles.js
@@ -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,
diff --git a/e2e/helpers/app.js b/e2e/helpers/app.js
index 5657d4b1..9841c412 100644
--- a/e2e/helpers/app.js
+++ b/e2e/helpers/app.js
@@ -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
};
\ No newline at end of file
diff --git a/e2e/tests/assorted/02-broadcast.spec.js b/e2e/tests/assorted/02-broadcast.spec.js
index 04e3964d..ada0641c 100644
--- a/e2e/tests/assorted/02-broadcast.spec.js
+++ b/e2e/tests/assorted/02-broadcast.spec.js
@@ -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();
diff --git a/e2e/tests/assorted/05-joinpublicroom.spec.js b/e2e/tests/assorted/05-joinpublicroom.spec.js
index 1e284db5..8ebd7146 100644
--- a/e2e/tests/assorted/05-joinpublicroom.spec.js
+++ b/e2e/tests/assorted/05-joinpublicroom.spec.js
@@ -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);
diff --git a/e2e/tests/onboarding/06-roomslist.spec.js b/e2e/tests/onboarding/06-roomslist.spec.js
index 990fe94c..5e7e1e8a 100644
--- a/e2e/tests/onboarding/06-roomslist.spec.js
+++ b/e2e/tests/onboarding/06-roomslist.spec.js
@@ -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();
diff --git a/e2e/tests/room/02-room.spec.js b/e2e/tests/room/02-room.spec.js
index f307e436..95906ae1 100644
--- a/e2e/tests/room/02-room.spec.js
+++ b/e2e/tests/room/02-room.spec.js
@@ -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);
diff --git a/e2e/tests/room/03-roomactions.spec.js b/e2e/tests/room/03-roomactions.spec.js
index 83f989b0..5294389a 100644
--- a/e2e/tests/room/03-roomactions.spec.js
+++ b/e2e/tests/room/03-roomactions.spec.js
@@ -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);
diff --git a/e2e/tests/room/04-roominfo.spec.js b/e2e/tests/room/04-roominfo.spec.js
index 5fc10eb9..35a45441 100644
--- a/e2e/tests/room/04-roominfo.spec.js
+++ b/e2e/tests/room/04-roominfo.spec.js
@@ -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();
diff --git a/patches/react-navigation-header-buttons+3.0.5.patch b/patches/react-navigation-header-buttons+3.0.5.patch
new file mode 100644
index 00000000..3b47953e
--- /dev/null
+++ b/patches/react-navigation-header-buttons+3.0.5.patch
@@ -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
+ },
+ });