diff --git a/NOTICE.txt b/NOTICE.txt
index b6cd29986..1ae5f6e74 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -74,39 +74,6 @@ THE SOFTWARE.
---
-## react-native-search-bar
-
-This product contains a modified portion of 'react-native-search-bar', a high-quality iOS native search bar for react native by Zhao Han.
-
-* HOMEPAGE:
- * https://github.com/umhan35/react-native-search-bar
-
-* LICENSE:
-
-The MIT License (MIT)
-
-Copyright (c) 2015-2016 Zhao Han
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-
----
-
## react-native-keyboard-aware-scroll
This product contains 'react-native-keyboard-aware-scroll', a ScrollView component that handles keyboard appearance and automatically scrolls to focused `TextInput`
diff --git a/app/components/channel_drawer/channel_drawer.js b/app/components/channel_drawer/channel_drawer.js
index bb8b249dd..88fb46ce5 100644
--- a/app/components/channel_drawer/channel_drawer.js
+++ b/app/components/channel_drawer/channel_drawer.js
@@ -11,10 +11,12 @@ import {
} from 'react-native';
import Drawer from 'app/components/drawer';
-import ChannelDrawerList from 'app/components/channel_drawer_list';
-import ChannelDrawerSwiper from 'app/components/channel_drawer_swiper';
-import ChannelDrawerTeams from 'app/components/channel_drawer_teams';
+import ChannelsList from './channels_list';
+import Swiper from './swiper';
+import TeamsList from './teams_list';
+
+import {General} from 'mattermost-redux/constants';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
const DRAWER_INITIAL_OFFSET = 40;
@@ -25,17 +27,19 @@ export default class ChannelDrawer extends PureComponent {
getTeams: PropTypes.func.isRequired,
handleSelectChannel: PropTypes.func.isRequired,
viewChannel: PropTypes.func.isRequired,
+ makeDirectChannel: PropTypes.func.isRequired,
markChannelAsRead: PropTypes.func.isRequired,
setChannelLoading: PropTypes.func.isRequired
}).isRequired,
blurPostTextBox: PropTypes.func.isRequired,
- navigator: PropTypes.object,
children: PropTypes.node,
- currentTeam: PropTypes.object,
- currentChannel: PropTypes.object,
channels: PropTypes.object,
+ currentChannel: PropTypes.object,
channelMembers: PropTypes.object,
+ currentTeam: PropTypes.object,
+ currentUserId: PropTypes.string.isRequired,
myTeamMembers: PropTypes.object.isRequired,
+ navigator: PropTypes.object,
theme: PropTypes.object.isRequired
};
@@ -93,7 +97,9 @@ export default class ChannelDrawer extends PureComponent {
handleDrawerOpen = () => {
this.setState({openDrawer: true});
- Keyboard.dismiss();
+ if (this.state.openDrawerOffset === DRAWER_INITIAL_OFFSET) {
+ Keyboard.dismiss();
+ }
setTimeout(() => {
InteractionManager.clearInteractionHandle(this.openLeftHandle);
});
@@ -148,10 +154,58 @@ export default class ChannelDrawer extends PureComponent {
});
};
+ joinChannel = (channel) => {
+ const {
+ actions,
+ currentChannel,
+ currentTeam,
+ currentUserId
+ } = this.props;
+
+ const {
+ handleSelectChannel,
+ joinChannel,
+ makeDirectChannel,
+ markChannelAsRead,
+ setChannelLoading,
+ viewChannel
+ } = actions;
+
+ if (channel.type === General.DM_CHANNEL) {
+ markChannelAsRead(currentChannel.id);
+ setChannelLoading();
+ viewChannel(currentChannel.id);
+ this.closeChannelDrawer();
+ InteractionManager.runAfterInteractions(() => {
+ makeDirectChannel(channel.id);
+ });
+ } else {
+ markChannelAsRead(currentChannel.id);
+ setChannelLoading();
+ viewChannel(currentChannel.id);
+ joinChannel(currentUserId, currentTeam.id, channel.id);
+ this.closeChannelDrawer();
+ InteractionManager.runAfterInteractions(() => {
+ handleSelectChannel(channel.id);
+ });
+ }
+ };
+
onPageSelected = (index) => {
this.swiperIndex = index;
};
+ onSearchEnds = () => {
+ //hack to update the drawer when the offset changes
+ this.refs.drawer._syncAfterUpdate = true; //eslint-disable-line no-underscore-dangle
+ this.setState({openDrawerOffset: DRAWER_INITIAL_OFFSET});
+ };
+
+ onSearchStart = () => {
+ this.refs.drawer._syncAfterUpdate = true; //eslint-disable-line no-underscore-dangle
+ this.setState({openDrawerOffset: 0});
+ };
+
showTeams = () => {
const teamsCount = Object.keys(this.props.myTeamMembers).length;
if (this.swiperIndex === 1 && teamsCount > 1) {
@@ -160,7 +214,9 @@ export default class ChannelDrawer extends PureComponent {
};
resetDrawer = () => {
- this.refs.swiper.resetPage();
+ if (this.swiperIndex !== 1) {
+ this.refs.swiper.resetPage();
+ }
};
renderContent = () => {
@@ -173,13 +229,14 @@ export default class ChannelDrawer extends PureComponent {
myTeamMembers,
theme
} = this.props;
- const showTeams = Object.keys(myTeamMembers).length > 1;
+ const {openDrawerOffset} = this.state;
+ const showTeams = openDrawerOffset === DRAWER_INITIAL_OFFSET && Object.keys(myTeamMembers).length > 1;
let teams;
if (showTeams) {
teams = (
-
-
);
return (
-
{teams}
{channelsList}
-
+
);
};
@@ -224,6 +284,7 @@ export default class ChannelDrawer extends PureComponent {
return (
{
+ searchProfiles(term);
+ searchChannels(currentTeam.id, term);
+ }, General.SEARCH_TIMEOUT_MILLISECONDS);
+ }
+ }
+
+ onSelectChannel = (channel) => {
+ const {
+ makeGroupMessageVisibleIfNecessary,
+ setChannelDisplayName
+ } = this.props.actions;
+
+ setChannelDisplayName(channel.display_name);
+ if (channel.type === General.GM_CHANNEL) {
+ makeGroupMessageVisibleIfNecessary(channel.id);
+ }
+
+ this.props.onSelectChannel(channel);
+ };
+
+ createChannelElement = (channel) => {
+ return (
+
+ );
+ };
+
+ filterChannels = (channels, term) => {
+ if (!term) {
+ return channels;
+ }
+
+ const text = term.toLowerCase();
+ return channels.filter((c) => {
+ return c.display_name.toLowerCase().includes(text);
+ });
+ };
+
+ completeDirectGroupInfo = (channel) => {
+ const {currentUserId, myPreferences, profiles, profilesInChannel} = this.props;
+ const profilesIds = profilesInChannel[channel.id];
+ if (profilesIds) {
+ function sortUsernames(a, b) {
+ const locale = profiles[currentUserId].locale;
+ return a.localeCompare(b, locale, {numeric: true});
+ }
+
+ const displayName = [];
+ profilesIds.forEach((teammateId) => {
+ if (teammateId !== currentUserId) {
+ displayName.push(displayUsername(profiles[teammateId], myPreferences));
+ }
+ });
+
+ const gm = {...channel};
+ return Object.assign(gm, {
+ display_name: displayName.sort(sortUsernames).join(', ')
+ });
+ }
+ return channel;
+ };
+
+ buildChannelsForSearch = (props, term) => {
+ const data = [];
+ const {groupChannels, otherChannels, styles} = props;
+ const {
+ unreadChannels,
+ favoriteChannels,
+ publicChannels,
+ privateChannels
+ } = props.channels;
+
+ const notMemberOf = otherChannels.map((o) => {
+ return {
+ ...o,
+ fake: true
+ };
+ });
+
+ const favorites = favoriteChannels.filter((c) => {
+ return c.type !== General.DM_CHANNEL && c.type !== General.GM_CHANNEL;
+ });
+
+ const unreads = this.filterChannels(unreadChannels, term);
+ const channels = this.filterChannels([...favorites, ...publicChannels, ...privateChannels], term);
+ const others = this.filterChannels(notMemberOf, term);
+ const groups = this.filterChannels(groupChannels.map((g) => this.completeDirectGroupInfo(g)), term);
+ const fakeDms = this.filterChannels(this.buildFakeDms(props), term);
+ const directMessages = [...groups, ...fakeDms].sort(sortChannelsByDisplayName.bind(null, props.intl.locale));
+
+ if (unreads.length) {
+ data.push(
+ this.renderTitle(styles, 'mobile.channel_list.unreads', 'UNREADS', null, false, true),
+ ...unreads
+ );
+ }
+
+ if (channels.length) {
+ data.push(
+ this.renderTitle(styles, 'sidebar.channels', 'CHANNELS', null, unreads.length > 0, true),
+ ...channels
+ );
+ }
+
+ if (others.length) {
+ data.push(
+ this.renderTitle(styles, 'mobile.channel_list.not_member', 'NOT A MEMBER', null, channels.length > 0, true),
+ ...others
+ );
+ }
+
+ if (directMessages.length) {
+ data.push(
+ this.renderTitle(styles, 'sidebar.direct', 'DIRECT MESSAGES', null, others.length > 0, true),
+ ...directMessages
+ );
+ }
+
+ return data;
+ };
+
+ buildFakeDms = (props) => {
+ const {myPreferences, profiles, statuses} = props;
+ const users = Object.values(profiles);
+
+ return users.map((u) => {
+ return {
+ id: u.id,
+ status: statuses[u.id],
+ display_name: displayUsername(u, myPreferences),
+ type: General.DM_CHANNEL,
+ fake: true
+ };
+ });
+ };
+
+ buildData = (props, term) => {
+ if (!props.currentChannel) {
+ return null;
+ }
+
+ return this.buildChannelsForSearch(props, term);
+ };
+
+ renderSectionAction = (styles, action) => {
+ const {theme} = this.props;
+ return (
+ preventDoubleTap(action, this)}
+ underlayColor={changeOpacity(theme.sidebarTextHoverBg, 0.5)}
+ >
+
+
+ );
+ };
+
+ renderDivider = (styles, marginLeft) => {
+ return (
+
+ );
+ };
+
+ renderItem = ({item}) => {
+ if (!item.isTitle) {
+ return this.createChannelElement(item);
+ }
+ return item.title;
+ };
+
+ renderTitle = (styles, id, defaultMessage, action, topDivider, bottomDivider) => {
+ const {formatMessage} = this.props.intl;
+
+ return {
+ id,
+ isTitle: true,
+ title: (
+
+ {topDivider && this.renderDivider(styles, 0)}
+
+
+ {formatMessage({id, defaultMessage}).toUpperCase()}
+
+ {action && this.renderSectionAction(styles, action)}
+
+ {bottomDivider && this.renderDivider(styles, 16)}
+
+ )
+ };
+ };
+
+ render() {
+ const {styles} = this.props;
+
+ const {dataSource} = this.state;
+
+ return (
+
+ item.id}
+ onViewableItemsChanged={this.updateUnreadIndicators}
+ keyboardDismissMode='on-drag'
+ maxToRenderPerBatch={10}
+ viewabilityConfig={{
+ viewAreaCoveragePercentThreshold: 3,
+ waitForInteraction: false
+ }}
+ />
+
+ );
+ }
+}
+
+export default injectIntl(ChannelDrawerList);
diff --git a/app/components/channel_drawer/channels_list/filtered_list/index.js b/app/components/channel_drawer/channels_list/filtered_list/index.js
new file mode 100644
index 000000000..9a29afcff
--- /dev/null
+++ b/app/components/channel_drawer/channels_list/filtered_list/index.js
@@ -0,0 +1,44 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {bindActionCreators} from 'redux';
+import {connect} from 'react-redux';
+
+import {searchChannels} from 'mattermost-redux/actions/channels';
+import {searchProfiles} from 'mattermost-redux/actions/users';
+import {makeGroupMessageVisibleIfNecessary} from 'mattermost-redux/actions/preferences';
+import {getUserIdsInChannels, getUsers, getUserStatuses} from 'mattermost-redux/selectors/entities/users';
+import {getGroupChannels, getOtherChannels} from 'mattermost-redux/selectors/entities/channels';
+import {getMyPreferences} from 'mattermost-redux/selectors/entities/preferences';
+
+import {setChannelDisplayName} from 'app/actions/views/channel';
+
+import FilteredList from './filtered_list';
+
+function mapStateToProps(state, ownProps) {
+ const {currentUserId} = state.entities.users;
+
+ return {
+ currentUserId,
+ otherChannels: getOtherChannels(state),
+ groupChannels: getGroupChannels(state),
+ profiles: getUsers(state),
+ profilesInChannel: getUserIdsInChannels(state),
+ myPreferences: getMyPreferences(state),
+ statuses: getUserStatuses(state),
+ ...ownProps
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ return {
+ actions: bindActionCreators({
+ makeGroupMessageVisibleIfNecessary,
+ searchChannels,
+ searchProfiles,
+ setChannelDisplayName
+ }, dispatch)
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(FilteredList);
diff --git a/app/components/channel_drawer/channels_list/index.js b/app/components/channel_drawer/channels_list/index.js
new file mode 100644
index 000000000..a198f3b01
--- /dev/null
+++ b/app/components/channel_drawer/channels_list/index.js
@@ -0,0 +1,396 @@
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React, {Component} from 'react';
+import PropTypes from 'prop-types';
+import {
+ Platform,
+ StyleSheet,
+ Text,
+ TouchableHighlight,
+ View
+} from 'react-native';
+import {injectIntl, intlShape} from 'react-intl';
+import AwesomeIcon from 'react-native-vector-icons/FontAwesome';
+import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
+
+import Badge from 'app/components/badge';
+import SearchBar from 'app/components/search_bar';
+import {preventDoubleTap} from 'app/utils/tap';
+import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
+
+import FilteredList from './filtered_list';
+import List from './list';
+
+class ChannelsList extends Component {
+ static propTypes = {
+ channels: PropTypes.object.isRequired,
+ channelMembers: PropTypes.object,
+ currentTeam: PropTypes.object.isRequired,
+ currentChannel: PropTypes.object,
+ intl: intlShape.isRequired,
+ myTeamMembers: PropTypes.object.isRequired,
+ navigator: PropTypes.object,
+ onJoinChannel: PropTypes.func.isRequired,
+ onSearchEnds: PropTypes.func.isRequired,
+ onSearchStart: PropTypes.func.isRequired,
+ onSelectChannel: PropTypes.func.isRequired,
+ onShowTeams: PropTypes.func.isRequired,
+ theme: PropTypes.object.isRequired
+ };
+
+ static defaultProps = {
+ currentTeam: {},
+ currentChannel: {}
+ };
+
+ constructor(props) {
+ super(props);
+ this.firstUnreadChannel = null;
+ this.state = {
+ searching: false,
+ term: null
+ };
+
+ MaterialIcon.getImageSource('close', 20, this.props.theme.sidebarHeaderTextColor).
+ then((source) => {
+ this.closeButton = source;
+ });
+ }
+
+ onSelectChannel = (channel) => {
+ if (channel.fake) {
+ this.props.onJoinChannel(channel);
+ } else {
+ this.props.onSelectChannel(channel.id);
+ }
+
+ this.refs.search_bar.cancel();
+ };
+
+ openSettingsModal = () => {
+ const {intl, navigator, theme} = this.props;
+
+ navigator.showModal({
+ screen: 'Settings',
+ title: intl.formatMessage({id: 'mobile.routes.settings', defaultMessage: 'Settings'}),
+ animationType: 'slide-up',
+ animated: true,
+ backButtonTitle: '',
+ navigatorStyle: {
+ navBarTextColor: theme.sidebarHeaderTextColor,
+ navBarBackgroundColor: theme.sidebarHeaderBg,
+ navBarButtonColor: theme.sidebarHeaderTextColor,
+ screenBackgroundColor: theme.centerChannelBg
+ },
+ navigatorButtons: {
+ leftButtons: [{
+ id: 'close-settings',
+ icon: this.closeButton
+ }]
+ }
+ });
+ };
+
+ onSearch = (term) => {
+ this.setState({term});
+ };
+
+ onSearchFocused = () => {
+ this.setState({searching: true});
+ this.props.onSearchStart();
+ };
+
+ cancelSearch = () => {
+ this.props.onSearchEnds();
+ this.setState({searching: false});
+ this.onSearch(null);
+ };
+
+ render() {
+ const {
+ currentChannel,
+ currentTeam,
+ intl,
+ myTeamMembers,
+ onShowTeams,
+ theme
+ } = this.props;
+
+ if (!currentChannel) {
+ return {'Loading'};
+ }
+
+ const {searching, term} = this.state;
+ const teamMembers = Object.values(myTeamMembers);
+ const styles = getStyleSheet(theme);
+
+ let settings;
+ let list;
+ if (searching) {
+ const listProps = {...this.props, onSelectChannel: this.onSelectChannel, styles, term};
+ list = ;
+ } else {
+ settings = (
+ preventDoubleTap(this.openSettingsModal)}
+ underlayColor={changeOpacity(theme.sidebarHeaderBg, 0.5)}
+ >
+
+
+ );
+
+ const listProps = {...this.props, onSelectChannel: this.onSelectChannel, styles};
+ list =
;
+ }
+
+ const title = (
+
+
+
+ );
+
+ let badge;
+ let switcher;
+ if (teamMembers.length > 1 && !searching) {
+ let mentionCount = 0;
+ let messageCount = 0;
+ teamMembers.forEach((m) => {
+ if (m.team_id !== currentTeam.id) {
+ mentionCount = mentionCount + (m.mention_count || 0);
+ messageCount = messageCount + (m.msg_count || 0);
+ }
+ });
+
+ let badgeCount = 0;
+ if (mentionCount) {
+ badgeCount = mentionCount;
+ } else if (messageCount) {
+ badgeCount = -1;
+ }
+
+ if (badgeCount) {
+ badge = (
+
+ );
+ }
+
+ switcher = (
+ preventDoubleTap(onShowTeams)}
+ underlayColor={changeOpacity(theme.sidebarHeaderBg, 0.5)}
+ >
+
+
+
+
+ {currentTeam.display_name.substr(0, 2).toUpperCase()}
+
+
+
+ );
+ }
+
+ return (
+
+
+
+ {switcher}
+ {title}
+ {settings}
+ {badge}
+
+
+ {list}
+
+ );
+ }
+}
+
+const getStyleSheet = makeStyleSheetFromTheme((theme) => {
+ return StyleSheet.create({
+ container: {
+ backgroundColor: theme.sidebarBg,
+ flex: 1
+ },
+ statusBar: {
+ backgroundColor: theme.sidebarHeaderBg,
+ ...Platform.select({
+ ios: {
+ paddingTop: 20
+ }
+ })
+ },
+ headerContainer: {
+ alignItems: 'center',
+ backgroundColor: theme.sidebarHeaderBg,
+ flexDirection: 'row',
+ borderBottomWidth: 1,
+ borderBottomColor: changeOpacity(theme.sidebarHeaderTextColor, 0.10),
+ ...Platform.select({
+ android: {
+ height: 46
+ },
+ ios: {
+ height: 44
+ }
+ })
+ },
+ header: {
+ color: theme.sidebarHeaderTextColor,
+ flex: 1,
+ fontSize: 17,
+ fontWeight: 'normal',
+ paddingLeft: 16
+ },
+ settingsContainer: {
+ alignItems: 'center',
+ justifyContent: 'center',
+ paddingHorizontal: 10,
+ ...Platform.select({
+ android: {
+ height: 46
+ },
+ ios: {
+ height: 44
+ }
+ })
+ },
+ settings: {
+ color: theme.sidebarHeaderTextColor,
+ fontSize: 18,
+ fontWeight: '300'
+ },
+ titleContainer: {
+ alignItems: 'center',
+ flex: 1,
+ flexDirection: 'row',
+ height: 48,
+ marginLeft: 16
+ },
+ title: {
+ flex: 1,
+ color: theme.sidebarText,
+ opacity: 1,
+ fontSize: 15,
+ fontWeight: '400',
+ letterSpacing: 0.8,
+ lineHeight: 18
+ },
+ searchContainer: {
+ flex: 1,
+ ...Platform.select({
+ android: {
+ marginBottom: 1
+ },
+ ios: {
+ marginBottom: 3
+ }
+ })
+ },
+ switcherContainer: {
+ alignItems: 'center',
+ backgroundColor: theme.sidebarHeaderTextColor,
+ borderRadius: 2,
+ flexDirection: 'row',
+ height: 32,
+ justifyContent: 'center',
+ marginLeft: 16,
+ paddingHorizontal: 6
+ },
+ switcherDivider: {
+ backgroundColor: theme.sidebarHeaderBg,
+ height: 15,
+ marginHorizontal: 6,
+ width: 1
+ },
+ switcherTeam: {
+ color: theme.sidebarHeaderBg,
+ fontFamily: 'OpenSans',
+ fontSize: 14
+ },
+ badge: {
+ backgroundColor: theme.mentionBj,
+ borderColor: theme.sidebarHeaderBg,
+ borderRadius: 10,
+ borderWidth: 1,
+ flexDirection: 'row',
+ height: 20,
+ padding: 3,
+ position: 'absolute',
+ left: 5,
+ top: 0,
+ width: 20
+ },
+ mention: {
+ color: theme.mentionColor,
+ fontSize: 10
+ },
+ divider: {
+ backgroundColor: changeOpacity(theme.sidebarText, 0.1),
+ height: 1
+ },
+ actionContainer: {
+ alignItems: 'center',
+ height: 48,
+ justifyContent: 'center',
+ width: 50
+ },
+ action: {
+ color: theme.sidebarText,
+ fontSize: 20,
+ fontWeight: '500',
+ lineHeight: 18
+ },
+ above: {
+ backgroundColor: theme.mentionBj,
+ top: 9
+ },
+ indicatorText: {
+ backgroundColor: 'transparent',
+ color: theme.mentionColor,
+ fontSize: 14,
+ paddingVertical: 2,
+ paddingHorizontal: 4,
+ textAlign: 'center',
+ textAlignVertical: 'center'
+ }
+ });
+});
+
+export default injectIntl(ChannelsList);
diff --git a/app/components/channel_drawer_list/index.js b/app/components/channel_drawer/channels_list/list/index.js
similarity index 88%
rename from app/components/channel_drawer_list/index.js
rename to app/components/channel_drawer/channels_list/list/index.js
index 3168119a7..518e87080 100644
--- a/app/components/channel_drawer_list/index.js
+++ b/app/components/channel_drawer/channels_list/list/index.js
@@ -11,7 +11,7 @@ import {isAdmin, isSystemAdmin} from 'mattermost-redux/utils/user_utils';
import {setChannelDisplayName} from 'app/actions/views/channel';
-import ChannelDrawerList from './channel_drawer_list';
+import List from './list';
function mapStateToProps(state, ownProps) {
const {config, license} = state.entities.general;
@@ -31,4 +31,4 @@ function mapDispatchToProps(dispatch) {
};
}
-export default connect(mapStateToProps, mapDispatchToProps)(ChannelDrawerList);
+export default connect(mapStateToProps, mapDispatchToProps)(List);
diff --git a/app/components/channel_drawer_list/channel_drawer_list.js b/app/components/channel_drawer/channels_list/list/list.js
similarity index 56%
rename from app/components/channel_drawer_list/channel_drawer_list.js
rename to app/components/channel_drawer/channels_list/list/list.js
index dfd299dd9..7ced2d444 100644
--- a/app/components/channel_drawer_list/channel_drawer_list.js
+++ b/app/components/channel_drawer/channels_list/list/list.js
@@ -6,27 +6,23 @@ import React, {Component} from 'react';
import PropTypes from 'prop-types';
import {
FlatList,
- Platform,
- StyleSheet,
Text,
TouchableHighlight,
View
} from 'react-native';
import {injectIntl, intlShape} from 'react-intl';
-import AwesomeIcon from 'react-native-vector-icons/FontAwesome';
import MaterialIcon from 'react-native-vector-icons/MaterialIcons';
-import Badge from 'app/components/badge';
import FormattedText from 'app/components/formatted_text';
import {preventDoubleTap} from 'app/utils/tap';
-import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
+import {changeOpacity} from 'app/utils/theme';
import {General} from 'mattermost-redux/constants';
-import ChannelDrawerItem from './channel_drawer_item';
-import UnreadIndicator from './unread_indicator';
+import ChannelItem from 'app/components/channel_drawer/channels_list/channel_item';
+import UnreadIndicator from 'app/components/channel_drawer/channels_list/unread_indicator';
-class ChannelDrawerList extends Component {
+class List extends Component {
static propTypes = {
actions: PropTypes.shape({
setChannelDisplayName: PropTypes.func.isRequired
@@ -34,18 +30,15 @@ class ChannelDrawerList extends Component {
canCreatePrivateChannels: PropTypes.bool.isRequired,
channels: PropTypes.object.isRequired,
channelMembers: PropTypes.object,
- currentTeam: PropTypes.object.isRequired,
currentChannel: PropTypes.object,
intl: intlShape.isRequired,
- myTeamMembers: PropTypes.object.isRequired,
navigator: PropTypes.object,
onSelectChannel: PropTypes.func.isRequired,
- onShowTeams: PropTypes.func.isRequired,
+ styles: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired
};
static defaultProps = {
- currentTeam: {},
currentChannel: {}
};
@@ -53,9 +46,10 @@ class ChannelDrawerList extends Component {
super(props);
this.firstUnreadChannel = null;
this.state = {
- showAbove: false,
- dataSource: this.buildData(props)
+ dataSource: this.buildData(props),
+ showAbove: false
};
+
MaterialIcon.getImageSource('close', 20, this.props.theme.sidebarHeaderTextColor).
then((source) => {
this.closeButton = source;
@@ -102,7 +96,7 @@ class ChannelDrawerList extends Component {
onSelectChannel = (channel) => {
this.props.actions.setChannelDisplayName(channel.display_name);
- this.props.onSelectChannel(channel.id);
+ this.props.onSelectChannel(channel);
};
onLayout = (event) => {
@@ -150,7 +144,7 @@ class ChannelDrawerList extends Component {
const unread = msgCount > 0;
return (
- {
- const data = [];
-
- if (!props.currentChannel) {
- return data;
- }
-
- const {canCreatePrivateChannels, theme} = this.props;
- const styles = getStyleSheet(theme);
-
+ buildChannels = (props) => {
+ const {canCreatePrivateChannels, styles} = props;
const {
unreadChannels,
favoriteChannels,
@@ -202,16 +188,18 @@ class ChannelDrawerList extends Component {
directAndGroupChannels
} = props.channels;
+ const data = [];
+
if (unreadChannels.length) {
data.push(
- this.renderTitle(styles, 'mobile.channel_list.unreads', 'UNREADS', null, false, unreadChannels.length > 0),
+ this.renderTitle(styles, 'mobile.channel_list.unreads', 'UNREADS', null, false, true),
...unreadChannels
);
}
if (favoriteChannels.length) {
data.push(
- this.renderTitle(styles, 'sidebar.favorite', 'FAVORITES', null, unreadChannels.length > 0, favoriteChannels.length > 0),
+ this.renderTitle(styles, 'sidebar.favorite', 'FAVORITES', null, unreadChannels.length > 0, true),
...favoriteChannels
);
}
@@ -235,36 +223,21 @@ class ChannelDrawerList extends Component {
...directAndGroupChannels
);
+ return data;
+ };
+
+ buildData = (props) => {
+ if (!props.currentChannel) {
+ return null;
+ }
+
+ const data = this.buildChannels(props);
this.firstUnreadChannel = null;
this.findUnreadChannels(data);
return data;
};
- openSettingsModal = () => {
- const {intl, navigator, theme} = this.props;
-
- navigator.showModal({
- screen: 'Settings',
- title: intl.formatMessage({id: 'mobile.routes.settings', defaultMessage: 'Settings'}),
- animationType: 'slide-up',
- animated: true,
- backButtonTitle: '',
- navigatorStyle: {
- navBarTextColor: theme.sidebarHeaderTextColor,
- navBarBackgroundColor: theme.sidebarHeaderBg,
- navBarButtonColor: theme.sidebarHeaderTextColor,
- screenBackgroundColor: theme.centerChannelBg
- },
- navigatorButtons: {
- leftButtons: [{
- id: 'close-settings',
- icon: this.closeButton
- }]
- }
- });
- };
-
showDirectMessagesModal = () => {
const {intl, navigator, theme} = this.props;
@@ -364,34 +337,9 @@ class ChannelDrawerList extends Component {
};
render() {
- const {
- currentChannel,
- currentTeam,
- myTeamMembers,
- onShowTeams,
- theme
- } = this.props;
+ const {styles} = this.props;
const {dataSource, showAbove} = this.state;
- const teamMembers = Object.values(myTeamMembers);
-
- if (!currentChannel) {
- return {'Loading'};
- }
- const styles = getStyleSheet(theme);
-
- const settings = (
- preventDoubleTap(this.openSettingsModal)}
- underlayColor={changeOpacity(theme.sidebarHeaderBg, 0.5)}
- >
-
-
- );
let above;
if (showAbove) {
@@ -410,87 +358,18 @@ class ChannelDrawerList extends Component {
);
}
- const title = (
-
- {currentTeam.display_name}
-
- );
-
- let badge;
- let switcher;
- if (teamMembers.length > 1) {
- let mentionCount = 0;
- let messageCount = 0;
- teamMembers.forEach((m) => {
- if (m.team_id !== currentTeam.id) {
- mentionCount = mentionCount + (m.mention_count || 0);
- messageCount = messageCount + (m.msg_count || 0);
- }
- });
-
- let badgeCount = 0;
- if (mentionCount) {
- badgeCount = mentionCount;
- } else if (messageCount) {
- badgeCount = -1;
- }
-
- if (badgeCount) {
- badge = (
-
- );
- }
-
- switcher = (
- preventDoubleTap(onShowTeams)}
- underlayColor={changeOpacity(theme.sidebarHeaderBg, 0.5)}
- >
-
-
-
-
- {currentTeam.display_name.substr(0, 2).toUpperCase()}
-
-
-
- );
- }
-
return (
-
-
- {switcher}
- {title}
- {settings}
- {badge}
-
-
item.id}
onViewableItemsChanged={this.updateUnreadIndicators}
+ keyboardDismissMode='on-drag'
maxToRenderPerBatch={10}
viewabilityConfig={{
viewAreaCoveragePercentThreshold: 3,
@@ -503,144 +382,4 @@ class ChannelDrawerList extends Component {
}
}
-const getStyleSheet = makeStyleSheetFromTheme((theme) => {
- return StyleSheet.create({
- container: {
- backgroundColor: theme.sidebarBg,
- flex: 1
- },
- statusBar: {
- backgroundColor: theme.sidebarHeaderBg,
- ...Platform.select({
- ios: {
- paddingTop: 20
- }
- })
- },
- headerContainer: {
- alignItems: 'center',
- backgroundColor: theme.sidebarHeaderBg,
- flexDirection: 'row',
- borderBottomWidth: 1,
- borderBottomColor: changeOpacity(theme.sidebarHeaderTextColor, 0.10),
- ...Platform.select({
- android: {
- height: 46
- },
- ios: {
- height: 44
- }
- })
- },
- header: {
- color: theme.sidebarHeaderTextColor,
- flex: 1,
- fontSize: 17,
- fontWeight: 'normal',
- paddingLeft: 16
- },
- settingsContainer: {
- alignItems: 'center',
- justifyContent: 'center',
- width: 50,
- ...Platform.select({
- android: {
- height: 46
- },
- ios: {
- height: 44
- }
- })
- },
- settings: {
- color: theme.sidebarHeaderTextColor,
- fontSize: 18,
- fontWeight: '300'
- },
- titleContainer: {
- alignItems: 'center',
- flex: 1,
- flexDirection: 'row',
- height: 48,
- marginLeft: 16
- },
- title: {
- flex: 1,
- color: theme.sidebarText,
- opacity: 1,
- fontSize: 15,
- fontWeight: '400',
- letterSpacing: 0.8,
- lineHeight: 18
- },
- switcherContainer: {
- alignItems: 'center',
- backgroundColor: theme.sidebarHeaderTextColor,
- borderRadius: 2,
- flexDirection: 'row',
- height: 32,
- justifyContent: 'center',
- marginLeft: 16,
- paddingHorizontal: 6
- },
- switcherDivider: {
- backgroundColor: theme.sidebarHeaderBg,
- height: 15,
- marginHorizontal: 6,
- width: 1
- },
- switcherTeam: {
- color: theme.sidebarHeaderBg,
- fontFamily: 'OpenSans',
- fontSize: 14
- },
- badge: {
- backgroundColor: theme.mentionBj,
- borderColor: theme.sidebarHeaderBg,
- borderRadius: 10,
- borderWidth: 1,
- flexDirection: 'row',
- height: 20,
- padding: 3,
- position: 'absolute',
- left: 5,
- top: 0,
- width: 20
- },
- mention: {
- color: theme.mentionColor,
- fontSize: 10
- },
- divider: {
- backgroundColor: changeOpacity(theme.sidebarText, 0.1),
- height: 1
- },
- actionContainer: {
- alignItems: 'center',
- height: 48,
- justifyContent: 'center',
- width: 50
- },
- action: {
- color: theme.sidebarText,
- fontSize: 20,
- fontWeight: '500',
- lineHeight: 18
- },
- above: {
- backgroundColor: theme.mentionBj,
- top: 79
- },
- indicatorText: {
- backgroundColor: 'transparent',
- color: theme.mentionColor,
- fontSize: 14,
- paddingVertical: 2,
- paddingHorizontal: 4,
- textAlign: 'center',
- textAlignVertical: 'center'
- }
- });
-});
-
-export default injectIntl(ChannelDrawerList);
+export default injectIntl(List);
diff --git a/app/components/channel_drawer_list/unread_indicator.js b/app/components/channel_drawer/channels_list/unread_indicator.js
similarity index 100%
rename from app/components/channel_drawer_list/unread_indicator.js
rename to app/components/channel_drawer/channels_list/unread_indicator.js
diff --git a/app/components/channel_drawer/index.js b/app/components/channel_drawer/index.js
index 4a574183f..988461370 100644
--- a/app/components/channel_drawer/index.js
+++ b/app/components/channel_drawer/index.js
@@ -4,21 +4,25 @@
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
-import {viewChannel, markChannelAsRead} from 'mattermost-redux/actions/channels';
+import {joinChannel, viewChannel, markChannelAsRead} from 'mattermost-redux/actions/channels';
import {getTeams} from 'mattermost-redux/actions/teams';
import {getChannelsWithUnreadSection, getCurrentChannel} from 'mattermost-redux/selectors/entities/channels';
import {getCurrentTeam, getTeamMemberships} from 'mattermost-redux/selectors/entities/teams';
import {handleSelectChannel, setChannelLoading} from 'app/actions/views/channel';
+import {makeDirectChannel} from 'app/actions/views/more_dms';
import {getTheme} from 'app/selectors/preferences';
import ChannelDrawer from './channel_drawer.js';
function mapStateToProps(state, ownProps) {
+ const {currentUserId} = state.entities.users;
+
return {
...ownProps,
currentTeam: getCurrentTeam(state),
currentChannel: getCurrentChannel(state),
+ currentUserId,
channels: getChannelsWithUnreadSection(state),
channelMembers: state.entities.channels.myMembers,
myTeamMembers: getTeamMemberships(state),
@@ -31,7 +35,9 @@ function mapDispatchToProps(dispatch) {
actions: bindActionCreators({
getTeams,
handleSelectChannel,
+ joinChannel,
viewChannel,
+ makeDirectChannel,
markChannelAsRead,
setChannelLoading
}, dispatch)
diff --git a/app/components/channel_drawer_swiper/index.js b/app/components/channel_drawer/swiper/index.js
similarity index 63%
rename from app/components/channel_drawer_swiper/index.js
rename to app/components/channel_drawer/swiper/index.js
index 5cf0965d2..212b0eefc 100644
--- a/app/components/channel_drawer_swiper/index.js
+++ b/app/components/channel_drawer/swiper/index.js
@@ -2,5 +2,5 @@
// See License.txt for license information.
// Used to leverage the platform specific components
-import ChannelDrawerSwiper from './channel_drawer_swiper';
-export default ChannelDrawerSwiper;
+import Swiper from './swiper';
+export default Swiper;
diff --git a/app/components/channel_drawer_swiper/channel_drawer_swiper.android.js b/app/components/channel_drawer/swiper/swiper.android.js
similarity index 95%
rename from app/components/channel_drawer_swiper/channel_drawer_swiper.android.js
rename to app/components/channel_drawer/swiper/swiper.android.js
index 85d2de02d..8605dca98 100644
--- a/app/components/channel_drawer_swiper/channel_drawer_swiper.android.js
+++ b/app/components/channel_drawer/swiper/swiper.android.js
@@ -5,7 +5,7 @@ import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {ViewPagerAndroid} from 'react-native';
-export default class ChannelDrawerSwiper extends PureComponent {
+export default class SwiperAndroid extends PureComponent {
static propTypes = {
children: PropTypes.node.isRequired,
onPageSelected: PropTypes.func,
diff --git a/app/components/channel_drawer_swiper/channel_drawer_swiper.ios.js b/app/components/channel_drawer/swiper/swiper.ios.js
similarity index 94%
rename from app/components/channel_drawer_swiper/channel_drawer_swiper.ios.js
rename to app/components/channel_drawer/swiper/swiper.ios.js
index d3d7bf055..d1566e7f8 100644
--- a/app/components/channel_drawer_swiper/channel_drawer_swiper.ios.js
+++ b/app/components/channel_drawer/swiper/swiper.ios.js
@@ -10,7 +10,7 @@ import {changeOpacity} from 'app/utils/theme';
const {height: deviceHeight, width: deviceWidth} = Dimensions.get('window');
-export default class ChannelDrawerSwiper extends PureComponent {
+export default class SwiperIos extends PureComponent {
static propTypes = {
children: PropTypes.node.isRequired,
onPageSelected: PropTypes.func,
@@ -61,6 +61,7 @@ export default class ChannelDrawerSwiper extends PureComponent {
automaticallyAdjustContentInsets={true}
scrollEnabled={showTeams}
showsPagination={showTeams}
+ keyboardShouldPersistTaps={'always'}
>
{children}
diff --git a/app/components/channel_drawer_teams/index.js b/app/components/channel_drawer/teams_list/index.js
similarity index 92%
rename from app/components/channel_drawer_teams/index.js
rename to app/components/channel_drawer/teams_list/index.js
index 7290775b9..7f75b0eae 100644
--- a/app/components/channel_drawer_teams/index.js
+++ b/app/components/channel_drawer/teams_list/index.js
@@ -14,7 +14,7 @@ import {handleTeamChange} from 'app/actions/views/select_team';
import {getTheme} from 'app/selectors/preferences';
import {removeProtocol} from 'app/utils/url';
-import ChannelDrawerTeams from './channel_drawer_teams';
+import TeamsList from './teams_list';
function mapStateToProps(state, ownProps) {
const user = getCurrentUser(state);
@@ -48,4 +48,4 @@ function mapDispatchToProps(dispatch) {
};
}
-export default connect(mapStateToProps, mapDispatchToProps)(ChannelDrawerTeams);
+export default connect(mapStateToProps, mapDispatchToProps)(TeamsList);
diff --git a/app/components/channel_drawer_teams/channel_drawer_teams.js b/app/components/channel_drawer/teams_list/teams_list.js
similarity index 99%
rename from app/components/channel_drawer_teams/channel_drawer_teams.js
rename to app/components/channel_drawer/teams_list/teams_list.js
index ca13dea1f..e97cd3fb3 100644
--- a/app/components/channel_drawer_teams/channel_drawer_teams.js
+++ b/app/components/channel_drawer/teams_list/teams_list.js
@@ -21,7 +21,7 @@ import FormattedText from 'app/components/formatted_text';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
-class ChannelDrawerTeams extends PureComponent {
+class TeamsList extends PureComponent {
static propTypes = {
actions: PropTypes.shape({
handleTeamChange: PropTypes.func.isRequired,
@@ -333,4 +333,4 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
});
});
-export default injectIntl(ChannelDrawerTeams);
+export default injectIntl(TeamsList);
diff --git a/app/components/post_list/new_messages_divider.js b/app/components/post_list/new_messages_divider.js
index f7276aa2d..1abca9e93 100644
--- a/app/components/post_list/new_messages_divider.js
+++ b/app/components/post_list/new_messages_divider.js
@@ -2,6 +2,7 @@
// See License.txt for license information.
import React from 'react';
+import PropTypes from 'prop-types';
import {
StyleSheet,
View,
@@ -9,14 +10,17 @@ import {
} from 'react-native';
import FormattedText from 'app/components/formatted_text';
+import {makeStyleSheetFromTheme} from 'app/utils/theme';
function NewMessagesDivider(props) {
+ const style = getStyleFromTheme(props.theme);
+
return (
@@ -27,28 +31,30 @@ function NewMessagesDivider(props) {
}
NewMessagesDivider.propTypes = {
- style: ViewPropTypes.style
+ style: ViewPropTypes.style,
+ theme: PropTypes.object
};
-const style = StyleSheet.create({
- container: {
- alignItems: 'center',
- flexDirection: 'row',
- height: 28
- },
- textContainer: {
- marginHorizontal: 15
- },
- line: {
- flex: 1,
- height: StyleSheet.hairlineWidth,
- backgroundColor: '#f80'
- },
- text: {
- fontSize: 14,
- fontWeight: '600',
- color: '#ffaf53'
- }
+const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
+ return StyleSheet.create({
+ container: {
+ alignItems: 'center',
+ flexDirection: 'row',
+ height: 28
+ },
+ textContainer: {
+ marginHorizontal: 15
+ },
+ line: {
+ flex: 1,
+ height: StyleSheet.hairlineWidth,
+ backgroundColor: theme.newMessageSeparator
+ },
+ text: {
+ fontSize: 14,
+ color: theme.newMessageSeparator
+ }
+ });
});
export default NewMessagesDivider;
diff --git a/app/components/search_bar/search_bar.android.js b/app/components/search_bar/search_bar.android.js
index 6dc6bb5ab..fd881fe63 100644
--- a/app/components/search_bar/search_bar.android.js
+++ b/app/components/search_bar/search_bar.android.js
@@ -3,176 +3,225 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
+ InteractionManager,
Keyboard,
TextInput,
StyleSheet,
View,
- TouchableOpacity
+ TouchableWithoutFeedback
} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
-const styles = StyleSheet.create({
- searchBar: {
- flexDirection: 'row',
- alignItems: 'center',
- backgroundColor: 'transparent',
- margin: 8
- },
- searchBarInput: {
- flex: 1,
- fontWeight: 'normal',
- backgroundColor: 'transparent'
- }
-});
+import {changeOpacity} from 'app/utils/theme';
export default class SearchBarAndroid extends PureComponent {
static propTypes = {
- height: PropTypes.number.isRequired,
- fontSize: PropTypes.number.isRequired,
- text: PropTypes.string,
- placeholder: PropTypes.string,
- showCancelButton: PropTypes.bool,
- textFieldBackgroundColor: PropTypes.string,
- placeholderTextColor: PropTypes.string,
- textColor: PropTypes.string,
- onChange: PropTypes.func,
+ onCancelButtonPress: PropTypes.func,
onChangeText: PropTypes.func,
onFocus: PropTypes.func,
- onBlur: PropTypes.func,
onSearchButtonPress: PropTypes.func,
- onCancelButtonPress: PropTypes.func
+ backgroundColor: PropTypes.string,
+ placeholderTextColor: PropTypes.string,
+ titleCancelColor: PropTypes.string,
+ tintColorSearch: PropTypes.string,
+ tintColorDelete: PropTypes.string,
+ inputStyle: PropTypes.oneOfType([
+ PropTypes.number,
+ PropTypes.object
+ ]),
+ placeholder: PropTypes.string,
+ returnKeyType: PropTypes.string,
+ keyboardType: PropTypes.string,
+ autoCapitalize: PropTypes.string,
+ inputHeight: PropTypes.number,
+ inputBorderRadius: PropTypes.number,
+ blurOnSubmit: PropTypes.bool
};
static defaultProps = {
+ blurOnSubmit: false,
placeholder: 'Search',
showCancelButton: true,
- placeholderTextColor: '#bdbdbd',
- textColor: '#212121',
+ placeholderTextColor: changeOpacity('#000', 0.5),
onSearchButtonPress: () => true,
onCancelButtonPress: () => true,
onChangeText: () => true,
- onFocus: () => true,
- onBlur: () => true
+ onFocus: () => true
};
constructor(props) {
super(props);
this.state = {
- isOnFocus: false,
- value: this.props.text
- };
- this.onFocus = this.onFocus.bind(this);
- this.onBlur = this.onBlur.bind(this);
- this.onCancelButtonPress = this.onCancelButtonPress.bind(this);
- this.onSearchButtonPress = this.onSearchButtonPress.bind(this);
- this.onChangeText = this.onChangeText.bind(this);
- }
-
- onSearchButtonPress() {
- const onSearchButtonPress = this.props.onSearchButtonPress;
- if (this.state.value) {
- onSearchButtonPress(this.state.value);
- }
- }
-
- onCancelButtonPress() {
- const onCancelButtonPress = this.props.onCancelButtonPress;
- this.setState({
- isOnFocus: false,
+ isFocused: false,
value: ''
+ };
+ }
+
+ cancel = () => {
+ this.onCancelButtonPress();
+ };
+
+ onSearchButtonPress = () => {
+ const {onSearchButtonPress} = this.props;
+ const {value} = this.state;
+
+ if (value && onSearchButtonPress) {
+ onSearchButtonPress(value);
+ }
+ };
+
+ onCancelButtonPress = () => {
+ const {onCancelButtonPress} = this.props;
+
+ Keyboard.dismiss();
+ InteractionManager.runAfterInteractions(() => {
+ this.setState({
+ isFocused: false,
+ value: ''
+ }, () => {
+ if (onCancelButtonPress) {
+ onCancelButtonPress();
+ }
+ });
});
+ };
- onCancelButtonPress();
- Keyboard.dismiss();
- }
-
- onChangeText(value) {
- const onChangeText = this.props.onChangeText;
+ onChangeText = (value) => {
+ const {onChangeText} = this.props;
this.setState({value});
- onChangeText(value);
- }
+ if (onChangeText) {
+ onChangeText(value);
+ }
+ };
- onFocus() {
- const onFocus = this.props.onFocus;
- this.setState({isOnFocus: true});
- onFocus();
- }
+ onFocus = () => {
+ const {onFocus} = this.props;
- onBlur() {
- const onBlur = this.props.onBlur;
- this.setState({isOnFocus: false});
- onBlur();
- Keyboard.dismiss();
- }
+ this.setState({isFocused: true});
- blur = () => {
- this.onBlur();
+ if (onFocus) {
+ onFocus();
+ }
};
render() {
const {
- height,
+ autoCapitalize,
+ backgroundColor,
+ blurOnSubmit,
+ inputHeight,
+ inputStyle,
+ keyboardType,
placeholder,
- fontSize,
placeholderTextColor,
- textColor,
- textFieldBackgroundColor
+ returnKeyType,
+ titleCancelColor,
+ tintColorDelete,
+ tintColorSearch
} = this.props;
+ const {isFocused, value} = this.state;
- const searchBarStyle = {
- height: height + 10,
- paddingLeft: height * 0.25,
- backgroundColor: textFieldBackgroundColor
+ const inputNoBackground = {
+ ...inputStyle
};
+ Reflect.deleteProperty(inputNoBackground, 'backgroundColor');
- const inputStyle = {
- paddingLeft: height * 0.5,
- fontSize,
- color: textColor
- };
+ let inputColor = styles.searchBarInput.backgroundColor;
+ if (inputStyle) {
+ inputColor = inputStyle.backgroundColor;
+ } else {
+ inputNoBackground.backgroundColor = '#fff';
+ }
return (
- {this.state.isOnFocus && this.props.showCancelButton ?
-
+
+ {isFocused ?
+
+
+ :
- :
-
- }
-
- {this.state.isOnFocus && this.state.value ?
- this.setState({value: ''})}>
-
- : null
- }
+ {isFocused && value ?
+ this.onChangeText('')}>
+
+ : null
+ }
+
);
}
}
+
+const styles = StyleSheet.create({
+ container: {
+ backgroundColor: 'grey',
+ flexDirection: 'row',
+ justifyContent: 'flex-start',
+ alignItems: 'center',
+ padding: 5
+ },
+ searchBar: {
+ flex: 1,
+ flexDirection: 'row',
+ backgroundColor: 'red',
+ alignItems: 'center'
+ },
+ searchBarInput: {
+ flex: 1,
+ fontWeight: 'normal',
+ textAlignVertical: 'center',
+ padding: 0,
+ includeFontPadding: false
+ }
+});
diff --git a/app/components/search_bar/search_bar.ios.js b/app/components/search_bar/search_bar.ios.js
index 2d3df10a0..03c9ae066 100644
--- a/app/components/search_bar/search_bar.ios.js
+++ b/app/components/search_bar/search_bar.ios.js
@@ -1,96 +1,145 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import React, {PureComponent} from 'react';
-import PropTypes from 'prop-types';
-import SearchBar from 'react-native-search-bar';
-export default class SearchBarIos extends PureComponent {
+import React, {Component} from 'react';
+import {InteractionManager, Text, Keyboard} from 'react-native';
+import PropTypes from 'prop-types';
+import Search from 'react-native-search-box';
+
+export default class SearchBarIos extends Component {
static propTypes = {
- barStyle: PropTypes.oneOf(['default', 'black']),
- searchBarStyle: PropTypes.oneOf(['default', 'prominent', 'minimal']),
- text: PropTypes.string,
- placeholder: PropTypes.string,
- showCancelButton: PropTypes.bool,
- hideBackground: PropTypes.bool,
- textFieldBackgroundColor: PropTypes.string,
- placeholderTextColor: PropTypes.string,
- textColor: PropTypes.string,
- barTintColor: PropTypes.string, //color of the background container
- tintColor: PropTypes.string, // color of the carret and the cancel button
- onChange: PropTypes.func,
+ onCancelButtonPress: PropTypes.func,
onChangeText: PropTypes.func,
onFocus: PropTypes.func,
- onBlur: PropTypes.func,
onSearchButtonPress: PropTypes.func,
- onCancelButtonPress: PropTypes.func
- };
-
- static defaultProps = {
- barStyle: 'default',
- searchBarStyle: 'minimal',
- placeholder: 'Search',
- showCancelButton: true,
- hideBackground: true,
- onFocus: () => true,
- onBlur: () => true
+ backgroundColor: PropTypes.string,
+ placeholderTextColor: PropTypes.string,
+ titleCancelColor: PropTypes.string,
+ tintColorSearch: PropTypes.string,
+ tintColorDelete: PropTypes.string,
+ cancelButtonStyle: PropTypes.PropTypes.oneOfType([
+ PropTypes.number,
+ PropTypes.object
+ ]),
+ inputStyle: PropTypes.oneOfType([
+ PropTypes.number,
+ PropTypes.object
+ ]),
+ placeholder: PropTypes.string,
+ cancelTitle: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.object
+ ]),
+ returnKeyType: PropTypes.string,
+ keyboardType: PropTypes.string,
+ autoCapitalize: PropTypes.string,
+ inputHeight: PropTypes.number,
+ inputBorderRadius: PropTypes.number,
+ blurOnSubmit: PropTypes.bool
};
constructor(props) {
super(props);
this.state = {
- displayCancelButton: false
+ placeholderWidth: 0
};
}
- onFocus = (event) => {
- const onFocus = this.props.onFocus;
- if (this.props.showCancelButton) {
- this.setState({displayCancelButton: true});
- }
+ shouldComponentUpdate(nextProps, nextState) {
+ return nextState.placeholderWidth !== this.state.placeholderWidth;
+ }
- onFocus(event);
+ afterDelete = () => {
+ return new Promise((resolve) => {
+ this.refs.search.focus();
+ resolve();
+ });
};
- onBlur = (event) => {
- const onBlur = this.props.onBlur;
- if (this.props.showCancelButton) {
- this.setState({displayCancelButton: false});
- }
-
- onBlur(event);
+ cancel = () => {
+ this.refs.search.onCancel();
};
- blur = () => {
- this.searchBar.blur();
+ onCancel = () => {
+ return new Promise((resolve) => {
+ Keyboard.dismiss();
+ InteractionManager.runAfterInteractions(() => {
+ if (this.props.onCancelButtonPress) {
+ this.props.onCancelButtonPress();
+ }
+ resolve();
+ });
+ });
};
- searchBarRef = (ref) => {
- this.searchBar = ref;
+ onChangeText = (text) => {
+ return new Promise((resolve) => {
+ if (this.props.onChangeText) {
+ this.props.onChangeText(text);
+ }
+ resolve();
+ });
+ };
+
+ onDelete = () => {
+ return new Promise((resolve) => {
+ if (this.props.onChangeText) {
+ this.props.onChangeText('');
+ }
+ resolve();
+ });
+ };
+
+ onFocus = () => {
+ return new Promise((resolve) => {
+ if (this.props.onFocus) {
+ this.props.onFocus();
+ }
+ resolve();
+ });
+ };
+
+ onSearch = (text) => {
+ return new Promise((resolve) => {
+ if (this.props.onSearchButtonPress) {
+ this.props.onSearchButtonPress(text);
+ }
+ resolve();
+ });
};
render() {
+ if (this.state.placeholderWidth) {
+ return (
+
+ );
+ }
+
return (
-
+ {
+ const placeholderWidth = (event.nativeEvent.layout.width / 2);
+ this.setState({placeholderWidth});
+ }}
+ >
+ {this.props.placeholder}
+
);
}
}
diff --git a/app/i18n/index.js b/app/i18n/index.js
index dc5c6d663..cb1d7c5ec 100644
--- a/app/i18n/index.js
+++ b/app/i18n/index.js
@@ -2,6 +2,19 @@
// See License.txt for license information.
import 'intl';
+import {addLocaleData} from 'react-intl';
+import deLocaleData from 'react-intl/locale-data/de';
+import enLocaleData from 'react-intl/locale-data/en';
+import esLocaleData from 'react-intl/locale-data/es';
+import frLocaleData from 'react-intl/locale-data/fr';
+import jaLocaleData from 'react-intl/locale-data/ja';
+import koLocaleData from 'react-intl/locale-data/ko';
+import nlLocaleData from 'react-intl/locale-data/nl';
+import plLocaleData from 'react-intl/locale-data/pl';
+import ptLocaleData from 'react-intl/locale-data/pt';
+import trLocaleData from 'react-intl/locale-data/tr';
+import ruLocaleData from 'react-intl/locale-data/ru';
+import zhLocaleData from 'react-intl/locale-data/zh';
import de from 'assets/i18n/de.json';
import en from 'assets/i18n/en.json';
@@ -12,6 +25,7 @@ import ko from 'assets/i18n/ko.json';
import nl from 'assets/i18n/nl.json';
import pl from 'assets/i18n/pl.json';
import ptBR from 'assets/i18n/pt-BR.json';
+import tr from 'assets/i18n/tr.json';
import ru from 'assets/i18n/ru.json';
import zhCN from 'assets/i18n/zh-CN.json';
import zhTW from 'assets/i18n/zh-TW.json';
@@ -27,12 +41,28 @@ const TRANSLATIONS = {
ko,
nl,
pl,
- ptBR,
+ 'pt-BR': ptBR,
+ tr,
ru,
- zhCN,
- zhTW
+ 'zh-CN': zhCN,
+ 'zh-TW': zhTW
};
+addLocaleData([
+ deLocaleData,
+ enLocaleData,
+ esLocaleData,
+ frLocaleData,
+ jaLocaleData,
+ koLocaleData,
+ nlLocaleData,
+ plLocaleData,
+ ptLocaleData,
+ trLocaleData,
+ ruLocaleData,
+ zhLocaleData
+]);
+
export function getTranslations(locale) {
return TRANSLATIONS[locale] || TRANSLATIONS[DEFAULT_LOCALE];
}
diff --git a/app/mattermost.js b/app/mattermost.js
index daf4fbadd..5b3460b1f 100644
--- a/app/mattermost.js
+++ b/app/mattermost.js
@@ -21,6 +21,7 @@ import {General} from 'mattermost-redux/constants';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
import {goToNotification, loadConfigAndLicense, queueNotification} from 'app/actions/views/root';
+import {setChannelDisplayName} from 'app/actions/views/channel';
import {NavigationTypes, ViewTypes} from 'app/constants';
import initialState from 'app/initial_state';
import PushNotifications from 'app/push_notifications';
@@ -40,6 +41,7 @@ export default class Mattermost {
AppState.addEventListener('change', this.handleAppStateChange);
EventEmitter.on(General.CONFIG_CHANGED, this.handleConfigChanged);
EventEmitter.on(NavigationTypes.NAVIGATION_RESET, this.handleReset);
+ EventEmitter.on(General.DEFAULT_CHANNEL, this.handleResetDisplayName);
this.handleAppStateChange(AppState.currentState);
Client4.setUserAgent(DeviceInfo.getUserAgent());
@@ -78,6 +80,10 @@ export default class Mattermost {
this.startApp('fade');
};
+ handleResetDisplayName = (displayName) => {
+ store.dispatch(setChannelDisplayName(displayName));
+ };
+
handleVersionUpgrade = async () => {
const {dispatch, getState} = store;
diff --git a/app/screens/channel_add_members/channel_add_members.js b/app/screens/channel_add_members/channel_add_members.js
index 4b975da40..64bc37035 100644
--- a/app/screens/channel_add_members/channel_add_members.js
+++ b/app/screens/channel_add_members/channel_add_members.js
@@ -6,7 +6,6 @@ import PropTypes from 'prop-types';
import {injectIntl, intlShape} from 'react-intl';
import {
Alert,
- Keyboard,
Platform,
InteractionManager,
StyleSheet,
@@ -74,10 +73,6 @@ class ChannelAddMembers extends PureComponent {
}
componentDidMount() {
- if (Platform.OS === 'android') {
- Keyboard.addListener('keyboardDidHide', this.handleAndroidKeyboard);
- }
-
InteractionManager.runAfterInteractions(() => {
this.props.actions.getProfilesNotInChannel(this.props.currentTeam.id, this.props.currentChannel.id, 0);
this.props.actions.getTeamStats(this.props.currentTeam.id);
@@ -86,12 +81,6 @@ class ChannelAddMembers extends PureComponent {
this.emitCanAddMembers(false);
}
- componentWillUnmount() {
- if (Platform.OS === 'android') {
- Keyboard.removeListener('keyboardDidHide', this.handleAndroidKeyboard);
- }
- }
-
componentWillReceiveProps(nextProps) {
const {loadMoreRequestStatus} = this.props;
if (loadMoreRequestStatus === RequestStatus.STARTED &&
@@ -170,10 +159,6 @@ class ChannelAddMembers extends PureComponent {
actions.handleAddChannelMembers(currentChannel.id, membersToAdd);
};
- handleAndroidKeyboard = () => {
- this.onSearchButtonPress();
- };
-
handleRowSelect = (id) => {
const selectedMembers = Object.assign({}, this.state.selectedMembers, {[id]: !this.state.selectedMembers[id]});
@@ -215,16 +200,8 @@ class ChannelAddMembers extends PureComponent {
}
};
- onSearchButtonPress = () => {
- this.searchBar.blur();
- };
-
- searchBarRef = (ref) => {
- this.searchBar = ref;
- };
-
- searchProfiles = (event) => {
- const term = event.nativeEvent.text.toLowerCase();
+ searchProfiles = (text) => {
+ const term = text.toLowerCase();
const {actions, currentChannel, currentTeam} = this.props;
if (term) {
@@ -264,15 +241,22 @@ class ChannelAddMembers extends PureComponent {
style={{marginVertical: 5}}
>
diff --git a/app/screens/channel_info/channel_info.js b/app/screens/channel_info/channel_info.js
index 02f720e1c..135a99dc9 100644
--- a/app/screens/channel_info/channel_info.js
+++ b/app/screens/channel_info/channel_info.js
@@ -16,6 +16,7 @@ import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
import {General} from 'mattermost-redux/constants';
+import EventEmitter from 'mattermost-redux/utils/event_emitter';
import ChannelInfoHeader from './channel_info_header';
import ChannelInfoRow from './channel_info_row';
@@ -63,6 +64,11 @@ class ChannelInfo extends PureComponent {
}
}
+ close = () => {
+ EventEmitter.emit(General.DEFAULT_CHANNEL, '');
+ this.props.navigator.pop({animated: true});
+ };
+
goToChannelAddMembers = () => {
const {intl, navigator, theme} = this.props;
navigator.push({
@@ -115,7 +121,7 @@ class ChannelInfo extends PureComponent {
};
onPressAction = () => {
this.props.actions.leaveChannel(channel, true).then(() => {
- this.props.navigator.pop({animated: true});
+ this.close();
});
};
} else if (eventType === 'delete') {
@@ -126,7 +132,7 @@ class ChannelInfo extends PureComponent {
};
onPressAction = () => {
this.props.actions.deleteChannel(channel.id).then(() => {
- this.props.navigator.pop({animated: true});
+ this.close();
});
};
}
@@ -157,12 +163,12 @@ class ChannelInfo extends PureComponent {
switch (channel.type) {
case General.DM_CHANNEL:
closeDMChannel(channel).then(() => {
- this.props.navigator.pop({animated: true});
+ this.close();
});
break;
case General.GM_CHANNEL:
closeGMChannel(channel).then(() => {
- this.props.navigator.pop({animated: true});
+ this.close();
});
break;
}
diff --git a/app/screens/channel_members/channel_members.js b/app/screens/channel_members/channel_members.js
index f506b983e..7574e72a7 100644
--- a/app/screens/channel_members/channel_members.js
+++ b/app/screens/channel_members/channel_members.js
@@ -5,7 +5,6 @@ import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {
Alert,
- Keyboard,
Platform,
InteractionManager,
StyleSheet,
@@ -76,10 +75,6 @@ class ChannelMembers extends PureComponent {
}
componentDidMount() {
- if (Platform.OS === 'android') {
- Keyboard.addListener('keyboardDidHide', this.handleAndroidKeyboard);
- }
-
InteractionManager.runAfterInteractions(() => {
this.props.actions.getProfilesInChannel(this.props.currentChannel.id, 0);
});
@@ -87,12 +82,6 @@ class ChannelMembers extends PureComponent {
this.emitCanRemoveMembers(false);
}
- componentWillUnmount() {
- if (Platform.OS === 'android') {
- Keyboard.removeListener('keyboardDidHide', this.handleAndroidKeyboard);
- }
- }
-
componentWillReceiveProps(nextProps) {
const {requestStatus} = this.props;
if (requestStatus === RequestStatus.STARTED &&
@@ -159,10 +148,6 @@ class ChannelMembers extends PureComponent {
});
};
- handleAndroidKeyboard = () => {
- this.onSearchButtonPress();
- };
-
handleRemoveMembersPress = () => {
const {selectedMembers} = this.state;
const membersToRemove = Object.keys(selectedMembers).filter((m) => selectedMembers[m]);
@@ -240,10 +225,6 @@ class ChannelMembers extends PureComponent {
}
};
- onSearchButtonPress = () => {
- this.searchBar.blur();
- };
-
removeMembers = (membersToRemove) => {
const {actions, currentChannel} = this.props;
actions.handleRemoveChannelMembers(currentChannel.id, membersToRemove);
@@ -275,12 +256,8 @@ class ChannelMembers extends PureComponent {
);
};
- searchBarRef = (ref) => {
- this.searchBar = ref;
- };
-
- searchProfiles = (event) => {
- const term = event.nativeEvent.text.toLowerCase();
+ searchProfiles = (text) => {
+ const term = text.toLowerCase();
if (term) {
this.setState({searching: true, term});
@@ -319,15 +296,22 @@ class ChannelMembers extends PureComponent {
style={{marginVertical: 5}}
>
diff --git a/app/screens/more_channels/more_channels.js b/app/screens/more_channels/more_channels.js
index b21348526..a897a7cd9 100644
--- a/app/screens/more_channels/more_channels.js
+++ b/app/screens/more_channels/more_channels.js
@@ -5,7 +5,6 @@ import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {injectIntl, intlShape} from 'react-intl';
import {
- Keyboard,
Platform,
InteractionManager,
StyleSheet,
@@ -79,10 +78,6 @@ class MoreChannels extends PureComponent {
}
componentDidMount() {
- if (Platform.OS === 'android') {
- Keyboard.addListener('keyboardDidHide', this.handleAndroidKeyboard);
- }
-
// set the timeout to 400 cause is the time that the modal takes to open
// Somehow interactionManager doesn't care
setTimeout(() => {
@@ -104,12 +99,6 @@ class MoreChannels extends PureComponent {
}
}
- componentWillUnmount() {
- if (Platform.OS === 'android') {
- Keyboard.removeListener('keyboardDidHide', this.handleAndroidKeyboard);
- }
- }
-
close = () => {
this.props.navigator.dismissModal({animationType: 'slide-down'});
};
@@ -132,20 +121,8 @@ class MoreChannels extends PureComponent {
});
};
- handleAndroidKeyboard = () => {
- this.onSearchButtonPress();
- };
-
- searchBarRef = (ref) => {
- this.searchBar = ref;
- };
-
- onSearchButtonPress = () => {
- this.searchBar.blur();
- };
-
- searchProfiles = (event) => {
- const term = event.nativeEvent.text.toLowerCase();
+ searchProfiles = (text) => {
+ const term = text.toLowerCase();
if (term) {
const channels = this.filterChannels(this.state.channels, term);
@@ -226,7 +203,6 @@ class MoreChannels extends PureComponent {
onSelectChannel = async (id) => {
this.emitCanCreateChannel(false);
this.setState({adding: true});
- this.searchBar.blur();
await this.props.actions.joinChannel(
this.props.currentUserId,
this.props.currentTeamId,
@@ -278,15 +254,22 @@ class MoreChannels extends PureComponent {
style={{marginVertical: 5}}
>
diff --git a/app/screens/more_dms/more_dms.js b/app/screens/more_dms/more_dms.js
index 6079edd29..1d51500a8 100644
--- a/app/screens/more_dms/more_dms.js
+++ b/app/screens/more_dms/more_dms.js
@@ -5,7 +5,6 @@ import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {injectIntl, intlShape} from 'react-intl';
import {
- Keyboard,
Platform,
InteractionManager,
StyleSheet,
@@ -70,10 +69,6 @@ class MoreDirectMessages extends PureComponent {
}
componentDidMount() {
- if (Platform.OS === 'android') {
- Keyboard.addListener('keyboardDidHide', this.handleAndroidKeyboard);
- }
-
// set the timeout to 400 cause is the time that the modal takes to open
// Somehow interactionManager doesn't care
setTimeout(() => {
@@ -81,26 +76,12 @@ class MoreDirectMessages extends PureComponent {
}, 400);
}
- componentWillUnmount() {
- if (Platform.OS === 'android') {
- Keyboard.removeListener('keyboardDidHide', this.handleAndroidKeyboard);
- }
- }
-
close = () => {
this.props.navigator.dismissModal({
animationType: 'slide-down'
});
};
- handleAndroidKeyboard = () => {
- this.onSearchButtonPress();
- };
-
- searchBarRef = (ref) => {
- this.searchBar = ref;
- };
-
onNavigatorEvent = (event) => {
if (event.type === 'NavBarButtonPress') {
if (event.id === 'close-dms') {
@@ -109,15 +90,11 @@ class MoreDirectMessages extends PureComponent {
}
};
- onSearchButtonPress = () => {
- this.searchBar.blur();
- };
-
- searchProfiles = (event) => {
- const term = event.nativeEvent.text;
+ searchProfiles = (text) => {
+ const term = text.toLowerCase();
if (term) {
- this.setState({searching: true, term: term.toLowerCase()});
+ this.setState({searching: true, term});
clearTimeout(this.searchTimeoutId);
this.searchTimeoutId = setTimeout(() => {
@@ -156,7 +133,6 @@ class MoreDirectMessages extends PureComponent {
onSelectMember = async (id) => {
this.setState({adding: true});
- this.searchBar.blur();
await this.props.actions.makeDirectChannel(id);
EventEmitter.emit('close_channel_drawer');
@@ -190,15 +166,22 @@ class MoreDirectMessages extends PureComponent {
style={{marginVertical: 5}}
>
diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json
index 963ca1dc0..8c7813a84 100644
--- a/assets/base/i18n/en.json
+++ b/assets/base/i18n/en.json
@@ -1684,6 +1684,7 @@
"mobile.account_notifications.threads_mentions": "Mentions in threads",
"mobile.account_notifications.threads_start": "Threads that I start",
"mobile.account_notifications.threads_start_participate": "Threads that I start or participate in",
+ "mobile.channel_drawer.search": "Jump to a conversation",
"mobile.channel_info.alertMessageDeleteChannel": "Are you sure you want to delete the {term} {name}?",
"mobile.channel_info.alertMessageLeaveChannel": "Are you sure you want to leave the {term} {name}?",
"mobile.channel_info.alertNo": "No",
@@ -1700,6 +1701,7 @@
"mobile.channel_list.closeGM": "Close Group Message",
"mobile.channel_list.dm": "Direct Message",
"mobile.channel_list.gm": "Group Message",
+ "mobile.channel_list.not_member": "NOT A MEMBER",
"mobile.channel_list.open": "Open {term}",
"mobile.channel_list.openDM": "Open Direct Message",
"mobile.channel_list.openGM": "Open Group Message",
@@ -1716,6 +1718,7 @@
"mobile.create_channel.private": "New Private Channel",
"mobile.create_channel.public": "New Public Channel",
"mobile.custom_list.no_results": "No Results",
+ "mobile.drawer.teamsTitle": "Teams",
"mobile.edit_post.title": "Editing Message",
"mobile.file_upload.camera": "Take Photo or Video",
"mobile.file_upload.library": "Photo Library",
diff --git a/package.json b/package.json
index 9ff2ba364..1e0c1e5c1 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,7 @@
"react-native-notifications": "enahum/react-native-notifications.git",
"react-native-orientation": "enahum/react-native-orientation.git",
"react-native-search-bar": "enahum/react-native-search-bar.git",
+ "react-native-search-box": "0.0.9",
"react-native-svg": "5.1.8",
"react-native-swiper": "1.5.4",
"react-native-tooltip": "enahum/react-native-tooltip",
diff --git a/yarn.lock b/yarn.lock
index f417aa29d..3d0aa2e23 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3645,7 +3645,7 @@ makeerror@1.0.x:
mattermost-redux@mattermost/mattermost-redux#master:
version "0.0.1"
- resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/9a2e8099daea436dab76aa612be60492311387b3"
+ resolved "https://codeload.github.com/mattermost/mattermost-redux/tar.gz/488faa22c03e1c187c714a5db7f0a42b69d50ae8"
dependencies:
deep-equal "1.0.1"
harmony-reflect "1.5.1"
@@ -4556,6 +4556,10 @@ react-native-search-bar@enahum/react-native-search-bar.git:
version "2.16.0"
resolved "https://codeload.github.com/enahum/react-native-search-bar/tar.gz/88e6f5ba68012440ec21bde8388ca9e3124921c0"
+react-native-search-box@0.0.9:
+ version "0.0.9"
+ resolved "https://registry.yarnpkg.com/react-native-search-box/-/react-native-search-box-0.0.9.tgz#c1677e127d6d2b346850b031b2cb216447fa66f0"
+
react-native-svg-mock@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/react-native-svg-mock/-/react-native-svg-mock-1.0.2.tgz#2dc35fb97ca1e0ea393ece6d23782ba4f58e178f"
@@ -4575,10 +4579,6 @@ react-native-tooltip@enahum/react-native-tooltip:
version "5.0.0"
resolved "https://codeload.github.com/enahum/react-native-tooltip/tar.gz/97d58d19636df8d8df66d6b5737154c9fab727c8"
-react-native-tooltip@enahum/react-native-tooltip.git:
- version "5.0.0"
- resolved "https://codeload.github.com/enahum/react-native-tooltip/tar.gz/d4b491c7ef1f93cc6c62e5012e36be1087b45b34"
-
react-native-vector-icons@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/react-native-vector-icons/-/react-native-vector-icons-4.1.1.tgz#9ac75bde77d9243346668c51dca7756775428087"