Use local channels on channel autocomplete (#6272)

* Use local channels on channel autocomplete

* Only load local channels on demand
This commit is contained in:
Daniel Espino García
2022-05-19 09:12:05 +02:00
committed by GitHub
parent a4674915c1
commit 7e27113f61

View File

@@ -12,10 +12,14 @@ import {General} from '@constants';
import {CHANNEL_MENTION_REGEX, CHANNEL_MENTION_SEARCH_REGEX} from '@constants/autocomplete';
import {useServerUrl} from '@context/server';
import {useTheme} from '@context/theme';
import DatabaseManager from '@database/manager';
import useDidUpdate from '@hooks/did_update';
import {t} from '@i18n';
import {queryAllChannelsForTeam} from '@queries/servers/channel';
import {getCurrentTeamId} from '@queries/servers/system';
import {makeStyleSheetFromTheme} from '@utils/theme';
import type ChannelModel from '@typings/database/models/servers/channel';
import type MyChannelModel from '@typings/database/models/servers/my_channel';
const keyExtractor = (item: Channel) => {
@@ -48,9 +52,9 @@ const getMatchTermForChannelMention = (() => {
};
})();
const reduceChannelsForSearch = (channels: Channel[], members: MyChannelModel[]) => {
const reduceChannelsForSearch = (channels: Array<Channel | ChannelModel>, members: MyChannelModel[]) => {
const memberIds = new Set(members.map((m) => m.id));
return channels.reduce<Channel[][]>(([pubC, priC, dms], c) => {
return channels.reduce<Array<Array<Channel | ChannelModel>>>(([pubC, priC, dms], c) => {
switch (c.type) {
case General.OPEN_CHANNEL:
if (memberIds.has(c.id)) {
@@ -68,9 +72,9 @@ const reduceChannelsForSearch = (channels: Channel[], members: MyChannelModel[])
}, [[], [], []]);
};
const reduceChannelsForAutocomplete = (channels: Channel[], members: MyChannelModel[]) => {
const reduceChannelsForAutocomplete = (channels: Array<Channel | ChannelModel>, members: MyChannelModel[]) => {
const memberIds = new Set(members.map((m) => m.id));
return channels.reduce<Channel[][]>(([myC, otherC], c) => {
return channels.reduce<Array<Array<Channel | ChannelModel>>>(([myC, otherC], c) => {
if (memberIds.has(c.id)) {
myC.push(c);
} else {
@@ -80,7 +84,7 @@ const reduceChannelsForAutocomplete = (channels: Channel[], members: MyChannelMo
}, [[], []]);
};
const makeSections = (channels: Channel[], myMembers: MyChannelModel[], isSearch = false) => {
const makeSections = (channels: Array<Channel | ChannelModel>, myMembers: MyChannelModel[], isSearch = false) => {
const newSections = [];
if (isSearch) {
const [publicChannels, privateChannels, directAndGroupMessages] = reduceChannelsForSearch(channels, myMembers);
@@ -143,6 +147,14 @@ const makeSections = (channels: Channel[], myMembers: MyChannelModel[], isSearch
return newSections;
};
const filterLocalResults = (channels: ChannelModel[], term: string) => {
return channels.filter((c) =>
c.name.toLowerCase().startsWith(term) ||
c.displayName.toLowerCase().startsWith(term),
);
};
type Props = {
cursorPosition: number;
isSearch: boolean;
@@ -163,8 +175,19 @@ const getStyleFromTheme = makeStyleSheetFromTheme((theme) => {
};
});
const getAllChannels = async (serverUrl: string) => {
const database = DatabaseManager.serverDatabases[serverUrl]?.database;
if (!database) {
return [];
}
const teamId = await getCurrentTeamId(database);
return queryAllChannelsForTeam(database, teamId).fetch();
};
const emptySections: Array<SectionListData<Channel>> = [];
const emptyChannels: Channel[] = [];
const emptyModelList: ChannelModel[] = [];
const ChannelMention = ({
cursorPosition,
@@ -180,11 +203,14 @@ const ChannelMention = ({
const theme = useTheme();
const style = getStyleFromTheme(theme);
const [sections, setSections] = useState<Array<SectionListData<Channel>>>(emptySections);
const [sections, setSections] = useState<Array<SectionListData<(Channel | ChannelModel)>>>(emptySections);
const [channels, setChannels] = useState<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 listStyle = useMemo(() =>
[style.listView, {maxHeight: maxListHeight}]
@@ -192,8 +218,18 @@ const ChannelMention = ({
const runSearch = useMemo(() => debounce(async (sUrl: string, term: string) => {
setLoading(true);
const {channels: receivedChannels} = await searchChannels(sUrl, term);
if (receivedChannels) {
const {channels: receivedChannels, error} = await searchChannels(sUrl, term);
setUseLocal(Boolean(error));
if (error) {
let fallbackChannels = localChannels;
if (!fallbackChannels) {
fallbackChannels = await getAllChannels(sUrl);
setlocalChannels(fallbackChannels);
}
const filteredChannels = filterLocalResults(fallbackChannels, term);
setFilteredLocalChannels(filteredChannels.length ? filteredChannels : emptyModelList);
} else if (receivedChannels) {
setChannels(receivedChannels.length ? receivedChannels : emptyChannels);
}
setLoading(false);
@@ -201,6 +237,7 @@ const ChannelMention = ({
const matchTerm = getMatchTermForChannelMention(value.substring(0, localCursorPosition), isSearch);
const resetState = () => {
setFilteredLocalChannels(emptyModelList);
setChannels(emptyChannels);
setSections(emptySections);
runSearch.cancel();
@@ -285,7 +322,7 @@ const ChannelMention = ({
}, [matchTerm]);
useDidUpdate(() => {
const newSections = makeSections(channels, myMembers, isSearch);
const newSections = makeSections(useLocal ? filteredLocalChannels : channels, myMembers, isSearch);
const nSections = newSections.length;
if (!loading && !nSections && noResultsTerm == null) {