mattermost-mobile/app/selectors/autocomplete.js
Dmitry Samuylov 1ecb209712 Feature/search after before on (#2028)
* initial partial implementation of search date flags UI (on: before: after:)

* Make the calendar show correctly

* dismiss keyboard when showing the date suggestion since this type of input will primarily be entered through the calendar and later edited if needed

* fixed code style errors

* updates as per pull request comments

* fixed eslint errors

* changes as per pull request comments, removing unnecessary code

* added handling for hiding the date filter feature if the server version doesn't support it

* fixed eslint errors

* removed unnecessary property defaults and server version checks, as per PR suggestions

* this default is needed for the initial load of the component

* changed the way we capture the flag from the search string, this handles scenarios where multiple flags might be present in the search string that are handled by the same suggestion component as is the case here with date based flags

* need property declared to pass jslint rules

* eslint style fix, moved regex out into constants

* removed unused code and references

* updated as per pull request feedback

* updated to latest redux version

* changed to new search posts api that supports passing in of time zone utc offset to support date based search flags

* updated to the upcoming release version in preparation for merging to master

* Properly memoize DateSuggestion mapStateToProps

* Remove moment-timezone
2018-08-28 18:01:11 -04:00

244 lines
7.9 KiB
JavaScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {createSelector} from 'reselect';
import {General} from 'mattermost-redux/constants';
import {getMyChannels, getOtherChannels} from 'mattermost-redux/selectors/entities/channels';
import {
getCurrentUser, getCurrentUserId, getProfilesInCurrentChannel,
getProfilesNotInCurrentChannel, getProfilesInCurrentTeam,
} from 'mattermost-redux/selectors/entities/users';
import {sortChannelsByDisplayName} from 'mattermost-redux/utils/channel_utils';
import {sortByUsername} from 'mattermost-redux/utils/user_utils';
import * as Autocomplete from 'app/constants/autocomplete';
import {getCurrentLocale} from 'app/selectors/i18n';
export const getMatchTermForAtMention = (() => {
let lastMatchTerm = null;
let lastValue;
let lastIsSearch;
return (value, isSearch) => {
if (value !== lastValue || isSearch !== lastIsSearch) {
const regex = isSearch ? Autocomplete.AT_MENTION_SEARCH_REGEX : Autocomplete.AT_MENTION_REGEX;
const match = value.match(regex);
lastValue = value;
lastIsSearch = isSearch;
if (match) {
lastMatchTerm = isSearch ? match[1] : match[2];
} else {
lastMatchTerm = null;
}
}
return lastMatchTerm;
};
})();
export const getMatchTermForChannelMention = (() => {
let lastMatchTerm = null;
let lastValue;
let lastIsSearch;
return (value, isSearch) => {
if (value !== lastValue || isSearch !== lastIsSearch) {
const regex = isSearch ? Autocomplete.CHANNEL_MENTION_SEARCH_REGEX : Autocomplete.CHANNEL_MENTION_REGEX;
const match = value.match(regex);
lastValue = value;
lastIsSearch = isSearch;
if (match) {
lastMatchTerm = isSearch ? match[1] : match[2];
} else {
lastMatchTerm = null;
}
}
return lastMatchTerm;
};
})();
export const filterMembersInChannel = createSelector(
getProfilesInCurrentChannel,
getCurrentUserId,
(state, matchTerm) => matchTerm,
(profilesInChannel, currentUserId, matchTerm) => {
if (matchTerm === null) {
return null;
}
let profiles;
if (matchTerm) {
profiles = profilesInChannel.filter((p) => {
return ((p.id !== currentUserId) && (
p.username.toLowerCase().includes(matchTerm) || p.email.toLowerCase().includes(matchTerm) ||
p.first_name.toLowerCase().includes(matchTerm) || p.last_name.toLowerCase().includes(matchTerm)));
});
} else {
profiles = profilesInChannel.filter((p) => p.id !== currentUserId);
}
// already sorted
return profiles.map((p) => p.id);
}
);
export const filterMembersNotInChannel = createSelector(
getProfilesNotInCurrentChannel,
getCurrentUserId,
(state, matchTerm) => matchTerm,
(profilesNotInChannel, currentUserId, matchTerm) => {
if (matchTerm === null) {
return null;
}
let profiles;
if (matchTerm) {
profiles = profilesNotInChannel.filter((p) => {
return ((p.id !== currentUserId) && (
p.username.toLowerCase().includes(matchTerm) || p.email.toLowerCase().includes(matchTerm) ||
p.first_name.toLowerCase().includes(matchTerm) || p.last_name.toLowerCase().includes(matchTerm)));
});
} else {
profiles = profilesNotInChannel;
}
return profiles.map((p) => p.id);
}
);
export const filterMembersInCurrentTeam = createSelector(
getProfilesInCurrentTeam,
getCurrentUser,
(state, matchTerm) => matchTerm,
(profilesInTeam, currentUser, matchTerm) => {
if (matchTerm === null) {
return null;
}
// FIXME: We need to include the currentUser here as is not in profilesInTeam on the redux store
let profiles;
if (matchTerm) {
profiles = [...profilesInTeam, currentUser].filter((p) => {
return (p.username.toLowerCase().includes(matchTerm) || p.email.toLowerCase().includes(matchTerm) ||
p.first_name.toLowerCase().includes(matchTerm) || p.last_name.toLowerCase().includes(matchTerm));
});
} else {
profiles = [...profilesInTeam, currentUser];
}
return profiles.sort(sortByUsername).map((p) => p.id);
}
);
export const filterMyChannels = createSelector(
getMyChannels,
(state, opts) => opts,
(myChannels, matchTerm) => {
if (matchTerm === null) {
return null;
}
let channels;
if (matchTerm) {
channels = myChannels.filter((c) => {
return (c.type === General.OPEN_CHANNEL || c.type === General.PRIVATE_CHANNEL) &&
(c.name.startsWith(matchTerm) || c.display_name.startsWith(matchTerm));
});
} else {
channels = myChannels.filter((c) => {
return (c.type === General.OPEN_CHANNEL || c.type === General.PRIVATE_CHANNEL);
});
}
return channels.map((c) => c.id);
}
);
export const filterOtherChannels = createSelector(
getOtherChannels,
(state, matchTerm) => matchTerm,
(otherChannels, matchTerm) => {
if (matchTerm === null) {
return null;
}
let channels;
if (matchTerm) {
channels = otherChannels.filter((c) => {
return (c.name.startsWith(matchTerm) || c.display_name.startsWith(matchTerm));
});
} else {
channels = otherChannels;
}
return channels.map((c) => c.id);
}
);
export const filterPublicChannels = createSelector(
getMyChannels,
getOtherChannels,
getCurrentLocale,
(state, matchTerm) => matchTerm,
(myChannels, otherChannels, locale, matchTerm) => {
if (matchTerm === null) {
return null;
}
let channels;
if (matchTerm) {
channels = myChannels.filter((c) => {
return c.type === General.OPEN_CHANNEL &&
(c.name.startsWith(matchTerm) || c.display_name.startsWith(matchTerm));
}).concat(
otherChannels.filter((c) => c.name.startsWith(matchTerm) || c.display_name.startsWith(matchTerm))
);
} else {
channels = myChannels.filter((c) => {
return (c.type === General.OPEN_CHANNEL);
}).concat(otherChannels);
}
return channels.sort(sortChannelsByDisplayName.bind(null, locale)).map((c) => c.id);
}
);
export const filterPrivateChannels = createSelector(
getMyChannels,
(state, matchTerm) => matchTerm,
(myChannels, matchTerm) => {
if (matchTerm === null) {
return null;
}
let channels;
if (matchTerm) {
channels = myChannels.filter((c) => {
return c.type === General.PRIVATE_CHANNEL &&
(c.name.startsWith(matchTerm) || c.display_name.startsWith(matchTerm));
});
} else {
channels = myChannels.filter((c) => {
return c.type === General.PRIVATE_CHANNEL;
});
}
return channels.map((c) => c.id);
}
);
export const makeGetMatchTermForDateMention = () => {
let lastMatchTerm = null;
let lastValue;
return (value) => {
if (value !== lastValue) {
const regex = Autocomplete.DATE_MENTION_SEARCH_REGEX;
const match = value.match(regex);
lastValue = value;
if (match) {
lastMatchTerm = match[1];
} else {
lastMatchTerm = null;
}
}
return lastMatchTerm;
};
};