CBA support for iOS (#1767)
* CBA Support * cba support for iOS Share extension * Autologin with credentials * Set initial config for ExperimentalClientSideCertEnable as false * Fix mattermost-redux to cba branch * feedback review * Fix eslint
This commit is contained in:
parent
8372e95d4a
commit
7b18047800
24 changed files with 521 additions and 187 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -20,6 +20,7 @@ build/
|
|||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
xcshareddata
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
import {configure, shallow} from 'enzyme';
|
||||
|
|
|
|||
|
|
@ -22,8 +22,11 @@ import tinyColor from 'tinycolor2';
|
|||
import {getFileUrl} from 'mattermost-redux/utils/file_utils.js';
|
||||
|
||||
import {DeviceTypes} from 'app/constants/';
|
||||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
import {changeOpacity} from 'app/utils/theme';
|
||||
|
||||
import LocalConfig from 'assets/config';
|
||||
|
||||
import FileAttachmentIcon from './file_attachment_icon';
|
||||
|
||||
const {DOCUMENTS_PATH} = DeviceTypes;
|
||||
|
|
@ -106,6 +109,7 @@ export default class FileAttachmentDocument extends PureComponent {
|
|||
this.setState({didCancel: false});
|
||||
|
||||
try {
|
||||
const certificate = await mattermostBucket.getPreference('cert', LocalConfig.AppGroupId);
|
||||
const isDir = await RNFetchBlob.fs.isDir(DOCUMENTS_PATH);
|
||||
if (!isDir) {
|
||||
try {
|
||||
|
|
@ -122,6 +126,7 @@ export default class FileAttachmentDocument extends PureComponent {
|
|||
indicator: true,
|
||||
overwrite: true,
|
||||
path,
|
||||
certificate,
|
||||
};
|
||||
|
||||
const mime = data.mime_type.split(';')[0];
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ import IonIcon from 'react-native-vector-icons/Ionicons';
|
|||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import {ViewTypes} from 'app/constants';
|
||||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
import checkNetwork from 'app/utils/network';
|
||||
import LocalConfig from 'assets/config';
|
||||
|
||||
import {RequestStatus} from 'mattermost-redux/constants';
|
||||
|
||||
|
|
@ -95,7 +97,7 @@ export default class OfflineIndicator extends Component {
|
|||
this.setState({network: CONNECTING}, () => {
|
||||
if (result) {
|
||||
this.props.actions.connection(true);
|
||||
this.props.actions.initWebSocket(Platform.OS);
|
||||
this.initializeWebSocket();
|
||||
} else {
|
||||
this.setState({network: OFFLINE});
|
||||
}
|
||||
|
|
@ -154,6 +156,18 @@ export default class OfflineIndicator extends Component {
|
|||
return IOS_TOP_PORTRAIT;
|
||||
};
|
||||
|
||||
initializeWebSocket = async () => {
|
||||
const {actions} = this.props;
|
||||
const {initWebSocket} = actions;
|
||||
const platform = Platform.OS;
|
||||
let certificate = null;
|
||||
if (platform === 'ios') {
|
||||
certificate = await mattermostBucket.getPreference('cert', LocalConfig.AppGroupId);
|
||||
}
|
||||
|
||||
initWebSocket(platform, null, null, null, {certificate});
|
||||
};
|
||||
|
||||
offline = () => {
|
||||
this.setState({network: OFFLINE}, () => {
|
||||
this.show();
|
||||
|
|
|
|||
|
|
@ -1,12 +1,16 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {Platform} from 'react-native';
|
||||
import RNFetchBlob from 'react-native-fetch-blob';
|
||||
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
import {General} from 'mattermost-redux/constants';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
|
||||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
import LocalConfig from 'assets/config';
|
||||
|
||||
const HEADER_X_VERSION_ID = 'X-Version-Id';
|
||||
const HEADER_X_CLUSTER_ID = 'X-Cluster-Id';
|
||||
const HEADER_TOKEN = 'Token';
|
||||
|
|
@ -91,6 +95,15 @@ Client4.doFetchWithResponse = async (url, options) => {
|
|||
};
|
||||
};
|
||||
|
||||
window.fetch = new RNFetchBlob.polyfill.Fetch({
|
||||
auto: true,
|
||||
}).build();
|
||||
if (Platform.OS === 'ios') {
|
||||
mattermostBucket.getPreference('cert', LocalConfig.AppGroupId).then((certificate) => {
|
||||
window.fetch = new RNFetchBlob.polyfill.Fetch({
|
||||
auto: true,
|
||||
certificate,
|
||||
}).build();
|
||||
});
|
||||
} else {
|
||||
window.fetch = new RNFetchBlob.polyfill.Fetch({
|
||||
auto: true,
|
||||
}).build();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import KeyboardLayout from 'app/components/layout/keyboard_layout';
|
|||
import OfflineIndicator from 'app/components/offline_indicator';
|
||||
import SafeAreaView from 'app/components/safe_area_view';
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
||||
import PostTextbox from 'app/components/post_textbox';
|
||||
|
|
@ -175,13 +176,12 @@ export default class Channel extends PureComponent {
|
|||
const {actions} = this.props;
|
||||
const {
|
||||
closeWebSocket,
|
||||
initWebSocket,
|
||||
startPeriodicStatusUpdates,
|
||||
stopPeriodicStatusUpdates,
|
||||
} = actions;
|
||||
|
||||
if (open) {
|
||||
initWebSocket(Platform.OS);
|
||||
this.initializeWebSocket();
|
||||
startPeriodicStatusUpdates();
|
||||
} else {
|
||||
closeWebSocket(true);
|
||||
|
|
@ -204,6 +204,18 @@ export default class Channel extends PureComponent {
|
|||
this.props.actions.selectDefaultTeam();
|
||||
};
|
||||
|
||||
initializeWebSocket = async () => {
|
||||
const {actions} = this.props;
|
||||
const {initWebSocket} = actions;
|
||||
const platform = Platform.OS;
|
||||
let certificate = null;
|
||||
if (platform === 'ios') {
|
||||
certificate = await mattermostBucket.getPreference('cert', LocalConfig.AppGroupId);
|
||||
}
|
||||
|
||||
initWebSocket(platform, null, null, null, {certificate});
|
||||
};
|
||||
|
||||
loadChannels = (teamId) => {
|
||||
const {
|
||||
loadChannelsIfNecessary,
|
||||
|
|
|
|||
|
|
@ -12,8 +12,11 @@ import {intlShape} from 'react-intl';
|
|||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
import {emptyFunction} from 'app/utils/general';
|
||||
|
||||
import LocalConfig from 'assets/config';
|
||||
|
||||
const {View: AnimatedView} = Animated;
|
||||
|
||||
export default class Downloader extends PureComponent {
|
||||
|
|
@ -294,12 +297,14 @@ export default class Downloader extends PureComponent {
|
|||
}
|
||||
|
||||
if (downloadFile) {
|
||||
const certificate = await mattermostBucket.getPreference('cert', LocalConfig.AppGroupId);
|
||||
const imageUrl = Client4.getFileUrl(data.id);
|
||||
const options = {
|
||||
session: data.id,
|
||||
timeout: 10000,
|
||||
indicator: true,
|
||||
overwrite: true,
|
||||
certificate,
|
||||
};
|
||||
|
||||
if (downloadPath && prompt) {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@ import {bindActionCreators} from 'redux';
|
|||
import {connect} from 'react-redux';
|
||||
|
||||
import {getPing, resetPing, setServerVersion} from 'mattermost-redux/actions/general';
|
||||
import {login} from 'mattermost-redux/actions/users';
|
||||
|
||||
import {setLastUpgradeCheck} from 'app/actions/views/client_upgrade';
|
||||
import {getSession, handleSuccessfulLogin} from 'app/actions/views/login';
|
||||
import {loadConfigAndLicense} from 'app/actions/views/root';
|
||||
import {handleServerUrlChanged} from 'app/actions/views/select_server';
|
||||
import getClientUpgrade from 'app/selectors/client_upgrade';
|
||||
|
|
@ -33,9 +35,12 @@ function mapStateToProps(state) {
|
|||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
handleSuccessfulLogin,
|
||||
getPing,
|
||||
getSession,
|
||||
handleServerUrlChanged,
|
||||
loadConfigAndLicense,
|
||||
login,
|
||||
resetPing,
|
||||
setLastUpgradeCheck,
|
||||
setServerVersion,
|
||||
|
|
|
|||
|
|
@ -18,16 +18,20 @@ import {
|
|||
View,
|
||||
} from 'react-native';
|
||||
import Button from 'react-native-button';
|
||||
import RNFetchBlob from 'react-native-fetch-blob';
|
||||
|
||||
import {Client4} from 'mattermost-redux/client';
|
||||
|
||||
import ErrorText from 'app/components/error_text';
|
||||
import FormattedText from 'app/components/formatted_text';
|
||||
import {UpgradeTypes} from 'app/constants/view';
|
||||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
import PushNotifications from 'app/push_notifications';
|
||||
import {GlobalStyles} from 'app/styles';
|
||||
import checkUpgradeType from 'app/utils/client_upgrade';
|
||||
import {isValidUrl, stripTrailingSlashes} from 'app/utils/url';
|
||||
import {preventDoubleTap} from 'app/utils/tap';
|
||||
import tracker from 'app/utils/time_tracker';
|
||||
|
||||
import LocalConfig from 'assets/config';
|
||||
|
||||
|
|
@ -36,7 +40,10 @@ export default class SelectServer extends PureComponent {
|
|||
actions: PropTypes.shape({
|
||||
getPing: PropTypes.func.isRequired,
|
||||
handleServerUrlChanged: PropTypes.func.isRequired,
|
||||
handleSuccessfulLogin: PropTypes.func.isRequired,
|
||||
getSession: PropTypes.func.isRequired,
|
||||
loadConfigAndLicense: PropTypes.func.isRequired,
|
||||
login: PropTypes.func.isRequired,
|
||||
resetPing: PropTypes.func.isRequired,
|
||||
setLastUpgradeCheck: PropTypes.func.isRequired,
|
||||
setServerVersion: PropTypes.func.isRequired,
|
||||
|
|
@ -108,6 +115,23 @@ export default class SelectServer extends PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
goToNextScreen = (screen, title) => {
|
||||
const {navigator, theme} = this.props;
|
||||
navigator.push({
|
||||
screen,
|
||||
title,
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
navigatorStyle: {
|
||||
navBarHidden: LocalConfig.AutoSelectServerUrl,
|
||||
disabledBackGesture: LocalConfig.AutoSelectServerUrl,
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
handleNavigatorEvent = (event) => {
|
||||
if (event.id === 'didDisappear') {
|
||||
this.setState({
|
||||
|
|
@ -125,7 +149,7 @@ export default class SelectServer extends PureComponent {
|
|||
title: intl.formatMessage({id: 'mobile.client_upgrade', defaultMessage: 'Client Upgrade'}),
|
||||
backButtonTitle: '',
|
||||
navigatorStyle: {
|
||||
navBarHidden: false,
|
||||
navBarHidden: LocalConfig.AutoSelectServerUrl,
|
||||
disabledBackGesture: LocalConfig.AutoSelectServerUrl,
|
||||
statusBarHidden: true,
|
||||
statusBarHideWithNavBar: true,
|
||||
|
|
@ -138,11 +162,11 @@ export default class SelectServer extends PureComponent {
|
|||
upgradeType,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
handleLoginOptions = (props = this.props) => {
|
||||
const {intl} = this.context;
|
||||
const {config, license, theme} = props;
|
||||
const {config, license} = props;
|
||||
const samlEnabled = config.EnableSaml === 'true' && license.IsLicensed === 'true' && license.SAML === 'true';
|
||||
const gitlabEnabled = config.EnableSignUpWithGitLab === 'true';
|
||||
|
||||
|
|
@ -161,21 +185,21 @@ export default class SelectServer extends PureComponent {
|
|||
title = intl.formatMessage({id: 'mobile.routes.login', defaultMessage: 'Login'});
|
||||
}
|
||||
|
||||
this.props.navigator.push({
|
||||
screen,
|
||||
title,
|
||||
animated: true,
|
||||
backButtonTitle: '',
|
||||
navigatorStyle: {
|
||||
navBarHidden: LocalConfig.AutoSelectServerUrl,
|
||||
disabledBackGesture: LocalConfig.AutoSelectServerUrl,
|
||||
navBarTextColor: theme.sidebarHeaderTextColor,
|
||||
navBarBackgroundColor: theme.sidebarHeaderBg,
|
||||
navBarButtonColor: theme.sidebarHeaderTextColor,
|
||||
},
|
||||
});
|
||||
|
||||
this.props.actions.resetPing();
|
||||
|
||||
if (Platform.OS === 'ios' && LocalConfig.ExperimentalClientSideCertEnable) {
|
||||
if (config.ExperimentalClientSideCertEnable === 'true' && config.ExperimentalClientSideCertCheck === 'primary') {
|
||||
// log in automatically and send directly to the channel screen
|
||||
this.loginWithCertificate();
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
this.goToNextScreen(screen, title);
|
||||
}, 350);
|
||||
} else {
|
||||
this.goToNextScreen(screen, title);
|
||||
}
|
||||
};
|
||||
|
||||
handleAndroidKeyboard = () => {
|
||||
|
|
@ -186,6 +210,44 @@ export default class SelectServer extends PureComponent {
|
|||
this.setState({url});
|
||||
};
|
||||
|
||||
loginWithCertificate = async () => {
|
||||
const {intl, navigator} = this.props;
|
||||
|
||||
tracker.initialLoad = Date.now();
|
||||
|
||||
await this.props.actions.login('credential', 'password');
|
||||
await this.props.actions.handleSuccessfulLogin();
|
||||
const expiresAt = await this.props.actions.getSession();
|
||||
|
||||
if (expiresAt) {
|
||||
PushNotifications.localNotificationSchedule({
|
||||
date: new Date(expiresAt),
|
||||
message: intl.formatMessage({
|
||||
id: 'mobile.session_expired',
|
||||
defaultMessage: 'Session Expired: Please log in to continue receiving notifications.',
|
||||
}),
|
||||
userInfo: {
|
||||
localNotification: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
navigator.resetTo({
|
||||
screen: 'Channel',
|
||||
title: '',
|
||||
animated: false,
|
||||
backButtonTitle: '',
|
||||
navigatorStyle: {
|
||||
animated: true,
|
||||
animationType: 'fade',
|
||||
navBarHidden: true,
|
||||
statusBarHidden: false,
|
||||
statusBarHideWithNavBar: false,
|
||||
screenBackgroundColor: 'transparent',
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
onClick = preventDoubleTap(async () => {
|
||||
const urlParse = require('url-parse');
|
||||
const preUrl = urlParse(this.state.url, true);
|
||||
|
|
@ -212,7 +274,20 @@ export default class SelectServer extends PureComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
this.pingServer(url);
|
||||
if (LocalConfig.ExperimentalClientSideCertEnable && Platform.OS === 'ios') {
|
||||
RNFetchBlob.cba.selectCertificate((certificate) => {
|
||||
if (certificate) {
|
||||
mattermostBucket.setPreference('cert', certificate, LocalConfig.AppGroupId);
|
||||
window.fetch = new RNFetchBlob.polyfill.Fetch({
|
||||
auto: true,
|
||||
certificate,
|
||||
}).build();
|
||||
this.pingServer(url);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.pingServer(url);
|
||||
}
|
||||
});
|
||||
|
||||
pingServer = (url) => {
|
||||
|
|
|
|||
|
|
@ -361,6 +361,7 @@ export function shareExtensionData() {
|
|||
|
||||
switch (action.type) {
|
||||
case UserTypes.LOGOUT_SUCCESS:
|
||||
mattermostBucket.removePreference('cert', Config.AppGroupId);
|
||||
mattermostBucket.removePreference('emm', Config.AppGroupId);
|
||||
mattermostBucket.removeFile('entities', Config.AppGroupId);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@
|
|||
import RNFetchBlob from 'react-native-fetch-blob';
|
||||
|
||||
import {DeviceTypes} from 'app/constants';
|
||||
import mattermostBucket from 'app/mattermost_bucket';
|
||||
|
||||
import LocalConfig from 'assets/config';
|
||||
|
||||
const {IMAGES_PATH} = DeviceTypes;
|
||||
|
||||
|
|
@ -31,12 +34,14 @@ export default class ImageCacheManager {
|
|||
}
|
||||
|
||||
try {
|
||||
const certificate = await mattermostBucket.getPreference('cert', LocalConfig.AppGroupId);
|
||||
const options = {
|
||||
session: uri,
|
||||
timeout: 10000,
|
||||
indicator: true,
|
||||
overwrite: true,
|
||||
path,
|
||||
certificate,
|
||||
};
|
||||
|
||||
this.downloadTask = await RNFetchBlob.config(options).fetch('GET', uri);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
"MobileNoticeURL": "https://about.mattermost.com/mobile-notice-txt/",
|
||||
"SegmentApiKey": "oPPT2MA24VYZTBGDyXtUDa7u50lkPIE4",
|
||||
"ExperimentalUsernamePressIsMention": false,
|
||||
"ExperimentalClientSideCertEnable": false,
|
||||
|
||||
"HideEmailLoginExperimental": false,
|
||||
"HideGitLabLoginExperimental": false,
|
||||
|
|
|
|||
BIN
ios/0155-keys.png
Normal file
BIN
ios/0155-keys.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 KiB |
74
ios/KeyShareConsumer.storyboard
Normal file
74
ios/KeyShareConsumer.storyboard
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
|
||||
<device id="retina4_0" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment version="2352" identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="CBAViewController" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="s2F-x5-Lv6">
|
||||
<rect key="frame" x="14" y="58" width="292" height="482"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="BYZ-38-t0r" id="2eX-Ud-Pqn"/>
|
||||
<outlet property="delegate" destination="BYZ-38-t0r" id="uB2-Oo-O5Z"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fCA-uB-nbn">
|
||||
<rect key="frame" x="20" y="20" width="79" height="30"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="79" id="gZp-zo-dTl"/>
|
||||
</constraints>
|
||||
<state key="normal" title="Import Key">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="openImportDocumentPicker:" destination="BYZ-38-t0r" eventType="touchUpInside" id="QQR-Nf-Rse"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="hhu-eU-kQH">
|
||||
<rect key="frame" x="258" y="20" width="48" height="30"/>
|
||||
<state key="normal" title="Cancel">
|
||||
<color key="titleShadowColor" red="0.5" green="0.5" blue="0.5" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="close:" destination="BYZ-38-t0r" eventType="touchUpInside" id="N2N-e8-aR3"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="s2F-x5-Lv6" firstAttribute="trailing" secondItem="hhu-eU-kQH" secondAttribute="trailing" id="22Z-D9-3i3"/>
|
||||
<constraint firstItem="hhu-eU-kQH" firstAttribute="top" secondItem="k11-9p-T25" secondAttribute="top" id="77c-ha-usL"/>
|
||||
<constraint firstItem="s2F-x5-Lv6" firstAttribute="centerX" secondItem="k11-9p-T25" secondAttribute="centerX" id="Kp3-FJ-pX7"/>
|
||||
<constraint firstItem="s2F-x5-Lv6" firstAttribute="top" secondItem="fCA-uB-nbn" secondAttribute="bottom" constant="8" symbolic="YES" id="LS1-e5-n55"/>
|
||||
<constraint firstItem="s2F-x5-Lv6" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" constant="-2" id="Pco-NV-Fdf"/>
|
||||
<constraint firstItem="fCA-uB-nbn" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" constant="4" id="hjV-xC-rfW"/>
|
||||
<constraint firstItem="fCA-uB-nbn" firstAttribute="baseline" secondItem="hhu-eU-kQH" secondAttribute="baseline" id="o5a-K8-ztC"/>
|
||||
<constraint firstItem="k11-9p-T25" firstAttribute="bottom" secondItem="s2F-x5-Lv6" secondAttribute="bottom" constant="28" id="yUG-Ri-Rz9"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="k11-9p-T25"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="tableViewKeyChain" destination="s2F-x5-Lv6" id="GnK-3t-7Wi"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="564" y="538"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
|
@ -46,7 +46,6 @@
|
|||
7F292AA71E8ABB1100A450A3 /* splash.png in Resources */ = {isa = PBXBuildFile; fileRef = 7F292AA51E8ABB1100A450A3 /* splash.png */; };
|
||||
7F380D0B1FDB3CFC0061AAD2 /* libRCTVideo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F93F9D91FBB726B0088E416 /* libRCTVideo.a */; };
|
||||
7F3F66021FE426EE0085CA0E /* libRNSVG.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FDF28E11E1F4B1F00DBBE56 /* libRNSVG.a */; };
|
||||
7F3F660F1FE4280D0085CA0E /* libReactNativeExceptionHandler.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FA7950B1F61A1A500C02924 /* libReactNativeExceptionHandler.a */; };
|
||||
7F3F66101FE4281A0085CA0E /* libRNSentry.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FA795061F61A1A500C02924 /* libRNSentry.a */; };
|
||||
7F43D5D61F6BF8C2001FC614 /* libRNLocalAuth.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F8C49871F3DFC30003A22BA /* libRNLocalAuth.a */; };
|
||||
7F43D5D71F6BF8D0001FC614 /* libRNPasscodeStatus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F8C49F81F3E0710003A22BA /* libRNPasscodeStatus.a */; };
|
||||
|
|
@ -69,6 +68,7 @@
|
|||
7F6877B31E7836070094B63F /* libToolTipMenu.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F6877B01E7835E50094B63F /* libToolTipMenu.a */; };
|
||||
7F6C47A51FE87E8C00F5A912 /* PerformRequests.m in Sources */ = {isa = PBXBuildFile; fileRef = 7F6C47A41FE87E8C00F5A912 /* PerformRequests.m */; };
|
||||
7F7D7F98201645E100D31155 /* libReactNativePermissions.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F7D7F87201645D300D31155 /* libReactNativePermissions.a */; };
|
||||
7F95132F208646CE00616E3E /* 0155-keys.png in Resources */ = {isa = PBXBuildFile; fileRef = 7F9512EA208646CD00616E3E /* 0155-keys.png */; };
|
||||
7FB6006B1FE3116800DB284F /* libRNFetchBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 375218411F4B9E320035444B /* libRNFetchBlob.a */; };
|
||||
7FBB5E9B1E1F5A4B000DE18A /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FDF290C1E1F4B4E00DBBE56 /* libRNVectorIcons.a */; };
|
||||
7FC200E81EBB65370099331B /* libReactNativeNavigation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FC200DF1EBB65100099331B /* libReactNativeNavigation.a */; };
|
||||
|
|
@ -79,11 +79,13 @@
|
|||
7FEB10981F6101710039A015 /* BlurAppScreen.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FEB10971F6101710039A015 /* BlurAppScreen.m */; };
|
||||
7FEB109D1F61019C0039A015 /* MattermostManaged.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FEB109A1F61019C0039A015 /* MattermostManaged.m */; };
|
||||
7FEB109E1F61019C0039A015 /* UIImage+ImageEffects.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FEB109C1F61019C0039A015 /* UIImage+ImageEffects.m */; };
|
||||
7FF2AF8B2086483E00FFBDF4 /* KeyShareConsumer.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7FF2AF8A2086483E00FFBDF4 /* KeyShareConsumer.storyboard */; };
|
||||
7FF7BE2C1FDEE4AE005E55FE /* MattermostManaged.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FEB109A1F61019C0039A015 /* MattermostManaged.m */; };
|
||||
7FF7BE6D1FDEE5E8005E55FE /* MattermostBucket.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FF7BE6C1FDEE5E8005E55FE /* MattermostBucket.m */; };
|
||||
7FF7BE6E1FDEE5E8005E55FE /* MattermostBucket.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FF7BE6C1FDEE5E8005E55FE /* MattermostBucket.m */; };
|
||||
7FF7BE6F1FDF3CE4005E55FE /* libRNVectorIcons.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7FDF290C1E1F4B4E00DBBE56 /* libRNVectorIcons.a */; };
|
||||
7FF7BE701FDF3EE7005E55FE /* Ionicons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 2DCFD31D3F4A4154822AB532 /* Ionicons.ttf */; };
|
||||
7FF7BE711FE004A3005E55FE /* libRNDeviceInfo.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 37DD11281E79EBE1004111BA /* libRNDeviceInfo.a */; };
|
||||
7FFDB3191FE3566C009E3BCF /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 005346E5C0E542BFABAE1411 /* FontAwesome.ttf */; };
|
||||
7FFE329E1FD9CB650038C7A0 /* ShareViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 7FFE329D1FD9CB650038C7A0 /* ShareViewController.m */; };
|
||||
7FFE32A11FD9CB650038C7A0 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7FFE329F1FD9CB650038C7A0 /* MainInterface.storyboard */; };
|
||||
|
|
@ -720,6 +722,7 @@
|
|||
7F6C47A31FE87E8C00F5A912 /* PerformRequests.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PerformRequests.h; sourceTree = "<group>"; };
|
||||
7F6C47A41FE87E8C00F5A912 /* PerformRequests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PerformRequests.m; sourceTree = "<group>"; };
|
||||
7F7D7F52201645D300D31155 /* ReactNativePermissions.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactNativePermissions.xcodeproj; path = "../node_modules/react-native-permissions/ios/ReactNativePermissions.xcodeproj"; sourceTree = "<group>"; };
|
||||
7F9512EA208646CD00616E3E /* 0155-keys.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "0155-keys.png"; sourceTree = "<group>"; };
|
||||
7FC200BC1EBB65100099331B /* ReactNativeNavigation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ReactNativeNavigation.xcodeproj; path = "../node_modules/react-native-navigation/ios/ReactNativeNavigation.xcodeproj"; sourceTree = "<group>"; };
|
||||
7FD8DE972029ECDE001AAC5E /* RNTableView.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNTableView.xcodeproj; path = "../node_modules/react-native-tableview/RNTableView.xcodeproj"; sourceTree = "<group>"; };
|
||||
7FDB92751F706F45006CDFD1 /* RNImagePicker.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RNImagePicker.xcodeproj; path = "../node_modules/react-native-image-picker/ios/RNImagePicker.xcodeproj"; sourceTree = "<group>"; };
|
||||
|
|
@ -729,6 +732,7 @@
|
|||
7FEB109A1F61019C0039A015 /* MattermostManaged.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MattermostManaged.m; path = Mattermost/MattermostManaged.m; sourceTree = "<group>"; };
|
||||
7FEB109B1F61019C0039A015 /* UIImage+ImageEffects.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+ImageEffects.h"; path = "Mattermost/UIImage+ImageEffects.h"; sourceTree = "<group>"; };
|
||||
7FEB109C1F61019C0039A015 /* UIImage+ImageEffects.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImage+ImageEffects.m"; path = "Mattermost/UIImage+ImageEffects.m"; sourceTree = "<group>"; };
|
||||
7FF2AF8A2086483E00FFBDF4 /* KeyShareConsumer.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = KeyShareConsumer.storyboard; sourceTree = "<group>"; };
|
||||
7FF7BE6B1FDEE5E8005E55FE /* MattermostBucket.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MattermostBucket.h; sourceTree = "<group>"; };
|
||||
7FF7BE6C1FDEE5E8005E55FE /* MattermostBucket.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MattermostBucket.m; sourceTree = "<group>"; };
|
||||
7FFE329A1FD9CB640038C7A0 /* MattermostShare.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = MattermostShare.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
|
@ -832,6 +836,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7FFE32EA1FD9CD050038C7A0 /* libReact.a in Frameworks */,
|
||||
7FFE32E71FD9CCD00038C7A0 /* libART.a in Frameworks */,
|
||||
7F3F66101FE4281A0085CA0E /* libRNSentry.a in Frameworks */,
|
||||
7F642DF1209353A400F3165E /* libRNDeviceInfo.a in Frameworks */,
|
||||
|
|
@ -846,7 +851,6 @@
|
|||
7FFE32ED1FD9CD450038C7A0 /* libRCTNetwork.a in Frameworks */,
|
||||
7FFE32EC1FD9CD360038C7A0 /* libRCTText.a in Frameworks */,
|
||||
7FFE32EB1FD9CD170038C7A0 /* libRCTImage.a in Frameworks */,
|
||||
7FFE32EA1FD9CD050038C7A0 /* libReact.a in Frameworks */,
|
||||
7FFE32E91FD9CCF40038C7A0 /* libRCTAnimation.a in Frameworks */,
|
||||
7FFE32E81FD9CCDE0038C7A0 /* libRCTCameraRoll.a in Frameworks */,
|
||||
7FD8DEDE2029EDB7001AAC5E /* libRNTableView.a in Frameworks */,
|
||||
|
|
@ -973,15 +977,17 @@
|
|||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||
7F292AA41E8ABB1100A450A3 /* LaunchScreen.xib */,
|
||||
7FF2AF8A2086483E00FFBDF4 /* KeyShareConsumer.storyboard */,
|
||||
13B07FB71A68108700A75B9A /* main.m */,
|
||||
7F63D2C21E6DD98A001FAE12 /* Mattermost.entitlements */,
|
||||
7FF7BE6B1FDEE5E8005E55FE /* MattermostBucket.h */,
|
||||
7FF7BE6C1FDEE5E8005E55FE /* MattermostBucket.m */,
|
||||
7FEB10991F61019C0039A015 /* MattermostManaged.h */,
|
||||
7FEB109A1F61019C0039A015 /* MattermostManaged.m */,
|
||||
7F292AA51E8ABB1100A450A3 /* splash.png */,
|
||||
7FEB109B1F61019C0039A015 /* UIImage+ImageEffects.h */,
|
||||
7FEB109C1F61019C0039A015 /* UIImage+ImageEffects.m */,
|
||||
7F9512EA208646CD00616E3E /* 0155-keys.png */,
|
||||
7F292AA51E8ABB1100A450A3 /* splash.png */,
|
||||
);
|
||||
name = Mattermost;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1328,6 +1334,7 @@
|
|||
7F5CA956208FE38F004F91CE /* RNDocumentPicker.xcodeproj */,
|
||||
7F642DE72093530B00F3165E /* RNDeviceInfo.xcodeproj */,
|
||||
74D116A4208A8D3000CF8A79 /* RNKeychain.xcodeproj */,
|
||||
546CB0A5BB8F453890CBA559 /* RNFetchBlob.xcodeproj */,
|
||||
7FD8DE972029ECDE001AAC5E /* RNTableView.xcodeproj */,
|
||||
7F7D7F52201645D300D31155 /* ReactNativePermissions.xcodeproj */,
|
||||
7FDB92751F706F45006CDFD1 /* RNImagePicker.xcodeproj */,
|
||||
|
|
@ -1355,7 +1362,6 @@
|
|||
EBA6063A99C141098D40C67A /* RNPasscodeStatus.xcodeproj */,
|
||||
27A6EA89298440439DA9F98D /* JailMonkey.xcodeproj */,
|
||||
50ECB1D221E44F51B5690DF2 /* FastImage.xcodeproj */,
|
||||
546CB0A5BB8F453890CBA559 /* RNFetchBlob.xcodeproj */,
|
||||
3B1E210BF3B649BBBF427977 /* ReactNativeExceptionHandler.xcodeproj */,
|
||||
9552041247E749308CE7B412 /* RNSentry.xcodeproj */,
|
||||
62A58E5E984E4CF1811620B8 /* RNCookieManagerIOS.xcodeproj */,
|
||||
|
|
@ -1494,6 +1500,9 @@
|
|||
com.apple.BackgroundModes = {
|
||||
enabled = 1;
|
||||
};
|
||||
com.apple.Keychain = {
|
||||
enabled = 1;
|
||||
};
|
||||
com.apple.Push = {
|
||||
enabled = 1;
|
||||
};
|
||||
|
|
@ -1511,6 +1520,12 @@
|
|||
com.apple.ApplicationGroups.iOS = {
|
||||
enabled = 1;
|
||||
};
|
||||
com.apple.Keychain = {
|
||||
enabled = 1;
|
||||
};
|
||||
com.apple.iCloud = {
|
||||
enabled = 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
@ -2204,6 +2219,7 @@
|
|||
F006936FC2884C24A1321FC0 /* MaterialIcons.ttf in Resources */,
|
||||
7F292AA71E8ABB1100A450A3 /* splash.png in Resources */,
|
||||
420A7328E12C4B72AEF420CE /* Octicons.ttf in Resources */,
|
||||
7FF2AF8B2086483E00FFBDF4 /* KeyShareConsumer.storyboard in Resources */,
|
||||
7F292AA61E8ABB1100A450A3 /* LaunchScreen.xib in Resources */,
|
||||
7F292A711E8AB73400A450A3 /* SplashScreenResource in Resources */,
|
||||
2B4C9B708010475DA575B81D /* SimpleLineIcons.ttf in Resources */,
|
||||
|
|
@ -2212,6 +2228,7 @@
|
|||
55C6561DDBBA45929D88B6D1 /* OpenSans-BoldItalic.ttf in Resources */,
|
||||
D719A67137964F08BE47A5FC /* OpenSans-ExtraBold.ttf in Resources */,
|
||||
0111A42B7F264BCF8CBDE3ED /* OpenSans-ExtraBoldItalic.ttf in Resources */,
|
||||
7F95132F208646CE00616E3E /* 0155-keys.png in Resources */,
|
||||
0C0D24F53F254F75869E5951 /* OpenSans-Italic.ttf in Resources */,
|
||||
2D5296A8926B4D7FBAF2D6E2 /* OpenSans-Light.ttf in Resources */,
|
||||
F083DB472349411A8E6E7AAD /* OpenSans-LightItalic.ttf in Resources */,
|
||||
|
|
@ -2637,16 +2654,15 @@
|
|||
"$(SRCROOT)/../node_modules/react-native-passcode-status/ios",
|
||||
"$(SRCROOT)/../node_modules/jail-monkey/JailMonkey",
|
||||
"$(SRCROOT)/../node_modules/react-native/Libraries/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-navigation/ios/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-local-auth/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-passcode-status/ios/**",
|
||||
"$(SRCROOT)/../node_modules/jail-monkey/JailMonkey/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-fast-image/ios/FastImage/**",
|
||||
"$(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-cookies/ios/RNCookieManagerIOS",
|
||||
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
|
||||
"$(SRCROOT)/../node_modules/react-native-youtube/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-video/ios",
|
||||
"$(SRCROOT)/../node_modules/react-native-doc-viewer/ios/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-safe-area/ios/RNSafeArea",
|
||||
"$(SRCROOT)/../node_modules/react-native-cookies/ios/RNCookieManagerIOS/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-video/ios/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-tableview/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-device-info/ios/**",
|
||||
);
|
||||
|
|
@ -2697,16 +2713,15 @@
|
|||
"$(SRCROOT)/../node_modules/react-native-passcode-status/ios",
|
||||
"$(SRCROOT)/../node_modules/jail-monkey/JailMonkey",
|
||||
"$(SRCROOT)/../node_modules/react-native/Libraries/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-navigation/ios/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-local-auth/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-passcode-status/ios/**",
|
||||
"$(SRCROOT)/../node_modules/jail-monkey/JailMonkey/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-fast-image/ios/FastImage/**",
|
||||
"$(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-cookies/ios/RNCookieManagerIOS",
|
||||
"$(SRCROOT)/../node_modules/react-native-image-picker/ios",
|
||||
"$(SRCROOT)/../node_modules/react-native-youtube/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-video/ios",
|
||||
"$(SRCROOT)/../node_modules/react-native-doc-viewer/ios/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-safe-area/ios/RNSafeArea",
|
||||
"$(SRCROOT)/../node_modules/react-native-cookies/ios/RNCookieManagerIOS/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-video/ios/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-tableview/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-device-info/ios/**",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@
|
|||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
|
|
@ -84,7 +83,6 @@
|
|||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
language = ""
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
|
|
|
|||
|
|
@ -22,5 +22,9 @@
|
|||
<array>
|
||||
<string>group.com.mattermost.rnbeta</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)com.mattermost.rnbeta</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -2,9 +2,27 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.icloud-container-identifiers</key>
|
||||
<array>
|
||||
<string>iCloud.$(CFBundleIdentifier)</string>
|
||||
</array>
|
||||
<key>com.apple.developer.icloud-services</key>
|
||||
<array>
|
||||
<string>CloudDocuments</string>
|
||||
</array>
|
||||
<key>com.apple.developer.ubiquity-container-identifiers</key>
|
||||
<array>
|
||||
<string>iCloud.$(CFBundleIdentifier)</string>
|
||||
</array>
|
||||
<key>com.apple.developer.ubiquity-kvstore-identifier</key>
|
||||
<string>$(TeamIdentifierPrefix)$(CFBundleIdentifier)</string>
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.com.mattermost.rnbeta</string>
|
||||
</array>
|
||||
<key>keychain-access-groups</key>
|
||||
<array>
|
||||
<string>$(AppIdentifierPrefix)com.mattermost.rnbeta</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import "MattermostBucket.h"
|
||||
#import "KeyChainDataSource.h"
|
||||
|
||||
@interface SessionManager : NSObject<NSURLSessionDelegate, NSURLSessionTaskDelegate>
|
||||
@property (nonatomic, copy) void (^savedCompletionHandler)(void);
|
||||
@property MattermostBucket *bucket;
|
||||
@property (nonatomic, retain) KeyChainDataSource *keyChain;
|
||||
@property (nonatomic, strong) NSString *requestWithGroup;
|
||||
@property (nonatomic, strong) NSString *certificateName;
|
||||
@property (nonatomic) BOOL isBackground;
|
||||
|
||||
|
||||
+(instancetype)sharedSession;
|
||||
-(NSString *)getAppGroupIdFromRequestIdentifier:(NSString *) requestWithGroup;
|
||||
-(NSURLSession *)createSessionForRequestRequest:(NSString *)requestId;
|
||||
-(void)setRequestWithGroup:(NSString *)requestWithGroup certificateName:(NSString *)certificateName;
|
||||
-(void)setDataForRequest:(NSDictionary *)data forRequestWithGroup:(NSString *)requestId;
|
||||
-(void)createPostForRequest:(NSString *)requestId;
|
||||
-(NSURL*)tempContainerURL:(NSString*)appGroupId;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@
|
|||
#import "MattermostBucket.h"
|
||||
|
||||
@implementation SessionManager
|
||||
|
||||
@synthesize keyChain;
|
||||
|
||||
+ (instancetype)sharedSession {
|
||||
static id sharedMyManager = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
|
|
@ -15,46 +18,213 @@
|
|||
self = [super init];
|
||||
if (self) {
|
||||
self.bucket = [[MattermostBucket alloc] init];
|
||||
self.keyChain = [[KeyChainDataSource alloc] initWithMode:KSM_Identities];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)setRequestWithGroup:(NSString *)requestWithGroup certificateName:(NSString *)certificateName {
|
||||
self.requestWithGroup = requestWithGroup;
|
||||
self.certificateName = certificateName;
|
||||
self.isBackground = certificateName == nil;
|
||||
}
|
||||
|
||||
-(void)setDataForRequest:(NSDictionary *)data forRequestWithGroup:(NSString *)requestWithGroup {
|
||||
NSString *appGroupId = [self getAppGroupIdFromRequestIdentifier:requestWithGroup];
|
||||
[[self.bucket bucketByName:appGroupId] setObject:data forKey:requestWithGroup];
|
||||
}
|
||||
|
||||
|
||||
-(NSString *)getAppGroupIdFromRequestIdentifier:(NSString *) requestWithGroup {
|
||||
return [requestWithGroup componentsSeparatedByString:@"|"][1];
|
||||
}
|
||||
|
||||
-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
|
||||
if (self.savedCompletionHandler) {
|
||||
self.savedCompletionHandler();
|
||||
self.savedCompletionHandler = nil;
|
||||
-(NSDictionary *)getCredentialsForRequest:(NSString *)requestWithGroup {
|
||||
NSString * appGroupId = [self getAppGroupIdFromRequestIdentifier:requestWithGroup];
|
||||
NSString *entitiesString = [self.bucket readFromFile:@"entities" appGroupId:appGroupId];
|
||||
NSData *entitiesData = [entitiesString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSError *error;
|
||||
NSDictionary *entities = [NSJSONSerialization JSONObjectWithData:entitiesData options:NSJSONReadingMutableContainers error:&error];
|
||||
if (error != nil) {
|
||||
return nil;
|
||||
}
|
||||
return [[entities objectForKey:@"general"] objectForKey:@"credentials"];
|
||||
}
|
||||
|
||||
-(NSDictionary *)getDataForRequest:(NSString *)requestWithGroup {
|
||||
NSString *appGroupId = [self getAppGroupIdFromRequestIdentifier:requestWithGroup];
|
||||
return [[self.bucket bucketByName:appGroupId] objectForKey:requestWithGroup];
|
||||
}
|
||||
|
||||
-(NSURLSession *)createSessionForRequestRequest:(NSString *)requestWithGroup {
|
||||
NSString *appGroupId = [self getAppGroupIdFromRequestIdentifier:requestWithGroup];
|
||||
NSURLSessionConfiguration* config;
|
||||
if (self.isBackground) {
|
||||
config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:requestWithGroup];
|
||||
} else {
|
||||
config = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
}
|
||||
|
||||
config.sharedContainerIdentifier = appGroupId;
|
||||
return [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
|
||||
}
|
||||
|
||||
-(void) createPost:(NSDictionary *) post
|
||||
withFiles:(NSArray *)files
|
||||
credentials:(NSDictionary *) credentials
|
||||
requestWithGroup:(NSString *)requestWithGroup {
|
||||
NSString *serverUrl = [credentials objectForKey:@"url"];
|
||||
NSString *token = [credentials objectForKey:@"token"];
|
||||
NSString *channelId = [post objectForKey:@"channel_id"];
|
||||
NSURLSession *session = [self createSessionForRequestRequest:requestWithGroup];
|
||||
NSString *appGroupId = [self getAppGroupIdFromRequestIdentifier:requestWithGroup];
|
||||
|
||||
for (id file in files) {
|
||||
NSURL *filePath = [NSURL fileURLWithPath:[file objectForKey:@"filePath"]];
|
||||
NSString *fileName = [file objectForKey:@"filename"];
|
||||
|
||||
NSError *err;
|
||||
NSURL *tempContainerURL = [self tempContainerURL:appGroupId];
|
||||
NSURL *destinationURL = [tempContainerURL URLByAppendingPathComponent: fileName];
|
||||
BOOL bVal = [[NSFileManager defaultManager] copyItemAtURL:filePath toURL:destinationURL error:&err];
|
||||
|
||||
NSCharacterSet *allowedCharacters = [NSCharacterSet URLQueryAllowedCharacterSet];
|
||||
NSString *encodedFilename = [[file objectForKey:@"filename"] stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacters];
|
||||
NSString *url = [serverUrl stringByAppendingString:@"/api/v4/files"];
|
||||
NSString *queryString = [NSString stringWithFormat:@"?channel_id=%@&filename=%@", channelId, encodedFilename];
|
||||
NSURL *filesUrl = [NSURL URLWithString:[url stringByAppendingString:queryString]];
|
||||
NSMutableURLRequest *uploadRequest = [NSMutableURLRequest requestWithURL:filesUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:120.0];
|
||||
[uploadRequest setHTTPMethod:@"POST"];
|
||||
[uploadRequest setValue:[@"Bearer " stringByAppendingString:token] forHTTPHeaderField:@"Authorization"];
|
||||
[uploadRequest setValue:@"application/json" forHTTPHeaderField:@"Accept"];
|
||||
|
||||
NSURLSessionUploadTask *task = [session uploadTaskWithRequest:uploadRequest fromFile:destinationURL];
|
||||
NSLog(@"Executing file request %@", fileName);
|
||||
[task resume];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionDataTask *)task didCompleteWithError:(nullable NSError *)error {
|
||||
NSString *requestWithGroup = [[session configuration] identifier];
|
||||
-(void) createPost:(NSMutableDictionary *)post
|
||||
withFileIds:(NSArray *)fileIds
|
||||
credentials:(NSDictionary *) credentials {
|
||||
NSString *serverUrl = [credentials objectForKey:@"url"];
|
||||
NSString *token = [credentials objectForKey:@"token"];
|
||||
|
||||
if(error != nil) {
|
||||
NSLog(@"ERROR %@", [error userInfo]);
|
||||
NSLog(@"invalidating session %@", requestWithGroup);
|
||||
[session invalidateAndCancel];
|
||||
} else if (requestWithGroup != nil) {
|
||||
NSString *appGroupId = [self getAppGroupIdFromRequestIdentifier:requestWithGroup];
|
||||
NSURL *requestUrl = [[task originalRequest] URL];
|
||||
if (fileIds != nil && [fileIds count] > 0) {
|
||||
[post setObject:fileIds forKey:@"file_ids"];
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
NSData *postData = [NSJSONSerialization dataWithJSONObject:post options:NSJSONWritingPrettyPrinted error:&error];
|
||||
|
||||
if (error == nil) {
|
||||
NSString* postAsString = [[NSString alloc] initWithData:postData encoding:NSUTF8StringEncoding];
|
||||
|
||||
NSDictionary *data = [self getDataForRequest:requestWithGroup];
|
||||
NSArray *files = [data objectForKey:@"files"];
|
||||
NSMutableArray *fileIds = [data objectForKey:@"file_ids"];
|
||||
NSURL *createUrl = [NSURL URLWithString:[serverUrl stringByAppendingString:@"/api/v4/posts"]];
|
||||
|
||||
if ([[requestUrl absoluteString] containsString:@"files"] &&
|
||||
[files count] == [fileIds count]) {
|
||||
[self sendPostRequestForId:requestWithGroup];
|
||||
[[self.bucket bucketByName:appGroupId] removeObjectForKey:requestWithGroup];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:createUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0];
|
||||
[request setHTTPMethod:@"POST"];
|
||||
[request setValue:[@"Bearer " stringByAppendingString:token] forHTTPHeaderField:@"Authorization"];
|
||||
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
|
||||
[request setValue:@"application/json; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
|
||||
[request setHTTPBody:[postAsString dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
NSURLSessionConfiguration* config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
|
||||
NSURLSession *createSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
|
||||
NSURLSessionDataTask *createTask = [createSession dataTaskWithRequest:request];
|
||||
NSLog(@"Executing post request");
|
||||
[createTask resume];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)createPostForRequest:(NSString *)requestWithGroup {
|
||||
NSDictionary *data = [self getDataForRequest:requestWithGroup];
|
||||
NSDictionary *post = [data objectForKey:@"post"];
|
||||
NSArray *files = [data objectForKey:@"files"];
|
||||
NSDictionary *credentials = [self getCredentialsForRequest:requestWithGroup];
|
||||
|
||||
if (credentials == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (files != nil && [files count] > 0) {
|
||||
[self createPost:post withFiles:files credentials:credentials requestWithGroup:requestWithGroup];
|
||||
}
|
||||
else {
|
||||
[self createPost:[post mutableCopy] withFileIds:nil credentials:credentials];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)sendPostRequestForId:(NSString *)requestWithGroup {
|
||||
NSDictionary *data = [self getDataForRequest:requestWithGroup];
|
||||
NSDictionary *credentials = [self getCredentialsForRequest:requestWithGroup];
|
||||
|
||||
NSMutableDictionary *post = [[data objectForKey:@"post"] mutableCopy];
|
||||
NSArray *fileIds = [data objectForKey:@"file_ids"];
|
||||
|
||||
[self createPost:post withFileIds:fileIds credentials:credentials];
|
||||
}
|
||||
|
||||
-(NSURL*)tempContainerURL:(NSString*)appGroupId {
|
||||
NSFileManager *manager = [NSFileManager defaultManager];
|
||||
NSURL *containerURL = [manager containerURLForSecurityApplicationGroupIdentifier:appGroupId];
|
||||
NSURL *tempDirectoryURL = [containerURL URLByAppendingPathComponent:@"shareTempItems"];
|
||||
if (![manager fileExistsAtPath:[tempDirectoryURL path]]) {
|
||||
NSError *err;
|
||||
[manager createDirectoryAtURL:tempDirectoryURL withIntermediateDirectories:YES attributes:nil error:&err];
|
||||
if (err) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
return tempDirectoryURL;
|
||||
}
|
||||
|
||||
|
||||
-(void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * _Nullable))completionHandler {
|
||||
NSLog(@"completition handler from normal challenge");
|
||||
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]) {
|
||||
if (self.certificateName) {
|
||||
SecIdentityRef identity = [keyChain GetIdentityByName:self.certificateName];
|
||||
if (identity != nil) {
|
||||
SecCertificateRef certificate = NULL;
|
||||
OSStatus status = SecIdentityCopyCertificate(identity, &certificate);
|
||||
if (!status) {
|
||||
CFArrayRef emailAddresses = NULL;
|
||||
SecCertificateCopyEmailAddresses(certificate, &emailAddresses);
|
||||
NSArray *emails = (NSArray *)CFBridgingRelease(emailAddresses);
|
||||
CFStringRef summaryRef = SecCertificateCopySubjectSummary(certificate);
|
||||
NSString *tagstr = (NSString*)CFBridgingRelease(summaryRef);
|
||||
NSString *email = @"";
|
||||
if ([emails count] > 0){
|
||||
email = [emails objectAtIndex:0];
|
||||
}
|
||||
NSLog(@"completition %@ %@", tagstr, email);
|
||||
const void *certs[] = {certificate};
|
||||
CFArrayRef certArray = CFArrayCreate(kCFAllocatorDefault, certs, 1, NULL);
|
||||
NSURLCredential *credential = [NSURLCredential credentialWithIdentity:identity certificates:(__bridge NSArray*)certArray persistence:NSURLCredentialPersistencePermanent];
|
||||
[challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
|
||||
completionHandler(NSURLSessionAuthChallengeUseCredential,credential);
|
||||
NSLog(@"completition handler for certificate");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
|
||||
NSLog(@"completition handler that should not be reached");
|
||||
} else {
|
||||
NSLog(@"completition handler regular stuff");
|
||||
completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
|
||||
}
|
||||
}
|
||||
|
||||
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data {
|
||||
NSString *requestWithGroup = [[session configuration] identifier];
|
||||
NSString *requestWithGroup;
|
||||
if (self.isBackground) {
|
||||
requestWithGroup = [[session configuration] identifier];
|
||||
} else {
|
||||
requestWithGroup = self.requestWithGroup;
|
||||
}
|
||||
NSURL *requestUrl = [[dataTask originalRequest] URL];
|
||||
|
||||
if ([[requestUrl absoluteString] containsString:@"files"]) {
|
||||
|
|
@ -84,135 +254,41 @@
|
|||
}
|
||||
}
|
||||
|
||||
-(void)setDataForRequest:(NSDictionary *)data forRequestWithGroup:(NSString *)requestWithGroup {
|
||||
NSString *appGroupId = [self getAppGroupIdFromRequestIdentifier:requestWithGroup];
|
||||
[[self.bucket bucketByName:appGroupId] setObject:data forKey:requestWithGroup];
|
||||
}
|
||||
|
||||
-(NSDictionary *)getDataForRequest:(NSString *)requestWithGroup {
|
||||
NSString *appGroupId = [self getAppGroupIdFromRequestIdentifier:requestWithGroup];
|
||||
return [[self.bucket bucketByName:appGroupId] objectForKey:requestWithGroup];
|
||||
}
|
||||
|
||||
-(NSURLSession *)createSessionForRequestRequest:(NSString *)requestWithGroup {
|
||||
NSString *appGroupId = [self getAppGroupIdFromRequestIdentifier:requestWithGroup];
|
||||
NSURLSessionConfiguration* config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:requestWithGroup];
|
||||
config.sharedContainerIdentifier = appGroupId;
|
||||
return [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
|
||||
}
|
||||
|
||||
-(NSDictionary *)getCredentialsForRequest:(NSString *)requestWithGroup {
|
||||
NSString * appGroupId = [self getAppGroupIdFromRequestIdentifier:requestWithGroup];
|
||||
NSString *entitiesString = [self.bucket readFromFile:@"entities" appGroupId:appGroupId];
|
||||
NSData *entitiesData = [entitiesString dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSError *error;
|
||||
NSDictionary *entities = [NSJSONSerialization JSONObjectWithData:entitiesData options:NSJSONReadingMutableContainers error:&error];
|
||||
if (error != nil) {
|
||||
return nil;
|
||||
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionDataTask *)task didCompleteWithError:(nullable NSError *)error {
|
||||
NSString *requestWithGroup;
|
||||
if (self.isBackground) {
|
||||
requestWithGroup = [[session configuration] identifier];
|
||||
} else {
|
||||
requestWithGroup = self.requestWithGroup;
|
||||
}
|
||||
return [[entities objectForKey:@"general"] objectForKey:@"credentials"];
|
||||
}
|
||||
|
||||
-(void)createPostForRequest:(NSString *)requestWithGroup {
|
||||
NSDictionary *data = [self getDataForRequest:requestWithGroup];
|
||||
NSDictionary *post = [data objectForKey:@"post"];
|
||||
NSArray *files = [data objectForKey:@"files"];
|
||||
|
||||
if (files != nil && [files count] > 0) {
|
||||
NSString *channelId = [post objectForKey:@"channel_id"];
|
||||
NSDictionary *credentials = [self getCredentialsForRequest:requestWithGroup];
|
||||
if(error != nil) {
|
||||
NSLog(@"completition ERROR %@", [error userInfo]);
|
||||
NSLog(@"invalidating session %@", requestWithGroup);
|
||||
[session invalidateAndCancel];
|
||||
} else if (requestWithGroup != nil) {
|
||||
NSString *appGroupId = [self getAppGroupIdFromRequestIdentifier:requestWithGroup];
|
||||
NSURL *requestUrl = [[task originalRequest] URL];
|
||||
|
||||
if (credentials == nil) {
|
||||
return;
|
||||
}
|
||||
NSDictionary *data = [self getDataForRequest:requestWithGroup];
|
||||
NSArray *files = [data objectForKey:@"files"];
|
||||
NSMutableArray *fileIds = [data objectForKey:@"file_ids"];
|
||||
|
||||
NSString *serverUrl = [credentials objectForKey:@"url"];
|
||||
NSString *token = [credentials objectForKey:@"token"];
|
||||
NSURLSession *session = [self createSessionForRequestRequest:requestWithGroup];
|
||||
|
||||
for (id file in files) {
|
||||
NSURL *filePath = [NSURL fileURLWithPath:[file objectForKey:@"filePath"]];
|
||||
NSString *fileName = [file objectForKey:@"filename"];
|
||||
|
||||
NSError *err;
|
||||
NSURL *tempContainerURL = [self tempContainerURL:appGroupId];
|
||||
NSURL *destinationURL = [tempContainerURL URLByAppendingPathComponent: fileName];
|
||||
BOOL bVal = [[NSFileManager defaultManager] copyItemAtURL:filePath toURL:destinationURL error:&err];
|
||||
|
||||
NSCharacterSet *allowedCharacters = [NSCharacterSet URLQueryAllowedCharacterSet];
|
||||
NSString *encodedFilename = [[file objectForKey:@"filename"] stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacters];
|
||||
NSString *url = [serverUrl stringByAppendingString:@"/api/v4/files"];
|
||||
NSString *queryString = [NSString stringWithFormat:@"?channel_id=%@&filename=%@", channelId, encodedFilename];
|
||||
NSURL *filesUrl = [NSURL URLWithString:[url stringByAppendingString:queryString]];
|
||||
NSMutableURLRequest *uploadRequest = [NSMutableURLRequest requestWithURL:filesUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:120.0];
|
||||
[uploadRequest setHTTPMethod:@"POST"];
|
||||
[uploadRequest setValue:[@"Bearer " stringByAppendingString:token] forHTTPHeaderField:@"Authorization"];
|
||||
[uploadRequest setValue:@"application/json" forHTTPHeaderField:@"Accept"];
|
||||
|
||||
NSURLSessionUploadTask *task = [session uploadTaskWithRequest:uploadRequest fromFile:destinationURL];
|
||||
NSLog(@"Executing file request %@", fileName);
|
||||
[task resume];
|
||||
if ([[requestUrl absoluteString] containsString:@"files"] &&
|
||||
[files count] == [fileIds count]) {
|
||||
[self sendPostRequestForId:requestWithGroup];
|
||||
[[self.bucket bucketByName:appGroupId] removeObjectForKey:requestWithGroup];
|
||||
}
|
||||
} else {
|
||||
[self sendPostRequestForId:requestWithGroup];
|
||||
NSLog(@"SOMETHING ELSE");
|
||||
}
|
||||
}
|
||||
|
||||
-(void)sendPostRequestForId:(NSString *)requestWithGroup {
|
||||
NSDictionary *data = [self getDataForRequest:requestWithGroup];
|
||||
NSDictionary *credentials = [self getCredentialsForRequest:requestWithGroup];
|
||||
|
||||
if (credentials == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *serverUrl = [credentials objectForKey:@"url"];
|
||||
NSString *token = [credentials objectForKey:@"token"];
|
||||
NSMutableDictionary *post = [[data objectForKey:@"post"] mutableCopy];
|
||||
NSArray *fileIds = [data objectForKey:@"file_ids"];
|
||||
|
||||
if (fileIds != nil && [fileIds count] > 0) {
|
||||
[post setObject:fileIds forKey:@"file_ids"];
|
||||
}
|
||||
|
||||
NSError *error;
|
||||
NSData *postData = [NSJSONSerialization dataWithJSONObject:post options:NSJSONWritingPrettyPrinted error:&error];
|
||||
|
||||
if (error == nil) {
|
||||
NSString* postAsString = [[NSString alloc] initWithData:postData encoding:NSUTF8StringEncoding];
|
||||
|
||||
NSURL *createUrl = [NSURL URLWithString:[serverUrl stringByAppendingString:@"/api/v4/posts"]];
|
||||
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:createUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:5.0];
|
||||
[request setHTTPMethod:@"POST"];
|
||||
[request setValue:[@"Bearer " stringByAppendingString:token] forHTTPHeaderField:@"Authorization"];
|
||||
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
|
||||
[request setValue:@"application/json; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
|
||||
[request setHTTPBody:[postAsString dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
NSURLSessionConfiguration* config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
|
||||
NSURLSession *createSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
|
||||
NSURLSessionDataTask *createTask = [createSession dataTaskWithRequest:request];
|
||||
NSLog(@"Executing post request");
|
||||
[createTask resume];
|
||||
-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
|
||||
if (self.savedCompletionHandler) {
|
||||
self.savedCompletionHandler();
|
||||
self.savedCompletionHandler = nil;
|
||||
}
|
||||
}
|
||||
|
||||
-(NSURL*)tempContainerURL:(NSString*)appGroupId {
|
||||
NSFileManager *manager = [NSFileManager defaultManager];
|
||||
NSURL *containerURL = [manager containerURLForSecurityApplicationGroupIdentifier:appGroupId];
|
||||
NSURL *tempDirectoryURL = [containerURL URLByAppendingPathComponent:@"shareTempItems"];
|
||||
if (![manager fileExistsAtPath:[tempDirectoryURL path]]) {
|
||||
NSError *err;
|
||||
[manager createDirectoryAtURL:tempDirectoryURL withIntermediateDirectories:YES attributes:nil error:&err];
|
||||
if (err) {
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
return tempDirectoryURL;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -52,10 +52,12 @@ RCT_EXPORT_METHOD(close:(NSDictionary *)data appGroupId:(NSString *)appGroupId)
|
|||
if (data != nil) {
|
||||
NSString *requestId = [data objectForKey:@"requestId"];
|
||||
NSString *useBackgroundUpload = [data objectForKey:@"useBackgroundUpload"];
|
||||
BOOL isBackgroundUpload = useBackgroundUpload ? [useBackgroundUpload boolValue] : NO;
|
||||
NSString *certificateName = [data objectForKey:@"certificate"];
|
||||
BOOL tryToUploadInTheBackgound = useBackgroundUpload ? [useBackgroundUpload boolValue] : NO;
|
||||
|
||||
if (isBackgroundUpload) {
|
||||
if (tryToUploadInTheBackgound) {
|
||||
NSString *requestWithGroup = [NSString stringWithFormat:@"%@|%@", requestId, appGroupId];
|
||||
[[SessionManager sharedSession] setRequestWithGroup:requestWithGroup certificateName:certificateName];
|
||||
[[SessionManager sharedSession] setDataForRequest:data forRequestWithGroup:requestWithGroup];
|
||||
[[SessionManager sharedSession] createPostForRequest:requestWithGroup];
|
||||
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ module.exports = ['./node_modules/react-native/Libraries/ART/ARTSerializablePath
|
|||
'./node_modules/app/actions/views/thread.js',
|
||||
'./node_modules/app/actions/views/typing.js',
|
||||
'./node_modules/app/app.js',
|
||||
'./node_modules/app/fetch_preconfig.js',
|
||||
'./node_modules/app/components/announcement_banner/announcement_banner.js',
|
||||
'./node_modules/app/components/announcement_banner/index.js',
|
||||
'./node_modules/app/components/at_mention/at_mention.js',
|
||||
|
|
|
|||
|
|
@ -569,6 +569,7 @@ export default class ExtensionPost extends PureComponent {
|
|||
|
||||
if (currentUserId && authenticated) {
|
||||
await this.emmAuthenticationIfNeeded();
|
||||
const certificate = await mattermostBucket.getPreference('cert', Config.AppGroupId);
|
||||
|
||||
try {
|
||||
// Check to see if the use still belongs to the channel
|
||||
|
|
@ -584,6 +585,7 @@ export default class ExtensionPost extends PureComponent {
|
|||
post,
|
||||
requestId: generateId().replace(/-/g, ''),
|
||||
useBackgroundUpload: this.useBackgroundUpload,
|
||||
certificate,
|
||||
};
|
||||
|
||||
this.setState({sending: true});
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import DeviceInfo from 'react-native-device-info';
|
|||
|
||||
import Config from 'assets/config';
|
||||
import {getTranslations} from 'app/i18n';
|
||||
import 'app/fetch_preconfig';
|
||||
|
||||
import {captureExceptionWithoutState, initializeSentry, LOGGER_EXTENSION} from 'app/utils/sentry';
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue