Merge pull request #1981 from mattermost/release-1.11

Release 1.11 into Master
This commit is contained in:
Elias Nahum 2018-08-07 14:41:53 -04:00 committed by GitHub
commit fcddcfcf73
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 1449 additions and 136 deletions

View file

@ -32,7 +32,8 @@
"expect": true,
"it": true,
"jest": true,
"test": true
"test": true,
"__DEV__": true
},
"rules": {
"array-bracket-spacing": [2, "never"],

View file

@ -113,7 +113,7 @@ android {
applicationId "com.mattermost.rnbeta"
minSdkVersion 21
targetSdkVersion 23
versionCode 128
versionCode 129
versionName "1.11.0"
ndk {
abiFilters "armeabi-v7a", "x86"

View file

@ -66,7 +66,7 @@ public class MainApplication extends NavigationApplication implements INotificat
new JailMonkeyPackage(),
new RNFetchBlobPackage(),
new MattermostPackage(this),
new RNSentryPackage(this),
new RNSentryPackage(),
new ReactNativeExceptionHandlerPackage(),
new ReactNativeYouTube(),
new ReactVideoPackage(),

View file

@ -6,6 +6,7 @@ import android.net.Uri;
import android.os.Build;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.content.ContentUris;
import android.content.ContentResolver;
import android.os.Environment;
@ -95,15 +96,33 @@ public class RealPathUtil {
public static String getPathFromSavingTempFile(Context context, final Uri uri) {
File tmpFile;
String fileName = null;
// Try and get the filename from the Uri
try {
String fileName = uri.getLastPathSegment();
Cursor returnCursor =
context.getContentResolver().query(uri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
returnCursor.moveToFirst();
fileName = returnCursor.getString(nameIndex);
} catch (Exception e) {
// just continue to get the filename with the last segment of the path
}
try {
if (fileName == null) {
fileName = uri.getLastPathSegment().toString().trim();
}
File cacheDir = new File(context.getCacheDir(), "mmShare");
if (!cacheDir.exists()) {
cacheDir.mkdirs();
}
String mimeType = getMimeType(uri.getPath());
tmpFile = File.createTempFile("tmp", fileName, cacheDir);
tmpFile = new File(cacheDir, fileName);
tmpFile.createNewFile();
ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(uri, "r");

View file

@ -259,8 +259,11 @@ export function selectInitialChannel(teamId) {
const isGMVisible = lastChannel && lastChannel.type === General.GM_CHANNEL &&
isGroupChannelVisible(myPreferences, lastChannel);
if (lastChannelId && myMembers[lastChannelId] &&
(lastChannel.team_id === teamId || isDMVisible || isGMVisible)) {
if (
myMembers[lastChannelId] &&
lastChannel &&
(lastChannel.team_id === teamId || isDMVisible || isGMVisible)
) {
handleSelectChannel(lastChannelId)(dispatch, getState);
markChannelAsRead(lastChannelId)(dispatch, getState);
return;

View file

@ -6,7 +6,7 @@ import {GeneralTypes} from 'mattermost-redux/action_types';
import {autoUpdateTimezone} from 'mattermost-redux/actions/timezone';
import {Client4} from 'mattermost-redux/client';
import {getConfig, getLicense} from 'mattermost-redux/selectors/entities/general';
import {getCurrentUserId} from 'mattermost-redux/selectors/entities/users';
import {getCurrentUserId, getSessions} from 'mattermost-redux/selectors/entities/users';
import {ViewTypes} from 'app/constants';
import {app} from 'app/mattermost';
@ -73,15 +73,25 @@ export function getSession() {
const {credentials} = state.entities.general;
const token = credentials && credentials.token;
if (currentUserId && token) {
const session = await Client4.getSessions(currentUserId, token);
if (Array.isArray(session) && session[0]) {
const s = session[0];
return s.expires_at;
}
if (!currentUserId || !token) {
return 0;
}
return false;
let sessions;
try {
sessions = await getSessions(currentUserId);
} catch (e) {
console.warn('Failed to get current session', e); // eslint-disable-line no-console
return 0;
}
if (!Array.isArray(sessions)) {
return 0;
}
const session = sessions.find((s) => s.token === token);
return session && session.expires_at ? session.expires_at : 0;
};
}

View file

@ -55,10 +55,15 @@ export function loadFromPushNotification(notification) {
const {data} = notification;
const {currentTeamId, teams, myMembers: myTeamMembers} = state.entities.teams;
const {currentChannelId, channels} = state.entities.channels;
const channelId = data ? data.channel_id : '';
// when the notification does not have a team id is because its from a DM or GM
const teamId = data.team_id || currentTeamId;
let channelId = '';
let teamId = currentTeamId;
if (data) {
channelId = data.channel_id;
// when the notification does not have a team id is because its from a DM or GM
teamId = data.team_id || currentTeamId;
}
// load any missing data
const loading = [];
@ -97,7 +102,9 @@ export function purgeOfflineStore() {
return {type: General.OFFLINE_STORE_PURGE};
}
export function createPost(post) {
// A non-optimistic version of the createPost action in mattermost-redux with the file handling
// removed since it's not needed.
export function createPostForNotificationReply(post) {
return (dispatch, getState) => {
const state = getState();
const currentUserId = state.entities.users.currentUserId;

View file

@ -161,6 +161,7 @@ export default class App {
if (!currentUserId) {
return;
}
const username = `${deviceToken}, ${currentUserId}`;
const password = `${token},${url}`;
@ -172,7 +173,11 @@ export default class App {
// Only save to keychain if the url and token are set
if (url && token) {
setGenericPassword(username, password);
try {
setGenericPassword(username, password);
} catch (e) {
console.warn('could not set credentials', e); //eslint-disable-line no-console
}
}
};
@ -254,7 +259,13 @@ export default class App {
if (this.token && this.url) {
screen = 'Channel';
tracker.initialLoad = Date.now();
dispatch(loadMe());
try {
dispatch(loadMe());
} catch (e) {
// Fall through since we should have a previous version of the current user because we have a token
console.warn('Failed to load current user when starting on Channel screen', e); // eslint-disable-line no-console
}
}
switch (screen) {

View file

@ -304,14 +304,24 @@ export default class CombinedSystemMessage extends React.PureComponent {
);
}
renderMessage(postType, userIds, actorId, style) {
return (
<React.Fragment key={postType + actorId}>
{this.renderFormattedMessage(postType, userIds, actorId, {baseText: style.baseText, linkText: style.linkText})}
</React.Fragment>
);
}
render() {
const {
currentUserId,
messageData,
theme,
} = this.props;
const style = getStyleSheet(theme);
const content = [];
const removedUserIds = [];
for (const message of messageData) {
const {
postType,
@ -319,23 +329,29 @@ export default class CombinedSystemMessage extends React.PureComponent {
} = message;
let userIds = message.userIds;
if (!this.props.showJoinLeave && actorId !== this.props.currentUserId) {
const affectsCurrentUser = userIds.indexOf(this.props.currentUserId) !== -1;
if (!this.props.showJoinLeave && actorId !== currentUserId) {
const affectsCurrentUser = userIds.indexOf(currentUserId) !== -1;
if (affectsCurrentUser) {
// Only show the message that the current user was added, etc
userIds = [this.props.currentUserId];
userIds = [currentUserId];
} else {
// Not something the current user did or was affected by
continue;
}
}
content.push(
<React.Fragment key={postType + actorId}>
{this.renderFormattedMessage(postType, userIds, actorId, {baseText: style.baseText, linkText: style.linkText})}
</React.Fragment>
);
if (postType === REMOVE_FROM_CHANNEL) {
removedUserIds.push(...userIds);
continue;
}
content.push(this.renderMessage(postType, userIds, actorId, style));
}
if (removedUserIds.length > 0) {
const uniqueRemovedUserIds = removedUserIds.filter((id, index, arr) => arr.indexOf(id) === index);
content.push(this.renderMessage(REMOVE_FROM_CHANNEL, uniqueRemovedUserIds, currentUserId, style));
}
return (

View file

@ -15,7 +15,7 @@ function makeMapStateToProps() {
const getProfilesByIdsAndUsernames = makeGetProfilesByIdsAndUsernames();
return (state, ownProps) => {
const currentUser = getCurrentUser(state);
const currentUser = getCurrentUser(state) || {};
const {allUserIds, allUsernames} = ownProps;
return {
currentUserId: currentUser.id,

View file

@ -0,0 +1,153 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`LoadingPlaceholder should match snapshot 1`] = `
ShallowWrapper {
"length": 1,
Symbol(enzyme.__root__): [Circular],
Symbol(enzyme.__unrendered__): <LoadingPlaceholder />,
Symbol(enzyme.__renderer__): Object {
"batchedUpdates": [Function],
"getNode": [Function],
"render": [Function],
"simulateEvent": [Function],
"unmount": [Function],
},
Symbol(enzyme.__node__): Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"accessible": true,
"allowFontScaling": true,
"children": Array [
<InjectIntl(FormattedText)
defaultMessage="Loading"
id="loading_screen.loading"
/>,
<AnimatedEllipsis
animationDelay={300}
minOpacity={0.4}
numberOfDots={3}
style={
Object {
"fontSize": 28,
"letterSpacing": -3,
"lineHeight": 30,
"marginLeft": 5,
}
}
/>,
],
"ellipsizeMode": "tail",
},
"ref": null,
"rendered": Array [
Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"defaultMessage": "Loading",
"id": "loading_screen.loading",
},
"ref": null,
"rendered": null,
"type": [Function],
},
Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"animationDelay": 300,
"minOpacity": 0.4,
"numberOfDots": 3,
"style": Object {
"fontSize": 28,
"letterSpacing": -3,
"lineHeight": 30,
"marginLeft": 5,
},
},
"ref": null,
"rendered": null,
"type": [Function],
},
],
"type": [Function],
},
Symbol(enzyme.__nodes__): Array [
Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"accessible": true,
"allowFontScaling": true,
"children": Array [
<InjectIntl(FormattedText)
defaultMessage="Loading"
id="loading_screen.loading"
/>,
<AnimatedEllipsis
animationDelay={300}
minOpacity={0.4}
numberOfDots={3}
style={
Object {
"fontSize": 28,
"letterSpacing": -3,
"lineHeight": 30,
"marginLeft": 5,
}
}
/>,
],
"ellipsizeMode": "tail",
},
"ref": null,
"rendered": Array [
Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"defaultMessage": "Loading",
"id": "loading_screen.loading",
},
"ref": null,
"rendered": null,
"type": [Function],
},
Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"animationDelay": 300,
"minOpacity": 0.4,
"numberOfDots": 3,
"style": Object {
"fontSize": 28,
"letterSpacing": -3,
"lineHeight": 30,
"marginLeft": 5,
},
},
"ref": null,
"rendered": null,
"type": [Function],
},
],
"type": [Function],
},
],
Symbol(enzyme.__options__): Object {
"adapter": ReactSixteenAdapter {
"options": Object {
"enableComponentDidUpdateOnSetState": true,
},
},
},
}
`;

View file

@ -0,0 +1,28 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import AnimatedEllipsis from 'react-native-animated-ellipsis';
import FormattedText from 'app/components/formatted_text';
import {Text} from 'react-native';
const LoadingPlaceholder = () => (
<Text>
<FormattedText
id='loading_screen.loading'
defaultMessage='Loading'
/>
<AnimatedEllipsis
minOpacity={0.4}
style={{
fontSize: 28,
lineHeight: 30,
letterSpacing: -3,
marginLeft: 5,
}}
/>
</Text>
);
export default LoadingPlaceholder;

View file

@ -0,0 +1,19 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import React from 'react';
import {configure, shallow} from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({adapter: new Adapter()});
import LoadingPlaceholder from './index.js';
describe('LoadingPlaceholder', () => {
test('should match snapshot', () => {
const wrapper = shallow(
<LoadingPlaceholder/>
);
expect(wrapper).toMatchSnapshot();
});
});

View file

@ -22,7 +22,7 @@ function makeMapStateToProps() {
return function mapStateToProps(state, ownProps) {
const post = getPost(state, ownProps.postId);
const channelId = post ? post.channel_id : '';
const channel = getChannel(state, channelId);
const channel = getChannel(state, channelId) || {};
const teamId = channel.team_id;
const channelIsArchived = channel.delete_at !== 0;

View file

@ -121,7 +121,9 @@ ShallowWrapper {
},
]
}
/>
>
<LoadingPlaceholder />
</Text>
</Component>
</Component>
</TouchableHighlight>,
@ -200,7 +202,9 @@ ShallowWrapper {
},
]
}
/>
>
<LoadingPlaceholder />
</Text>
</Component>
</Component>,
"delayPressOut": 100,
@ -271,7 +275,9 @@ ShallowWrapper {
},
]
}
/>
>
<LoadingPlaceholder />
</Text>
</Component>,
],
"style": Array [
@ -334,7 +340,9 @@ ShallowWrapper {
},
]
}
/>,
>
<LoadingPlaceholder />
</Text>,
undefined,
],
"style": Array [
@ -382,7 +390,7 @@ ShallowWrapper {
"props": Object {
"accessible": true,
"allowFontScaling": true,
"children": undefined,
"children": <LoadingPlaceholder />,
"ellipsizeMode": "tail",
"numberOfLines": 1,
"style": Array [
@ -402,7 +410,15 @@ ShallowWrapper {
],
},
"ref": null,
"rendered": null,
"rendered": Object {
"instance": null,
"key": undefined,
"nodeType": "function",
"props": Object {},
"ref": null,
"rendered": null,
"type": [Function],
},
"type": [Function],
},
undefined,
@ -496,7 +512,9 @@ ShallowWrapper {
},
]
}
/>
>
<LoadingPlaceholder />
</Text>
</Component>
</Component>
</TouchableHighlight>,
@ -575,7 +593,9 @@ ShallowWrapper {
},
]
}
/>
>
<LoadingPlaceholder />
</Text>
</Component>
</Component>,
"delayPressOut": 100,
@ -646,7 +666,9 @@ ShallowWrapper {
},
]
}
/>
>
<LoadingPlaceholder />
</Text>
</Component>,
],
"style": Array [
@ -709,7 +731,9 @@ ShallowWrapper {
},
]
}
/>,
>
<LoadingPlaceholder />
</Text>,
undefined,
],
"style": Array [
@ -757,7 +781,7 @@ ShallowWrapper {
"props": Object {
"accessible": true,
"allowFontScaling": true,
"children": undefined,
"children": <LoadingPlaceholder />,
"ellipsizeMode": "tail",
"numberOfLines": 1,
"style": Array [
@ -777,7 +801,15 @@ ShallowWrapper {
],
},
"ref": null,
"rendered": null,
"rendered": Object {
"instance": null,
"key": undefined,
"nodeType": "function",
"props": Object {},
"ref": null,
"rendered": null,
"type": [Function],
},
"type": [Function],
},
undefined,
@ -818,3 +850,854 @@ ShallowWrapper {
},
}
`;
exports[`ChannelItem should match snapshot for no displayName 1`] = `
ShallowWrapper {
"length": 1,
Symbol(enzyme.__root__): [Circular],
Symbol(enzyme.__unrendered__): <ChannelItem
channelId="channel_id"
currentChannelId="current_channel_id"
displayName=""
fake={false}
isArchived={false}
isChannelMuted={false}
isMyUser={true}
isUnread={true}
mentions={0}
navigator={
Object {
"push": [Function],
}
}
onSelectChannel={[Function]}
shouldHideChannel={false}
showUnreadForMsgs={true}
status="online"
teammateDeletedAt={0}
theme={
Object {
"sidebarText": "#aaa",
"sidebarTextActiveBorder": "#aaa",
"sidebarTextActiveColor": "#aaa",
"sidebarTextHoverBg": "#aaa",
}
}
type="O"
unreadMsgs={1}
/>,
Symbol(enzyme.__renderer__): Object {
"batchedUpdates": [Function],
"getNode": [Function],
"render": [Function],
"simulateEvent": [Function],
"unmount": [Function],
},
Symbol(enzyme.__node__): Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"children": <TouchableHighlight
activeOpacity={0.85}
delayPressOut={100}
onLongPress={[Function]}
onPress={[Function]}
underlayColor="rgba(170,170,170,0.5)"
>
<Component
style={
Array [
Object {
"flex": 1,
"flexDirection": "row",
"height": 44,
},
undefined,
]
}
>
<Component
style={
Array [
Object {
"alignItems": "center",
"flex": 1,
"flexDirection": "row",
"paddingLeft": 16,
},
undefined,
]
}
>
<ChannelIcon
channelId="channel_id"
isActive={false}
isArchived={false}
isInfo={false}
isUnread={true}
membersCount={1}
size={16}
status="online"
teammateDeletedAt={0}
theme={
Object {
"sidebarText": "#aaa",
"sidebarTextActiveBorder": "#aaa",
"sidebarTextActiveColor": "#aaa",
"sidebarTextHoverBg": "#aaa",
}
}
type="O"
/>
<Text
accessible={true}
allowFontScaling={true}
ellipsizeMode="tail"
numberOfLines={1}
style={
Array [
Object {
"color": "rgba(170,170,170,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"textAlignVertical": "center",
},
Object {
"color": undefined,
},
]
}
>
<LoadingPlaceholder />
</Text>
</Component>
</Component>
</TouchableHighlight>,
},
"ref": [Function],
"rendered": Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"activeOpacity": 0.85,
"children": <Component
style={
Array [
Object {
"flex": 1,
"flexDirection": "row",
"height": 44,
},
undefined,
]
}
>
<Component
style={
Array [
Object {
"alignItems": "center",
"flex": 1,
"flexDirection": "row",
"paddingLeft": 16,
},
undefined,
]
}
>
<ChannelIcon
channelId="channel_id"
isActive={false}
isArchived={false}
isInfo={false}
isUnread={true}
membersCount={1}
size={16}
status="online"
teammateDeletedAt={0}
theme={
Object {
"sidebarText": "#aaa",
"sidebarTextActiveBorder": "#aaa",
"sidebarTextActiveColor": "#aaa",
"sidebarTextHoverBg": "#aaa",
}
}
type="O"
/>
<Text
accessible={true}
allowFontScaling={true}
ellipsizeMode="tail"
numberOfLines={1}
style={
Array [
Object {
"color": "rgba(170,170,170,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"textAlignVertical": "center",
},
Object {
"color": undefined,
},
]
}
>
<LoadingPlaceholder />
</Text>
</Component>
</Component>,
"delayPressOut": 100,
"onLongPress": [Function],
"onPress": [Function],
"underlayColor": "rgba(170,170,170,0.5)",
},
"ref": null,
"rendered": Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"children": Array [
undefined,
<Component
style={
Array [
Object {
"alignItems": "center",
"flex": 1,
"flexDirection": "row",
"paddingLeft": 16,
},
undefined,
]
}
>
<ChannelIcon
channelId="channel_id"
isActive={false}
isArchived={false}
isInfo={false}
isUnread={true}
membersCount={1}
size={16}
status="online"
teammateDeletedAt={0}
theme={
Object {
"sidebarText": "#aaa",
"sidebarTextActiveBorder": "#aaa",
"sidebarTextActiveColor": "#aaa",
"sidebarTextHoverBg": "#aaa",
}
}
type="O"
/>
<Text
accessible={true}
allowFontScaling={true}
ellipsizeMode="tail"
numberOfLines={1}
style={
Array [
Object {
"color": "rgba(170,170,170,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"textAlignVertical": "center",
},
Object {
"color": undefined,
},
]
}
>
<LoadingPlaceholder />
</Text>
</Component>,
],
"style": Array [
Object {
"flex": 1,
"flexDirection": "row",
"height": 44,
},
undefined,
],
},
"ref": null,
"rendered": Array [
undefined,
Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"children": Array [
<ChannelIcon
channelId="channel_id"
isActive={false}
isArchived={false}
isInfo={false}
isUnread={true}
membersCount={1}
size={16}
status="online"
teammateDeletedAt={0}
theme={
Object {
"sidebarText": "#aaa",
"sidebarTextActiveBorder": "#aaa",
"sidebarTextActiveColor": "#aaa",
"sidebarTextHoverBg": "#aaa",
}
}
type="O"
/>,
<Text
accessible={true}
allowFontScaling={true}
ellipsizeMode="tail"
numberOfLines={1}
style={
Array [
Object {
"color": "rgba(170,170,170,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"textAlignVertical": "center",
},
Object {
"color": undefined,
},
]
}
>
<LoadingPlaceholder />
</Text>,
undefined,
],
"style": Array [
Object {
"alignItems": "center",
"flex": 1,
"flexDirection": "row",
"paddingLeft": 16,
},
undefined,
],
},
"ref": null,
"rendered": Array [
Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"channelId": "channel_id",
"isActive": false,
"isArchived": false,
"isInfo": false,
"isUnread": true,
"membersCount": 1,
"size": 16,
"status": "online",
"teammateDeletedAt": 0,
"theme": Object {
"sidebarText": "#aaa",
"sidebarTextActiveBorder": "#aaa",
"sidebarTextActiveColor": "#aaa",
"sidebarTextHoverBg": "#aaa",
},
"type": "O",
},
"ref": null,
"rendered": null,
"type": [Function],
},
Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"accessible": true,
"allowFontScaling": true,
"children": <LoadingPlaceholder />,
"ellipsizeMode": "tail",
"numberOfLines": 1,
"style": Array [
Object {
"color": "rgba(170,170,170,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"textAlignVertical": "center",
},
Object {
"color": undefined,
},
],
},
"ref": null,
"rendered": Object {
"instance": null,
"key": undefined,
"nodeType": "function",
"props": Object {},
"ref": null,
"rendered": null,
"type": [Function],
},
"type": [Function],
},
undefined,
],
"type": [Function],
},
],
"type": [Function],
},
"type": [Function],
},
"type": [Function],
},
Symbol(enzyme.__nodes__): Array [
Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"children": <TouchableHighlight
activeOpacity={0.85}
delayPressOut={100}
onLongPress={[Function]}
onPress={[Function]}
underlayColor="rgba(170,170,170,0.5)"
>
<Component
style={
Array [
Object {
"flex": 1,
"flexDirection": "row",
"height": 44,
},
undefined,
]
}
>
<Component
style={
Array [
Object {
"alignItems": "center",
"flex": 1,
"flexDirection": "row",
"paddingLeft": 16,
},
undefined,
]
}
>
<ChannelIcon
channelId="channel_id"
isActive={false}
isArchived={false}
isInfo={false}
isUnread={true}
membersCount={1}
size={16}
status="online"
teammateDeletedAt={0}
theme={
Object {
"sidebarText": "#aaa",
"sidebarTextActiveBorder": "#aaa",
"sidebarTextActiveColor": "#aaa",
"sidebarTextHoverBg": "#aaa",
}
}
type="O"
/>
<Text
accessible={true}
allowFontScaling={true}
ellipsizeMode="tail"
numberOfLines={1}
style={
Array [
Object {
"color": "rgba(170,170,170,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"textAlignVertical": "center",
},
Object {
"color": undefined,
},
]
}
>
<LoadingPlaceholder />
</Text>
</Component>
</Component>
</TouchableHighlight>,
},
"ref": [Function],
"rendered": Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"activeOpacity": 0.85,
"children": <Component
style={
Array [
Object {
"flex": 1,
"flexDirection": "row",
"height": 44,
},
undefined,
]
}
>
<Component
style={
Array [
Object {
"alignItems": "center",
"flex": 1,
"flexDirection": "row",
"paddingLeft": 16,
},
undefined,
]
}
>
<ChannelIcon
channelId="channel_id"
isActive={false}
isArchived={false}
isInfo={false}
isUnread={true}
membersCount={1}
size={16}
status="online"
teammateDeletedAt={0}
theme={
Object {
"sidebarText": "#aaa",
"sidebarTextActiveBorder": "#aaa",
"sidebarTextActiveColor": "#aaa",
"sidebarTextHoverBg": "#aaa",
}
}
type="O"
/>
<Text
accessible={true}
allowFontScaling={true}
ellipsizeMode="tail"
numberOfLines={1}
style={
Array [
Object {
"color": "rgba(170,170,170,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"textAlignVertical": "center",
},
Object {
"color": undefined,
},
]
}
>
<LoadingPlaceholder />
</Text>
</Component>
</Component>,
"delayPressOut": 100,
"onLongPress": [Function],
"onPress": [Function],
"underlayColor": "rgba(170,170,170,0.5)",
},
"ref": null,
"rendered": Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"children": Array [
undefined,
<Component
style={
Array [
Object {
"alignItems": "center",
"flex": 1,
"flexDirection": "row",
"paddingLeft": 16,
},
undefined,
]
}
>
<ChannelIcon
channelId="channel_id"
isActive={false}
isArchived={false}
isInfo={false}
isUnread={true}
membersCount={1}
size={16}
status="online"
teammateDeletedAt={0}
theme={
Object {
"sidebarText": "#aaa",
"sidebarTextActiveBorder": "#aaa",
"sidebarTextActiveColor": "#aaa",
"sidebarTextHoverBg": "#aaa",
}
}
type="O"
/>
<Text
accessible={true}
allowFontScaling={true}
ellipsizeMode="tail"
numberOfLines={1}
style={
Array [
Object {
"color": "rgba(170,170,170,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"textAlignVertical": "center",
},
Object {
"color": undefined,
},
]
}
>
<LoadingPlaceholder />
</Text>
</Component>,
],
"style": Array [
Object {
"flex": 1,
"flexDirection": "row",
"height": 44,
},
undefined,
],
},
"ref": null,
"rendered": Array [
undefined,
Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"children": Array [
<ChannelIcon
channelId="channel_id"
isActive={false}
isArchived={false}
isInfo={false}
isUnread={true}
membersCount={1}
size={16}
status="online"
teammateDeletedAt={0}
theme={
Object {
"sidebarText": "#aaa",
"sidebarTextActiveBorder": "#aaa",
"sidebarTextActiveColor": "#aaa",
"sidebarTextHoverBg": "#aaa",
}
}
type="O"
/>,
<Text
accessible={true}
allowFontScaling={true}
ellipsizeMode="tail"
numberOfLines={1}
style={
Array [
Object {
"color": "rgba(170,170,170,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"textAlignVertical": "center",
},
Object {
"color": undefined,
},
]
}
>
<LoadingPlaceholder />
</Text>,
undefined,
],
"style": Array [
Object {
"alignItems": "center",
"flex": 1,
"flexDirection": "row",
"paddingLeft": 16,
},
undefined,
],
},
"ref": null,
"rendered": Array [
Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"channelId": "channel_id",
"isActive": false,
"isArchived": false,
"isInfo": false,
"isUnread": true,
"membersCount": 1,
"size": 16,
"status": "online",
"teammateDeletedAt": 0,
"theme": Object {
"sidebarText": "#aaa",
"sidebarTextActiveBorder": "#aaa",
"sidebarTextActiveColor": "#aaa",
"sidebarTextHoverBg": "#aaa",
},
"type": "O",
},
"ref": null,
"rendered": null,
"type": [Function],
},
Object {
"instance": null,
"key": undefined,
"nodeType": "class",
"props": Object {
"accessible": true,
"allowFontScaling": true,
"children": <LoadingPlaceholder />,
"ellipsizeMode": "tail",
"numberOfLines": 1,
"style": Array [
Object {
"color": "rgba(170,170,170,0.4)",
"flex": 1,
"fontSize": 14,
"fontWeight": "600",
"height": "100%",
"lineHeight": 44,
"paddingRight": 40,
"textAlignVertical": "center",
},
Object {
"color": undefined,
},
],
},
"ref": null,
"rendered": Object {
"instance": null,
"key": undefined,
"nodeType": "function",
"props": Object {},
"ref": null,
"rendered": null,
"type": [Function],
},
"type": [Function],
},
undefined,
],
"type": [Function],
},
],
"type": [Function],
},
"type": [Function],
},
"type": [Function],
},
],
Symbol(enzyme.__options__): Object {
"adapter": ReactSixteenAdapter {
"options": Object {
"enableComponentDidUpdateOnSetState": true,
},
},
"context": Object {
"intl": Object {
"formatMessage": [MockFunction] {
"calls": Array [
Array [
Object {
"defaultMessage": "{displayName} (you)",
"id": "channel_header.directchannel.you",
},
Object {
"displayname": "",
},
],
],
},
},
},
},
}
`;

View file

@ -14,6 +14,7 @@ import {intlShape} from 'react-intl';
import Badge from 'app/components/badge';
import ChannelIcon from 'app/components/channel_icon';
import LoadingPlaceholder from 'app/components/loading_placeholder';
import {preventDoubleTap} from 'app/utils/tap';
import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
@ -113,14 +114,6 @@ export default class ChannelItem extends PureComponent {
const {intl} = this.context;
let channelDisplayName = displayName;
if (isMyUser) {
channelDisplayName = intl.formatMessage({
id: 'channel_header.directchannel.you',
defaultMessage: '{displayName} (you)',
}, {displayname: displayName});
}
const style = getStyleSheet(theme);
const isActive = channelId === currentChannelId;
@ -140,6 +133,20 @@ export default class ChannelItem extends PureComponent {
extraTextStyle = style.textUnread;
}
let channelDisplayName = displayName;
if (isMyUser) {
channelDisplayName = intl.formatMessage({
id: 'channel_header.directchannel.you',
defaultMessage: '{displayName} (you)',
}, {displayname: displayName});
}
if (!channelDisplayName) {
channelDisplayName = (
<LoadingPlaceholder/>
);
}
let badge;
if (mentions) {
badge = (

View file

@ -45,4 +45,18 @@ describe('ChannelItem', () => {
expect(wrapper).toMatchSnapshot();
});
test('should match snapshot for no displayName', () => {
const props = {
...baseProps,
displayName: '',
};
const wrapper = shallow(
<ChannelItem {...props}/>,
{context: {intl: {formatMessage: jest.fn()}}},
);
expect(wrapper).toMatchSnapshot();
});
});

View file

@ -10,9 +10,10 @@ import {
getMyChannelMember,
shouldHideDefaultChannel,
} from 'mattermost-redux/selectors/entities/channels';
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
import {getTheme, getTeammateNameDisplaySetting} from 'mattermost-redux/selectors/entities/preferences';
import {getCurrentUserId, getUser} from 'mattermost-redux/selectors/entities/users';
import {isChannelMuted} from 'mattermost-redux/utils/channel_utils';
import {displayUsername} from 'mattermost-redux/utils/user_utils';
import ChannelItem from './channel_item';
@ -26,12 +27,15 @@ function makeMapStateToProps() {
let isMyUser = false;
let teammateDeletedAt = 0;
let displayName = channel.display_name;
if (channel.type === General.DM_CHANNEL && channel.teammate_id) {
isMyUser = channel.teammate_id === currentUserId;
const teammate = getUser(state, channel.teammate_id);
if (teammate && teammate.delete_at) {
teammateDeletedAt = teammate.delete_at;
}
const teammateNameDisplay = getTeammateNameDisplaySetting(state);
displayName = displayUsername(teammate, teammateNameDisplay, false);
}
const currentChannelId = getCurrentChannelId(state);
@ -57,10 +61,9 @@ function makeMapStateToProps() {
if (member && member.notify_props) {
showUnreadForMsgs = member.notify_props.mark_unread !== General.MENTION;
}
return {
currentChannelId,
displayName: channel.display_name,
displayName,
fake: channel.fake,
isChannelMuted: isChannelMuted(member),
isMyUser,

View file

@ -9,7 +9,7 @@ import {getCurrentTeam, getMyTeamsCount, getChannelDrawerBadgeCount} from 'matte
import SwitchTeamsButton from './switch_teams_button';
function mapStateToProps(state) {
const team = getCurrentTeam(state);
const team = getCurrentTeam(state) || {};
return {
currentTeamId: team.id,

View file

@ -13,7 +13,7 @@ import {getDimensions} from 'app/selectors/device';
import SettingsSidebar from './settings_sidebar';
function mapStateToProps(state) {
const currentUser = getCurrentUser(state);
const currentUser = getCurrentUser(state) || {};
const status = getStatusForUserId(state, currentUser.id);
return {

View file

@ -42,6 +42,7 @@ Client4.doFetchWithResponse = async (url, options) => {
}
throw {
message: 'Received invalid response from the server.',
intl: {
id: 'mobile.request.invalid_response',
defaultMessage: 'Received invalid response from the server.',

View file

@ -142,8 +142,13 @@ const handleLogout = () => {
const restartApp = async () => {
Navigation.dismissModal({animationType: 'none'});
await store.dispatch(loadConfigAndLicense());
await store.dispatch(loadMe());
try {
await store.dispatch(loadConfigAndLicense());
await store.dispatch(loadMe());
} catch (e) {
console.warn('Failed to load initial data while restarting', e); // eslint-disable-line no-console
}
launchChannel();
};

View file

@ -56,7 +56,7 @@ export default {
}
},
isTrustedDevice: () => {
if (__DEV__) { //eslint-disable-line no-undef
if (__DEV__) {
return true;
}

View file

@ -55,7 +55,7 @@ export default {
}
},
isTrustedDevice: () => {
if (__DEV__) { //eslint-disable-line no-undef
if (__DEV__) {
return true;
}

View file

@ -15,7 +15,7 @@ import NotificationSettings from './notification_settings';
function mapStateToProps(state) {
const config = getConfig(state);
const currentUser = getCurrentUser(state);
const currentUser = getCurrentUser(state) || {};
const currentUserStatus = getStatusForUserId(state, currentUser.id);
const serverVersion = state.entities.general.serverVersion;
const enableAutoResponder = isMinimumServerVersion(serverVersion, 4, 9) && config.ExperimentalEnableAutomaticReplies === 'true';

View file

@ -187,12 +187,18 @@ class SSO extends PureComponent {
setStoreFromLocalData({url: this.props.serverUrl, token}).
then(handleSuccessfulLogin).
then(getSession).
then(this.goToLoadTeam);
then(this.goToLoadTeam).
catch(this.onLoadEndError);
}
});
}
};
onLoadEndError = (e) => {
console.warn('Failed to set store from local data', e); // eslint-disable-line no-console
this.setState({error: e.message});
}
renderLoading = () => {
return <Loading/>;
};

View file

@ -16,7 +16,7 @@ import Timezone from './timezone';
function mapStateToProps(state) {
const timezones = getTimezones(state);
const currentUser = getCurrentUser(state);
const currentUser = getCurrentUser(state) || {};
const userTimezone = getUserTimezone(state, currentUser.id);
return {

View file

@ -16,27 +16,35 @@ import {close as closeWebSocket} from 'mattermost-redux/actions/websocket';
import {purgeOfflineStore} from 'app/actions/views/root';
import {
captureException,
captureJSException,
initializeSentry,
LOGGER_JAVASCRIPT,
LOGGER_NATIVE,
} from 'app/utils/sentry';
import {app, store} from 'app/mattermost';
const errorHandler = (e, isFatal) => {
console.warn('Handling Javascript error ', e); // eslint-disable-line no-console
if (__DEV__ && !e && !isFatal) {
// react-native-exception-handler redirects console.error to call this, and React calls
// console.error without an exception when prop type validation fails, so this ends up
// being called with no arguments when the error handler is enabled in dev mode.
return;
}
console.warn('Handling Javascript error', e, isFatal); // eslint-disable-line no-console
captureJSException(e, isFatal, store);
const {dispatch} = store;
captureException(e, LOGGER_JAVASCRIPT, store);
const translations = app.getTranslations();
dispatch(closeWebSocket());
if (Client4.getUrl()) {
dispatch(logError(e));
}
if (isFatal) {
if (isFatal && e instanceof Error) {
const translations = app.getTranslations();
Alert.alert(
translations['mobile.error_handler.title'],
translations['mobile.error_handler.description'],

View file

@ -16,7 +16,7 @@ import EventEmitter from 'mattermost-redux/utils/event_emitter';
import {ViewTypes} from 'app/constants';
import {retryGetPostsAction} from 'app/actions/views/channel';
import {
createPost,
createPostForNotificationReply,
loadFromPushNotification,
} from 'app/actions/views/root';
@ -136,15 +136,20 @@ export const onPushNotificationReply = (data, text, badge, completed) => {
}
retryGetPostsAction(getPosts(data.channel_id), dispatch, getState);
dispatch(createPost(post)).then(() => {
dispatch(markChannelAsRead(data.channel_id));
dispatch(createPostForNotificationReply(post)).
then(() => {
dispatch(markChannelAsRead(data.channel_id));
if (badge >= 0) {
PushNotifications.setApplicationIconBadgeNumber(badge);
}
if (badge >= 0) {
PushNotifications.setApplicationIconBadgeNumber(badge);
}
app.setReplyNotificationData(null);
}).then(completed);
app.setReplyNotificationData(null);
}).
then(completed).
catch((e) => {
console.warn('Failed to send reply to push notification', e); // eslint-disable-line no-console
});
} else {
app.setReplyNotificationData({
data,

View file

@ -17,6 +17,9 @@ export const LOGGER_JAVASCRIPT_WARNING = 'javascript_warning';
export const LOGGER_NATIVE = 'native';
export const LOGGER_REDUX = 'redux';
export const BREADCRUMB_UNCAUGHT_APP_ERROR = 'uncaught-app-error';
export const BREADCRUMB_UNCAUGHT_NON_ERROR = 'uncaught-non-error';
export function initializeSentry() {
if (!Config.SentryEnabled) {
// Still allow Sentry to configure itself in case other code tries to call it
@ -44,12 +47,28 @@ function getDsn() {
return '';
}
export function captureException(error, logger, store) {
if (error && logger && store) {
capture(() => {
Sentry.captureException(error, {logger});
}, store);
export function captureJSException(error, isFatal, store) {
if (!error || !store) {
console.warn('captureJSException called with missing arguments', error, store); // eslint-disable-line no-console
return;
}
if (error instanceof Error) {
captureException(error, LOGGER_JAVASCRIPT, store);
} else {
captureNonErrorAsBreadcrumb(error, isFatal);
}
}
export function captureException(error, logger, store) {
if (!error || !logger || !store) {
console.warn('captureException called with missing arguments', error, logger, store); // eslint-disable-line no-console
return;
}
capture(() => {
Sentry.captureException(error, {logger});
}, store);
}
export function captureExceptionWithoutState(err, logger) {
@ -74,6 +93,54 @@ export function captureMessage(message, logger, store) {
}
}
export function captureNonErrorAsBreadcrumb(obj, isFatal) {
if (!obj || typeof obj !== 'object') {
console.warning('Invalid object passed to captureNonErrorAsBreadcrumb', obj); // eslint-disable-line no-console
return;
}
const isAppError = Boolean(obj.server_error_id);
const breadcrumb = {
category: isAppError ? BREADCRUMB_UNCAUGHT_APP_ERROR : BREADCRUMB_UNCAUGHT_NON_ERROR,
data: {
isFatal: String(isFatal),
},
level: 'warn',
};
if (obj.message) {
breadcrumb.message = obj.message;
} else if (obj.intl && obj.intl.defaultMessage) {
breadcrumb.message = obj.intl.defaultMessage;
} else {
breadcrumb.message = 'no message provided';
}
if (obj.server_error_id) {
breadcrumb.data.server_error_id = obj.server_error_id;
}
if (obj.status_code) {
breadcrumb.data.status_code = obj.status_code;
}
if (obj.url) {
const match = (/^(?:https?:\/\/)[^/]+(\/.*)$/);
if (match && match.length >= 2) {
breadcrumb.data.url = match[1];
}
}
try {
Sentry.captureBreadcrumb(breadcrumb);
} catch (e) {
// Do nothing since this is only here to make sure we don't crash when handling an exception
console.warn('Failed to capture breadcrumb of non-error', e); // eslint-disable-line no-console
}
}
// Wrapper function to any calls to Sentry so that we can gather any necessary extra data
// before sending.
function capture(captureFunc, store) {

View file

@ -2432,6 +2432,7 @@
"mobile.error_handler.description": "\nClick relaunch to open the app again. After restart, you can report the problem from the settings menu.\n",
"mobile.error_handler.title": "Unexpected error occurred",
"mobile.extension.authentication_required": "Authentication required: Please first login using the app.",
"mobile.extension.file_error": "There was an error reading the file to be shared.\nPlease try again.",
"mobile.extension.file_limit": "Sharing is limited to a maximum of 5 files.",
"mobile.extension.max_file_size": "File attachments shared in Mattermost must be less than {size}.",
"mobile.extension.permission": "Mattermost needs access to the device storage to share files.",

View file

@ -16,7 +16,6 @@ if (Platform.OS === 'android') {
/*
/!* eslint-disable no-console *!/
/!* eslint-disable no-undef *!/
if (__DEV__) {
const modules = require.getModules();
const moduleIds = Object.keys(modules);

View file

@ -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 = 128;
CURRENT_PROJECT_VERSION = 129;
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 = 128;
CURRENT_PROJECT_VERSION = 129;
DEAD_CODE_STRIPPING = NO;
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
ENABLE_BITCODE = NO;

View file

@ -34,7 +34,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>128</string>
<string>129</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>

View file

@ -23,7 +23,7 @@
<key>CFBundleShortVersionString</key>
<string>1.11.0</string>
<key>CFBundleVersion</key>
<string>128</string>
<string>129</string>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>

View file

@ -19,6 +19,6 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>128</string>
<string>129</string>
</dict>
</plist>

107
package-lock.json generated
View file

@ -2589,27 +2589,34 @@
}
},
"@sentry/cli": {
"version": "1.30.4",
"resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.30.4.tgz",
"integrity": "sha1-TS9PHbMZ8pUCYrjnW21okv4Xeco=",
"version": "1.34.0",
"resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.34.0.tgz",
"integrity": "sha512-j2nYoci4yaA3dIL3Gheai2KKAtMdDGNipq7Ws9vO5SaG6uY0FQ0EXTxY0CAMPiNXdAXWstR5rdxPptyrhxPq7Q==",
"requires": {
"https-proxy-agent": "^2.1.1",
"node-fetch": "^1.7.3",
"https-proxy-agent": "^2.2.1",
"node-fetch": "^2.1.2",
"progress": "2.0.0",
"proxy-from-env": "^1.0.0"
},
"dependencies": {
"node-fetch": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.2.0.tgz",
"integrity": "sha512-OayFWziIxiHY8bCUyLX6sTpDH8Jsbp4FfYd1j1f7vZyfgkcOnAyM4oQR16f8a0s7Gl/viMGRey8eScYk4V4EZA=="
}
}
},
"@sentry/wizard": {
"version": "0.9.6",
"resolved": "https://registry.npmjs.org/@sentry/wizard/-/wizard-0.9.6.tgz",
"integrity": "sha1-84+xsIgZ5rKAMFMOPTZOe2TJRZg=",
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/@sentry/wizard/-/wizard-0.10.3.tgz",
"integrity": "sha512-U+O8l6Kp9CTLJAgr/ChoRH16s/owGTF/dk/veTXgK9wMn+QKit6CEK/LAlah/nqgVx+CBvEVW+h4E8KIcSXMDg==",
"requires": {
"@sentry/cli": "^1.30.1",
"chalk": "^2.3.1",
"glob": "^7.1.2",
"inquirer": "^5.1.0",
"lodash": "^4.17.5",
"open": "^0.0.5",
"opn": "^5.3.0",
"r2": "^2.0.0",
"read-env": "^1.1.1",
"xcode": "^1.0.0",
@ -2669,6 +2676,14 @@
"through": "^2.3.6"
}
},
"opn": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz",
"integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==",
"requires": {
"is-wsl": "^1.1.0"
}
},
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
@ -2701,9 +2716,9 @@
}
},
"yargs": {
"version": "11.0.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-11.0.0.tgz",
"integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==",
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.0.tgz",
"integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==",
"requires": {
"cliui": "^4.0.0",
"decamelize": "^1.1.1",
@ -2788,9 +2803,9 @@
}
},
"agent-base": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.0.tgz",
"integrity": "sha512-c+R/U5X+2zz2+UCrCFv6odQzJdoqI+YecuhnAJLa1zYaMc13zPfwMwZrr91Pd1DYNo/yPRbiM4WVf9whgwFsIg==",
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
"integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
"requires": {
"es6-promisify": "^5.0.0"
}
@ -8378,6 +8393,11 @@
"resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
"integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA=="
},
"is-wsl": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
"integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0="
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
@ -10108,8 +10128,8 @@
}
},
"mattermost-redux": {
"version": "github:mattermost/mattermost-redux#12353bc88f20dde53b6191747790c6a82b8dd527",
"from": "github:mattermost/mattermost-redux#12353bc88f20dde53b6191747790c6a82b8dd527",
"version": "github:mattermost/mattermost-redux#f13022064f1ed24a0d1c085f433490f9d40cfc2f",
"from": "github:mattermost/mattermost-redux#f13022064f1ed24a0d1c085f433490f9d40cfc2f",
"requires": {
"deep-equal": "1.0.1",
"eslint-plugin-header": "1.2.0",
@ -13875,11 +13895,6 @@
"mimic-fn": "^1.0.0"
}
},
"open": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/open/-/open-0.0.5.tgz",
"integrity": "sha1-QsPhjslUZra/DcQvOilFw/DK2Pw="
},
"opn": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/opn/-/opn-3.0.3.tgz",
@ -14325,9 +14340,9 @@
},
"dependencies": {
"node-fetch": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz",
"integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U="
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.2.0.tgz",
"integrity": "sha512-OayFWziIxiHY8bCUyLX6sTpDH8Jsbp4FfYd1j1f7vZyfgkcOnAyM4oQR16f8a0s7Gl/viMGRey8eScYk4V4EZA=="
}
}
},
@ -14399,9 +14414,9 @@
"integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4="
},
"raven-js": {
"version": "3.24.2",
"resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.24.2.tgz",
"integrity": "sha512-Dy/FHDxuo5pXywVf8Nrs5utB2juMATpkxWGqHjVbpFD3m8CaWYLvkB5SEXjWFUZ5GvUsrBVVQ+Dfcp0x6Z7SOg=="
"version": "3.26.4",
"resolved": "https://registry.npmjs.org/raven-js/-/raven-js-3.26.4.tgz",
"integrity": "sha512-5VmC3IWhTQJkaiQaCY0S5V8za4bpUgbbuVT1MkDH7JVqgu8CPQ750XaFF8BVRbLV9F5nvoz7n0UT0CKteDuZAg=="
},
"raw-body": {
"version": "2.3.2",
@ -14626,6 +14641,13 @@
"prop-types": "^15.5.10"
}
},
"react-native-animated-ellipsis": {
"version": "github:sudheerDev/react-native-animated-ellipsis#326167b8e0fa3b6f0422c06be412bf177b725666",
"from": "github:sudheerDev/react-native-animated-ellipsis#326167b8e0fa3b6f0422c06be412bf177b725666",
"requires": {
"prop-types": "^15.5.10"
}
},
"react-native-bottom-sheet": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/react-native-bottom-sheet/-/react-native-bottom-sheet-1.0.3.tgz",
@ -14836,11 +14858,11 @@
"integrity": "sha512-kR6s97ubi1QevRWRC8RQ26NoD6EgLt36caY25GKBt9eZ+l9Qmm2JHti2cEK153cxLIMbD04Xv4+LjDhcNjxh/w=="
},
"react-native-sentry": {
"version": "0.36.0",
"resolved": "https://registry.npmjs.org/react-native-sentry/-/react-native-sentry-0.36.0.tgz",
"integrity": "sha1-HGbXKvVqqAHaqlVpwJ/H0EvqZUQ=",
"version": "0.38.3",
"resolved": "https://registry.npmjs.org/react-native-sentry/-/react-native-sentry-0.38.3.tgz",
"integrity": "sha512-+RsSsLdjSf7MmIOkjoMqIfGTOISG3ym2hcldMaxCfa9HRTVXL7dne4naWEqFbs32C9M6KgXe/RZlp5cDaELLsA==",
"requires": {
"@sentry/wizard": "^0.9.5",
"@sentry/wizard": "^0.10.2",
"raven-js": "^3.24.2"
}
},
@ -15013,11 +15035,18 @@
}
},
"read-env": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/read-env/-/read-env-1.1.1.tgz",
"integrity": "sha512-yzGQb8P7N8zf513nRu+kMiQPJlmADWqS7Qn2pd6aAvRcrMCxuFvRX1KL8KU0ckdWVqoTX7Qpj10U+Dx7cVL/LA==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/read-env/-/read-env-1.2.0.tgz",
"integrity": "sha512-29PX3GtJ9nUWsM8UckqJVdrqJzv8yWPDH3lxk2+CBFN3mJ1gJ42dluzxqmADCG7H0jL26G53G5+CwAqQph2Ctw==",
"requires": {
"camelcase": "^4.1.0"
"camelcase": "5.0.0"
},
"dependencies": {
"camelcase": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz",
"integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA=="
}
}
},
"read-pkg": {
@ -15766,9 +15795,9 @@
}
},
"rxjs": {
"version": "5.5.10",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.10.tgz",
"integrity": "sha512-SRjimIDUHJkon+2hFo7xnvNC4ZEHGzCRwh9P7nzX3zPkCGFEg/tuElrNR7L/rZMagnK2JeH2jQwPRpmyXyLB6A==",
"version": "5.5.11",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.11.tgz",
"integrity": "sha512-3bjO7UwWfA2CV7lmwYMBzj4fQ6Cq+ftHc2MvUe+WMS7wcdJ1LosDWmdjPQanYp2dBRj572p7PeU81JUxHKOcBA==",
"requires": {
"symbol-observable": "1.0.1"
},

View file

@ -15,13 +15,14 @@
"intl": "1.2.5",
"jail-monkey": "1.0.0",
"jsc-android": "216113.0.3",
"mattermost-redux": "github:mattermost/mattermost-redux#12353bc88f20dde53b6191747790c6a82b8dd527",
"mattermost-redux": "github:mattermost/mattermost-redux#f13022064f1ed24a0d1c085f433490f9d40cfc2f",
"mime-db": "1.33.0",
"prop-types": "15.6.1",
"react": "16.3.2",
"react-intl": "2.4.0",
"react-native": "github:enahum/react-native#mm",
"react-native-animatable": "1.2.4",
"react-native-animated-ellipsis": "sudheerDev/react-native-animated-ellipsis.git#326167b8e0fa3b6f0422c06be412bf177b725666",
"react-native-bottom-sheet": "1.0.3",
"react-native-button": "2.3.0",
"react-native-circular-progress": "0.2.0",
@ -45,7 +46,7 @@
"react-native-permissions": "1.1.1",
"react-native-safe-area": "0.2.3",
"react-native-section-list-get-item-layout": "2.2.2",
"react-native-sentry": "0.36.0",
"react-native-sentry": "0.38.3",
"react-native-slider": "0.11.0",
"react-native-status-bar-size": "0.3.3",
"react-native-svg": "6.3.1",

View file

@ -268,10 +268,7 @@ export default class ExtensionPost extends PureComponent {
const text = [];
const files = [];
let totalSize = 0;
this.props.navigation.setParams({
post: this.onPost,
});
let error;
for (let i = 0; i < items.length; i++) {
const item = items[i];
@ -280,8 +277,18 @@ export default class ExtensionPost extends PureComponent {
text.push(item.value);
break;
default: {
let fileSize = {size: 0};
const fullPath = item.value;
const fileSize = await RNFetchBlob.fs.stat(fullPath);
try {
fileSize = await RNFetchBlob.fs.stat(fullPath);
} catch (e) {
const {formatMessage} = this.context.intl;
error = formatMessage({
id: 'mobile.extension.file_error',
defaultMessage: 'There was an error reading the file to be shared.\nPlease try again.',
});
break;
}
let filename = fullPath.replace(/^.*[\\/]/, '');
let extension = filename.split('.').pop();
if (extension === filename) {
@ -305,7 +312,13 @@ export default class ExtensionPost extends PureComponent {
const value = text.join('\n');
this.setState({files, value, hasPermission: true, totalSize});
if (!error) {
this.props.navigation.setParams({
post: this.onPost,
});
}
this.setState({error, files, value, hasPermission: true, totalSize});
}
};
@ -476,7 +489,11 @@ export default class ExtensionPost extends PureComponent {
render() {
const {formatMessage} = this.context.intl;
const {maxFileSize, token, url} = this.props;
const {hasPermission, files, totalSize} = this.state;
const {error, hasPermission, files, totalSize} = this.state;
if (error) {
return this.renderErrorMessage(error);
}
if (token && url) {
if (hasPermission === false) {