From e1eeac4d82cd3614777c96fd0f8da1c8bb451d12 Mon Sep 17 00:00:00 2001 From: enahum Date: Mon, 20 Mar 2017 16:57:37 -0300 Subject: [PATCH] PLT-5498 Add ability to delete a message (#357) * ios tooltip menu with option to delete a post * bottom sheet for android * Feedback review --- android/app/build.gradle | 1 + .../java/com/mattermost/MainApplication.java | 2 + android/settings.gradle | 2 + .../file_attachment_list.js | 17 +- app/components/options_context/index.js | 6 + .../options_context.android.js | 39 ++++ .../options_context/options_context.ios.js | 43 ++++ app/components/post/post.js | 190 ++++++++++++++---- app/components/post/post_container.js | 20 +- assets/base/i18n/en.json | 3 + ios/Mattermost.xcodeproj/project.pbxproj | 159 ++++++++++++--- package.json | 2 + 12 files changed, 406 insertions(+), 78 deletions(-) create mode 100644 app/components/options_context/index.js create mode 100644 app/components/options_context/options_context.android.js create mode 100644 app/components/options_context/options_context.ios.js diff --git a/android/app/build.gradle b/android/app/build.gradle index c570b6bc3..34c1f16b1 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -143,6 +143,7 @@ android { } dependencies { + compile project(':react-native-bottom-sheet') compile project(':react-native-push-notification') compile ('com.google.android.gms:play-services-gcm:9.4.0') { force = true; diff --git a/android/app/src/main/java/com/mattermost/MainApplication.java b/android/app/src/main/java/com/mattermost/MainApplication.java index 2e43278fc..0bbe02ff5 100644 --- a/android/app/src/main/java/com/mattermost/MainApplication.java +++ b/android/app/src/main/java/com/mattermost/MainApplication.java @@ -4,6 +4,7 @@ import android.app.Application; import android.util.Log; import com.facebook.react.ReactApplication; +import com.gnet.bottomsheet.RNBottomSheetPackage; import com.learnium.RNDeviceInfo.RNDeviceInfo; import com.psykar.cookiemanager.CookieManagerPackage; import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage; @@ -31,6 +32,7 @@ public class MainApplication extends Application implements ReactApplication { protected List getPackages() { return Arrays.asList( new MainReactPackage(), + new RNBottomSheetPackage(), new RNDeviceInfo(), new CookieManagerPackage(), new ReactNativePushNotificationPackage(), diff --git a/android/settings.gradle b/android/settings.gradle index c5d66fcb7..9dc41c605 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,4 +1,6 @@ rootProject.name = 'Mattermost' +include ':react-native-bottom-sheet' +project(':react-native-bottom-sheet').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-bottom-sheet/android') include ':react-native-device-info' project(':react-native-device-info').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-device-info/android') include ':react-native-cookies' diff --git a/app/components/file_attachment_list/file_attachment_list.js b/app/components/file_attachment_list/file_attachment_list.js index eee22b410..0a67afe75 100644 --- a/app/components/file_attachment_list/file_attachment_list.js +++ b/app/components/file_attachment_list/file_attachment_list.js @@ -16,9 +16,12 @@ import FileAttachment from './file_attachment'; export default class FileAttachmentList extends Component { static propTypes = { actions: PropTypes.object.isRequired, - post: PropTypes.object.isRequired, files: PropTypes.array.isRequired, - theme: PropTypes.object.isRequired + hideOptionsContext: PropTypes.func.isRequired, + onLongPress: PropTypes.func, + post: PropTypes.object.isRequired, + theme: PropTypes.object.isRequired, + toggleSelected: PropTypes.func.isRequired }; componentDidMount() { @@ -26,11 +29,19 @@ export default class FileAttachmentList extends Component { this.props.actions.loadFilesForPostIfNecessary(post); } + handleOnPress = (file) => { + this.props.hideOptionsContext(); + this.props.actions.goToImagePreviewModal(this.props.post, file.id); + }; + render() { const fileAttachments = this.props.files.map((file) => ( this.props.actions.goToImagePreviewModal(this.props.post, file.id)} + onLongPress={this.props.onLongPress} + onPress={() => this.handleOnPress(file)} + onPressIn={() => this.props.toggleSelected(true)} + onPressOut={() => this.props.toggleSelected(false)} > { + const {actions, cancelText} = this.props; + if (actions.length) { + const actionsText = actions.map((a) => a.text); + RNBottomSheet.showBottomSheetWithOptions({ + options: [...actionsText, cancelText], + cancelButtonIndex: actions.length + }, (value) => { + if (value !== actions.length) { + const selectedOption = actions[value]; + if (selectedOption && selectedOption.onPress) { + selectedOption.onPress(); + } + } + }); + } + }; + + render() { + return null; + } +} diff --git a/app/components/options_context/options_context.ios.js b/app/components/options_context/options_context.ios.js new file mode 100644 index 000000000..b46350d26 --- /dev/null +++ b/app/components/options_context/options_context.ios.js @@ -0,0 +1,43 @@ +// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +import React, {PropTypes, PureComponent} from 'react'; +import ToolTip from 'react-native-tooltip'; + +export default class OptionsContext extends PureComponent { + static propTypes = { + actions: PropTypes.array, + children: PropTypes.node.isRequired, + onPress: PropTypes.func.isRequired, + toggleSelected: PropTypes.func.isRequired + }; + + static defaultProps = { + actions: [] + }; + + hide() { + this.refs.toolTip.hideMenu(); + } + + show() { + this.refs.toolTip.showMenu(); + } + + render() { + return ( + this.props.toggleSelected(false)} + onPress={this.props.onPress} + onShowUnderlay={() => this.props.toggleSelected(true)} + underlayColor='transparent' + > + {this.props.children} + + ); + } +} diff --git a/app/components/post/post.js b/app/components/post/post.js index 56a77516e..885f18ec8 100644 --- a/app/components/post/post.js +++ b/app/components/post/post.js @@ -1,8 +1,9 @@ // Copyright (c) 2016 Mattermost, Inc. All Rights Reserved. // See License.txt for license information. -import React, {Component, PropTypes} from 'react'; +import React, {PropTypes, PureComponent} from 'react'; import { + Alert, Image, Platform, StyleSheet, @@ -11,19 +12,26 @@ import { TouchableOpacity, View } from 'react-native'; +import {injectIntl, intlShape} from 'react-intl'; import FormattedText from 'app/components/formatted_text'; import FormattedTime from 'app/components/formatted_time'; import MattermostIcon from 'app/components/mattermost_icon'; import Markdown from 'app/components/markdown/markdown'; +import OptionsContext from 'app/components/options_context'; import ProfilePicture from 'app/components/profile_picture'; import FileAttachmentList from 'app/components/file_attachment_list/file_attachment_list_container'; import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme'; -import {isSystemMessage} from 'mattermost-redux/utils/post_utils.js'; +import {Constants} from 'mattermost-redux/constants'; +import {isSystemMessage} from 'mattermost-redux/utils/post_utils'; +import {isAdmin} from 'mattermost-redux/utils/user_utils'; -export default class Post extends Component { +class Post extends PureComponent { static propTypes = { + currentTeamId: PropTypes.string.isRequired, + currentUserId: PropTypes.string.isRequired, + intl: intlShape.isRequired, style: View.propTypes.style, post: PropTypes.object.isRequired, user: PropTypes.object, @@ -31,21 +39,57 @@ export default class Post extends Component { renderReplies: PropTypes.bool, isFirstReply: PropTypes.bool, isLastReply: PropTypes.bool, - commentedOnPost: PropTypes.object, commentedOnDisplayName: PropTypes.string, + commentedOnPost: PropTypes.object, + roles: PropTypes.string, theme: PropTypes.object.isRequired, onPress: PropTypes.func, actions: PropTypes.shape({ + deletePost: PropTypes.func.isRequired, goToUserProfile: PropTypes.func.isRequired }).isRequired }; + handlePostDelete = () => { + const {formatMessage} = this.props.intl; + const {currentTeamId, post, actions} = this.props; + + Alert.alert( + formatMessage({id: 'mobile.post.delete_title', defaultMessage: 'Delete Post'}), + formatMessage({id: 'mobile.post.delete_question', defaultMessage: 'Are you sure you want to delete this post?'}), + [{ + text: formatMessage({id: 'mobile.post.cancel', defaultMessage: 'Cancel'}), + style: 'cancel' + }, { + text: formatMessage({id: 'post_info.del', defaultMessage: 'Delete'}), + style: 'destructive', + onPress: () => { + actions.deletePost(currentTeamId, post); + } + }] + ); + }; + handlePress = () => { if (this.props.onPress) { this.props.onPress(this.props.post); } }; + hideOptionsContext = () => { + if (Platform.OS === 'ios') { + this.refs.tooltip.hide(); + } + }; + + showOptionsContext = () => { + if (Platform.OS === 'ios') { + return this.refs.tooltip.show(); + } + + return this.refs.bottomSheet.show(); + }; + renderCommentedOnMessage = (style) => { if (!this.props.renderReplies || !this.props.commentedOnPost) { return null; @@ -83,7 +127,7 @@ export default class Post extends Component { style={style.commentedOn} /> ); - } + }; renderReplyBar = (style) => { if (!this.props.renderReplies || !this.props.post.root_id) { @@ -106,47 +150,109 @@ export default class Post extends Component { renderFileAttachments() { const {post} = this.props; const fileIds = post.file_ids || []; + let attachments; if (fileIds.length > 0) { - attachments = (); + attachments = ( + + ); } return attachments; } + renderMessage = (style, messageStyle, replyBar = false) => { + const {formatMessage} = this.props.intl; + const {currentUserId, post, roles, theme} = this.props; + const actions = []; + + // we should check for the user roles and permissions + // actions.push({text: 'Flag', onPress: () => console.log('flag pressed')}); //eslint-disable-line no-console + // if (post.user_id === currentUserId) { + // actions.push({text: 'Edit', onPress: () => console.log('edit pressed')}); //eslint-disable-line no-console + // } + if (post.user_id === currentUserId || isAdmin(roles)) { + actions.push({text: formatMessage({id: 'post_info.del', defaultMessage: 'Delete'}), onPress: () => this.handlePostDelete()}); + } + + let messageContainer; + let message; + if (post.state === Constants.POST_DELETED) { + message = ( + + ); + } else if (this.props.post.message.length) { + message = ( + + ); + } + + if (Platform.OS === 'ios') { + messageContainer = ( + + {replyBar && this.renderReplyBar(style)} + + {message} + {this.renderFileAttachments()} + + + ); + } else { + messageContainer = ( + + {replyBar && this.renderReplyBar(style)} + this.toggleSelected(false)} + onLongPress={this.showOptionsContext} + onPress={this.handlePress} + onShowUnderlay={() => this.toggleSelected(true)} + underlayColor='transparent' + > + + {message} + + {this.renderFileAttachments()} + + + + ); + } + + return messageContainer; + }; + viewUserProfile = () => { this.props.actions.goToUserProfile(this.props.user.id); }; - renderMessage = (style, messageStyle, replyBar = false) => { - let contents; - if (this.props.post.message.length > 0) { - const theme = this.props.theme; - - contents = ( - - ); - } - - return ( - - - {replyBar && this.renderReplyBar(style)} - {contents} - {this.renderFileAttachments()} - - - ); + toggleSelected = (selected) => { + this.setState({selected}); }; render() { - const theme = this.props.theme; - const style = getStyleSheet(theme); - + const style = getStyleSheet(this.props.theme); const PROFILE_PICTURE_SIZE = 32; let profilePicture; @@ -191,8 +297,7 @@ export default class Post extends Component { {this.props.post.props.override_username} ); - - messageStyle = style.message; + messageStyle = [style.message, style.webhookMessage]; } else { profilePicture = ( @@ -224,10 +329,11 @@ export default class Post extends Component { messageStyle = style.message; } + const selected = this.state && this.state.selected ? style.selected : null; let contents; if (this.props.commentedOnPost) { contents = ( - + {profilePicture} @@ -247,7 +353,7 @@ export default class Post extends Component { ); } else { contents = ( - + {profilePicture} @@ -341,6 +447,14 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => { }, systemMessage: { opacity: 0.5 + }, + webhookMessage: { + color: theme.centerChannelColor, + fontSize: 16, + fontWeight: '600' + }, + selected: { + backgroundColor: changeOpacity(theme.centerChannelColor, 0.1) } }); }); @@ -415,3 +529,5 @@ const getMarkdownBlockStyles = makeStyleSheetFromTheme((theme) => { } }); }); + +export default injectIntl(Post); diff --git a/app/components/post/post_container.js b/app/components/post/post_container.js index 533d54803..bbefe0a90 100644 --- a/app/components/post/post_container.js +++ b/app/components/post/post_container.js @@ -4,25 +4,30 @@ import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; -import {getMyPreferences} from 'mattermost-redux/selectors/entities/preferences'; -import {getTheme} from 'app/selectors/preferences'; import {goToUserProfile} from 'app/actions/navigation'; -import {getUser} from 'mattermost-redux/selectors/entities/users'; +import {getTheme} from 'app/selectors/preferences'; + +import {deletePost} from 'mattermost-redux/actions/posts'; +import {getMyPreferences} from 'mattermost-redux/selectors/entities/preferences'; +import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams'; +import {getCurrentUserId, getCurrentUserRoles, getUser} from 'mattermost-redux/selectors/entities/users'; import {displayUsername} from 'mattermost-redux/utils/user_utils'; import Post from './post'; function mapStateToProps(state, ownProps) { - const user = getUser(state, ownProps.post.user_id); const commentedOnUser = ownProps.commentedOnPost ? getUser(state, ownProps.commentedOnPost.user_id) : null; - + const user = getUser(state, ownProps.post.user_id); const myPreferences = getMyPreferences(state); return { - user, - displayName: displayUsername(user, myPreferences), commentedOnDisplayName: displayUsername(commentedOnUser, myPreferences), + currentTeamId: getCurrentTeamId(state), + currentUserId: getCurrentUserId(state), + displayName: displayUsername(user, myPreferences), + roles: getCurrentUserRoles(state), theme: getTheme(state), + user, ...ownProps }; } @@ -30,6 +35,7 @@ function mapStateToProps(state, ownProps) { function mapDispatchToProps(dispatch) { return { actions: bindActionCreators({ + deletePost, goToUserProfile }, dispatch) }; diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json index 12ac6a3e1..33ee6b148 100644 --- a/assets/base/i18n/en.json +++ b/assets/base/i18n/en.json @@ -1521,6 +1521,9 @@ "mobile.loading_posts": "Loading Messages...", "mobile.request.invalid_response": "Received invalid response from the server.", "mobile.login_options.choose_title": "Choose your login method", + "mobile.post.cancel": "Cancel", + "mobile.post.delete_title": "Delete Post", + "mobile.post.delete_question": "Are you sure you want to delete this post?", "mobile.routes.channels": "Channels", "mobile.routes.enterServerUrl": "Enter Server URL", "mobile.routes.login": "Login", diff --git a/ios/Mattermost.xcodeproj/project.pbxproj b/ios/Mattermost.xcodeproj/project.pbxproj index 07a2efe29..d52b32baf 100644 --- a/ios/Mattermost.xcodeproj/project.pbxproj +++ b/ios/Mattermost.xcodeproj/project.pbxproj @@ -5,6 +5,7 @@ }; objectVersion = 46; objects = { + /* Begin PBXBuildFile section */ 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; @@ -12,6 +13,7 @@ 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; 00E356F31AD99517003FC87E /* MattermostTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* MattermostTests.m */; }; + 10CD747CE4304BD6AB38B4CD /* libRNDeviceInfo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EE671DF7637347CD8C069819 /* libRNDeviceInfo.a */; }; 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; }; @@ -26,20 +28,20 @@ 382D94CF15EE4FC292C3F341 /* Foundation.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 51F5A483EA9F4A9A87ACFB59 /* Foundation.ttf */; }; 3D38ABA732A34A9BB3294F90 /* EvilIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 349FBA7338E74D9BBD709528 /* EvilIcons.ttf */; }; 420A7328E12C4B72AEF420CE /* Octicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = EDC04CBCF81642219D199CBB /* Octicons.ttf */; }; + 584837D6B55F405F908A2053 /* libBVLinearGradient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ACC6B9FDC0AD45A6BFA4FBCD /* libBVLinearGradient.a */; }; 5E1AF7B72B8D4A4E9E53FF9D /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 005346E5C0E542BFABAE1411 /* FontAwesome.ttf */; }; 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; }; 7F63D2841E6C9585001FAE12 /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F63D2811E6C957C001FAE12 /* libRCTPushNotification.a */; }; + 7F6877B31E7836070094B63F /* libToolTipMenu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F6877B01E7835E50094B63F /* libToolTipMenu.a */; }; 7FBB5E9A1E1F5A4B000DE18A /* libRNSVG.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FDF28E11E1F4B1F00DBBE56 /* libRNSVG.a */; }; 7FBB5E9B1E1F5A4B000DE18A /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FDF290C1E1F4B4E00DBBE56 /* libRNVectorIcons.a */; }; + 7FED0C321E7C4AAC001A7CCA /* libRNCookieManagerIOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F906D971E6A1ADF00B8F49A /* libRNCookieManagerIOS.a */; }; 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; 895C9A56B94A45C1BAF568FE /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = AC6EB561E1F64C17A69D2FAD /* Entypo.ttf */; }; - 9906AC672C594EEB9D5EE503 /* libRNCookieManagerIOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CE45EB909FB44F3597EF3C60 /* libRNCookieManagerIOS.a */; }; B50561A109B4442299F82327 /* libRNSearchBar.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F81F6DC42D394831B4549928 /* libRNSearchBar.a */; }; C035DB50ED2045F09923FFAE /* MaterialCommunityIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 04AA5E8EF3B54735A11E3B95 /* MaterialCommunityIcons.ttf */; }; C337E8708D2845C6A8DBB47E /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2DCFD31D3F4A4154822AB532 /* Ionicons.ttf */; }; F006936FC2884C24A1321FC0 /* MaterialIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 356C9186FA374641A00EB2EA /* MaterialIcons.ttf */; }; - 10CD747CE4304BD6AB38B4CD /* libRNDeviceInfo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EE671DF7637347CD8C069819 /* libRNDeviceInfo.a */; }; - 584837D6B55F405F908A2053 /* libBVLinearGradient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ACC6B9FDC0AD45A6BFA4FBCD /* libBVLinearGradient.a */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -225,13 +227,6 @@ remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RNSearchBar; }; - 7F906D961E6A1ADF00B8F49A /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 31A0780E2F224AC8AF8E6930 /* RNCookieManagerIOS.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 1BD725DA1CF77A8B005DBD79; - remoteInfo = RNCookieManagerIOS; - }; 7F63D2801E6C957C001FAE12 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 7F63D27B1E6C957C001FAE12 /* RCTPushNotification.xcodeproj */; @@ -246,6 +241,34 @@ remoteGlobalIDString = 3D05745F1DE6004600184BB4; remoteInfo = "RCTPushNotification-tvOS"; }; + 7F6877A51E7835AD0094B63F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B97AA6F961BB47D3A3297E8E /* RNDeviceInfo.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = DA5891D81BA9A9FC002B4DB2; + remoteInfo = RNDeviceInfo; + }; + 7F6877AF1E7835E50094B63F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7F6877AA1E7835E50094B63F /* ToolTipMenu.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 4681C0211B05271A004D67D4; + remoteInfo = ToolTipMenu; + }; + 7F6877B11E7835E50094B63F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7F6877AA1E7835E50094B63F /* ToolTipMenu.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 4681C02C1B05271A004D67D4; + remoteInfo = ToolTipMenuTests; + }; + 7F906D961E6A1ADF00B8F49A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 31A0780E2F224AC8AF8E6930 /* RNCookieManagerIOS.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 1BD725DA1CF77A8B005DBD79; + remoteInfo = RNCookieManagerIOS; + }; 7FDF28E01E1F4B1F00DBBE56 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 2CBE9C0FB56E4FDA96C30792 /* RNSVG.xcodeproj */; @@ -260,6 +283,13 @@ remoteGlobalIDString = 5DBEB1501B18CEA900B34395; remoteInfo = RNVectorIcons; }; + 7FED0C0E1E7C4A94001A7CCA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7DCC3D826CE640AF8F491692 /* BVLinearGradient.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = BVLinearGradient; + }; 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; @@ -301,18 +331,18 @@ 59954D479A89488091EB588F /* RNSearchBar.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNSearchBar.xcodeproj; path = "../node_modules/react-native-search-bar/RNSearchBar.xcodeproj"; sourceTree = ""; }; 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = ""; }; 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; + 7DCC3D826CE640AF8F491692 /* BVLinearGradient.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = BVLinearGradient.xcodeproj; path = "../node_modules/react-native-linear-gradient/BVLinearGradient.xcodeproj"; sourceTree = ""; }; 7F63D27B1E6C957C001FAE12 /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTPushNotification.xcodeproj; path = "../node_modules/react-native/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj"; sourceTree = ""; }; 7F63D2C21E6DD98A001FAE12 /* Mattermost.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = Mattermost.entitlements; path = Mattermost/Mattermost.entitlements; sourceTree = ""; }; + 7F6877AA1E7835E50094B63F /* ToolTipMenu.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ToolTipMenu.xcodeproj; path = "../node_modules/react-native-tooltip/ToolTipMenu.xcodeproj"; sourceTree = ""; }; 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; AC6EB561E1F64C17A69D2FAD /* Entypo.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Entypo.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Entypo.ttf"; sourceTree = ""; }; - CE45EB909FB44F3597EF3C60 /* libRNCookieManagerIOS.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNCookieManagerIOS.a; sourceTree = ""; }; + ACC6B9FDC0AD45A6BFA4FBCD /* libBVLinearGradient.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libBVLinearGradient.a; sourceTree = ""; }; + B97AA6F961BB47D3A3297E8E /* RNDeviceInfo.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNDeviceInfo.xcodeproj; path = "../node_modules/react-native-device-info/RNDeviceInfo.xcodeproj"; sourceTree = ""; }; EDC04CBCF81642219D199CBB /* Octicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Octicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Octicons.ttf"; sourceTree = ""; }; + EE671DF7637347CD8C069819 /* libRNDeviceInfo.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNDeviceInfo.a; sourceTree = ""; }; F1F071EE85494E269A50AE88 /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = SimpleLineIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = ""; }; F81F6DC42D394831B4549928 /* libRNSearchBar.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNSearchBar.a; sourceTree = ""; }; - B97AA6F961BB47D3A3297E8E /* RNDeviceInfo.xcodeproj */ = {isa = PBXFileReference; name = "RNDeviceInfo.xcodeproj"; path = "../node_modules/react-native-device-info/RNDeviceInfo.xcodeproj"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; - EE671DF7637347CD8C069819 /* libRNDeviceInfo.a */ = {isa = PBXFileReference; name = "libRNDeviceInfo.a"; path = "libRNDeviceInfo.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; - 7DCC3D826CE640AF8F491692 /* BVLinearGradient.xcodeproj */ = {isa = PBXFileReference; name = "BVLinearGradient.xcodeproj"; path = "../node_modules/react-native-linear-gradient/BVLinearGradient.xcodeproj"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; }; - ACC6B9FDC0AD45A6BFA4FBCD /* libBVLinearGradient.a */ = {isa = PBXFileReference; name = "libBVLinearGradient.a"; path = "libBVLinearGradient.a"; sourceTree = ""; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -343,9 +373,10 @@ 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, 7F63D2841E6C9585001FAE12 /* libRCTPushNotification.a in Frameworks */, B50561A109B4442299F82327 /* libRNSearchBar.a in Frameworks */, - 9906AC672C594EEB9D5EE503 /* libRNCookieManagerIOS.a in Frameworks */, + 7FED0C321E7C4AAC001A7CCA /* libRNCookieManagerIOS.a in Frameworks */, 10CD747CE4304BD6AB38B4CD /* libRNDeviceInfo.a in Frameworks */, 584837D6B55F405F908A2053 /* libBVLinearGradient.a in Frameworks */, + 7F6877B31E7836070094B63F /* libToolTipMenu.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -501,14 +532,6 @@ name = Products; sourceTree = ""; }; - 7F906D7A1E6A1ADF00B8F49A /* Products */ = { - isa = PBXGroup; - children = ( - 7F906D971E6A1ADF00B8F49A /* libRNCookieManagerIOS.a */, - ); - name = Products; - sourceTree = ""; - }; 7F63D27C1E6C957C001FAE12 /* Products */ = { isa = PBXGroup; children = ( @@ -518,6 +541,31 @@ name = Products; sourceTree = ""; }; + 7F6877861E7835AD0094B63F /* Products */ = { + isa = PBXGroup; + children = ( + 7F6877A61E7835AD0094B63F /* libRNDeviceInfo.a */, + ); + name = Products; + sourceTree = ""; + }; + 7F6877AB1E7835E50094B63F /* Products */ = { + isa = PBXGroup; + children = ( + 7F6877B01E7835E50094B63F /* libToolTipMenu.a */, + 7F6877B21E7835E50094B63F /* ToolTipMenuTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 7F906D7A1E6A1ADF00B8F49A /* Products */ = { + isa = PBXGroup; + children = ( + 7F906D971E6A1ADF00B8F49A /* libRNCookieManagerIOS.a */, + ); + name = Products; + sourceTree = ""; + }; 7FDF28C41E1F4B1F00DBBE56 /* Products */ = { isa = PBXGroup; children = ( @@ -534,9 +582,18 @@ name = Products; sourceTree = ""; }; + 7FED0C0B1E7C4A94001A7CCA /* Products */ = { + isa = PBXGroup; + children = ( + 7FED0C0F1E7C4A94001A7CCA /* libBVLinearGradient.a */, + ); + name = Products; + sourceTree = ""; + }; 832341AE1AAA6A7D00B99B32 /* Libraries */ = { isa = PBXGroup; children = ( + 7F6877AA1E7835E50094B63F /* ToolTipMenu.xcodeproj */, 7F63D27B1E6C957C001FAE12 /* RCTPushNotification.xcodeproj */, 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */, 146833FF1AC3E56700842450 /* React.xcodeproj */, @@ -665,6 +722,10 @@ productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; projectDirPath = ""; projectReferences = ( + { + ProductGroup = 7FED0C0B1E7C4A94001A7CCA /* Products */; + ProjectRef = 7DCC3D826CE640AF8F491692 /* BVLinearGradient.xcodeproj */; + }, { ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; @@ -717,6 +778,10 @@ ProductGroup = 7F906D7A1E6A1ADF00B8F49A /* Products */; ProjectRef = 31A0780E2F224AC8AF8E6930 /* RNCookieManagerIOS.xcodeproj */; }, + { + ProductGroup = 7F6877861E7835AD0094B63F /* Products */; + ProjectRef = B97AA6F961BB47D3A3297E8E /* RNDeviceInfo.xcodeproj */; + }, { ProductGroup = 7F48904C1E3B7D3B008EDBEA /* Products */; ProjectRef = 59954D479A89488091EB588F /* RNSearchBar.xcodeproj */; @@ -729,6 +794,10 @@ ProductGroup = 7FDF28EE1E1F4B4E00DBBE56 /* Products */; ProjectRef = 2B25899FDAC149EB96ED3305 /* RNVectorIcons.xcodeproj */; }, + { + ProductGroup = 7F6877AB1E7835E50094B63F /* Products */; + ProjectRef = 7F6877AA1E7835E50094B63F /* ToolTipMenu.xcodeproj */; + }, ); projectRoot = ""; targets = ( @@ -914,13 +983,6 @@ remoteRef = 7F4890681E3B7D3B008EDBEA /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; - 7F906D971E6A1ADF00B8F49A /* libRNCookieManagerIOS.a */ = { - isa = PBXReferenceProxy; - fileType = archive.ar; - path = libRNCookieManagerIOS.a; - remoteRef = 7F906D961E6A1ADF00B8F49A /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; 7F63D2811E6C957C001FAE12 /* libRCTPushNotification.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -935,6 +997,34 @@ remoteRef = 7F63D2821E6C957C001FAE12 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 7F6877A61E7835AD0094B63F /* libRNDeviceInfo.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRNDeviceInfo.a; + remoteRef = 7F6877A51E7835AD0094B63F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 7F6877B01E7835E50094B63F /* libToolTipMenu.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libToolTipMenu.a; + remoteRef = 7F6877AF1E7835E50094B63F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 7F6877B21E7835E50094B63F /* ToolTipMenuTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = ToolTipMenuTests.xctest; + remoteRef = 7F6877B11E7835E50094B63F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 7F906D971E6A1ADF00B8F49A /* libRNCookieManagerIOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRNCookieManagerIOS.a; + remoteRef = 7F906D961E6A1ADF00B8F49A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 7FDF28E11E1F4B1F00DBBE56 /* libRNSVG.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; @@ -949,6 +1039,13 @@ remoteRef = 7FDF290B1E1F4B4E00DBBE56 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 7FED0C0F1E7C4A94001A7CCA /* libBVLinearGradient.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libBVLinearGradient.a; + remoteRef = 7FED0C0E1E7C4A94001A7CCA /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; diff --git a/package.json b/package.json index 2e6f827d9..3e4487a36 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "react-addons-pure-render-mixin": "15.4.1", "react-intl": "2.2.2", "react-native": "0.42.0", + "react-native-bottom-sheet": "1.0.1", "react-native-button": "1.7.1", "react-native-cookies": "2.0.0", "react-native-device-info": "0.10.1", @@ -25,6 +26,7 @@ "react-native-linear-gradient": "2.0.0", "react-native-search-bar": "enahum/react-native-search-bar.git", "react-native-svg": "4.5.0", + "react-native-tooltip": "5.0.0", "react-native-vector-icons": "4.0.0", "react-redux": "5.0.1", "redux": "3.6.0",