Avoid possible reading of length property on undefined (#2783)

This commit is contained in:
Miguel Alatzar 2019-05-10 09:34:23 -07:00 committed by GitHub
parent 1d6dccf0a5
commit 45e5bae8da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 99 additions and 12 deletions

View file

@ -185,7 +185,7 @@ export function loadPostsIfNecessaryWithRetry(channelId) {
// Get the first page of posts if it appears we haven't gotten it yet, like the webapp
received = await retryGetPostsAction(getPosts(channelId), dispatch, getState);
if (received) {
if (received?.order) {
const count = received.order.length;
loadMorePostsVisible = count >= ViewTypes.POST_VISIBILITY_CHUNK_SIZE;
actions.push({
@ -213,7 +213,7 @@ export function loadPostsIfNecessaryWithRetry(channelId) {
received = await retryGetPostsAction(getPostsSince(channelId, since), dispatch, getState);
if (received) {
if (received?.order) {
const count = received.order.length;
loadMorePostsVisible = postsIds.length + count >= ViewTypes.POST_VISIBILITY_CHUNK_SIZE;
actions.push({
@ -621,7 +621,7 @@ export function increasePostVisibility(channelId, postId) {
}];
let hasMorePost = false;
if (result) {
if (result?.order) {
const count = result.order.length;
hasMorePost = count >= pageSize;

View file

@ -41,6 +41,7 @@ export default class AtMention extends PureComponent {
defaultChannel: {},
isSearch: false,
value: '',
inChannel: [],
};
constructor(props) {

View file

@ -24,6 +24,11 @@ export default class AtMentionItem extends PureComponent {
theme: PropTypes.object.isRequired,
};
static defaultProps = {
firstName: '',
lastName: '',
};
completeMention = () => {
const {onPress, username} = this.props;
onPress(username);

View file

@ -44,6 +44,11 @@ export default class ChannelMention extends PureComponent {
static defaultProps = {
isSearch: false,
value: '',
publicChannels: [],
privateChannels: [],
directAndGroupMessages: [],
myChannels: [],
otherChannels: [],
};
constructor(props) {

View file

@ -29,6 +29,10 @@ class ChannelIntro extends PureComponent {
theme: PropTypes.object.isRequired,
};
static defaultProps = {
currentChannelMembers: [],
};
goToUserProfile = (userId) => {
const {intl, navigator, theme} = this.props;
const options = {

View file

@ -61,6 +61,10 @@ export default class LastUsers extends React.PureComponent {
usernames: PropTypes.array.isRequired,
};
static defaultProps = {
usernames: [],
};
constructor(props) {
super(props);

View file

@ -27,6 +27,10 @@ export default class FileUploadPreview extends PureComponent {
theme: PropTypes.object.isRequired,
};
static defaultProps = {
files: [],
};
buildFilePreviews = () => {
return this.props.files.map((file) => {
return (

View file

@ -36,6 +36,10 @@ export default class PostAddChannelMember extends React.PureComponent {
textStyles: PropTypes.object,
};
static defaultProps = {
usernames: [],
};
static contextTypes = {
intl: intlShape,
};

View file

@ -77,6 +77,7 @@ export default class PostBody extends PureComponent {
onFailedPostPress: emptyFunction,
onPress: emptyFunction,
replyBarStyle: [],
message: '',
};
static contextTypes = {

View file

@ -74,6 +74,7 @@ export default class PostList extends PureComponent {
refreshing: false,
serverURL: '',
siteURL: '',
postIds: [],
};
constructor(props) {

View file

@ -19,6 +19,10 @@ export default class Typing extends PureComponent {
typing: PropTypes.array.isRequired,
};
static defaultProps = {
typing: [],
};
state = {
typingHeight: new Animated.Value(0),
}

View file

@ -44,6 +44,7 @@ export default class List extends PureComponent {
static contextTypes = {
intl: intlShape,
unreadChannelIds: [],
};
constructor(props) {

View file

@ -14,6 +14,10 @@ export default class ToolTip extends PureComponent {
actions: PropTypes.array.isRequired,
};
static defaultProps = {
actions: [],
}
handleHide = () => {
if (this.props.onHide) {
this.props.onHide();

View file

@ -29,10 +29,12 @@ const handleRedirectProtocol = (url, response) => {
const serverUrl = Client4.getUrl();
const parsed = urlParse(url);
const {redirects} = response.rnfbRespInfo;
const redirectUrl = urlParse(redirects[redirects.length - 1]);
if (redirects) {
const redirectUrl = urlParse(redirects[redirects.length - 1]);
if (serverUrl === parsed.origin && parsed.host === redirectUrl.host && parsed.protocol !== redirectUrl.protocol) {
Client4.setUrl(serverUrl.replace(parsed.protocol, redirectUrl.protocol));
if (serverUrl === parsed.origin && parsed.host === redirectUrl.host && parsed.protocol !== redirectUrl.protocol) {
Client4.setUrl(serverUrl.replace(parsed.protocol, redirectUrl.protocol));
}
}
};

View file

@ -55,6 +55,10 @@ export default class ImagePreview extends PureComponent {
theme: PropTypes.object.isRequired,
};
static defaultProps = {
files: [],
};
static contextTypes = {
intl: intlShape,
};

View file

@ -38,6 +38,10 @@ export default class MoreChannels extends PureComponent {
theme: PropTypes.object.isRequired,
};
static defaultProps = {
channels: [],
};
static contextTypes = {
intl: intlShape.isRequired,
};

View file

@ -21,6 +21,10 @@ export default class OptionsModalList extends PureComponent {
onItemPress: PropTypes.func,
};
static defaultProps = {
items: [],
};
handleCancelPress = preventDoubleTap(() => {
if (this.props.onCancelPress) {
this.props.onCancelPress();

View file

@ -25,6 +25,10 @@ export default class OptionsModalList extends PureComponent {
]),
};
static defaultProps = {
items: [],
};
handleCancelPress = preventDoubleTap(() => {
if (this.props.onCancelPress) {
this.props.onCancelPress();

View file

@ -35,6 +35,10 @@ export default class ReactionList extends PureComponent {
userProfiles: PropTypes.array,
};
static defaultProps = {
userProfiles: [],
};
static contextTypes = {
intl: intlShape.isRequired,
};

View file

@ -53,6 +53,10 @@ export default class SelectTeam extends PureComponent {
teamsRequest: PropTypes.object.isRequired,
};
static defaultProps = {
teams: [],
};
constructor(props) {
super(props);
props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);

View file

@ -38,6 +38,11 @@ class Settings extends PureComponent {
theme: PropTypes.object,
};
static defaultProps = {
errors: [],
joinableTeams: [],
};
constructor(props) {
super(props);
this.props.navigator.setOnNavigatorEvent(this.onNavigatorEvent);

View file

@ -17,6 +17,10 @@ export default class NotificationSettingsMentionsBase extends PureComponent {
theme: PropTypes.object.isRequired,
};
static defaultProps = {
currentUser: {},
};
constructor(props) {
super(props);

View file

@ -284,7 +284,7 @@ class NotificationSettingsMentionsAndroid extends NotificationSettingsMentionsBa
style={style.scrollView}
contentContainerStyle={style.scrollViewContent}
>
{currentUser.first_name.length > 0 &&
{currentUser.first_name?.length > 0 &&
<View>
<SectionItem
label={(

View file

@ -62,7 +62,7 @@ class NotificationSettingsMentionsIos extends NotificationSettingsMentionsBase {
headerDefaultMessage='WORDS THAT TRIGGER MENTIONS'
theme={theme}
>
{currentUser.first_name.length > 0 &&
{currentUser.first_name?.length > 0 &&
<View>
<SectionItem
label={(

View file

@ -37,6 +37,10 @@ export default class Thread extends PureComponent {
threadLoadingStatus: PropTypes.object,
};
static defaultProps = {
postIds: [],
};
state = {};
static contextTypes = {

View file

@ -36,6 +36,10 @@ export default class Timezone extends PureComponent {
}).isRequired,
};
static defaultProps = {
timezones: [],
};
static contextTypes = {
intl: intlShape,
};
@ -71,7 +75,7 @@ export default class Timezone extends PureComponent {
return;
}
if (manualTimezone.length > 0) {
if (manualTimezone?.length > 0) {
// Preserve state change in server if manualTimezone exists
this.submitUser({
useAutomaticTimezone,

View file

@ -228,17 +228,17 @@ export function cleanUpState(action, keepCurrent = false) {
nextEntities.posts.postsInChannel = cleanUpPostsInChannel(payload.entities.posts.postsInChannel, lastChannelForTeam, keepCurrent ? currentChannelId : '');
postIdsToKeep.push(...getAllFromPostsInChannel(nextEntities.posts.postsInChannel));
// Keep any posts that appear in search resutls
// Keep any posts that appear in search results
let searchResults = [];
let flaggedPosts = [];
if (payload.entities.search) {
if (payload.entities.search.results.length) {
if (payload.entities.search.results?.length) {
const {results} = payload.entities.search;
searchResults = results;
postIdsToKeep.push(...results);
}
if (payload.entities.search.flagged.length) {
if (payload.entities.search.flagged?.length) {
const {flagged} = payload.entities.search;
flaggedPosts = flagged;
postIdsToKeep.push(...flagged);

View file

@ -86,6 +86,10 @@ describe('cleanUpState', () => {
postsInThread: {},
reactions: {},
},
search: {
results: ['post1', 'post2'],
flagged: ['post1', 'post2', 'post3'],
},
},
views: {
team: {
@ -101,6 +105,8 @@ describe('cleanUpState', () => {
expect(result.payload.entities.posts.posts.post1).toBeDefined();
expect(result.payload.entities.posts.posts.post2).toBeUndefined();
expect(result.payload.entities.posts.postsInChannel.channel1).toEqual([{order: ['post1'], recent: true}]);
expect(result.payload.entities.search.results).toEqual(['post1', 'post2']);
expect(result.payload.entities.search.flagged).toEqual(['post1', 'post2', 'post3']);
});
test('should keep failed pending post', () => {