Link Previews (#890)

* Link previews, Image previews and YouTube

* Install cocoapods dependencies
This commit is contained in:
enahum 2017-09-12 15:06:19 -03:00 committed by GitHub
parent 6e1d351a73
commit 7aebcbd52e
24 changed files with 1197 additions and 208 deletions

4
.gitignore vendored
View file

@ -69,3 +69,7 @@ ios/sentry.properties
# Testing
.nyc_output
# Pods
.podinstall
ios/Pods/

View file

@ -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:

View file

@ -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 <YouTube /> 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 <mark@remarkablemark.org>
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.

View file

@ -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

View file

@ -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()
);
}

View file

@ -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'

View file

@ -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);

View file

@ -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 (
<View style={style.container}>
<View style={style.flex}>
<Text
style={style.siteTitle}
numberOfLines={1}
ellipsizeMode='tail'
>
{openGraphData.site_name}
</Text>
</View>
<View style={style.wrapper}>
<TouchableOpacity
style={isThumbnail ? {width: offset} : style.flex}
onPress={this.goToLink}
>
<Text
style={style.siteSubtitle}
numberOfLines={3}
ellipsizeMode='tail'
>
{openGraphData.title}
</Text>
</TouchableOpacity>
{isThumbnail &&
<View style={style.thumbnail}>
<Image
style={[style.image, {width, height}]}
source={{uri: imageUrl}}
resizeMode='cover'
/>
</View>
}
</View>
<View style={style.flex}>
<Text
style={style.siteDescription}
numberOfLines={5}
ellipsizeMode='tail'
>
{openGraphData.description}
</Text>
</View>
{hasLargeImage && imageLoaded &&
<Image
style={[style.image, {width, height}]}
source={{uri: imageUrl}}
resizeMode='cover'
/>
}
</View>
);
}
}
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'
}
};
});

View file

@ -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,

View file

@ -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 (
<SlackAttachments
attachments={attachments}
baseTextStyle={baseStyle}
blockStyles={blockStyles}
navigator={navigator}
textStyles={textStyles}
theme={theme}
/>
);
}
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 = (<View>{messageComponent}</View>);
} else if (message.length) {
messageComponent = (
<View style={{flexDirection: 'row'}}>
@ -229,38 +214,53 @@ class PostBody extends PureComponent {
);
}
let body;
if (isSearchResult) {
body = (
<TouchableHighlight
onHideUnderlay={this.handleHideUnderlay}
onLongPress={this.show}
onPress={onPress}
onShowUnderlay={this.handleShowUnderlay}
underlayColor='transparent'
>
<View>
if (!hasBeenDeleted) {
if (isSearchResult) {
body = (
<TouchableHighlight
onHideUnderlay={this.handleHideUnderlay}
onLongPress={this.show}
onPress={onPress}
onShowUnderlay={this.handleShowUnderlay}
underlayColor='transparent'
>
<View>
{messageComponent}
<PostBodyAdditionalContent
baseTextStyle={messageStyle}
blockStyles={blockStyles}
navigator={navigator}
message={message}
postProps={postProps}
textStyles={textStyles}
/>
{this.renderFileAttachments()}
</View>
</TouchableHighlight>
);
} else {
body = (
<OptionsContext
actions={actions}
ref='options'
onPress={onPress}
toggleSelected={toggleSelected}
cancelText={formatMessage({id: 'channel_modal.cancel', defaultMessage: 'Cancel'})}
>
{messageComponent}
{this.renderSlackAttachments(messageStyle, blockStyles, textStyles)}
<PostBodyAdditionalContent
baseTextStyle={messageStyle}
blockStyles={blockStyles}
message={message}
navigator={navigator}
postProps={postProps}
textStyles={textStyles}
/>
{this.renderFileAttachments()}
</View>
</TouchableHighlight>
);
} else {
body = (
<OptionsContext
actions={actions}
ref='options'
onPress={onPress}
toggleSelected={toggleSelected}
cancelText={formatMessage({id: 'channel_modal.cancel', defaultMessage: 'Cancel'})}
>
{messageComponent}
{this.renderSlackAttachments(messageStyle, blockStyles, textStyles)}
{this.renderFileAttachments()}
{hasReactions && <Reactions postId={postId}/>}
</OptionsContext>
);
{hasReactions && <Reactions postId={postId}/>}
</OptionsContext>
);
}
}
return (

View file

@ -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);

View file

@ -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 (
<PostAttachmentOpenGraph
link={link}
openGraphData={openGraphData}
/>
);
}
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 (
<SlackAttachments
attachments={attachments}
baseTextStyle={baseTextStyle}
blockStyles={blockStyles}
navigator={navigator}
textStyles={textStyles}
theme={theme}
/>
);
}
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 (
<TouchableWithoutFeedback
style={styles.imageContainer}
{...this.responder}
onPress={this.playYouTubeVideo}
>
<Image
style={[styles.image, {width, height}]}
source={{uri: imgUrl}}
resizeMode={'cover'}
onError={this.handleLinkLoadError}
>
<TouchableWithoutFeedback onPress={this.playYouTubeVideo}>
<Image
source={youtubePlayIcon}
onPress={this.playYouTubeVideo}
/>
</TouchableWithoutFeedback>
</Image>
</TouchableWithoutFeedback>
);
}
if (isImage) {
return (
<View style={styles.imageContainer}>
<Image
style={[styles.image, {width, height}]}
source={{uri: link}}
resizeMode={'cover'}
onError={this.handleLinkLoadError}
/>
</View>
);
}
}
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
}
});

