Merge pull request #2094 from mattermost/release-1.12
Merge Release 1.12 into master
This commit is contained in:
commit
f1274e4977
36 changed files with 2072 additions and 137 deletions
|
|
@ -113,7 +113,7 @@ android {
|
|||
applicationId "com.mattermost.rnbeta"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 26
|
||||
versionCode 136
|
||||
versionCode 137
|
||||
versionName "1.12.0"
|
||||
multiDexEnabled = true
|
||||
ndk {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {markChannelAsRead, markChannelAsViewed} from 'mattermost-redux/actions/c
|
|||
import {ChannelTypes, TeamTypes} from 'mattermost-redux/action_types';
|
||||
import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
||||
import {getCurrentChannelId} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {RequestStatus} from 'mattermost-redux/constants';
|
||||
|
||||
import {NavigationTypes} from 'app/constants';
|
||||
|
||||
|
|
@ -56,6 +57,8 @@ export function selectDefaultTeam() {
|
|||
|
||||
if (defaultTeam) {
|
||||
handleTeamChange(defaultTeam.id)(dispatch, getState);
|
||||
} else if (state.requests.teams.getTeams.status === RequestStatus.FAILURE || state.requests.teams.getMyTeams.status === RequestStatus.FAILURE) {
|
||||
EventEmitter.emit(NavigationTypes.NAVIGATION_ERROR_TEAMS);
|
||||
} else {
|
||||
EventEmitter.emit(NavigationTypes.NAVIGATION_NO_TEAMS);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ export default class AtMention extends React.PureComponent {
|
|||
};
|
||||
|
||||
getUserDetailsFromMentionName(props) {
|
||||
let mentionName = props.mentionName;
|
||||
let mentionName = props.mentionName.toLowerCase();
|
||||
|
||||
while (mentionName.length > 0) {
|
||||
if (props.usersByUsername.hasOwnProperty(mentionName)) {
|
||||
|
|
|
|||
|
|
@ -24,11 +24,13 @@ export default class ChannelMention extends PureComponent {
|
|||
listHeight: PropTypes.number,
|
||||
matchTerm: PropTypes.string,
|
||||
myChannels: PropTypes.array,
|
||||
myMembers: PropTypes.object,
|
||||
otherChannels: PropTypes.array,
|
||||
onChangeText: PropTypes.func.isRequired,
|
||||
onResultCountChange: PropTypes.func.isRequired,
|
||||
privateChannels: PropTypes.array,
|
||||
publicChannels: PropTypes.array,
|
||||
deletedPublicChannels: PropTypes.instanceOf(Set),
|
||||
requestStatus: PropTypes.string.isRequired,
|
||||
theme: PropTypes.object.isRequired,
|
||||
value: PropTypes.string,
|
||||
|
|
@ -48,7 +50,7 @@ export default class ChannelMention extends PureComponent {
|
|||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const {isSearch, matchTerm, myChannels, otherChannels, privateChannels, publicChannels, requestStatus} = nextProps;
|
||||
const {isSearch, matchTerm, myChannels, otherChannels, privateChannels, publicChannels, requestStatus, myMembers, deletedPublicChannels} = nextProps;
|
||||
|
||||
if ((matchTerm !== this.props.matchTerm && matchTerm === null) || this.state.mentionComplete) {
|
||||
// if the term changes but is null or the mention has been completed we render this component as null
|
||||
|
|
@ -74,7 +76,8 @@ export default class ChannelMention extends PureComponent {
|
|||
|
||||
if (requestStatus !== RequestStatus.STARTED &&
|
||||
(myChannels !== this.props.myChannels || otherChannels !== this.props.otherChannels ||
|
||||
privateChannels !== this.props.privateChannels || publicChannels !== this.props.publicChannels)) {
|
||||
privateChannels !== this.props.privateChannels || publicChannels !== this.props.publicChannels ||
|
||||
myMembers !== this.props.myMembers || deletedPublicChannels !== this.props.deletedPublicChannels)) {
|
||||
// if the request is complete and the term is not null we show the autocomplete
|
||||
const sections = [];
|
||||
if (isSearch) {
|
||||
|
|
@ -82,7 +85,7 @@ export default class ChannelMention extends PureComponent {
|
|||
sections.push({
|
||||
id: 'suggestion.search.public',
|
||||
defaultMessage: 'Public Channels',
|
||||
data: publicChannels,
|
||||
data: publicChannels.filter((cId) => !deletedPublicChannels.has(cId) || myMembers[cId]),
|
||||
key: 'publicChannels',
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {bindActionCreators} from 'redux';
|
|||
import {connect} from 'react-redux';
|
||||
|
||||
import {searchChannels} from 'mattermost-redux/actions/channels';
|
||||
import {getMyChannelMemberships} from 'mattermost-redux/selectors/entities/channels';
|
||||
import {getCurrentTeamId} from 'mattermost-redux/selectors/entities/teams';
|
||||
|
||||
import {
|
||||
|
|
@ -12,6 +13,7 @@ import {
|
|||
filterOtherChannels,
|
||||
filterPublicChannels,
|
||||
filterPrivateChannels,
|
||||
getDeletedPublicChannelsIds,
|
||||
getMatchTermForChannelMention,
|
||||
} from 'app/selectors/autocomplete';
|
||||
import {getTheme} from 'mattermost-redux/selectors/entities/preferences';
|
||||
|
|
@ -38,8 +40,10 @@ function mapStateToProps(state, ownProps) {
|
|||
|
||||
return {
|
||||
myChannels,
|
||||
myMembers: getMyChannelMemberships(state),
|
||||
otherChannels,
|
||||
publicChannels,
|
||||
deletedPublicChannels: getDeletedPublicChannelsIds(state),
|
||||
privateChannels,
|
||||
currentTeamId: getCurrentTeamId(state),
|
||||
matchTerm,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import {Keyboard, StyleSheet} from 'react-native';
|
||||
import {Dimensions, Platform, StyleSheet} from 'react-native';
|
||||
import PropTypes from 'prop-types';
|
||||
import {CalendarList} from 'react-native-calendars';
|
||||
|
||||
|
|
@ -80,8 +80,6 @@ export default class DateSuggestion extends PureComponent {
|
|||
const currentDate = (new Date()).toDateString();
|
||||
const calendarStyle = calendarTheme(theme);
|
||||
|
||||
Keyboard.dismiss();
|
||||
|
||||
return (
|
||||
<CalendarList
|
||||
style={styles.calList}
|
||||
|
|
@ -90,28 +88,55 @@ export default class DateSuggestion extends PureComponent {
|
|||
futureScrollRange={0}
|
||||
scrollingEnabled={true}
|
||||
pagingEnabled={true}
|
||||
hideArrows={true}
|
||||
hideArrows={false}
|
||||
horizontal={true}
|
||||
showScrollIndicator={true}
|
||||
onDayPress={this.completeMention}
|
||||
showWeekNumbers={false}
|
||||
theme={calendarStyle}
|
||||
keyboardShouldPersistTaps='always'
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getDateFontSize = () => {
|
||||
let fontSize = 14;
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
const {height, width} = Dimensions.get('window');
|
||||
if (height < 375 || width < 375) {
|
||||
fontSize = 13;
|
||||
}
|
||||
}
|
||||
|
||||
return fontSize;
|
||||
};
|
||||
|
||||
const calendarTheme = memoizeResult((theme) => ({
|
||||
calendarBackground: theme.centerChannelBg,
|
||||
monthTextColor: changeOpacity(theme.centerChannelColor, 0.8),
|
||||
dayTextColor: theme.centerChannelColor,
|
||||
textSectionTitleColor: changeOpacity(theme.centerChannelColor, 0.25),
|
||||
'stylesheet.day.basic': {
|
||||
base: {
|
||||
width: 22,
|
||||
height: 22,
|
||||
alignItems: 'center',
|
||||
},
|
||||
text: {
|
||||
marginTop: 0,
|
||||
fontSize: getDateFontSize(),
|
||||
fontWeight: '300',
|
||||
color: theme.centerChannelColor,
|
||||
backgroundColor: 'rgba(255, 255, 255, 0)',
|
||||
lineHeight: 23,
|
||||
},
|
||||
today: {
|
||||
backgroundColor: theme.buttonBg,
|
||||
width: 33,
|
||||
height: 33,
|
||||
borderRadius: 16.5,
|
||||
width: 24,
|
||||
height: 24,
|
||||
borderRadius: 12,
|
||||
},
|
||||
todayText: {
|
||||
color: theme.buttonColor,
|
||||
|
|
|
|||
|
|
@ -124,7 +124,6 @@ export default class SlashSuggestion extends Component {
|
|||
|
||||
renderItem = ({item}) => (
|
||||
<SlashSuggestionItem
|
||||
displayName={item.display_name}
|
||||
description={item.auto_complete_desc}
|
||||
hint={item.auto_complete_hint}
|
||||
onPress={this.completeSuggestion}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import {changeOpacity, makeStyleSheetFromTheme} from 'app/utils/theme';
|
|||
|
||||
export default class SlashSuggestionItem extends PureComponent {
|
||||
static propTypes = {
|
||||
displayName: PropTypes.string,
|
||||
description: PropTypes.string,
|
||||
hint: PropTypes.string,
|
||||
onPress: PropTypes.func.isRequired,
|
||||
|
|
@ -27,7 +26,6 @@ export default class SlashSuggestionItem extends PureComponent {
|
|||
|
||||
render() {
|
||||
const {
|
||||
displayName,
|
||||
description,
|
||||
hint,
|
||||
theme,
|
||||
|
|
@ -41,7 +39,7 @@ export default class SlashSuggestionItem extends PureComponent {
|
|||
onPress={this.completeSuggestion}
|
||||
style={style.row}
|
||||
>
|
||||
<Text style={style.suggestionName}>{`/${displayName || trigger} ${hint}`}</Text>
|
||||
<Text style={style.suggestionName}>{`/${trigger} ${hint}`}</Text>
|
||||
<Text style={style.suggestionDescription}>{description}</Text>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -8,13 +8,11 @@ import {
|
|||
Text,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import {displayUsername} from 'mattermost-redux/utils/user_utils';
|
||||
import ProfilePicture from 'app/components/profile_picture';
|
||||
import {makeStyleSheetFromTheme, changeOpacity} from 'app/utils/theme';
|
||||
|
||||
import CustomListRow from 'app/components/custom_list/custom_list_row';
|
||||
|
||||
import {displayUsername} from 'mattermost-redux/utils/user_utils';
|
||||
|
||||
export default class UserListRow extends React.PureComponent {
|
||||
static propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
|
|
@ -58,6 +56,13 @@ export default class UserListRow extends React.PureComponent {
|
|||
}, {username});
|
||||
}
|
||||
|
||||
if (user.delete_at > 0) {
|
||||
usernameDisplay = formatMessage({
|
||||
id: 'more_direct_channels.directchannel.deactivated',
|
||||
defaultMessage: '{displayname} - Deactivated',
|
||||
}, {displayname: usernameDisplay});
|
||||
}
|
||||
|
||||
return (
|
||||
<CustomListRow
|
||||
id={id}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,89 @@
|
|||
// 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';
|
||||
import UserListRow from './user_list_row';
|
||||
|
||||
configure({adapter: new Adapter()});
|
||||
|
||||
jest.mock('react-intl');
|
||||
jest.mock('app/utils/theme', () => {
|
||||
const original = require.requireActual('app/utils/theme');
|
||||
return {
|
||||
...original,
|
||||
changeOpacity: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('rn-fetch-blob', () => ({
|
||||
fs: {
|
||||
dirs: {
|
||||
DocumentDir: () => jest.fn(),
|
||||
CacheDir: () => jest.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
jest.mock('rn-fetch-blob/fs', () => ({
|
||||
dirs: {
|
||||
DocumentDir: () => jest.fn(),
|
||||
CacheDir: () => jest.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('UserListRow', () => {
|
||||
const formatMessage = jest.fn();
|
||||
const baseProps = {
|
||||
id: '123455',
|
||||
isMyUser: false,
|
||||
user: {
|
||||
id: '21345',
|
||||
username: 'user',
|
||||
delete_at: 0,
|
||||
},
|
||||
theme: {},
|
||||
teammateNameDisplay: 'test',
|
||||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallow(
|
||||
<UserListRow {...baseProps}/>,
|
||||
{context: {intl: {formatMessage}}},
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot for deactivated user', () => {
|
||||
const deactivatedUser = {
|
||||
id: '21345',
|
||||
username: 'user',
|
||||
delete_at: 100,
|
||||
};
|
||||
|
||||
const newProps = {
|
||||
...baseProps,
|
||||
user: deactivatedUser,
|
||||
};
|
||||
|
||||
const wrapper = shallow(
|
||||
<UserListRow {...newProps}/>,
|
||||
{context: {intl: {formatMessage}}},
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should match snapshot for currentUser with (you) populated in list', () => {
|
||||
const newProps = {
|
||||
...baseProps,
|
||||
isMyUser: true,
|
||||
};
|
||||
|
||||
const wrapper = shallow(
|
||||
<UserListRow {...newProps}/>,
|
||||
{context: {intl: {formatMessage}}},
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
@ -57,10 +57,10 @@ export default class Emoji extends React.PureComponent {
|
|||
}
|
||||
|
||||
componentWillMount() {
|
||||
const {displayTextOnly, imageUrl} = this.props;
|
||||
const {displayTextOnly, emojiName, imageUrl} = this.props;
|
||||
this.mounted = true;
|
||||
if (!displayTextOnly && imageUrl) {
|
||||
ImageCacheManager.cache(imageUrl, imageUrl, this.setImageUrl);
|
||||
ImageCacheManager.cache(`emoji-${emojiName}`, imageUrl, this.setImageUrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ export default class Emoji extends React.PureComponent {
|
|||
|
||||
if (!displayTextOnly && imageUrl &&
|
||||
imageUrl !== this.props.imageUrl) {
|
||||
ImageCacheManager.cache(imageUrl, imageUrl, this.setImageUrl);
|
||||
ImageCacheManager.cache(`emoji-${emojiName}`, imageUrl, this.setImageUrl);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -27,34 +27,34 @@ export default class OptionsContext extends PureComponent {
|
|||
};
|
||||
}
|
||||
|
||||
handleHide = () => {
|
||||
this.isShowing = false;
|
||||
this.props.toggleSelected(false);
|
||||
};
|
||||
|
||||
handleHideUnderlay = () => {
|
||||
if (!this.isShowing) {
|
||||
this.props.toggleSelected(false, false);
|
||||
this.props.toggleSelected(false);
|
||||
}
|
||||
};
|
||||
|
||||
handleShowUnderlay = () => {
|
||||
this.props.toggleSelected(true, false);
|
||||
};
|
||||
|
||||
handleHide = () => {
|
||||
this.isShowing = false;
|
||||
this.props.toggleSelected(false, this.props.getPostActions().length > 0);
|
||||
};
|
||||
|
||||
handleShow = () => {
|
||||
this.isShowing = this.props.getPostActions().length > 0;
|
||||
this.props.toggleSelected(true, this.isShowing);
|
||||
this.show();
|
||||
this.props.toggleSelected(true);
|
||||
this.isShowing = this.state.actions.length > 0;
|
||||
};
|
||||
|
||||
hide = () => {
|
||||
this.setState({
|
||||
actions: this.props.getPostActions(),
|
||||
});
|
||||
|
||||
if (this.refs.toolTip) {
|
||||
this.refs.toolTip.hideMenu();
|
||||
}
|
||||
|
||||
this.setState({
|
||||
actions: this.props.getPostActions(),
|
||||
});
|
||||
this.isShowing = false;
|
||||
this.props.toggleSelected(false);
|
||||
};
|
||||
|
||||
show = (additionalAction) => {
|
||||
|
|
@ -74,7 +74,7 @@ export default class OptionsContext extends PureComponent {
|
|||
};
|
||||
|
||||
handlePress = () => {
|
||||
this.props.toggleSelected(false, this.props.getPostActions().length > 0);
|
||||
this.props.toggleSelected(false);
|
||||
this.props.onPress();
|
||||
};
|
||||
|
||||
|
|
@ -87,10 +87,9 @@ export default class OptionsContext extends PureComponent {
|
|||
actions={this.state.actions}
|
||||
arrowDirection='down'
|
||||
longPress={true}
|
||||
onHide={this.handleHide}
|
||||
onPress={this.handlePress}
|
||||
underlayColor='transparent'
|
||||
onShow={this.handleShow}
|
||||
onHide={this.handleHide}
|
||||
>
|
||||
{this.props.children}
|
||||
</ToolTip>
|
||||
|
|
|
|||
|
|
@ -110,10 +110,6 @@ export default class Post extends PureComponent {
|
|||
this.props.actions.insertToDraft(`@${username} `);
|
||||
};
|
||||
|
||||
handleEditDisable = () => {
|
||||
this.setState({canEdit: false});
|
||||
};
|
||||
|
||||
handlePostDelete = () => {
|
||||
const {formatMessage} = this.context.intl;
|
||||
const {actions, currentUserId, post} = this.props;
|
||||
|
|
@ -313,9 +309,7 @@ export default class Post extends PureComponent {
|
|||
});
|
||||
|
||||
toggleSelected = (selected) => {
|
||||
if (!getToolTipVisible()) {
|
||||
this.setState({selected});
|
||||
}
|
||||
this.setState({selected});
|
||||
};
|
||||
|
||||
handleCopyText = (text) => {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import {
|
|||
View,
|
||||
} from 'react-native';
|
||||
|
||||
import ProgressiveImage from 'app/components/progressive_image';
|
||||
import ImageCacheManager from 'app/utils/image_cache_manager';
|
||||
import {previewImageAtIndex, calculateDimensions} from 'app/utils/images';
|
||||
import {getNearestPoint} from 'app/utils/opengraph';
|
||||
|
|
@ -96,10 +95,22 @@ export default class PostAttachmentOpenGraph extends PureComponent {
|
|||
});
|
||||
|
||||
if (imageUrl) {
|
||||
ImageCacheManager.cache(null, imageUrl, this.getImageSize);
|
||||
ImageCacheManager.cache(this.getFilename(imageUrl), imageUrl, this.getImageSize);
|
||||
}
|
||||
}
|
||||
|
||||
getFilename = (link) => {
|
||||
let filename = link.substring(link.lastIndexOf('/') + 1, link.indexOf('?') === -1 ? link.length : link.indexOf('?'));
|
||||
const extension = filename.split('.').pop();
|
||||
|
||||
if (extension === filename) {
|
||||
const ext = filename.indexOf('.') === -1 ? '.png' : filename.substring(filename.lastIndexOf('.'));
|
||||
filename = `${filename}${ext}`;
|
||||
}
|
||||
|
||||
return `og-${filename}`;
|
||||
};
|
||||
|
||||
getImageSize = (imageUrl) => {
|
||||
let prefix = '';
|
||||
if (Platform.OS === 'android') {
|
||||
|
|
@ -141,13 +152,7 @@ export default class PostAttachmentOpenGraph extends PureComponent {
|
|||
originalWidth,
|
||||
originalHeight,
|
||||
} = this.state;
|
||||
let filename = link.substring(link.lastIndexOf('/') + 1, link.indexOf('?') === -1 ? link.length : link.indexOf('?'));
|
||||
const extension = filename.split('.').pop();
|
||||
|
||||
if (extension === filename) {
|
||||
const ext = filename.indexOf('.') === -1 ? '.png' : filename.substring(filename.lastIndexOf('.'));
|
||||
filename = `${filename}${ext}`;
|
||||
}
|
||||
const filename = this.getFilename(link);
|
||||
|
||||
const files = [{
|
||||
caption: filename,
|
||||
|
|
@ -175,6 +180,13 @@ export default class PostAttachmentOpenGraph extends PureComponent {
|
|||
const style = getStyleSheet(theme);
|
||||
|
||||
let description = null;
|
||||
let source;
|
||||
if (imageUrl) {
|
||||
source = {
|
||||
uri: imageUrl,
|
||||
};
|
||||
}
|
||||
|
||||
if (openGraphData.description) {
|
||||
description = (
|
||||
<View style={style.flex}>
|
||||
|
|
@ -216,19 +228,22 @@ export default class PostAttachmentOpenGraph extends PureComponent {
|
|||
</View>
|
||||
{description}
|
||||
{hasImage &&
|
||||
<View ref='item'>
|
||||
<TouchableWithoutFeedback
|
||||
onPress={this.handlePreviewImage}
|
||||
style={{width, height}}
|
||||
>
|
||||
<ProgressiveImage
|
||||
ref='image'
|
||||
style={[style.image, {width, height}]}
|
||||
imageUri={imageUrl}
|
||||
resizeMode='contain'
|
||||
/>
|
||||
</TouchableWithoutFeedback>
|
||||
</View>
|
||||
<View
|
||||
ref='item'
|
||||
style={style.imageContainer}
|
||||
>
|
||||
<TouchableWithoutFeedback
|
||||
onPress={this.handlePreviewImage}
|
||||
style={{width, height}}
|
||||
>
|
||||
<Image
|
||||
ref='image'
|
||||
style={[style.image, {width, height}]}
|
||||
source={source}
|
||||
resizeMode='contain'
|
||||
/>
|
||||
</TouchableWithoutFeedback>
|
||||
</View>
|
||||
}
|
||||
</View>
|
||||
);
|
||||
|
|
@ -266,6 +281,9 @@ const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
|||
color: changeOpacity(theme.centerChannelColor, 0.7),
|
||||
marginBottom: 10,
|
||||
},
|
||||
imageContainer: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
image: {
|
||||
borderRadius: 3,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -25,9 +25,11 @@ export default class QuickTextInput extends React.PureComponent {
|
|||
* The string value displayed in this input
|
||||
*/
|
||||
value: PropTypes.string.isRequired,
|
||||
refocusInput: PropTypes.bool,
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
refocusInput: true,
|
||||
delayInputUpdate: false,
|
||||
editable: true,
|
||||
value: '',
|
||||
|
|
@ -57,7 +59,7 @@ export default class QuickTextInput extends React.PureComponent {
|
|||
});
|
||||
}
|
||||
|
||||
this.hadFocus = this.input.isFocused();
|
||||
this.hadFocus = this.input.isFocused() && this.props.refocusInput;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ export default class Root extends PureComponent {
|
|||
EventEmitter.on(ViewTypes.NOTIFICATION_IN_APP, this.handleInAppNotification);
|
||||
EventEmitter.on(ViewTypes.NOTIFICATION_TAPPED, this.handleNotificationTapped);
|
||||
EventEmitter.on(NavigationTypes.NAVIGATION_NO_TEAMS, this.handleNoTeams);
|
||||
EventEmitter.on(NavigationTypes.NAVIGATION_ERROR_TEAMS, this.errorTeamsList);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -44,6 +45,7 @@ export default class Root extends PureComponent {
|
|||
EventEmitter.off(ViewTypes.NOTIFICATION_IN_APP, this.handleInAppNotification);
|
||||
EventEmitter.off(ViewTypes.NOTIFICATION_TAPPED, this.handleNotificationTapped);
|
||||
EventEmitter.off(NavigationTypes.NAVIGATION_NO_TEAMS, this.handleNoTeams);
|
||||
EventEmitter.off(NavigationTypes.NAVIGATION_ERROR_TEAMS, this.errorTeamsList);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -69,11 +71,23 @@ export default class Root extends PureComponent {
|
|||
setTimeout(this.handleNoTeams, 200);
|
||||
return;
|
||||
}
|
||||
this.navigateToTeamsPage('SelectTeam');
|
||||
};
|
||||
|
||||
errorTeamsList = () => {
|
||||
if (!this.refs.provider) {
|
||||
setTimeout(this.errorTeamsList, 200);
|
||||
return;
|
||||
}
|
||||
this.navigateToTeamsPage('ErrorTeamsList');
|
||||
}
|
||||
|
||||
navigateToTeamsPage = (screen) => {
|
||||
const {currentUrl, navigator, theme} = this.props;
|
||||
const {intl} = this.refs.provider.getChildContext();
|
||||
|
||||
let navigatorButtons;
|
||||
let passProps = {theme};
|
||||
if (Platform.OS === 'android') {
|
||||
navigatorButtons = {
|
||||
rightButtons: [{
|
||||
|
|
@ -93,8 +107,16 @@ export default class Root extends PureComponent {
|
|||
};
|
||||
}
|
||||
|
||||
if (screen === 'SelectTeam') {
|
||||
passProps = {
|
||||
...passProps,
|
||||
currentUrl,
|
||||
userWithoutTeams: true,
|
||||
};
|
||||
}
|
||||
|
||||
navigator.resetTo({
|
||||
screen: 'SelectTeam',
|
||||
screen,
|
||||
title: intl.formatMessage({id: 'mobile.routes.selectTeam', defaultMessage: 'Select Team'}),
|
||||
animated: false,
|
||||
backButtonTitle: '',
|
||||
|
|
@ -105,13 +127,9 @@ export default class Root extends PureComponent {
|
|||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
navigatorButtons,
|
||||
passProps: {
|
||||
currentUrl,
|
||||
userWithoutTeams: true,
|
||||
theme,
|
||||
},
|
||||
passProps,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
handleNotificationTapped = async () => {
|
||||
const {navigator} = this.props;
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ export default class SearchBarAndroid extends PureComponent {
|
|||
this.state = {
|
||||
value: props.value,
|
||||
isFocused: false,
|
||||
refocusInput: true,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -93,10 +94,12 @@ export default class SearchBarAndroid extends PureComponent {
|
|||
|
||||
onSearchButtonPress = () => {
|
||||
const {value} = this.props;
|
||||
|
||||
if (value) {
|
||||
this.props.onSearchButtonPress(value);
|
||||
}
|
||||
this.setState({refocusInput: false}, () => {
|
||||
if (value) {
|
||||
this.props.onSearchButtonPress(value);
|
||||
}
|
||||
this.setState({refocusInput: true});
|
||||
});
|
||||
};
|
||||
|
||||
onCancelButtonPress = () => {
|
||||
|
|
@ -215,6 +218,7 @@ export default class SearchBarAndroid extends PureComponent {
|
|||
<QuickTextInput
|
||||
ref='input'
|
||||
blurOnSubmit={blurOnSubmit}
|
||||
refocusInput={this.state.refocusInput}
|
||||
value={this.state.value}
|
||||
autoCapitalize={autoCapitalize}
|
||||
autoCorrect={false}
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ ShallowWrapper {
|
|||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"justifyContent": "center",
|
||||
"marginBottom": 10,
|
||||
"position": "relative",
|
||||
"top": 10,
|
||||
}
|
||||
|
|
@ -291,6 +292,7 @@ ShallowWrapper {
|
|||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"justifyContent": "center",
|
||||
"marginBottom": 10,
|
||||
"position": "relative",
|
||||
"top": 10,
|
||||
},
|
||||
|
|
@ -510,6 +512,7 @@ ShallowWrapper {
|
|||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"justifyContent": "center",
|
||||
"marginBottom": 10,
|
||||
"position": "relative",
|
||||
"top": 10,
|
||||
}
|
||||
|
|
@ -701,6 +704,7 @@ ShallowWrapper {
|
|||
"flex": 1,
|
||||
"flexDirection": "row",
|
||||
"justifyContent": "center",
|
||||
"marginBottom": 10,
|
||||
"position": "relative",
|
||||
"top": 10,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme, showMore) => {
|
|||
flexDirection: 'row',
|
||||
position: 'relative',
|
||||
top: showMore ? -7.5 : 10,
|
||||
marginBottom: 10,
|
||||
},
|
||||
dividerLeft: {
|
||||
backgroundColor: changeOpacity(theme.centerChannelColor, 0.2),
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ const NavigationTypes = keyMirror({
|
|||
NAVIGATION_CLOSE_MODAL: null,
|
||||
NAVIGATION_NO_TEAMS: null,
|
||||
RESTART_APP: null,
|
||||
NAVIGATION_ERROR_TEAMS: null,
|
||||
});
|
||||
|
||||
export default NavigationTypes;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,166 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ErrorTeamsList should match snapshot 1`] = `
|
||||
ShallowWrapper {
|
||||
"length": 1,
|
||||
Symbol(enzyme.__root__): [Circular],
|
||||
Symbol(enzyme.__unrendered__): <ErrorTeamsList
|
||||
actions={
|
||||
Object {
|
||||
"connection": [Function],
|
||||
"loadMe": [Function],
|
||||
"logout": [Function],
|
||||
"selectDefaultTeam": [Function],
|
||||
}
|
||||
}
|
||||
navigator={
|
||||
Object {
|
||||
"setOnNavigatorEvent": [Function],
|
||||
}
|
||||
}
|
||||
theme={Object {}}
|
||||
/>,
|
||||
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": Array [
|
||||
<Connect(StatusBar) />,
|
||||
<FailedNetworkAction
|
||||
errorDescription={
|
||||
Object {
|
||||
"defaultMessage": "Make sure you have an active connection and try again.",
|
||||
"id": "mobile.failed_network_action.shortDescription",
|
||||
}
|
||||
}
|
||||
errorTitle={
|
||||
Object {
|
||||
"defaultMessage": "Team Not Found",
|
||||
"id": "error.team_not_found.title",
|
||||
}
|
||||
}
|
||||
onRetry={[Function]}
|
||||
theme={Object {}}
|
||||
/>,
|
||||
],
|
||||
"style": Object {
|
||||
"backgroundColor": undefined,
|
||||
"flex": 1,
|
||||
},
|
||||
},
|
||||
"ref": null,
|
||||
"rendered": Array [
|
||||
Object {
|
||||
"instance": null,
|
||||
"key": undefined,
|
||||
"nodeType": "class",
|
||||
"props": Object {},
|
||||
"ref": null,
|
||||
"rendered": null,
|
||||
"type": [Function],
|
||||
},
|
||||
Object {
|
||||
"instance": null,
|
||||
"key": undefined,
|
||||
"nodeType": "class",
|
||||
"props": Object {
|
||||
"errorDescription": Object {
|
||||
"defaultMessage": "Make sure you have an active connection and try again.",
|
||||
"id": "mobile.failed_network_action.shortDescription",
|
||||
},
|
||||
"errorTitle": Object {
|
||||
"defaultMessage": "Team Not Found",
|
||||
"id": "error.team_not_found.title",
|
||||
},
|
||||
"onRetry": [Function],
|
||||
"theme": Object {},
|
||||
},
|
||||
"ref": null,
|
||||
"rendered": null,
|
||||
"type": [Function],
|
||||
},
|
||||
],
|
||||
"type": [Function],
|
||||
},
|
||||
Symbol(enzyme.__nodes__): Array [
|
||||
Object {
|
||||
"instance": null,
|
||||
"key": undefined,
|
||||
"nodeType": "class",
|
||||
"props": Object {
|
||||
"children": Array [
|
||||
<Connect(StatusBar) />,
|
||||
<FailedNetworkAction
|
||||
errorDescription={
|
||||
Object {
|
||||
"defaultMessage": "Make sure you have an active connection and try again.",
|
||||
"id": "mobile.failed_network_action.shortDescription",
|
||||
}
|
||||
}
|
||||
errorTitle={
|
||||
Object {
|
||||
"defaultMessage": "Team Not Found",
|
||||
"id": "error.team_not_found.title",
|
||||
}
|
||||
}
|
||||
onRetry={[Function]}
|
||||
theme={Object {}}
|
||||
/>,
|
||||
],
|
||||
"style": Object {
|
||||
"backgroundColor": undefined,
|
||||
"flex": 1,
|
||||
},
|
||||
},
|
||||
"ref": null,
|
||||
"rendered": Array [
|
||||
Object {
|
||||
"instance": null,
|
||||
"key": undefined,
|
||||
"nodeType": "class",
|
||||
"props": Object {},
|
||||
"ref": null,
|
||||
"rendered": null,
|
||||
"type": [Function],
|
||||
},
|
||||
Object {
|
||||
"instance": null,
|
||||
"key": undefined,
|
||||
"nodeType": "class",
|
||||
"props": Object {
|
||||
"errorDescription": Object {
|
||||
"defaultMessage": "Make sure you have an active connection and try again.",
|
||||
"id": "mobile.failed_network_action.shortDescription",
|
||||
},
|
||||
"errorTitle": Object {
|
||||
"defaultMessage": "Team Not Found",
|
||||
"id": "error.team_not_found.title",
|
||||
},
|
||||
"onRetry": [Function],
|
||||
"theme": Object {},
|
||||
},
|
||||
"ref": null,
|
||||
"rendered": null,
|
||||
"type": [Function],
|
||||
},
|
||||
],
|
||||
"type": [Function],
|
||||
},
|
||||
],
|
||||
Symbol(enzyme.__options__): Object {
|
||||
"adapter": ReactSixteenAdapter {
|
||||
"options": Object {
|
||||
"enableComponentDidUpdateOnSetState": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
`;
|
||||
116
app/screens/error_teams_list/error_teams_list.js
Normal file
116
app/screens/error_teams_list/error_teams_list.js
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import React, {PureComponent} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
InteractionManager,
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
import FailedNetworkAction from 'app/components/failed_network_action';
|
||||
import Loading from 'app/components/loading';
|
||||
import StatusBar from 'app/components/status_bar';
|
||||
import {makeStyleSheetFromTheme, setNavigatorStyles} from 'app/utils/theme';
|
||||
|
||||
const errorTitle = {
|
||||
id: 'error.team_not_found.title',
|
||||
defaultMessage: 'Team Not Found',
|
||||
};
|
||||
|
||||
const errorDescription = {
|
||||
id: 'mobile.failed_network_action.shortDescription',
|
||||
defaultMessage: 'Make sure you have an active connection and try again.',
|
||||
};
|
||||
|
||||
export default class ErrorTeamsList extends PureComponent {
|
||||
static propTypes = {
|
||||
actions: PropTypes.shape({
|
||||
loadMe: PropTypes.func.isRequired,
|
||||
connection: PropTypes.func.isRequired,
|
||||
logout: PropTypes.func.isRequired,
|
||||
selectDefaultTeam: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
navigator: PropTypes.object,
|
||||
theme: PropTypes.object,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);
|
||||
|
||||
this.state = {
|
||||
loading: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (this.props.theme !== nextProps.theme) {
|
||||
setNavigatorStyles(this.props.navigator, nextProps.theme);
|
||||
}
|
||||
}
|
||||
|
||||
goToChannelView = () => {
|
||||
const {navigator, theme} = this.props;
|
||||
|
||||
navigator.resetTo({
|
||||
screen: 'Channel',
|
||||
animated: false,
|
||||
navigatorStyle: {
|
||||
navBarHidden: true,
|
||||
statusBarHidden: false,
|
||||
statusBarHideWithNavBar: false,
|
||||
screenBackgroundColor: theme.centerChannelBg,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
getUserInfo = async () => {
|
||||
this.setState({loading: true});
|
||||
this.props.actions.connection(true);
|
||||
await this.props.actions.loadMe();
|
||||
this.props.actions.connection(false);
|
||||
this.setState({loading: false});
|
||||
this.props.actions.selectDefaultTeam();
|
||||
this.goToChannelView();
|
||||
}
|
||||
|
||||
onNavigatorEvent = (event) => {
|
||||
if (event.type === 'NavBarButtonPress') {
|
||||
const {logout} = this.props.actions;
|
||||
if (event.id === 'logout') {
|
||||
InteractionManager.runAfterInteractions(logout);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {theme} = this.props;
|
||||
const styles = getStyleSheet(theme);
|
||||
|
||||
if (this.state.loading) {
|
||||
return <Loading/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<StatusBar/>
|
||||
<FailedNetworkAction
|
||||
onRetry={this.getUserInfo}
|
||||
theme={theme}
|
||||
errorTitle={errorTitle}
|
||||
errorDescription={errorDescription}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const getStyleSheet = makeStyleSheetFromTheme((theme) => {
|
||||
return {
|
||||
container: {
|
||||
backgroundColor: theme.centerChannelBg,
|
||||
flex: 1,
|
||||
},
|
||||
};
|
||||
});
|
||||
65
app/screens/error_teams_list/error_teams_list.test.js
Normal file
65
app/screens/error_teams_list/error_teams_list.test.js
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
// 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';
|
||||
import FailedNetworkAction from 'app/components/failed_network_action';
|
||||
import ErrorTeamsList from './error_teams_list';
|
||||
|
||||
configure({adapter: new Adapter()});
|
||||
|
||||
describe('ErrorTeamsList', () => {
|
||||
const navigator = {
|
||||
setOnNavigatorEvent: () => {}, // eslint-disable-line no-empty-function
|
||||
};
|
||||
|
||||
const loadMe = async () => {
|
||||
return {
|
||||
data: {},
|
||||
};
|
||||
};
|
||||
|
||||
const baseProps = {
|
||||
actions: {
|
||||
loadMe: () => {}, // eslint-disable-line no-empty-function
|
||||
connection: () => {}, // eslint-disable-line no-empty-function
|
||||
logout: () => {}, // eslint-disable-line no-empty-function
|
||||
selectDefaultTeam: () => {}, // eslint-disable-line no-empty-function
|
||||
},
|
||||
theme: {},
|
||||
navigator,
|
||||
};
|
||||
|
||||
test('should match snapshot', () => {
|
||||
const wrapper = shallow(
|
||||
<ErrorTeamsList {...baseProps}/>
|
||||
);
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('should call for userInfo on retry', async () => {
|
||||
const connection = jest.fn();
|
||||
const selectDefaultTeam = jest.fn();
|
||||
const logout = jest.fn();
|
||||
const actions = {
|
||||
loadMe,
|
||||
logout,
|
||||
selectDefaultTeam,
|
||||
connection,
|
||||
};
|
||||
|
||||
const newProps = {
|
||||
...baseProps,
|
||||
actions,
|
||||
};
|
||||
|
||||
const wrapper = shallow(
|
||||
<ErrorTeamsList {...newProps}/>
|
||||
);
|
||||
|
||||
wrapper.find(FailedNetworkAction).props().onRetry();
|
||||
await loadMe();
|
||||
expect(selectDefaultTeam).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
24
app/screens/error_teams_list/index.js
Normal file
24
app/screens/error_teams_list/index.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
|
||||
import {logout, loadMe} from 'mattermost-redux/actions/users';
|
||||
import {connection} from 'app/actions/device';
|
||||
import {selectDefaultTeam} from 'app/actions/views/select_team';
|
||||
|
||||
import ErrorTeamsList from './error_teams_list.js';
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
logout,
|
||||
selectDefaultTeam,
|
||||
connection,
|
||||
loadMe,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(ErrorTeamsList);
|
||||
|
|
@ -58,5 +58,6 @@ export function registerScreens(store, Provider) {
|
|||
Navigation.registerComponent('TextPreview', () => wrapWithContextProvider(require('app/screens/text_preview').default), store, Provider);
|
||||
Navigation.registerComponent('Thread', () => wrapWithContextProvider(require('app/screens/thread').default), store, Provider);
|
||||
Navigation.registerComponent('TimezoneSettings', () => wrapWithContextProvider(require('app/screens/timezone').default), store, Provider);
|
||||
Navigation.registerComponent('ErrorTeamsList', () => wrapWithContextProvider(require('app/screens/error_teams_list').default), store, Provider);
|
||||
Navigation.registerComponent('UserProfile', () => wrapWithContextProvider(require('app/screens/user_profile').default), store, Provider);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ export default class Search extends PureComponent {
|
|||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const {searchingStatus: status, recent} = this.props;
|
||||
const {searchingStatus: status, recent, enableDateSuggestion} = this.props;
|
||||
const {searchingStatus: prevStatus} = prevProps;
|
||||
const recentLength = recent.length;
|
||||
const shouldScroll = prevStatus !== status && (status === RequestStatus.SUCCESS || status === RequestStatus.STARTED);
|
||||
|
|
@ -114,12 +114,13 @@ export default class Search extends PureComponent {
|
|||
}
|
||||
|
||||
if (shouldScroll) {
|
||||
requestAnimationFrame(() => {
|
||||
setTimeout(() => {
|
||||
const modifiersCount = enableDateSuggestion ? 5 : 2;
|
||||
this.refs.list._wrapperListRef.getListRef().scrollToOffset({ //eslint-disable-line no-underscore-dangle
|
||||
animated: true,
|
||||
offset: SECTION_HEIGHT + (2 * MODIFIER_LABEL_HEIGHT) + (recentLength * RECENT_LABEL_HEIGHT) + ((recentLength + 1) * RECENT_SEPARATOR_HEIGHT),
|
||||
offset: SECTION_HEIGHT + (modifiersCount * MODIFIER_LABEL_HEIGHT) + (recentLength * RECENT_LABEL_HEIGHT) + ((recentLength + 1) * RECENT_SEPARATOR_HEIGHT),
|
||||
});
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -242,3 +242,20 @@ export const makeGetMatchTermForDateMention = () => {
|
|||
return lastMatchTerm;
|
||||
};
|
||||
};
|
||||
|
||||
export const getDeletedPublicChannelsIds = createSelector(
|
||||
getMyChannels,
|
||||
getOtherChannels,
|
||||
(myChannels, otherChannels) => {
|
||||
const channels = myChannels.filter((c) => {
|
||||
return (c.type === General.OPEN_CHANNEL);
|
||||
}).concat(otherChannels);
|
||||
|
||||
return new Set(channels.reduce((acc, c) => {
|
||||
if (c.delete_at !== 0) {
|
||||
acc.push(c.id);
|
||||
}
|
||||
return acc;
|
||||
}, []));
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -27,33 +27,33 @@ export default class ImageCacheManager {
|
|||
listener(path);
|
||||
} else {
|
||||
addListener(uri, listener);
|
||||
if (!uri.startsWith('http')) {
|
||||
if (uri.startsWith('http')) {
|
||||
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);
|
||||
if (this.downloadTask.respInfo.respType === 'text') {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
notifyAll(uri, path);
|
||||
} catch (e) {
|
||||
RNFetchBlob.fs.unlink(path);
|
||||
notifyAll(uri, uri);
|
||||
}
|
||||
} else {
|
||||
// In case the uri we are trying to cache is already a local file just notify and return
|
||||
notifyAll(uri, uri);
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
if (this.downloadTask.respInfo.respType === 'text') {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
notifyAll(uri, path);
|
||||
} catch (e) {
|
||||
RNFetchBlob.fs.unlink(path);
|
||||
notifyAll(uri, uri);
|
||||
}
|
||||
unsubscribe(uri);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,17 +6,17 @@ GEM
|
|||
public_suffix (>= 2.0.2, < 4.0)
|
||||
atomos (0.1.3)
|
||||
aws-eventstream (1.0.1)
|
||||
aws-partitions (1.100.0)
|
||||
aws-sdk-core (3.24.1)
|
||||
aws-partitions (1.103.0)
|
||||
aws-sdk-core (3.27.0)
|
||||
aws-eventstream (~> 1.0)
|
||||
aws-partitions (~> 1.0)
|
||||
aws-sigv4 (~> 1.0)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-kms (1.7.0)
|
||||
aws-sdk-core (~> 3)
|
||||
aws-sdk-kms (1.9.0)
|
||||
aws-sdk-core (~> 3, >= 3.26.0)
|
||||
aws-sigv4 (~> 1.0)
|
||||
aws-sdk-s3 (1.17.0)
|
||||
aws-sdk-core (~> 3, >= 3.21.2)
|
||||
aws-sdk-s3 (1.19.0)
|
||||
aws-sdk-core (~> 3, >= 3.26.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.0)
|
||||
aws-sigv4 (1.0.3)
|
||||
|
|
@ -41,7 +41,7 @@ GEM
|
|||
faraday_middleware (0.12.2)
|
||||
faraday (>= 0.7.4, < 1.0)
|
||||
fastimage (2.1.3)
|
||||
fastlane (2.101.1)
|
||||
fastlane (2.103.1)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.3, < 3.0.0)
|
||||
babosa (>= 1.0.2, < 2.0.0)
|
||||
|
|
@ -74,22 +74,23 @@ GEM
|
|||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.5.7, < 2.0.0)
|
||||
xcpretty (~> 0.2.8)
|
||||
xcodeproj (>= 1.6.0, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
fastlane-plugin-android_change_package_identifier (0.1.0)
|
||||
fastlane-plugin-android_change_string_app_name (0.1.1)
|
||||
nokogiri
|
||||
fastlane-plugin-find_replace_string (0.1.0)
|
||||
gh_inspector (1.1.3)
|
||||
google-api-client (0.23.4)
|
||||
google-api-client (0.23.8)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.5, < 0.7.0)
|
||||
httpclient (>= 2.8.1, < 3.0)
|
||||
mime-types (~> 3.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
googleauth (0.6.4)
|
||||
signet (~> 0.9)
|
||||
googleauth (0.6.6)
|
||||
faraday (~> 0.12)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.12)
|
||||
|
|
@ -125,9 +126,9 @@ GEM
|
|||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rouge (2.0.7)
|
||||
rubyzip (1.2.1)
|
||||
rubyzip (1.2.2)
|
||||
security (0.1.3)
|
||||
signet (0.8.1)
|
||||
signet (0.9.1)
|
||||
addressable (~> 2.3)
|
||||
faraday (~> 0.9)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
|
|
@ -149,13 +150,13 @@ GEM
|
|||
unf_ext (0.0.7.5)
|
||||
unicode-display_width (1.4.0)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.5.9)
|
||||
xcodeproj (1.6.0)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.2)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.2.5)
|
||||
xcpretty (0.2.8)
|
||||
nanaimo (~> 0.2.6)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.0)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
|
|
|||
|
|
@ -2427,7 +2427,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 136;
|
||||
CURRENT_PROJECT_VERSION = 137;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
ENABLE_BITCODE = NO;
|
||||
|
|
@ -2476,7 +2476,7 @@
|
|||
CODE_SIGN_ENTITLEMENTS = Mattermost/Mattermost.entitlements;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
CURRENT_PROJECT_VERSION = 136;
|
||||
CURRENT_PROJECT_VERSION = 137;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = UQ8HT4Q2XM;
|
||||
ENABLE_BITCODE = NO;
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>136</string>
|
||||
<string>137</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.12.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>136</string>
|
||||
<string>137</string>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
|
|
|
|||
|
|
@ -19,6 +19,6 @@
|
|||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>136</string>
|
||||
<string>137</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
|||
9
package-lock.json
generated
9
package-lock.json
generated
|
|
@ -10178,8 +10178,8 @@
|
|||
}
|
||||
},
|
||||
"mattermost-redux": {
|
||||
"version": "github:mattermost/mattermost-redux#28be3683bff6ae8c563883bb0ff04d7bd9ea31ee",
|
||||
"from": "github:mattermost/mattermost-redux#28be3683bff6ae8c563883bb0ff04d7bd9ea31ee",
|
||||
"version": "github:mattermost/mattermost-redux#46dd5d74564fc5aa0d61adeebe73b58b0a36344e",
|
||||
"from": "github:mattermost/mattermost-redux#46dd5d74564fc5aa0d61adeebe73b58b0a36344e",
|
||||
"requires": {
|
||||
"deep-equal": "1.0.1",
|
||||
"eslint-plugin-header": "1.2.0",
|
||||
|
|
@ -14712,9 +14712,8 @@
|
|||
}
|
||||
},
|
||||
"react-native-calendars": {
|
||||
"version": "1.20.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-calendars/-/react-native-calendars-1.20.0.tgz",
|
||||
"integrity": "sha512-VlRoDcnEAWYE1JBPBh/Bie6baLQCmtuOGhw7V5yk09Y4j7Hy8BtuZIHh2+LU/TFYso+wEHJAFdj6D0QFttDOlg==",
|
||||
"version": "github:enahum/react-native-calendars#b96954bf85126222b311b638fe458a8194f25bed",
|
||||
"from": "github:enahum/react-native-calendars#b96954bf85126222b311b638fe458a8194f25bed",
|
||||
"requires": {
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
"intl": "1.2.5",
|
||||
"jail-monkey": "1.0.0",
|
||||
"jsc-android": "216113.0.3",
|
||||
"mattermost-redux": "github:mattermost/mattermost-redux#28be3683bff6ae8c563883bb0ff04d7bd9ea31ee",
|
||||
"mattermost-redux": "github:mattermost/mattermost-redux#46dd5d74564fc5aa0d61adeebe73b58b0a36344e",
|
||||
"mime-db": "1.33.0",
|
||||
"moment-timezone": "0.5.21",
|
||||
"prop-types": "15.6.1",
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
"react-native-animatable": "1.2.4",
|
||||
"react-native-bottom-sheet": "1.0.3",
|
||||
"react-native-button": "2.3.0",
|
||||
"react-native-calendars": "1.20.0",
|
||||
"react-native-calendars": "github:enahum/react-native-calendars#b96954bf85126222b311b638fe458a8194f25bed",
|
||||
"react-native-circular-progress": "0.2.0",
|
||||
"react-native-cookies": "3.2.0",
|
||||
"react-native-device-info": "github:enahum/react-native-device-info#a7bb3cff1086780b2c791a3e43e5b826fdf3ab11",
|
||||
|
|
|
|||
Loading…
Reference in a new issue