forked from Ivasoft/mattermost-mobile
Make it so a space after autocomplete channels and users closes the autocomplete (#6589)
This commit is contained in:
committed by
GitHub
parent
1035a3c2e0
commit
f874279d87
@@ -17,6 +17,7 @@ import {useTheme} from '@context/theme';
|
||||
import DatabaseManager from '@database/manager';
|
||||
import {t} from '@i18n';
|
||||
import {queryAllUsers} from '@queries/servers/user';
|
||||
import {hasTrailingSpaces} from '@utils/helpers';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import type GroupModel from '@typings/database/models/servers/group';
|
||||
@@ -85,13 +86,16 @@ const keyExtractor = (item: UserProfile) => {
|
||||
return item.id;
|
||||
};
|
||||
|
||||
const filterLocalResults = (users: UserModel[], term: string) => {
|
||||
return users.filter((u) =>
|
||||
u.username.toLowerCase().startsWith(term) ||
|
||||
u.nickname.toLowerCase().startsWith(term) ||
|
||||
u.firstName.toLowerCase().startsWith(term) ||
|
||||
u.lastName.toLowerCase().startsWith(term),
|
||||
);
|
||||
const filterResults = (users: Array<UserModel | UserProfile>, term: string) => {
|
||||
return users.filter((u) => {
|
||||
const firstName = ('firstName' in u ? u.firstName : u.first_name).toLowerCase();
|
||||
const lastName = ('lastName' in u ? u.lastName : u.last_name).toLowerCase();
|
||||
const fullName = `${firstName} ${lastName}`;
|
||||
return u.username.toLowerCase().includes(term) ||
|
||||
u.nickname.toLowerCase().includes(term) ||
|
||||
fullName.includes(term) ||
|
||||
u.email.toLowerCase().includes(term);
|
||||
});
|
||||
};
|
||||
|
||||
const makeSections = (teamMembers: Array<UserProfile | UserModel>, usersInChannel: Array<UserProfile | UserModel>, usersOutOfChannel: Array<UserProfile | UserModel>, groups: GroupModel[], showSpecialMentions: boolean, isLocal = false, isSearch = false) => {
|
||||
@@ -198,8 +202,7 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
|
||||
};
|
||||
});
|
||||
|
||||
const emptyProfileList: UserProfile[] = [];
|
||||
const emptyModelList: UserModel[] = [];
|
||||
const emptyUserlList: Array<UserModel | UserProfile> = [];
|
||||
const emptySectionList: UserMentionSections = [];
|
||||
const emptyGroupList: GroupModel[] = [];
|
||||
|
||||
@@ -232,15 +235,15 @@ const AtMention = ({
|
||||
const style = getStyleFromTheme(theme);
|
||||
|
||||
const [sections, setSections] = useState<UserMentionSections>(emptySectionList);
|
||||
const [usersInChannel, setUsersInChannel] = useState<UserProfile[]>(emptyProfileList);
|
||||
const [usersOutOfChannel, setUsersOutOfChannel] = useState<UserProfile[]>(emptyProfileList);
|
||||
const [usersInChannel, setUsersInChannel] = useState<Array<UserProfile | UserModel>>(emptyUserlList);
|
||||
const [usersOutOfChannel, setUsersOutOfChannel] = useState<Array<UserProfile | UserModel>>(emptyUserlList);
|
||||
const [groups, setGroups] = useState<GroupModel[]>(emptyGroupList);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [noResultsTerm, setNoResultsTerm] = useState<string|null>(null);
|
||||
const [localCursorPosition, setLocalCursorPosition] = useState(cursorPosition); // To avoid errors due to delay between value changes and cursor position changes.
|
||||
const [useLocal, setUseLocal] = useState(true);
|
||||
const [localUsers, setLocalUsers] = useState<UserModel[]>();
|
||||
const [filteredLocalUsers, setFilteredLocalUsers] = useState(emptyModelList);
|
||||
const [filteredLocalUsers, setFilteredLocalUsers] = useState(emptyUserlList);
|
||||
|
||||
const runSearch = useMemo(() => debounce(async (sUrl: string, term: string, cId?: string) => {
|
||||
setLoading(true);
|
||||
@@ -253,11 +256,19 @@ const AtMention = ({
|
||||
fallbackUsers = await getAllUsers(sUrl);
|
||||
setLocalUsers(fallbackUsers);
|
||||
}
|
||||
const filteredUsers = filterLocalResults(fallbackUsers, term);
|
||||
setFilteredLocalUsers(filteredUsers.length ? filteredUsers : emptyModelList);
|
||||
const filteredUsers = filterResults(fallbackUsers, term);
|
||||
setFilteredLocalUsers(filteredUsers.length ? filteredUsers : emptyUserlList);
|
||||
} else if (receivedUsers) {
|
||||
setUsersInChannel(receivedUsers.users.length ? receivedUsers.users : emptyProfileList);
|
||||
setUsersOutOfChannel(receivedUsers.out_of_channel?.length ? receivedUsers.out_of_channel : emptyProfileList);
|
||||
if (hasTrailingSpaces(term)) {
|
||||
const filteredReceivedUsers = filterResults(receivedUsers.users, term);
|
||||
const filteredReceivedOutOfChannelUsers = filterResults(receivedUsers.out_of_channel || [], term);
|
||||
|
||||
setUsersInChannel(filteredReceivedUsers.length ? filteredReceivedUsers : emptyUserlList);
|
||||
setUsersOutOfChannel(filteredReceivedOutOfChannelUsers.length ? filteredReceivedOutOfChannelUsers : emptyUserlList);
|
||||
} else {
|
||||
setUsersInChannel(receivedUsers.users.length ? receivedUsers.users : emptyUserlList);
|
||||
setUsersOutOfChannel(receivedUsers.out_of_channel?.length ? receivedUsers.out_of_channel : emptyUserlList);
|
||||
}
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
@@ -270,9 +281,9 @@ const AtMention = ({
|
||||
|
||||
const matchTerm = getMatchTermForAtMention(value.substring(0, localCursorPosition), isSearch);
|
||||
const resetState = () => {
|
||||
setUsersInChannel(emptyProfileList);
|
||||
setUsersOutOfChannel(emptyProfileList);
|
||||
setFilteredLocalUsers(emptyModelList);
|
||||
setUsersInChannel(emptyUserlList);
|
||||
setUsersOutOfChannel(emptyUserlList);
|
||||
setFilteredLocalUsers(emptyUserlList);
|
||||
setSections(emptySectionList);
|
||||
runSearch.cancel();
|
||||
};
|
||||
@@ -425,6 +436,10 @@ const AtMention = ({
|
||||
if (!loading && !nSections && noResultsTerm == null) {
|
||||
setNoResultsTerm(matchTerm);
|
||||
}
|
||||
|
||||
if (nSections && noResultsTerm) {
|
||||
setNoResultsTerm(null);
|
||||
}
|
||||
setSections(nSections ? newSections : emptySectionList);
|
||||
onShowingChange(Boolean(nSections));
|
||||
}, [!useLocal && usersInChannel, !useLocal && usersOutOfChannel, teamMembers, groups, loading, channelId, useLocal && filteredLocalUsers]);
|
||||
|
||||
@@ -17,6 +17,7 @@ import useDidUpdate from '@hooks/did_update';
|
||||
import {t} from '@i18n';
|
||||
import {queryAllChannelsForTeam} from '@queries/servers/channel';
|
||||
import {getCurrentTeamId} from '@queries/servers/system';
|
||||
import {hasTrailingSpaces} from '@utils/helpers';
|
||||
import {makeStyleSheetFromTheme} from '@utils/theme';
|
||||
|
||||
import type ChannelModel from '@typings/database/models/servers/channel';
|
||||
@@ -148,11 +149,12 @@ const makeSections = (channels: Array<Channel | ChannelModel>, myMembers: MyChan
|
||||
return newSections;
|
||||
};
|
||||
|
||||
const filterLocalResults = (channels: ChannelModel[], term: string) => {
|
||||
return channels.filter((c) =>
|
||||
c.name.toLowerCase().startsWith(term) ||
|
||||
c.displayName.toLowerCase().startsWith(term),
|
||||
);
|
||||
const filterResults = (channels: Array<Channel | ChannelModel>, term: string) => {
|
||||
return channels.filter((c) => {
|
||||
const displayName = ('displayName' in c ? c.displayName : c.display_name).toLowerCase();
|
||||
return c.name.toLowerCase().includes(term) ||
|
||||
displayName.includes(term);
|
||||
});
|
||||
};
|
||||
|
||||
type Props = {
|
||||
@@ -186,8 +188,7 @@ const getAllChannels = async (serverUrl: string) => {
|
||||
};
|
||||
|
||||
const emptySections: Array<SectionListData<Channel>> = [];
|
||||
const emptyChannels: Channel[] = [];
|
||||
const emptyModelList: ChannelModel[] = [];
|
||||
const emptyChannels: Array<Channel | ChannelModel> = [];
|
||||
|
||||
const ChannelMention = ({
|
||||
cursorPosition,
|
||||
@@ -204,13 +205,13 @@ const ChannelMention = ({
|
||||
const style = getStyleFromTheme(theme);
|
||||
|
||||
const [sections, setSections] = useState<Array<SectionListData<(Channel | ChannelModel)>>>(emptySections);
|
||||
const [channels, setChannels] = useState<Channel[]>(emptyChannels);
|
||||
const [channels, setChannels] = useState<Array<ChannelModel | Channel>>(emptyChannels);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [noResultsTerm, setNoResultsTerm] = useState<string|null>(null);
|
||||
const [localCursorPosition, setLocalCursorPosition] = useState(cursorPosition); // To avoid errors due to delay between value changes and cursor position changes.
|
||||
const [useLocal, setUseLocal] = useState(true);
|
||||
const [localChannels, setlocalChannels] = useState<ChannelModel[]>();
|
||||
const [filteredLocalChannels, setFilteredLocalChannels] = useState(emptyModelList);
|
||||
const [filteredLocalChannels, setFilteredLocalChannels] = useState(emptyChannels);
|
||||
|
||||
const listStyle = useMemo(() =>
|
||||
[style.listView, {maxHeight: maxListHeight}]
|
||||
@@ -227,17 +228,21 @@ const ChannelMention = ({
|
||||
fallbackChannels = await getAllChannels(sUrl);
|
||||
setlocalChannels(fallbackChannels);
|
||||
}
|
||||
const filteredChannels = filterLocalResults(fallbackChannels, term);
|
||||
setFilteredLocalChannels(filteredChannels.length ? filteredChannels : emptyModelList);
|
||||
const filteredChannels = filterResults(fallbackChannels, term);
|
||||
setFilteredLocalChannels(filteredChannels.length ? filteredChannels : emptyChannels);
|
||||
} else if (receivedChannels) {
|
||||
setChannels(receivedChannels.length ? receivedChannels : emptyChannels);
|
||||
let channelsToStore: Array<Channel | ChannelModel> = receivedChannels;
|
||||
if (hasTrailingSpaces(term)) {
|
||||
channelsToStore = filterResults(receivedChannels, term);
|
||||
}
|
||||
setChannels(channelsToStore.length ? channelsToStore : emptyChannels);
|
||||
}
|
||||
setLoading(false);
|
||||
}, 200), []);
|
||||
|
||||
const matchTerm = getMatchTermForChannelMention(value.substring(0, localCursorPosition), isSearch);
|
||||
const resetState = () => {
|
||||
setFilteredLocalChannels(emptyModelList);
|
||||
setFilteredLocalChannels(emptyChannels);
|
||||
setChannels(emptyChannels);
|
||||
setSections(emptySections);
|
||||
runSearch.cancel();
|
||||
|
||||
@@ -146,3 +146,7 @@ export const pluckUnique = (key: string) => (array: Array<{[key: string]: unknow
|
||||
export function bottomSheetSnapPoint(itemsCount: number, itemHeight: number, bottomInset = 0) {
|
||||
return ((itemsCount + Platform.select({android: 1, default: 0})) * itemHeight) + (bottomInset * 2.5);
|
||||
}
|
||||
|
||||
export function hasTrailingSpaces(term: string) {
|
||||
return term.length !== term.trimEnd().length;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user