diff --git a/android/app/build.gradle b/android/app/build.gradle
index 3175692cf..23b2e4ed6 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -113,7 +113,7 @@ android {
applicationId "com.mattermost.rnbeta"
minSdkVersion 21
targetSdkVersion 23
- versionCode 130
+ versionCode 131
versionName "1.11.0"
ndk {
abiFilters "armeabi-v7a", "x86"
diff --git a/app/components/sidebars/main/main_sidebar.js b/app/components/sidebars/main/main_sidebar.js
index fbbfb12ec..95182077b 100644
--- a/app/components/sidebars/main/main_sidebar.js
+++ b/app/components/sidebars/main/main_sidebar.js
@@ -178,6 +178,20 @@ export default class ChannelSidebar extends Component {
this.closeChannelDrawer();
+ if (!channel) {
+ const utils = require('app/utils/general');
+ const {intl} = this.context;
+
+ const unableToJoinMessage = {
+ id: 'mobile.open_unknown_channel.error',
+ defaultMessage: "We couldn't join the channel. Please reset the cache and try again.",
+ };
+ const erroMessage = {};
+
+ utils.alertErrorWithFallback(intl, erroMessage, unableToJoinMessage);
+ return;
+ }
+
setChannelLoading(channel.id !== currentChannelId);
setChannelDisplayName(channel.display_name);
EventEmitter.emit('switch_channel', channel, currentChannelId);
diff --git a/app/screens/image_preview/downloader.android.js b/app/screens/image_preview/downloader.android.js
index a61788b48..9a0ff9e24 100644
--- a/app/screens/image_preview/downloader.android.js
+++ b/app/screens/image_preview/downloader.android.js
@@ -17,7 +17,7 @@ import {Client4} from 'mattermost-redux/client';
import {DeviceTypes} from 'app/constants/';
import FormattedText from 'app/components/formatted_text';
-import {isDocument, isVideo} from 'app/utils/file';
+import {getLocalFilePathFromFile, isDocument, isVideo} from 'app/utils/file';
import {emptyFunction} from 'app/utils/general';
const {DOCUMENTS_PATH, VIDEOS_PATH} = DeviceTypes;
@@ -89,7 +89,7 @@ export default class Downloader extends PureComponent {
ToastAndroid.show(started, ToastAndroid.SHORT);
onDownloadStart();
- const dest = `${RNFetchBlob.fs.dirs.DownloadDir}/${data.id}-${file.caption}`;
+ let dest = `${RNFetchBlob.fs.dirs.DownloadDir}/${data.id}-${file.caption}`;
let downloadFile = true;
if (data.localPath) {
@@ -100,15 +100,16 @@ export default class Downloader extends PureComponent {
await RNFetchBlob.fs.cp(data.localPath, dest);
}
} else if (isVideo(data)) {
- const path = `${VIDEOS_PATH}/${data.id}-${file.caption}`;
+ const path = getLocalFilePathFromFile(VIDEOS_PATH, file);
const exists = await RNFetchBlob.fs.exists(path);
if (exists) {
downloadFile = false;
+ dest = getLocalFilePathFromFile(RNFetchBlob.fs.dirs.DownloadDir, file);
await RNFetchBlob.fs.cp(path, dest);
}
} else if (isDocument(data)) {
- const path = `${DOCUMENTS_PATH}/${data.id}-${file.caption}`;
+ const path = getLocalFilePathFromFile(DOCUMENTS_PATH, file);
const exists = await RNFetchBlob.fs.exists(path);
if (exists) {
diff --git a/app/screens/image_preview/downloader.ios.js b/app/screens/image_preview/downloader.ios.js
index a7f584913..84612a267 100644
--- a/app/screens/image_preview/downloader.ios.js
+++ b/app/screens/image_preview/downloader.ios.js
@@ -13,6 +13,7 @@ import {Client4} from 'mattermost-redux/client';
import FormattedText from 'app/components/formatted_text';
import mattermostBucket from 'app/mattermost_bucket';
+import {getLocalFilePathFromFile} from 'app/utils/file';
import {emptyFunction} from 'app/utils/general';
import LocalConfig from 'assets/config';
@@ -23,12 +24,12 @@ export default class Downloader extends PureComponent {
static propTypes = {
deviceHeight: PropTypes.number.isRequired,
deviceWidth: PropTypes.number.isRequired,
+ downloadPath: PropTypes.string,
file: PropTypes.object.isRequired,
onDownloadCancel: PropTypes.func,
onDownloadSuccess: PropTypes.func,
prompt: PropTypes.bool,
show: PropTypes.bool,
- downloadPath: PropTypes.string,
saveToCameraRoll: PropTypes.bool,
};
@@ -305,7 +306,7 @@ export default class Downloader extends PureComponent {
}
}
- options.path = `${downloadPath}/${data.id}-${file.caption}`;
+ options.path = getLocalFilePathFromFile(downloadPath, file);
} else {
options.fileCache = true;
options.appendExt = data.extension;
@@ -356,7 +357,7 @@ export default class Downloader extends PureComponent {
} catch (error) {
// cancellation throws so we need to catch
if (downloadPath) {
- RNFetchBlob.fs.unlink(`${downloadPath}/${data.id}-${file.caption}`);
+ RNFetchBlob.fs.unlink(getLocalFilePathFromFile(downloadPath, file));
}
if (error.message !== 'cancelled' && this.mounted) {
this.showDownloadFailedAlert();
diff --git a/app/screens/image_preview/image_preview.js b/app/screens/image_preview/image_preview.js
index 5bd403d2d..c7b50bdec 100644
--- a/app/screens/image_preview/image_preview.js
+++ b/app/screens/image_preview/image_preview.js
@@ -25,19 +25,17 @@ import Gallery from 'react-native-image-gallery';
import EventEmitter from 'mattermost-redux/utils/event_emitter';
-import {DeviceTypes} from 'app/constants/';
import FileAttachmentDocument from 'app/components/file_attachment_list/file_attachment_document';
import FileAttachmentIcon from 'app/components/file_attachment_list/file_attachment_icon';
-import {NavigationTypes, PermissionTypes} from 'app/constants';
-import {isDocument, isVideo} from 'app/utils/file';
+import ProgressiveImage from 'app/components/progressive_image';
+import {DeviceTypes, NavigationTypes, PermissionTypes} from 'app/constants';
+import {getLocalFilePathFromFile, isDocument, isVideo} from 'app/utils/file';
import {emptyFunction} from 'app/utils/general';
import {calculateDimensions} from 'app/utils/images';
import Downloader from './downloader';
import VideoPreview from './video_preview';
-import ProgressiveImage from 'app/components/progressive_image';
-
const {VIDEOS_PATH} = DeviceTypes;
const {View: AnimatedView} = Animated;
const AnimatedSafeAreaView = Animated.createAnimatedComponent(SafeAreaView);
@@ -455,11 +453,10 @@ export default class ImagePreview extends PureComponent {
saveVideoIOS = () => {
const file = this.getCurrentFile();
- const {data} = file;
if (this.refs.downloader) {
EventEmitter.emit(NavigationTypes.NAVIGATION_CLOSE_MODAL);
- this.refs.downloader.saveVideo(`${VIDEOS_PATH}/${data.id}-${file.caption}`);
+ this.refs.downloader.saveVideo(getLocalFilePathFromFile(VIDEOS_PATH, file));
}
};
@@ -540,7 +537,7 @@ export default class ImagePreview extends PureComponent {
}
if (isVideo(file.data)) {
- const path = `${VIDEOS_PATH}/${file.data.id}-${file.caption}`;
+ const path = getLocalFilePathFromFile(VIDEOS_PATH, file);
const exist = await RNFetchBlob.fs.exists(path);
if (exist) {
items.push({
diff --git a/app/screens/image_preview/video_preview.js b/app/screens/image_preview/video_preview.js
index 75b4702d5..ba545585a 100644
--- a/app/screens/image_preview/video_preview.js
+++ b/app/screens/image_preview/video_preview.js
@@ -18,6 +18,7 @@ import EventEmitter from 'mattermost-redux/utils/event_emitter';
import VideoControls, {PLAYER_STATE} from 'app/components/video_controls';
import {DeviceTypes} from 'app/constants/';
+import {getLocalFilePathFromFile} from 'app/utils/file';
import Downloader from './downloader.ios';
@@ -70,7 +71,7 @@ export default class VideoPreview extends PureComponent {
async initializeComponent() {
const {file} = this.props;
const prefix = Platform.OS === 'android' ? 'file:/' : '';
- const path = `${VIDEOS_PATH}/${file.data.id}-${file.caption}`;
+ const path = getLocalFilePathFromFile(VIDEOS_PATH, file);
const exist = await RNFetchBlob.fs.exists(`${prefix}${path}`);
if (exist) {
@@ -90,7 +91,7 @@ export default class VideoPreview extends PureComponent {
onDownloadSuccess = () => {
const {file} = this.props;
- const path = file.data.localPath || `${VIDEOS_PATH}/${file.data.id}-${file.caption}`;
+ const path = file.data.localPath || getLocalFilePathFromFile(VIDEOS_PATH, file);
this.setState({showDownloader: false, path});
};
diff --git a/app/utils/file.js b/app/utils/file.js
index 51e6fc4c0..fc6435c71 100644
--- a/app/utils/file.js
+++ b/app/utils/file.js
@@ -215,3 +215,11 @@ function populateMaps() {
}
});
}
+
+export function getLocalFilePathFromFile(dir, file) {
+ if (dir && file && file.caption && file.data && file.data.id) {
+ return `${dir}/${file.data.id}-${decodeURIComponent(file.caption).replace(/\s+/g, '-')}`;
+ }
+
+ return null;
+}
diff --git a/assets/base/i18n/en.json b/assets/base/i18n/en.json
index 06201695f..b5245db94 100644
--- a/assets/base/i18n/en.json
+++ b/assets/base/i18n/en.json
@@ -2527,6 +2527,7 @@
"mobile.offlineIndicator.offline": "Cannot connect to the server",
"mobile.open_dm.error": "We couldn't open a direct message with {displayName}. Please check your connection and try again.",
"mobile.open_gm.error": "We couldn't open a group message with those users. Please check your connection and try again.",
+ "mobile.open_unknown_channel.error": "Unable to join the channel. Please reset the cache and try again.",
"mobile.post.cancel": "Cancel",
"mobile.post.delete_question": "Are you sure you want to delete this post?",
"mobile.post.delete_title": "Delete Post",
diff --git a/ios/Mattermost.xcodeproj/project.pbxproj b/ios/Mattermost.xcodeproj/project.pbxproj
index bfded788e..5170a8032 100644
--- a/ios/Mattermost.xcodeproj/project.pbxproj
+++ b/ios/Mattermost.xcodeproj/project.pbxproj
@@ -2531,7 +2531,7 @@
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
- CURRENT_PROJECT_VERSION = 130;
+ CURRENT_PROJECT_VERSION = 131;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;
@@ -2581,7 +2581,7 @@
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
- CURRENT_PROJECT_VERSION = 130;
+ CURRENT_PROJECT_VERSION = 131;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;
diff --git a/ios/Mattermost/Info.plist b/ios/Mattermost/Info.plist
index 66f92eabf..8a333494e 100644
--- a/ios/Mattermost/Info.plist
+++ b/ios/Mattermost/Info.plist
@@ -34,7 +34,7 @@
CFBundleVersion
- 130
+ 131
ITSAppUsesNonExemptEncryption
LSRequiresIPhoneOS
diff --git a/ios/MattermostShare/Info.plist b/ios/MattermostShare/Info.plist
index abac4fcbd..35d8a8aeb 100644
--- a/ios/MattermostShare/Info.plist
+++ b/ios/MattermostShare/Info.plist
@@ -23,7 +23,7 @@
CFBundleShortVersionString
1.11.0
CFBundleVersion
- 130
+ 131
NSAppTransportSecurity
NSAllowsArbitraryLoads
diff --git a/ios/MattermostTests/Info.plist b/ios/MattermostTests/Info.plist
index 987b24f91..231e058bd 100644
--- a/ios/MattermostTests/Info.plist
+++ b/ios/MattermostTests/Info.plist
@@ -19,6 +19,6 @@
CFBundleSignature
????
CFBundleVersion
- 130
+ 131
diff --git a/package-lock.json b/package-lock.json
index c44ff0ad8..53bc4c968 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14928,9 +14928,9 @@
}
},
"react-native-video": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-2.0.0.tgz",
- "integrity": "sha1-8z+m+35+PJOrV4eUTO/Vi/c1WGc=",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-3.2.0.tgz",
+ "integrity": "sha512-LLqyV9xK67FQTcQDpYruyRODlkQdE59uLExGoXjBngBHrf0q/R13yYaLk3G4CU2Bz+bi3cVzzI6E+q03eNeVYQ==",
"requires": {
"keymirror": "0.1.1",
"prop-types": "^15.5.10"
diff --git a/package.json b/package.json
index 196c317a3..799440f74 100644
--- a/package.json
+++ b/package.json
@@ -52,7 +52,7 @@
"react-native-tableview": "2.1.0",
"react-native-tooltip": "5.2.0",
"react-native-vector-icons": "4.6.0",
- "react-native-video": "2.0.0",
+ "react-native-video": "3.2.0",
"react-native-youtube": "enahum/react-native-youtube#3f395b620ae4e05a3f1c6bdeef3a1158e78daadc",
"react-navigation": "1.5.11",
"react-redux": "5.0.7",
diff --git a/share_extension/ios/extension_post.js b/share_extension/ios/extension_post.js
index 396559147..0fff7983b 100644
--- a/share_extension/ios/extension_post.js
+++ b/share_extension/ios/extension_post.js
@@ -250,7 +250,7 @@ export default class ExtensionPost extends PureComponent {
const fullPath = item.value;
const filePath = decodeURIComponent(fullPath.replace('file://', ''));
const fileSize = await RNFetchBlob.fs.stat(filePath);
- const filename = fullPath.replace(/^.*[\\/]/, '');
+ const filename = decodeURIComponent(fullPath.replace(/^.*[\\/]/, ''));
const extension = filename.split('.').pop();
if (this.useBackgroundUpload) {