View file

@ -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'
};

23
app/utils/opengraph.js Normal file
View file

@ -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;
}

View file

@ -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]|<br\/?>)((?: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]);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -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 = "<group>"; };
3752184A1F4B9E980035444B /* RCTCameraRoll.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTCameraRoll.xcodeproj; path = "../node_modules/react-native/Libraries/CameraRoll/RCTCameraRoll.xcodeproj"; sourceTree = "<group>"; };
37ABD3971F4CE13B001FDE6B /* ART.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ART.xcodeproj; path = "../node_modules/react-native/Libraries/ART/ART.xcodeproj"; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
4B19E38FBCB94C57BB03B8E6 /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFetchBlob.a; sourceTree = "<group>"; };
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 = "<group>"; };
@ -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 = "<group>"; };
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 = "<group>"; };
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
7F26919B1EE1DC51007574FE /* RNNotifications.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNNotifications.xcodeproj; path = "../node_modules/react-native-notifications/RNNotifications/RNNotifications.xcodeproj"; sourceTree = "<group>"; };
7F292A701E8AB73400A450A3 /* SplashScreenResource */ = {isa = PBXFileReference; lastKnownFileType = folder; path = SplashScreenResource; sourceTree = "<group>"; };
@ -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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
ACC6B9FDC0AD45A6BFA4FBCD /* libBVLinearGradient.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libBVLinearGradient.a; sourceTree = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
@ -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 = "<group>";
@ -753,6 +777,26 @@
name = Products;
sourceTree = "<group>";
};
4B992D7BAAEDF8759DB525B5 /* Frameworks */ = {
isa = PBXGroup;
children = (
65FD5EA57EBAE06106094B2F /* libPods-Mattermost.a */,
4246DC09024BB33A3E491E25 /* libPods-MattermostTests.a */,
);
name = Frameworks;
sourceTree = "<group>";
};
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 = "<group>";
};
5E91572E1DD0AC6500FF2AA8 /* Products */ = {
isa = PBXGroup;
children = (
@ -837,6 +881,30 @@
name = Products;
sourceTree = "<group>";
};
7FA794CE1F61A1A400C02924 /* Products */ = {
isa = PBXGroup;
children = (
7FA794ED1F61A1A500C02924 /* libRCTYouTube.a */,
);
name = Products;
sourceTree = "<group>";
};
7FA795021F61A1A500C02924 /* Products */ = {
isa = PBXGroup;
children = (
7FA795061F61A1A500C02924 /* libRNSentry.a */,
);
name = Products;
sourceTree = "<group>";
};
7FA795071F61A1A500C02924 /* Products */ = {
isa = PBXGroup;
children = (
7FA7950B1F61A1A500C02924 /* libReactNativeExceptionHandler.a */,
);
name = Products;
sourceTree = "<group>";
};
7FC200BD1EBB65100099331B /* Products */ = {
isa = PBXGroup;
children = (
@ -861,22 +929,6 @@
name = Products;
sourceTree = "<group>";
};
7FEB105F1F6101090039A015 /* Products */ = {
isa = PBXGroup;
children = (
7FEB10861F6101090039A015 /* libReactNativeExceptionHandler.a */,
);
name = Products;
sourceTree = "<group>";
};
7FEB10611F6101090039A015 /* Products */ = {
isa = PBXGroup;
children = (
7FEB10911F61010A0039A015 /* libRNSentry.a */,
);
name = Products;
sourceTree = "<group>";
};
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
isa = PBXGroup;
children = (
@ -911,6 +963,7 @@
546CB0A5BB8F453890CBA559 /* RNFetchBlob.xcodeproj */,
3B1E210BF3B649BBBF427977 /* ReactNativeExceptionHandler.xcodeproj */,
9552041247E749308CE7B412 /* RNSentry.xcodeproj */,
A2968536BEC74A4ABBB73BD9 /* RCTYouTube.xcodeproj */,
);
name = Libraries;
sourceTree = "<group>";
@ -933,6 +986,8 @@
00E356EF1AD99517003FC87E /* MattermostTests */,
83CBBA001A601CBA00E9B192 /* Products */,
0156F464626F49C2977D7982 /* Resources */,
57D053CEA78013E949257E00 /* Pods */,
4B992D7BAAEDF8759DB525B5 /* Frameworks */,
);
indentWidth = 2;
sourceTree = "<group>";
@ -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";

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Mattermost.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>

View file

@ -8,6 +8,7 @@
*/
#import "AppDelegate.h"
#import <AVFoundation/AVFoundation.h>
#import <React/RCTBundleURLProvider.h>
#import <React/RCTRootView.h>
#if __has_include(<React/RNSentry.h>)
@ -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;
}

View file

@ -1,99 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>Mattermost Beta</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.2.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>49</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
<key>NSCameraUsageDescription</key>
<string>Take a Photo or Video and upload it to your mattermost instance</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string/>
<key>NSPhotoLibraryUsageDescription</key>
<string>Upload Photos and Videos to your Mattermost instance</string>
<key>UIAppFonts</key>
<array>
<string>Entypo.ttf</string>
<string>EvilIcons.ttf</string>
<string>FontAwesome.ttf</string>
<string>Foundation.ttf</string>
<string>Ionicons.ttf</string>
<string>MaterialIcons.ttf</string>
<string>Octicons.ttf</string>
<string>Zocial.ttf</string>
<string>MaterialCommunityIcons.ttf</string>
<string>SimpleLineIcons.ttf</string>
<string>OpenSans-Bold.ttf</string>
<string>OpenSans-BoldItalic.ttf</string>
<string>OpenSans-ExtraBold.ttf</string>
<string>OpenSans-ExtraBoldItalic.ttf</string>
<string>OpenSans-Italic.ttf</string>
<string>OpenSans-Light.ttf</string>
<string>OpenSans-LightItalic.ttf</string>
<string>OpenSans-Regular.ttf</string>
<string>OpenSans-Semibold.ttf</string>
<string>OpenSans-SemiboldItalic.ttf</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.mattermost.rnbeta</string>
<key>CFBundleURLSchemes</key>
<array>
<string>mattermost</string>
</array>
</dict>
</array>
</dict>
</plist>
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>Mattermost Beta</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.2.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>49</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionDomains</key>
<dict>
<key>localhost</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
</dict>
</dict>
</dict>
<key>NSCameraUsageDescription</key>
<string>Take a Photo or Video and upload it to your mattermost instance</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string/>
<key>NSPhotoLibraryUsageDescription</key>
<string>Upload Photos and Videos to your Mattermost instance</string>
<key>UIAppFonts</key>
<array>
<string>Entypo.ttf</string>
<string>EvilIcons.ttf</string>
<string>FontAwesome.ttf</string>
<string>Foundation.ttf</string>
<string>Ionicons.ttf</string>
<string>MaterialIcons.ttf</string>
<string>Octicons.ttf</string>
<string>Zocial.ttf</string>
<string>MaterialCommunityIcons.ttf</string>
<string>SimpleLineIcons.ttf</string>
<string>OpenSans-Bold.ttf</string>
<string>OpenSans-BoldItalic.ttf</string>
<string>OpenSans-ExtraBold.ttf</string>
<string>OpenSans-ExtraBoldItalic.ttf</string>
<string>OpenSans-Italic.ttf</string>
<string>OpenSans-Light.ttf</string>
<string>OpenSans-LightItalic.ttf</string>
<string>OpenSans-Regular.ttf</string>
<string>OpenSans-Semibold.ttf</string>
<string>OpenSans-SemiboldItalic.ttf</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>com.mattermost.rnbeta</string>
<key>CFBundleURLSchemes</key>
<array>
<string>mattermost</string>
</array>
</dict>
</array>
</dict>
</plist>

16
ios/Podfile Normal file
View file

@ -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

12
ios/Podfile.lock Normal file
View file

@ -0,0 +1,12 @@
PODS:
- XCDYouTubeKit (2.5.0)
DEPENDENCIES:
- XCDYouTubeKit (= 2.5)
SPEC CHECKSUMS:
XCDYouTubeKit: 9db45d0f5af8fb5a47561ac7f2fe6de885f57453
PODFILE CHECKSUM: 76eab0e20eee91ac7f71b70b29082dce0d6eba08
COCOAPODS: 1.3.0

View file

@ -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",

View file

@ -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"