diff --git a/.gitignore b/.gitignore
index b25298785..6b3e8ed0e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -69,3 +69,7 @@ ios/sentry.properties
# Testing
.nyc_output
+
+# Pods
+.podinstall
+ios/Pods/
diff --git a/Makefile b/Makefile
index acbe41d03..151c54d79 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,7 @@
ios_target := $(filter-out build-ios,$(MAKECMDGOALS))
android_target := $(filter-out build-android,$(MAKECMDGOALS))
+POD := $(shell command -v pod 2> /dev/null)
.yarninstall: package.json
@if ! [ $(shell command -v yarn 2> /dev/null) ]; then \
@@ -16,7 +17,15 @@ android_target := $(filter-out build-android,$(MAKECMDGOALS))
yarn install --pure-lockfile
- touch $@
+ @touch $@
+
+.podinstall:
+ifdef POD
+ @echo Getting CocoaPods dependencies;
+ @cd ios && pod install;
+endif
+
+ @touch $@
BASE_ASSETS = $(shell find assets/base -type d) $(shell find assets/base -type f -name '*')
OVERRIDE_ASSETS = $(shell find assets/override -type d 2> /dev/null) $(shell find assets/override -type f -name '*' 2> /dev/null)
@@ -30,7 +39,7 @@ dist/assets: $(BASE_ASSETS) $(OVERRIDE_ASSETS)
node scripts/make-dist-assets.js
-pre-run: .yarninstall dist/assets
+pre-run: | .yarninstall .podinstall dist/assets
run: run-ios
@@ -89,8 +98,10 @@ clean:
yarn cache clean
rm -rf node_modules
rm -f .yarninstall
+ rm -f .podinstall
rm -rf dist
rm -rf ios/build
+ rm -rf ios/Pods
rm -rf android/app/build
post-install:
diff --git a/NOTICE.txt b/NOTICE.txt
index 537b4cd85..f6c4b843d 100644
--- a/NOTICE.txt
+++ b/NOTICE.txt
@@ -1378,3 +1378,70 @@ 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-youtube
+
+This product contains 'react-native-youtube', A component for React Native.
+
+* HOMEPAGE:
+ * https://github.com/inProgress-team/react-native-youtube
+
+* LICENSE:
+
+The MIT License (MIT)
+
+Copyright (c) 2015 Param Aggarwal
+
+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.
+
+---
+
+## youtube-video-id
+
+This product contains 'youtube-video-id', Extracts the YouTube video ID from a url or string.
+
+* HOMEPAGE:
+ * https://github.com/remarkablemark/youtube-video-id
+
+* LICENSE:
+
+MIT License
+
+Copyright (c) 2016 Menglin "Mark" Xu
+
+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.
diff --git a/android/app/build.gradle b/android/app/build.gradle
index a3272f47f..aa14ea9cf 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -152,6 +152,7 @@ android {
}
dependencies {
+ compile project(':react-native-youtube')
compile project(':react-native-sentry')
compile project(':react-native-exception-handler')
compile project(':react-native-fetch-blob')
@@ -173,6 +174,13 @@ dependencies {
compile project(':react-native-svg')
compile project(':react-native-local-auth')
compile project(':jail-monkey')
+
+ // For animated GIF support
+ compile 'com.facebook.fresco:animated-base-support:1.0.1'
+ // For WebP support, including animated WebP
+ compile 'com.facebook.fresco:animated-gif:1.0.1'
+ compile 'com.facebook.fresco:animated-webp:1.0.1'
+ compile 'com.facebook.fresco:webpsupport:1.0.1'
}
// Run this once to be able to run the application with BUCK
diff --git a/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.java b/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.java
index 0a5993a8d..0a3b378b7 100644
--- a/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.java
+++ b/android/app/src/main/java/com/mattermost/rnbeta/MainApplication.java
@@ -6,6 +6,7 @@ import android.content.Context;
import android.os.Bundle;
import com.facebook.react.ReactApplication;
+import com.inprogress.reactnativeyoutube.ReactNativeYouTube;
import io.sentry.RNSentryPackage;
import com.masteratul.exceptionhandler.ReactNativeExceptionHandlerPackage;
import com.RNFetchBlob.RNFetchBlobPackage;
@@ -64,7 +65,8 @@ public class MainApplication extends NavigationApplication implements INotificat
new RNFetchBlobPackage(),
new MattermostManagedPackage(),
new RNSentryPackage(this),
- new ReactNativeExceptionHandlerPackage()
+ new ReactNativeExceptionHandlerPackage(),
+ new ReactNativeYouTube()
);
}
diff --git a/android/settings.gradle b/android/settings.gradle
index 17e329d8d..c85e18ae2 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -1,4 +1,6 @@
rootProject.name = 'Mattermost'
+include ':react-native-youtube'
+project(':react-native-youtube').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-youtube/android')
include ':react-native-sentry'
project(':react-native-sentry').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sentry/android')
include ':react-native-exception-handler'
diff --git a/app/components/post_attachment_opengraph/index.js b/app/components/post_attachment_opengraph/index.js
new file mode 100644
index 000000000..ab2da21e0
--- /dev/null
+++ b/app/components/post_attachment_opengraph/index.js
@@ -0,0 +1,28 @@
+// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {connect} from 'react-redux';
+import {bindActionCreators} from 'redux';
+
+import {getOpenGraphMetadata} from 'mattermost-redux/actions/posts';
+
+import {getTheme} from 'app/selectors/preferences';
+
+import PostAttachmentOpenGraph from './post_attachment_opengraph';
+
+function mapStateToProps(state, ownProps) {
+ return {
+ ...ownProps,
+ theme: getTheme(state)
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ return {
+ actions: bindActionCreators({
+ getOpenGraphMetadata
+ }, dispatch)
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(PostAttachmentOpenGraph);
diff --git a/app/components/post_attachment_opengraph/post_attachment_opengraph.js b/app/components/post_attachment_opengraph/post_attachment_opengraph.js
new file mode 100644
index 000000000..a20aac6b4
--- /dev/null
+++ b/app/components/post_attachment_opengraph/post_attachment_opengraph.js
@@ -0,0 +1,252 @@
+// 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 {
+ Dimensions,
+ Image,
+ Linking,
+ PixelRatio,
+ Text,
+ TouchableOpacity,
+ View
+} from 'react-native';
+
+import {getNearestPoint} from 'app/utils/opengraph';
+import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
+
+const LARGE_IMAGE_MIN_WIDTH = 150;
+const LARGE_IMAGE_MIN_RATIO = (16 / 9);
+const MAX_IMAGE_HEIGHT = 150;
+const THUMBNAIL_SIZE = 75;
+
+export default class PostAttachmentOpenGraph extends PureComponent {
+ static propTypes = {
+ actions: PropTypes.shape({
+ getOpenGraphMetadata: PropTypes.func.isRequired
+ }).isRequired,
+ link: PropTypes.string.isRequired,
+ openGraphData: PropTypes.object,
+ theme: PropTypes.object.isRequired
+ };
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ imageLoaded: false,
+ hasLargeImage: false
+ };
+ }
+
+ componentWillMount() {
+ this.fetchData(this.props.link, this.props.openGraphData);
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (nextProps.link !== this.props.link) {
+ this.setState({imageLoaded: false});
+ this.fetchData(nextProps.link, nextProps.openGraphData);
+ }
+ }
+
+ calculateLargeImageDimensions = (width, height) => {
+ const {width: deviceWidth} = Dimensions.get('window');
+ let maxHeight = MAX_IMAGE_HEIGHT;
+ let maxWidth = deviceWidth - 88;
+
+ if (height <= MAX_IMAGE_HEIGHT) {
+ maxHeight = height;
+ } else {
+ maxHeight = (height / width) * maxWidth;
+ if (maxHeight > MAX_IMAGE_HEIGHT) {
+ maxHeight = MAX_IMAGE_HEIGHT;
+ }
+ }
+
+ if (height > width) {
+ maxWidth = (width / height) * maxHeight;
+ }
+
+ return {width: maxWidth, height: maxHeight};
+ };
+
+ calculateSmallImageDimensions = (width, height) => {
+ const {width: deviceWidth} = Dimensions.get('window');
+ const offset = deviceWidth - 170;
+
+ let ratio;
+ let maxWidth;
+ let maxHeight;
+
+ if (width >= height) {
+ ratio = width / height;
+ maxWidth = THUMBNAIL_SIZE;
+ maxHeight = PixelRatio.roundToNearestPixel(maxWidth / ratio);
+ } else {
+ ratio = height / width;
+ maxHeight = THUMBNAIL_SIZE;
+ maxWidth = PixelRatio.roundToNearestPixel(maxHeight / ratio);
+ }
+
+ return {width: maxWidth, height: maxHeight, offset};
+ };
+
+ fetchData(url, openGraphData) {
+ if (!openGraphData) {
+ this.props.actions.getOpenGraphMetadata(url);
+ }
+ }
+
+ getBestImageUrl(data) {
+ if (!data.images) {
+ return null;
+ }
+
+ const bestDimensions = {
+ width: Dimensions.get('window').width - 88,
+ height: MAX_IMAGE_HEIGHT
+ };
+ const bestImage = getNearestPoint(bestDimensions, data.images, 'width', 'height');
+ return bestImage.secure_url || bestImage.url;
+ }
+
+ getImageSize = (imageUrl) => {
+ if (!this.state.imageLoaded) {
+ Image.getSize(imageUrl, (width, height) => {
+ const {hasLargeImage} = this.state;
+ const imageRatio = width / height;
+
+ let isLarge = false;
+ let dimensions;
+ if (width >= LARGE_IMAGE_MIN_WIDTH && imageRatio >= LARGE_IMAGE_MIN_RATIO && !hasLargeImage) {
+ isLarge = true;
+ dimensions = this.calculateLargeImageDimensions(width, height);
+ } else {
+ dimensions = this.calculateSmallImageDimensions(width, height);
+ }
+ this.setState({
+ ...dimensions,
+ hasLargeImage: isLarge,
+ imageLoaded: true
+ });
+ }, () => null);
+ }
+ };
+
+ goToLink = () => {
+ Linking.openURL(this.props.link);
+ };
+
+ render() {
+ const {openGraphData, theme} = this.props;
+ const {hasLargeImage, height, imageLoaded, offset, width} = this.state;
+
+ if (!openGraphData || !openGraphData.description) {
+ return null;
+ }
+
+ const style = getStyleSheet(theme);
+ const imageUrl = this.getBestImageUrl(openGraphData);
+ const isThumbnail = !hasLargeImage && imageLoaded;
+ if (imageUrl) {
+ this.getImageSize(imageUrl);
+ }
+
+ return (
+
+
+
+ {openGraphData.site_name}
+
+
+
+
+
+ {openGraphData.title}
+
+
+ {isThumbnail &&
+
+
+
+ }
+
+
+
+ {openGraphData.description}
+
+
+ {hasLargeImage && imageLoaded &&
+
+ }
+
+ );
+ }
+}
+
+const getStyleSheet = makeStyleSheetFromTheme((theme) => {
+ return {
+ container: {
+ flex: 1,
+ borderColor: changeOpacity(theme.centerChannelColor, 0.2),
+ borderWidth: 1,
+ marginTop: 10,
+ padding: 10
+ },
+ flex: {
+ flex: 1
+ },
+ wrapper: {
+ flex: 1,
+ flexDirection: 'row'
+ },
+ siteTitle: {
+ fontSize: 12,
+ color: changeOpacity(theme.centerChannelColor, 0.5),
+ marginBottom: 10
+ },
+ siteSubtitle: {
+ fontSize: 14,
+ color: theme.linkColor,
+ marginBottom: 10
+ },
+ siteDescription: {
+ fontSize: 13,
+ color: changeOpacity(theme.centerChannelColor, 0.7),
+ marginBottom: 10
+ },
+ image: {
+ borderRadius: 3
+ },
+ thumbnail: {
+ flex: 1,
+ alignItems: 'flex-end',
+ justifyContent: 'flex-start'
+ }
+ };
+});
diff --git a/app/components/post_body/index.js b/app/components/post_body/index.js
index a5d3001ba..83a577299 100644
--- a/app/components/post_body/index.js
+++ b/app/components/post_body/index.js
@@ -20,7 +20,7 @@ function mapStateToProps(state, ownProps) {
return {
...ownProps,
- attachments: post.props && post.props.attachments,
+ postProps: post.props || {},
fileIds: post.file_ids,
hasBeenDeleted: post.state === Posts.POST_DELETED,
hasReactions: post.has_reactions,
diff --git a/app/components/post_body/post_body.js b/app/components/post_body/post_body.js
index 0b38876dd..60976d007 100644
--- a/app/components/post_body/post_body.js
+++ b/app/components/post_body/post_body.js
@@ -16,7 +16,8 @@ import FileAttachmentList from 'app/components/file_attachment_list';
import FormattedText from 'app/components/formatted_text';
import Markdown from 'app/components/markdown';
import OptionsContext from 'app/components/options_context';
-import SlackAttachments from 'app/components/slack_attachments';
+
+import PostBodyAdditionalContent from 'app/components/post_body_additional_content';
import {emptyFunction} from 'app/utils/general';
import {getMarkdownTextStyles, getMarkdownBlockStyles} from 'app/utils/markdown';
@@ -29,7 +30,6 @@ class PostBody extends PureComponent {
flagPost: PropTypes.func.isRequired,
unflagPost: PropTypes.func.isRequired
}).isRequired,
- attachments: PropTypes.array,
canDelete: PropTypes.bool,
canEdit: PropTypes.bool,
fileIds: PropTypes.array,
@@ -49,6 +49,7 @@ class PostBody extends PureComponent {
onPostEdit: PropTypes.func,
onPress: PropTypes.func,
postId: PropTypes.string.isRequired,
+ postProps: PropTypes.object,
renderReplyBar: PropTypes.func,
theme: PropTypes.object,
toggleSelected: PropTypes.func
@@ -123,25 +124,6 @@ class PostBody extends PureComponent {
return attachments;
}
- renderSlackAttachments = (baseStyle, blockStyles, textStyles) => {
- const {attachments, navigator, theme} = this.props;
-
- if (attachments && attachments.length) {
- return (
-
- );
- }
-
- return null;
- };
-
render() {
const {
canDelete,
@@ -161,6 +143,7 @@ class PostBody extends PureComponent {
onPostEdit,
onPress,
postId,
+ postProps,
renderReplyBar,
theme,
toggleSelected
@@ -201,6 +184,7 @@ class PostBody extends PureComponent {
});
}
+ let body;
let messageComponent;
if (hasBeenDeleted) {
messageComponent = (
@@ -210,6 +194,7 @@ class PostBody extends PureComponent {
defaultMessage='(message deleted)'
/>
);
+ body = ({messageComponent});
} else if (message.length) {
messageComponent = (
@@ -229,38 +214,53 @@ class PostBody extends PureComponent {
);
}
- let body;
- if (isSearchResult) {
- body = (
-
-
+ if (!hasBeenDeleted) {
+ if (isSearchResult) {
+ body = (
+
+
+ {messageComponent}
+
+ {this.renderFileAttachments()}
+
+
+ );
+ } else {
+ body = (
+
{messageComponent}
- {this.renderSlackAttachments(messageStyle, blockStyles, textStyles)}
+
{this.renderFileAttachments()}
-
-
- );
- } else {
- body = (
-
- {messageComponent}
- {this.renderSlackAttachments(messageStyle, blockStyles, textStyles)}
- {this.renderFileAttachments()}
- {hasReactions && }
-
- );
+ {hasReactions && }
+
+ );
+ }
}
return (
diff --git a/app/components/post_body_additional_content/index.js b/app/components/post_body_additional_content/index.js
new file mode 100644
index 000000000..5f5c95dbf
--- /dev/null
+++ b/app/components/post_body_additional_content/index.js
@@ -0,0 +1,35 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {connect} from 'react-redux';
+
+import {Preferences} from 'mattermost-redux/constants';
+import {getConfig} from 'mattermost-redux/selectors/entities/general';
+import {getOpenGraphMetadataForUrl} from 'mattermost-redux/selectors/entities/posts';
+import {getBool} from 'mattermost-redux/selectors/entities/preferences';
+
+import {ViewTypes} from 'app/constants';
+import {getTheme} from 'app/selectors/preferences';
+
+import {extractFirstLink} from 'app/utils/url';
+import PostBodyAdditionalContent from './post_body_additional_content';
+
+function mapStateToProps(state, ownProps) {
+ const config = getConfig(state);
+ const previewsEnabled = getBool(state, Preferences.CATEGORY_ADVANCED_SETTINGS, `${ViewTypes.FEATURE_TOGGLE_PREFIX}${ViewTypes.EMBED_PREVIEW}`);
+ const link = extractFirstLink(ownProps.message);
+
+ return {
+ ...ownProps,
+ config,
+ link,
+ openGraphData: getOpenGraphMetadataForUrl(state, link),
+ showLinkPreviews: previewsEnabled && config.EnableLinkPreviews === 'true',
+ theme: getTheme(state)
+ };
+}
+
+export default connect(mapStateToProps)(PostBodyAdditionalContent);
diff --git a/app/components/post_body_additional_content/post_body_additional_content.js b/app/components/post_body_additional_content/post_body_additional_content.js
new file mode 100644
index 000000000..ac4402ddb
--- /dev/null
+++ b/app/components/post_body_additional_content/post_body_additional_content.js
@@ -0,0 +1,280 @@
+// 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 {
+ Dimensions,
+ Image,
+ Linking,
+ Platform,
+ StyleSheet,
+ TouchableWithoutFeedback,
+ View
+} from 'react-native';
+import Orientation from 'react-native-orientation';
+import {YouTubeStandaloneAndroid, YouTubeStandaloneIOS} from 'react-native-youtube';
+import youTubeVideoId from 'youtube-video-id';
+import youtubePlayIcon from 'assets/images/icons/youtube-play-icon.png';
+
+import PostAttachmentOpenGraph from 'app/components/post_attachment_opengraph';
+import SlackAttachments from 'app/components/slack_attachments';
+import CustomPropTypes from 'app/constants/custom_prop_types';
+import {isImageLink, isYoutubeLink} from 'app/utils/url';
+
+const MAX_IMAGE_HEIGHT = 150;
+
+export default class PostBodyAdditionalContent extends PureComponent {
+ static propTypes = {
+ baseTextStyle: CustomPropTypes.Style,
+ blockStyles: PropTypes.object,
+ config: PropTypes.object,
+ link: PropTypes.string,
+ message: PropTypes.string.isRequired,
+ navigator: PropTypes.object.isRequired,
+ openGraphData: PropTypes.object,
+ postProps: PropTypes.object.isRequired,
+ showLinkPreviews: PropTypes.bool.isRequired,
+ textStyles: PropTypes.object,
+ theme: PropTypes.object.isRequired
+ };
+
+ constructor(props) {
+ super(props);
+
+ this.state = {
+ linkLoadError: false,
+ linkLoaded: false
+ };
+
+ this.mounted = false;
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+ this.getImageSize();
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ componentWillReceiveProps(nextProps) {
+ if (nextProps.message !== this.props.message) {
+ this.setState({
+ linkLoadError: false,
+ linkLoaded: false
+ }, () => {
+ this.getImageSize();
+ });
+ }
+ }
+
+ calculateDimensions = (width, height) => {
+ const {width: deviceWidth} = Dimensions.get('window');
+ let maxHeight = MAX_IMAGE_HEIGHT;
+ let maxWidth = deviceWidth - 68;
+
+ if (height <= MAX_IMAGE_HEIGHT) {
+ maxHeight = height;
+ } else {
+ maxHeight = (height / width) * maxWidth;
+ if (maxHeight > MAX_IMAGE_HEIGHT) {
+ maxHeight = MAX_IMAGE_HEIGHT;
+ }
+ }
+
+ if (height > width) {
+ maxWidth = (width / height) * maxHeight;
+ }
+
+ return {width: maxWidth, height: maxHeight};
+ };
+
+ generateStaticEmbed = (isYouTube) => {
+ if (isYouTube) {
+ return null;
+ }
+
+ const {link, openGraphData, showLinkPreviews} = this.props;
+ const attachments = this.getSlackAttachment();
+ if (attachments) {
+ return attachments;
+ }
+
+ if (link && showLinkPreviews) {
+ return (
+
+ );
+ }
+
+ return null;
+ };
+
+ getImageSize = () => {
+ const {link} = this.props;
+ const {linkLoaded} = this.state;
+
+ let imageUrl;
+ if (isImageLink(link)) {
+ imageUrl = link;
+ } else if (isYoutubeLink(link)) {
+ const videoId = youTubeVideoId(link);
+ imageUrl = `https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`;
+ }
+
+ if (imageUrl && !linkLoaded) {
+ Image.getSize(imageUrl, (width, height) => {
+ if (!this.mounted) {
+ return;
+ }
+
+ const dimensions = this.calculateDimensions(width, height);
+ this.setState({...dimensions, linkLoaded: true});
+ }, () => null);
+ }
+ };
+
+ getSlackAttachment = () => {
+ const {
+ postProps,
+ baseTextStyle,
+ blockStyles,
+ navigator,
+ textStyles,
+ theme
+ } = this.props;
+ const {attachments} = postProps;
+
+ if (attachments && attachments.length) {
+ return (
+
+ );
+ }
+
+ return null;
+ };
+
+ generateToggleableEmbed = (isImage, isYouTube) => {
+ const {link} = this.props;
+ const {width, height} = this.state;
+
+ if (link) {
+ if (isYouTube) {
+ const videoId = youTubeVideoId(link);
+ const imgUrl = `https://i.ytimg.com/vi/${videoId}/hqdefault.jpg`;
+
+ return (
+
+
+
+
+
+
+
+ );
+ }
+
+ if (isImage) {
+ return (
+
+
+
+ );
+ }
+ }
+
+ return null;
+ };
+
+ playYouTubeVideo = () => {
+ const {link} = this.props;
+ const videoId = youTubeVideoId(link);
+
+ Orientation.unlockAllOrientations();
+ if (Platform.OS === 'ios') {
+ YouTubeStandaloneIOS.playVideo(videoId).then(() => {
+ Orientation.lockToPortrait();
+ });
+ } else {
+ const {config} = this.props;
+
+ if (config.GoogleDeveloperKey) {
+ YouTubeStandaloneAndroid.playVideo({
+ apiKey: config.GoogleDeveloperKey,
+ videoId,
+ autoplay: true
+ }).then(() => {
+ Orientation.lockToPortrait();
+ });
+ } else {
+ Linking.openURL(link);
+ Orientation.lockToPortrait();
+ }
+ }
+ };
+
+ handleLinkLoadError = () => {
+ this.setState({linkLoadError: true});
+ };
+
+ render() {
+ const {link, openGraphData} = this.props;
+ const {linkLoaded, linkLoadError} = this.state;
+ const isYouTube = isYoutubeLink(link);
+ const isImage = isImageLink(link);
+ const isOpenGraph = Boolean(openGraphData && openGraphData.description);
+
+ if ((isImage && !isOpenGraph && !linkLoadError) || isYouTube) {
+ const embed = this.generateToggleableEmbed(isImage, isYouTube);
+ if (embed && (linkLoaded || isYouTube)) {
+ return embed;
+ }
+ }
+
+ return this.generateStaticEmbed(isYouTube);
+ }
+}
+
+const styles = StyleSheet.create({
+ imageContainer: {
+ alignItems: 'flex-start',
+ flex: 1,
+ justifyContent: 'flex-start',
+ marginBottom: 6,
+ marginTop: 10
+ },
+ image: {
+ alignItems: 'center',
+ borderRadius: 3,
+ justifyContent: 'center',
+ marginVertical: 1
+ }
+});
diff --git a/app/constants/view.js b/app/constants/view.js
index 2028dc320..f2e5dc6f0 100644
--- a/app/constants/view.js
+++ b/app/constants/view.js
@@ -54,5 +54,7 @@ const ViewTypes = keyMirror({
export default {
...ViewTypes,
- POST_VISIBILITY_CHUNK_SIZE: Posts.POST_CHUNK_SIZE / 2
+ POST_VISIBILITY_CHUNK_SIZE: Posts.POST_CHUNK_SIZE / 2,
+ FEATURE_TOGGLE_PREFIX: 'feature_enabled_',
+ EMBED_PREVIEW: 'embed_preview'
};
diff --git a/app/utils/opengraph.js b/app/utils/opengraph.js
new file mode 100644
index 000000000..00e1eb759
--- /dev/null
+++ b/app/utils/opengraph.js
@@ -0,0 +1,23 @@
+// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+export function getDistanceBW2Points(point1, point2, xAttr = 'x', yAttr = 'y') {
+ return Math.sqrt(Math.pow(point1[xAttr] - point2[xAttr], 2) + Math.pow(point1[yAttr] - point2[yAttr], 2));
+}
+
+/**
+ * Funtion to return nearest point of given pivot point.
+ * It return two points one nearest and other nearest but having both coorditanes smaller than the given point's coordinates.
+ */
+export function getNearestPoint(pivotPoint, points, xAttr = 'x', yAttr = 'y') {
+ let nearestPoint = {};
+ for (const point of points) {
+ if (typeof nearestPoint[xAttr] === 'undefined' || typeof nearestPoint[yAttr] === 'undefined') {
+ nearestPoint = point;
+ } else if (getDistanceBW2Points(point, pivotPoint, xAttr, yAttr) < getDistanceBW2Points(nearestPoint, pivotPoint, xAttr, yAttr)) {
+ // Check for bestImage
+ nearestPoint = point;
+ }
+ }
+ return nearestPoint;
+}
diff --git a/app/utils/url.js b/app/utils/url.js
index 39d9eeb6b..13152b038 100644
--- a/app/utils/url.js
+++ b/app/utils/url.js
@@ -1,6 +1,9 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
+const ytRegex = /(?:http|https):\/\/(?:www\.|m\.)?(?:(?:youtube\.com\/(?:(?:v\/)|(?:(?:watch|embed\/watch)(?:\/|.*v=))|(?:embed\/)|(?:user\/[^/]+\/u\/[0-9]\/)))|(?:youtu\.be\/))([^#&?]*)/;
+const imgRegex = /.+\/(.+\.(?:jpg|gif|bmp|png|jpeg))(?:\?.*)?$/i;
+
export function isValidUrl(url = '') {
const regex = /^https?:\/\//i;
return regex.test(url);
@@ -13,3 +16,30 @@ export function stripTrailingSlashes(url = '') {
export function removeProtocol(url = '') {
return url.replace(/(^\w+:|^)\/\//, '');
}
+
+export function extractFirstLink(text) {
+ const pattern = /(^|[\s\n]|
)((?:https?|ftp):\/\/[-A-Z0-9+\u0026\u2019@#/%?=()~_|!:,.;]*[-A-Z0-9+\u0026@#/%=~()_|])/i;
+ let inText = text;
+
+ // strip out code blocks
+ inText = inText.replace(/`[^`]*`/g, '');
+
+ // strip out inline markdown images
+ inText = inText.replace(/!\[[^\]]*]\([^)]*\)/g, '');
+
+ const match = pattern.exec(inText);
+ if (match) {
+ return match[0].trim();
+ }
+
+ return '';
+}
+
+export function isYoutubeLink(link) {
+ return link.trim().match(ytRegex);
+}
+
+export function isImageLink(link) {
+ const match = link.trim().match(imgRegex);
+ return Boolean(match && match[1]);
+}
diff --git a/assets/base/images/icons/youtube-play-icon.png b/assets/base/images/icons/youtube-play-icon.png
new file mode 100644
index 000000000..8bfcd53ae
Binary files /dev/null and b/assets/base/images/icons/youtube-play-icon.png differ
diff --git a/ios/Mattermost.xcodeproj/project.pbxproj b/ios/Mattermost.xcodeproj/project.pbxproj
index c4da3abe5..6317ec684 100644
--- a/ios/Mattermost.xcodeproj/project.pbxproj
+++ b/ios/Mattermost.xcodeproj/project.pbxproj
@@ -40,8 +40,10 @@
420A7328E12C4B72AEF420CE /* Octicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = EDC04CBCF81642219D199CBB /* Octicons.ttf */; };
55C6561DDBBA45929D88B6D1 /* OpenSans-BoldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 32AC3D4EA79E44738A6E9766 /* OpenSans-BoldItalic.ttf */; };
584837D6B55F405F908A2053 /* libBVLinearGradient.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ACC6B9FDC0AD45A6BFA4FBCD /* libBVLinearGradient.a */; };
+ 5CB86E9DFB94485CAA748DF3 /* YTPlayerView-iframe-player.html in Resources */ = {isa = PBXBuildFile; fileRef = 79CB6EBA24FE4ABFB0C155F0 /* YTPlayerView-iframe-player.html */; };
5E1AF7B72B8D4A4E9E53FF9D /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 005346E5C0E542BFABAE1411 /* FontAwesome.ttf */; };
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
+ 5F190C97C28E4E5E93D933C0 /* libRCTYouTube.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8EAB7CA059E14A73918789B6 /* libRCTYouTube.a */; };
615D2E7805814A3A9B9C69EC /* libRNLocalAuth.a in Frameworks */ = {isa = PBXBuildFile; fileRef = EE3EE4548D3F4A49B1274722 /* libRNLocalAuth.a */; };
62A8448264674B4D95A5A7C2 /* OpenSans-Semibold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = C78A387124874496AD2C1466 /* OpenSans-Semibold.ttf */; };
69AC753E496743BABB7A7124 /* OpenSans-SemiboldItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 0E617BF0F36D4E738F51D169 /* OpenSans-SemiboldItalic.ttf */; };
@@ -61,6 +63,7 @@
895C9A56B94A45C1BAF568FE /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = AC6EB561E1F64C17A69D2FAD /* Entypo.ttf */; };
9358B95F95184EE0A4DCE629 /* OpenSans-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D4B1B363C2414DA19C1AC521 /* OpenSans-Bold.ttf */; };
A08D512E7ADC40CCAD055A9E /* OpenSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BC977883E2624E05975CA65B /* OpenSans-Regular.ttf */; };
+ A14B2A56A554E84DFE3D6580 /* libPods-Mattermost.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 65FD5EA57EBAE06106094B2F /* libPods-Mattermost.a */; };
C035DB50ED2045F09923FFAE /* MaterialCommunityIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 04AA5E8EF3B54735A11E3B95 /* MaterialCommunityIcons.ttf */; };
C337E8708D2845C6A8DBB47E /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2DCFD31D3F4A4154822AB532 /* Ionicons.ttf */; };
CB1B312483534997BEE7E65E /* libRNSentry.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2AE23B96BF7545B9A77C0C87 /* libRNSentry.a */; };
@@ -68,6 +71,7 @@
DEBCF44F1F46413BB407D316 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8606EB1EB7E349EF8248933E /* libz.tbd */; };
F006936FC2884C24A1321FC0 /* MaterialIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 356C9186FA374641A00EB2EA /* MaterialIcons.ttf */; };
F083DB472349411A8E6E7AAD /* OpenSans-LightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BE17F630DB5D41FD93F32D22 /* OpenSans-LightItalic.ttf */; };
+ F23C99AA5FA10E457A76803A /* libPods-MattermostTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4246DC09024BB33A3E491E25 /* libPods-MattermostTests.a */; };
FDEF24E9D9AB47728E11033B /* libRNPasscodeStatus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 17A75F5A3D0D4A4A995DCD76 /* libRNPasscodeStatus.a */; };
/* End PBXBuildFile section */
@@ -366,6 +370,27 @@
remoteGlobalIDString = 1BD725DA1CF77A8B005DBD79;
remoteInfo = RNCookieManagerIOS;
};
+ 7FA794EC1F61A1A500C02924 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = A2968536BEC74A4ABBB73BD9 /* RCTYouTube.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = 8BDB9BC91B4289AE00EF9FEF;
+ remoteInfo = RCTYouTube;
+ };
+ 7FA795051F61A1A500C02924 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 9552041247E749308CE7B412 /* RNSentry.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = 134814201AA4EA6300B7C361;
+ remoteInfo = RNSentry;
+ };
+ 7FA7950A1F61A1A500C02924 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 3B1E210BF3B649BBBF427977 /* ReactNativeExceptionHandler.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = 134814201AA4EA6300B7C361;
+ remoteInfo = ReactNativeExceptionHandler;
+ };
7FC200DE1EBB65100099331B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 7FC200BC1EBB65100099331B /* ReactNativeNavigation.xcodeproj */;
@@ -387,20 +412,6 @@
remoteGlobalIDString = 5DBEB1501B18CEA900B34395;
remoteInfo = RNVectorIcons;
};
- 7FEB10851F6101090039A015 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 3B1E210BF3B649BBBF427977 /* ReactNativeExceptionHandler.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 134814201AA4EA6300B7C361;
- remoteInfo = ReactNativeExceptionHandler;
- };
- 7FEB10901F61010A0039A015 /* PBXContainerItemProxy */ = {
- isa = PBXContainerItemProxy;
- containerPortal = 9552041247E749308CE7B412 /* RNSentry.xcodeproj */;
- proxyType = 2;
- remoteGlobalIDString = 134814201AA4EA6300B7C361;
- remoteInfo = RNSentry;
- };
832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */;
@@ -459,8 +470,11 @@
3647DF63D6764CF093375861 /* OpenSans-ExtraBold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "OpenSans-ExtraBold.ttf"; path = "../assets/fonts/OpenSans-ExtraBold.ttf"; sourceTree = ""; };
3752184A1F4B9E980035444B /* RCTCameraRoll.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTCameraRoll.xcodeproj; path = "../node_modules/react-native/Libraries/CameraRoll/RCTCameraRoll.xcodeproj"; sourceTree = ""; };
37ABD3971F4CE13B001FDE6B /* ART.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ART.xcodeproj; path = "../node_modules/react-native/Libraries/ART/ART.xcodeproj"; sourceTree = ""; };
+ 3820CC3BBC4B0129958555A9 /* Pods-MattermostTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MattermostTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.debug.xcconfig"; sourceTree = ""; };
3B1E210BF3B649BBBF427977 /* ReactNativeExceptionHandler.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = ReactNativeExceptionHandler.xcodeproj; path = "../node_modules/react-native-exception-handler/ios/ReactNativeExceptionHandler.xcodeproj"; sourceTree = ""; };
+ 41465DE822E9429EB8B90CB4 /* Pods-MattermostTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MattermostTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests.release.xcconfig"; sourceTree = ""; };
41F3AFE83AAF4B74878AB78A /* OpenSans-Italic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "OpenSans-Italic.ttf"; path = "../assets/fonts/OpenSans-Italic.ttf"; sourceTree = ""; };
+ 4246DC09024BB33A3E491E25 /* libPods-MattermostTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-MattermostTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
4A22BC320BA04E18A7F2A4E6 /* libReactNativeExceptionHandler.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libReactNativeExceptionHandler.a; sourceTree = ""; };
4B19E38FBCB94C57BB03B8E6 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFetchBlob.a; sourceTree = ""; };
50ECB1D221E44F51B5690DF2 /* FastImage.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = FastImage.xcodeproj; path = "../node_modules/react-native-fast-image/ios/FastImage.xcodeproj"; sourceTree = ""; };
@@ -468,9 +482,12 @@
546CB0A5BB8F453890CBA559 /* RNFetchBlob.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNFetchBlob.xcodeproj; path = "../node_modules/react-native-fetch-blob/ios/RNFetchBlob.xcodeproj"; sourceTree = ""; };
5C3B95629BA74FB3A8377CB7 /* RCTOrientation.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTOrientation.xcodeproj; path = "../node_modules/react-native-orientation/iOS/RCTOrientation.xcodeproj"; sourceTree = ""; };
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = ""; };
+ 634A8F047C73D24A87850EC0 /* Pods-Mattermost.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mattermost.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.debug.xcconfig"; sourceTree = ""; };
6561AEAC21CC40B8A72ABB93 /* OpenSans-Light.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "OpenSans-Light.ttf"; path = "../assets/fonts/OpenSans-Light.ttf"; sourceTree = ""; };
+ 65FD5EA57EBAE06106094B2F /* libPods-Mattermost.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Mattermost.a"; sourceTree = BUILT_PRODUCTS_DIR; };
77810F0A063349439B0D8B6B /* libJailMonkey.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libJailMonkey.a; sourceTree = ""; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; };
+ 79CB6EBA24FE4ABFB0C155F0 /* YTPlayerView-iframe-player.html */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "YTPlayerView-iframe-player.html"; path = "../node_modules/react-native-youtube/assets/YTPlayerView-iframe-player.html"; 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 = ""; };
7F26919B1EE1DC51007574FE /* RNNotifications.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNNotifications.xcodeproj; path = "../node_modules/react-native-notifications/RNNotifications/RNNotifications.xcodeproj"; sourceTree = ""; };
7F292A701E8AB73400A450A3 /* SplashScreenResource */ = {isa = PBXFileReference; lastKnownFileType = folder; path = SplashScreenResource; sourceTree = ""; };
@@ -489,12 +506,15 @@
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; };
85E82BCA0FD245EEA520D106 /* RNImagePicker.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNImagePicker.xcodeproj; path = "../node_modules/react-native-image-picker/ios/RNImagePicker.xcodeproj"; sourceTree = ""; };
8606EB1EB7E349EF8248933E /* libz.tbd */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; };
+ 8EAB7CA059E14A73918789B6 /* libRCTYouTube.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTYouTube.a; sourceTree = ""; };
9552041247E749308CE7B412 /* RNSentry.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNSentry.xcodeproj; path = "../node_modules/react-native-sentry/ios/RNSentry.xcodeproj"; sourceTree = ""; };
+ A2968536BEC74A4ABBB73BD9 /* RCTYouTube.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTYouTube.xcodeproj; path = "../node_modules/react-native-youtube/RCTYouTube.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 = ""; };
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 = ""; };
BC977883E2624E05975CA65B /* OpenSans-Regular.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "OpenSans-Regular.ttf"; path = "../assets/fonts/OpenSans-Regular.ttf"; sourceTree = ""; };
BE17F630DB5D41FD93F32D22 /* OpenSans-LightItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "OpenSans-LightItalic.ttf"; path = "../assets/fonts/OpenSans-LightItalic.ttf"; sourceTree = ""; };
+ BFB7025EA936C1B5DC9725C2 /* Pods-Mattermost.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Mattermost.release.xcconfig"; path = "Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost.release.xcconfig"; sourceTree = ""; };
C78A387124874496AD2C1466 /* OpenSans-Semibold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "OpenSans-Semibold.ttf"; path = "../assets/fonts/OpenSans-Semibold.ttf"; sourceTree = ""; };
D0281D64B98143668D6AD42B /* RNLocalAuth.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNLocalAuth.xcodeproj; path = "../node_modules/react-native-local-auth/RNLocalAuth.xcodeproj"; sourceTree = ""; };
D4B1B363C2414DA19C1AC521 /* OpenSans-Bold.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "OpenSans-Bold.ttf"; path = "../assets/fonts/OpenSans-Bold.ttf"; sourceTree = ""; };
@@ -513,6 +533,7 @@
buildActionMask = 2147483647;
files = (
140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */,
+ F23C99AA5FA10E457A76803A /* libPods-MattermostTests.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -551,6 +572,8 @@
1B5204C4347D45A9A7AC3EA5 /* libReactNativeExceptionHandler.a in Frameworks */,
CB1B312483534997BEE7E65E /* libRNSentry.a in Frameworks */,
DEBCF44F1F46413BB407D316 /* libz.tbd in Frameworks */,
+ 5F190C97C28E4E5E93D933C0 /* libRCTYouTube.a in Frameworks */,
+ A14B2A56A554E84DFE3D6580 /* libPods-Mattermost.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -639,6 +662,7 @@
BC977883E2624E05975CA65B /* OpenSans-Regular.ttf */,
C78A387124874496AD2C1466 /* OpenSans-Semibold.ttf */,
0E617BF0F36D4E738F51D169 /* OpenSans-SemiboldItalic.ttf */,
+ 79CB6EBA24FE4ABFB0C155F0 /* YTPlayerView-iframe-player.html */,
);
name = Resources;
sourceTree = "";
@@ -753,6 +777,26 @@
name = Products;
sourceTree = "";
};
+ 4B992D7BAAEDF8759DB525B5 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 65FD5EA57EBAE06106094B2F /* libPods-Mattermost.a */,
+ 4246DC09024BB33A3E491E25 /* libPods-MattermostTests.a */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ 57D053CEA78013E949257E00 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 634A8F047C73D24A87850EC0 /* Pods-Mattermost.debug.xcconfig */,
+ BFB7025EA936C1B5DC9725C2 /* Pods-Mattermost.release.xcconfig */,
+ 3820CC3BBC4B0129958555A9 /* Pods-MattermostTests.debug.xcconfig */,
+ 41465DE822E9429EB8B90CB4 /* Pods-MattermostTests.release.xcconfig */,
+ );
+ name = Pods;
+ sourceTree = "";
+ };
5E91572E1DD0AC6500FF2AA8 /* Products */ = {
isa = PBXGroup;
children = (
@@ -837,6 +881,30 @@
name = Products;
sourceTree = "";
};
+ 7FA794CE1F61A1A400C02924 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 7FA794ED1F61A1A500C02924 /* libRCTYouTube.a */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 7FA795021F61A1A500C02924 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 7FA795061F61A1A500C02924 /* libRNSentry.a */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 7FA795071F61A1A500C02924 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 7FA7950B1F61A1A500C02924 /* libReactNativeExceptionHandler.a */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
7FC200BD1EBB65100099331B /* Products */ = {
isa = PBXGroup;
children = (
@@ -861,22 +929,6 @@
name = Products;
sourceTree = "";
};
- 7FEB105F1F6101090039A015 /* Products */ = {
- isa = PBXGroup;
- children = (
- 7FEB10861F6101090039A015 /* libReactNativeExceptionHandler.a */,
- );
- name = Products;
- sourceTree = "";
- };
- 7FEB10611F6101090039A015 /* Products */ = {
- isa = PBXGroup;
- children = (
- 7FEB10911F61010A0039A015 /* libRNSentry.a */,
- );
- name = Products;
- sourceTree = "";
- };
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
isa = PBXGroup;
children = (
@@ -911,6 +963,7 @@
546CB0A5BB8F453890CBA559 /* RNFetchBlob.xcodeproj */,
3B1E210BF3B649BBBF427977 /* ReactNativeExceptionHandler.xcodeproj */,
9552041247E749308CE7B412 /* RNSentry.xcodeproj */,
+ A2968536BEC74A4ABBB73BD9 /* RCTYouTube.xcodeproj */,
);
name = Libraries;
sourceTree = "";
@@ -933,6 +986,8 @@
00E356EF1AD99517003FC87E /* MattermostTests */,
83CBBA001A601CBA00E9B192 /* Products */,
0156F464626F49C2977D7982 /* Resources */,
+ 57D053CEA78013E949257E00 /* Pods */,
+ 4B992D7BAAEDF8759DB525B5 /* Frameworks */,
);
indentWidth = 2;
sourceTree = "";
@@ -954,9 +1009,12 @@
isa = PBXNativeTarget;
buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "MattermostTests" */;
buildPhases = (
+ EEC0B569A776EB6F23DA5874 /* [CP] Check Pods Manifest.lock */,
00E356EA1AD99517003FC87E /* Sources */,
00E356EB1AD99517003FC87E /* Frameworks */,
00E356EC1AD99517003FC87E /* Resources */,
+ 24BAEFA847BB924860ACEAE9 /* [CP] Embed Pods Frameworks */,
+ 1E7A58BF4E81655D1CC6DCC6 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -972,12 +1030,15 @@
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Mattermost" */;
buildPhases = (
+ 0D421A536CD412E685542AA3 /* [CP] Check Pods Manifest.lock */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
37DA4BA41E6F55AD002B058E /* Embed Frameworks */,
AE4769B235D14E6C9C64EA78 /* Upload Debug Symbols to Sentry */,
+ BC5024E82B42A23CE05E72ED /* [CP] Embed Pods Frameworks */,
+ 509B02AF14835503C5C6029D /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -1092,12 +1153,16 @@
ProductGroup = 139FDEE71B06529A00C62182 /* Products */;
ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
},
+ {
+ ProductGroup = 7FA794CE1F61A1A400C02924 /* Products */;
+ ProjectRef = A2968536BEC74A4ABBB73BD9 /* RCTYouTube.xcodeproj */;
+ },
{
ProductGroup = 146834001AC3E56700842450 /* Products */;
ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */;
},
{
- ProductGroup = 7FEB105F1F6101090039A015 /* Products */;
+ ProductGroup = 7FA795071F61A1A500C02924 /* Products */;
ProjectRef = 3B1E210BF3B649BBBF427977 /* ReactNativeExceptionHandler.xcodeproj */;
},
{
@@ -1133,7 +1198,7 @@
ProjectRef = EBA6063A99C141098D40C67A /* RNPasscodeStatus.xcodeproj */;
},
{
- ProductGroup = 7FEB10611F6101090039A015 /* Products */;
+ ProductGroup = 7FA795021F61A1A500C02924 /* Products */;
ProjectRef = 9552041247E749308CE7B412 /* RNSentry.xcodeproj */;
},
{
@@ -1445,6 +1510,27 @@
remoteRef = 7F906D961E6A1ADF00B8F49A /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
+ 7FA794ED1F61A1A500C02924 /* libRCTYouTube.a */ = {
+ isa = PBXReferenceProxy;
+ fileType = archive.ar;
+ path = libRCTYouTube.a;
+ remoteRef = 7FA794EC1F61A1A500C02924 /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ 7FA795061F61A1A500C02924 /* libRNSentry.a */ = {
+ isa = PBXReferenceProxy;
+ fileType = archive.ar;
+ path = libRNSentry.a;
+ remoteRef = 7FA795051F61A1A500C02924 /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ 7FA7950B1F61A1A500C02924 /* libReactNativeExceptionHandler.a */ = {
+ isa = PBXReferenceProxy;
+ fileType = archive.ar;
+ path = libReactNativeExceptionHandler.a;
+ remoteRef = 7FA7950A1F61A1A500C02924 /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
7FC200DF1EBB65100099331B /* libReactNativeNavigation.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
@@ -1466,20 +1552,6 @@
remoteRef = 7FDF290B1E1F4B4E00DBBE56 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
- 7FEB10861F6101090039A015 /* libReactNativeExceptionHandler.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libReactNativeExceptionHandler.a;
- remoteRef = 7FEB10851F6101090039A015 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
- 7FEB10911F61010A0039A015 /* libRNSentry.a */ = {
- isa = PBXReferenceProxy;
- fileType = archive.ar;
- path = libRNSentry.a;
- remoteRef = 7FEB10901F61010A0039A015 /* PBXContainerItemProxy */;
- sourceTree = BUILT_PRODUCTS_DIR;
- };
832341B51AAA6A8300B99B32 /* libRCTText.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
@@ -1525,6 +1597,7 @@
A08D512E7ADC40CCAD055A9E /* OpenSans-Regular.ttf in Resources */,
62A8448264674B4D95A5A7C2 /* OpenSans-Semibold.ttf in Resources */,
69AC753E496743BABB7A7124 /* OpenSans-SemiboldItalic.ttf in Resources */,
+ 5CB86E9DFB94485CAA748DF3 /* YTPlayerView-iframe-player.html in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1545,6 +1618,69 @@
shellPath = /bin/sh;
shellScript = ./bundleReactNative.sh;
};
+ 0D421A536CD412E685542AA3 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Mattermost-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 1E7A58BF4E81655D1CC6DCC6 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "[CP] Copy Pods Resources";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 24BAEFA847BB924860ACEAE9 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MattermostTests/Pods-MattermostTests-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 509B02AF14835503C5C6029D /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "[CP] Copy Pods Resources";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
AE4769B235D14E6C9C64EA78 /* Upload Debug Symbols to Sentry */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -1559,6 +1695,39 @@
shellPath = /bin/sh;
shellScript = ./uploadDebugSymbols.sh;
};
+ BC5024E82B42A23CE05E72ED /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Mattermost/Pods-Mattermost-frameworks.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ EEC0B569A776EB6F23DA5874 /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-MattermostTests-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -1595,16 +1764,26 @@
/* Begin XCBuildConfiguration section */
00E356F61AD99517003FC87E /* Debug */ = {
isa = XCBuildConfiguration;
+ baseConfigurationReference = 3820CC3BBC4B0129958555A9 /* Pods-MattermostTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
+ HEADER_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = MattermostTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
- LIBRARY_SEARCH_PATHS = "$(inherited)";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SRCROOT)/$(TARGET_NAME)\"",
+ );
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-ObjC",
+ "-lc++",
+ );
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Mattermost.app/Mattermost";
@@ -1613,13 +1792,23 @@
};
00E356F71AD99517003FC87E /* Release */ = {
isa = XCBuildConfiguration;
+ baseConfigurationReference = 41465DE822E9429EB8B90CB4 /* Pods-MattermostTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
COPY_PHASE_STRIP = NO;
+ HEADER_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = MattermostTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
- LIBRARY_SEARCH_PATHS = "$(inherited)";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "\"$(SRCROOT)/$(TARGET_NAME)\"",
+ );
+ OTHER_LDFLAGS = (
+ "$(inherited)",
+ "-ObjC",
+ "-lc++",
+ );
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Mattermost.app/Mattermost";
@@ -1628,6 +1817,7 @@
};
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
+ baseConfigurationReference = 634A8F047C73D24A87850EC0 /* Pods-Mattermost.debug.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
@@ -1652,6 +1842,7 @@
"$(SRCROOT)/../node_modules/react-native-fetch-blob/ios/**",
"$(SRCROOT)/../node_modules/react-native-exception-handler/ios",
"$(SRCROOT)/../node_modules/react-native-sentry/ios/**",
+ "$(SRCROOT)/../node_modules/react-native-youtube/**",
);
INFOPLIST_FILE = Mattermost/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
@@ -1670,6 +1861,7 @@
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
+ baseConfigurationReference = BFB7025EA936C1B5DC9725C2 /* Pods-Mattermost.release.xcconfig */;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
@@ -1694,6 +1886,7 @@
"$(SRCROOT)/../node_modules/react-native-fetch-blob/ios/**",
"$(SRCROOT)/../node_modules/react-native-exception-handler/ios",
"$(SRCROOT)/../node_modules/react-native-sentry/ios/**",
+ "$(SRCROOT)/../node_modules/react-native-youtube/**",
);
INFOPLIST_FILE = Mattermost/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
diff --git a/ios/Mattermost.xcworkspace/contents.xcworkspacedata b/ios/Mattermost.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 000000000..cfb65c02c
--- /dev/null
+++ b/ios/Mattermost.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
diff --git a/ios/Mattermost/AppDelegate.m b/ios/Mattermost/AppDelegate.m
index 27b1813fc..95beae4b7 100644
--- a/ios/Mattermost/AppDelegate.m
+++ b/ios/Mattermost/AppDelegate.m
@@ -8,6 +8,7 @@
*/
#import "AppDelegate.h"
+#import
#import
#import
#if __has_include()
@@ -31,6 +32,7 @@
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.backgroundColor = [UIColor whiteColor];
[[RCCManager sharedInstance] initBridgeWithBundleURL:jsCodeLocation launchOptions:launchOptions];
+ [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error: nil];
return YES;
}
diff --git a/ios/Mattermost/Info.plist b/ios/Mattermost/Info.plist
index 80914d7b7..44fafaeda 100644
--- a/ios/Mattermost/Info.plist
+++ b/ios/Mattermost/Info.plist
@@ -1,99 +1,99 @@
-
- CFBundleDevelopmentRegion
- en
- CFBundleDisplayName
- Mattermost Beta
- CFBundleExecutable
- $(EXECUTABLE_NAME)
- CFBundleIdentifier
- $(PRODUCT_BUNDLE_IDENTIFIER)
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- $(PRODUCT_NAME)
- CFBundlePackageType
- APPL
- CFBundleShortVersionString
- 1.2.0
- CFBundleSignature
- ????
- CFBundleVersion
- 49
- ITSAppUsesNonExemptEncryption
-
- LSRequiresIPhoneOS
-
- NSAllowsArbitraryLoads
-
- NSAppTransportSecurity
-
- NSAllowsArbitraryLoads
-
- NSExceptionDomains
-
- localhost
-
- NSExceptionAllowsInsecureHTTPLoads
-
-
-
-
- NSCameraUsageDescription
- Take a Photo or Video and upload it to your mattermost instance
- NSLocationWhenInUseUsageDescription
-
- NSPhotoLibraryUsageDescription
- Upload Photos and Videos to your Mattermost instance
- UIAppFonts
-
- Entypo.ttf
- EvilIcons.ttf
- FontAwesome.ttf
- Foundation.ttf
- Ionicons.ttf
- MaterialIcons.ttf
- Octicons.ttf
- Zocial.ttf
- MaterialCommunityIcons.ttf
- SimpleLineIcons.ttf
- OpenSans-Bold.ttf
- OpenSans-BoldItalic.ttf
- OpenSans-ExtraBold.ttf
- OpenSans-ExtraBoldItalic.ttf
- OpenSans-Italic.ttf
- OpenSans-Light.ttf
- OpenSans-LightItalic.ttf
- OpenSans-Regular.ttf
- OpenSans-Semibold.ttf
- OpenSans-SemiboldItalic.ttf
-
- UILaunchStoryboardName
- LaunchScreen
- UIRequiredDeviceCapabilities
-
- armv7
-
- UISupportedInterfaceOrientations
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
- UIViewControllerBasedStatusBarAppearance
-
- CFBundleURLTypes
-
-
- CFBundleURLName
- com.mattermost.rnbeta
- CFBundleURLSchemes
-
- mattermost
-
-
-
-
-
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ Mattermost Beta
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.2.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 49
+ ITSAppUsesNonExemptEncryption
+
+ LSRequiresIPhoneOS
+
+ NSAllowsArbitraryLoads
+
+ NSAppTransportSecurity
+
+ NSAllowsArbitraryLoads
+
+ NSExceptionDomains
+
+ localhost
+
+ NSExceptionAllowsInsecureHTTPLoads
+
+
+
+
+ NSCameraUsageDescription
+ Take a Photo or Video and upload it to your mattermost instance
+ NSLocationWhenInUseUsageDescription
+
+ NSPhotoLibraryUsageDescription
+ Upload Photos and Videos to your Mattermost instance
+ UIAppFonts
+
+ Entypo.ttf
+ EvilIcons.ttf
+ FontAwesome.ttf
+ Foundation.ttf
+ Ionicons.ttf
+ MaterialIcons.ttf
+ Octicons.ttf
+ Zocial.ttf
+ MaterialCommunityIcons.ttf
+ SimpleLineIcons.ttf
+ OpenSans-Bold.ttf
+ OpenSans-BoldItalic.ttf
+ OpenSans-ExtraBold.ttf
+ OpenSans-ExtraBoldItalic.ttf
+ OpenSans-Italic.ttf
+ OpenSans-Light.ttf
+ OpenSans-LightItalic.ttf
+ OpenSans-Regular.ttf
+ OpenSans-Semibold.ttf
+ OpenSans-SemiboldItalic.ttf
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+ CFBundleURLTypes
+
+
+ CFBundleURLName
+ com.mattermost.rnbeta
+ CFBundleURLSchemes
+
+ mattermost
+
+
+
+
+
\ No newline at end of file
diff --git a/ios/Podfile b/ios/Podfile
new file mode 100644
index 000000000..7b42ddad3
--- /dev/null
+++ b/ios/Podfile
@@ -0,0 +1,16 @@
+# Uncomment the next line to define a global platform for your project
+# platform :ios, '9.0'
+
+target 'Mattermost' do
+ # Uncomment the next line if you're using Swift or would like to use dynamic frameworks
+ # use_frameworks!
+
+ # Pods for Mattermost
+ pod 'XCDYouTubeKit', '2.5'
+
+ target 'MattermostTests' do
+ inherit! :search_paths
+ # Pods for testing
+ end
+
+end
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
new file mode 100644
index 000000000..7c928130c
--- /dev/null
+++ b/ios/Podfile.lock
@@ -0,0 +1,12 @@
+PODS:
+ - XCDYouTubeKit (2.5.0)
+
+DEPENDENCIES:
+ - XCDYouTubeKit (= 2.5)
+
+SPEC CHECKSUMS:
+ XCDYouTubeKit: 9db45d0f5af8fb5a47561ac7f2fe6de885f57453
+
+PODFILE CHECKSUM: 76eab0e20eee91ac7f71b70b29082dce0d6eba08
+
+COCOAPODS: 1.3.0
diff --git a/package.json b/package.json
index c8887c8e9..243a63888 100644
--- a/package.json
+++ b/package.json
@@ -43,6 +43,7 @@
"react-native-swiper": "1.5.4",
"react-native-tooltip": "enahum/react-native-tooltip",
"react-native-vector-icons": "4.1.1",
+ "react-native-youtube": "1.0.0-beta.3",
"react-redux": "5.0.4",
"redux": "3.6.0",
"redux-batched-actions": "0.2.0",
@@ -52,7 +53,8 @@
"reselect": "3.0.1",
"semver": "5.3.0",
"tinycolor2": "1.4.1",
- "url-parse": "1.1.9"
+ "url-parse": "1.1.9",
+ "youtube-video-id": "0.0.2"
},
"devDependencies": {
"babel-cli": "6.24.1",
diff --git a/yarn.lock b/yarn.lock
index 83bc96e40..3bb54ea90 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4363,7 +4363,7 @@ promise@^7.1.1:
dependencies:
asap "~2.0.3"
-prop-types@15.5.10, prop-types@^15.0.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@~15.5.7:
+prop-types@15.5.10, prop-types@^15.0.0, prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.8, prop-types@~15.5.7:
version "15.5.10"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
dependencies:
@@ -4580,6 +4580,12 @@ react-native-exception-handler@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/react-native-exception-handler/-/react-native-exception-handler-2.0.1.tgz#4ab9fc668927c2672adfffce3784316d81f130e8"
+react-native-fast-image@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/react-native-fast-image/-/react-native-fast-image-1.0.0.tgz#a88337efa18fd34d530bab9ee05680374f3e2a2d"
+ dependencies:
+ prop-types "^15.5.10"
+
react-native-fetch-blob@0.10.8:
version "0.10.8"
resolved "https://registry.yarnpkg.com/react-native-fetch-blob/-/react-native-fetch-blob-0.10.8.tgz#4fc256abae0cb5f10e7c41f28c11b3ff330d72a9"
@@ -4587,12 +4593,6 @@ react-native-fetch-blob@0.10.8:
base-64 "0.1.0"
glob "7.0.6"
-react-native-fast-image@1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/react-native-fast-image/-/react-native-fast-image-1.0.0.tgz#a88337efa18fd34d530bab9ee05680374f3e2a2d"
- dependencies:
- prop-types "^15.5.10"
-
react-native-image-picker@jp928/react-native-image-picker#6ee35b69f3dbd6c7c66f580fd4d9eabf398703d4:
version "0.26.2"
resolved "https://codeload.github.com/jp928/react-native-image-picker/tar.gz/6ee35b69f3dbd6c7c66f580fd4d9eabf398703d4"
@@ -4708,6 +4708,12 @@ react-native-vector-icons@4.1.1:
prop-types "^15.5.8"
yargs "^6.3.0"
+react-native-youtube@1.0.0-beta.3:
+ version "1.0.0-beta.3"
+ resolved "https://registry.yarnpkg.com/react-native-youtube/-/react-native-youtube-1.0.0-beta.3.tgz#0dc51c943cd92868c1457880d78dd5ba72b6001a"
+ dependencies:
+ prop-types "^15.5.0"
+
react-native@0.44.0:
version "0.44.0"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.44.0.tgz#06427a30053f2d555c60fe0b9afcc6c778db09de"
@@ -6256,3 +6262,7 @@ yargs@~3.10.0:
cliui "^2.1.0"
decamelize "^1.0.0"
window-size "0.1.0"
+
+youtube-video-id@0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/youtube-video-id/-/youtube-video-id-0.0.2.tgz#1c2874b8c6d36ef140c36739336863e22f49e438"