Skip to content

Commit

Permalink
[IMPROVE] Use PaginatedSelectFiltered in department edition (#23054)
Browse files Browse the repository at this point in the history
* Fix only first 10 channels getting listed in channels autocomplete

* Use paginated select component for displaying channel select data

* Fix lint issues

Co-authored-by: Tiago Evangelista Pinto <tiago.evangelista@rocket.chat>
Co-authored-by: Kevin Aleman <kevin.aleman@rocket.chat>
  • Loading branch information
3 people authored Sep 3, 2021
1 parent e72aea4 commit 995c558
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 24 deletions.
31 changes: 30 additions & 1 deletion app/api/server/lib/rooms.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { Rooms } from '../../../models/server/raw';
import { Subscriptions } from '../../../models';
import { Subscriptions } from '../../../models/server';

export async function findAdminRooms({ uid, filter, types = [], pagination: { offset, count, sort } }) {
if (!await hasPermissionAsync(uid, 'view-room-administration')) {
Expand Down Expand Up @@ -119,6 +119,35 @@ export async function findChannelAndPrivateAutocomplete({ uid, selector }) {
};
}

export async function findChannelAndPrivateAutocompleteWithPagination({ uid, selector, pagination: { offset, count, sort } }) {
const userRoomsIds = Subscriptions.cachedFindByUserId(uid, { fields: { rid: 1 } })
.fetch()
.map((item) => item.rid);

const options = {
fields: {
_id: 1,
fname: 1,
name: 1,
t: 1,
avatarETag: 1,
},
sort: sort || { name: 1 },
skip: offset,
limit: count,
};

const cursor = await Rooms.findRoomsWithoutDiscussionsByRoomIds(selector.name, userRoomsIds, options);

const total = await cursor.count();
const rooms = await cursor.toArray();

return {
items: rooms,
total,
};
}

export async function findRoomsAvailableForTeams({ uid, name }) {
const options = {
fields: {
Expand Down
24 changes: 23 additions & 1 deletion app/api/server/v1/rooms.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Meteor } from 'meteor/meteor';
import { FileUpload } from '../../../file-upload';
import { Rooms, Messages } from '../../../models';
import { API } from '../api';
import { findAdminRooms, findChannelAndPrivateAutocomplete, findAdminRoom, findRoomsAvailableForTeams } from '../lib/rooms';
import { findAdminRooms, findChannelAndPrivateAutocomplete, findAdminRoom, findRoomsAvailableForTeams, findChannelAndPrivateAutocompleteWithPagination } from '../lib/rooms';
import { sendFile, sendViaEmail } from '../../../../server/lib/channelExport';
import { canAccessRoom, hasPermission } from '../../../authorization/server';
import { Media } from '../../../../server/sdk';
Expand Down Expand Up @@ -314,6 +314,28 @@ API.v1.addRoute('rooms.autocomplete.channelAndPrivate', { authRequired: true },
},
});

API.v1.addRoute('rooms.autocomplete.channelAndPrivate.withPagination', { authRequired: true }, {
get() {
const { selector } = this.queryParams;
const { offset, count } = this.getPaginationItems();
const { sort } = this.parseJsonQuery();

if (!selector) {
return API.v1.failure('The \'selector\' param is required');
}

return API.v1.success(Promise.await(findChannelAndPrivateAutocompleteWithPagination({
uid: this.userId,
selector: JSON.parse(selector),
pagination: {
offset,
count,
sort,
},
})));
},
});

API.v1.addRoute('rooms.autocomplete.availableForTeams', { authRequired: true }, {
get() {
const { name } = this.queryParams;
Expand Down
63 changes: 63 additions & 0 deletions client/components/RoomAutoComplete/hooks/useRoomsList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { useCallback, useState } from 'react';

import { IRoom } from '../../../../definition/IRoom';
import { useEndpoint } from '../../../contexts/ServerContext';
import { useScrollableRecordList } from '../../../hooks/lists/useScrollableRecordList';
import { useComponentDidUpdate } from '../../../hooks/useComponentDidUpdate';
import { RecordList } from '../../../lib/lists/RecordList';

type RoomListOptions = {
text: string;
};

export const useRoomsList = (
options: RoomListOptions,
): {
itemsList: RecordList<IRoom>;
initialItemCount: number;
reload: () => void;
loadMoreItems: (start: number, end: number) => void;
} => {
const [itemsList, setItemsList] = useState(() => new RecordList<IRoom>());
const reload = useCallback(() => setItemsList(new RecordList<IRoom>()), []);
const endpoint = 'rooms.autocomplete.channelAndPrivate.withPagination';

const getRooms = useEndpoint('GET', endpoint);

useComponentDidUpdate(() => {
options && reload();
}, [options, reload]);

const fetchData = useCallback(
async (start, end) => {
const { items: rooms, total } = await getRooms({
selector: JSON.stringify({ name: options.text || '' }),
offset: start,
count: start + end,
sort: JSON.stringify({ name: 1 }),
});

const items = rooms.map((room: any) => {
room._updatedAt = new Date(room._updatedAt);
room.label = room.name;
room.value = room.name;
return room;
});

return {
items,
itemCount: total,
};
},
[getRooms, options.text],
);

const { loadMoreItems, initialItemCount } = useScrollableRecordList(itemsList, fetchData, 25);

return {
reload,
itemsList,
loadMoreItems,
initialItemCount,
};
};
2 changes: 2 additions & 0 deletions client/contexts/ServerContext/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { CannedResponseEndpoint } from './endpoints/v1/omnichannel/cannedRespons
import { CannedResponsesEndpoint } from './endpoints/v1/omnichannel/cannedResponses';
import { AutocompleteAvailableForTeamsEndpoint as RoomsAutocompleteTeamsEndpoint } from './endpoints/v1/rooms/autocompleteAvailableForTeams';
import { AutocompleteChannelAndPrivateEndpoint as RoomsAutocompleteEndpoint } from './endpoints/v1/rooms/autocompleteChannelAndPrivate';
import { AutocompleteChannelAndPrivateEndpointWithPagination as RoomsAutocompleteEndpointWithPagination } from './endpoints/v1/rooms/autocompleteChannelAndPrivateWithPagination';
import { RoomsInfo as RoomsInfoEndpoint } from './endpoints/v1/rooms/roomsInfo';
import { AddRoomsEndpoint as TeamsAddRoomsEndpoint } from './endpoints/v1/teams/addRooms';
import { ListRoomsEndpoint } from './endpoints/v1/teams/listRooms';
Expand Down Expand Up @@ -59,6 +60,7 @@ export type ServerEndpoints = {
'custom-user-status.list': CustomUserStatusListEndpoint;
'/apps/externalComponents': AppsExternalComponentsEndpoint;
'rooms.autocomplete.channelAndPrivate': RoomsAutocompleteEndpoint;
'rooms.autocomplete.channelAndPrivate.withPagination': RoomsAutocompleteEndpointWithPagination;
'rooms.autocomplete.availableForTeams': RoomsAutocompleteTeamsEndpoint;
'teams.listRooms': ListRoomsEndpoint;
'teams.listRoomsOfUser': ListRoomsOfUserEndpoint;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { IRoom } from '../../../../../../definition/IRoom';

export type AutocompleteChannelAndPrivateEndpointWithPagination = {
GET: (params: { selector: string; offset?: number; count?: number; sort?: string }) => {
items: IRoom[];
count: number;
offset: number;
total: number;
};
};
42 changes: 20 additions & 22 deletions client/views/omnichannel/departments/EditDepartment.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,34 @@ import {
Field,
TextInput,
Chip,
SelectFiltered,
Box,
Icon,
Divider,
ToggleSwitch,
TextAreaInput,
ButtonGroup,
Button,
PaginatedSelectFiltered,
} from '@rocket.chat/fuselage';
import { useMutableCallback, useUniqueId } from '@rocket.chat/fuselage-hooks';
import React, { useMemo, useState, useRef } from 'react';
import { useSubscription } from 'use-subscription';

import { isEmail } from '../../../../app/utils/client';
import Page from '../../../components/Page';
import { useRoomsList } from '../../../components/RoomAutoComplete/hooks/useRoomsList';
import { useRoute } from '../../../contexts/RouterContext';
import { useMethod } from '../../../contexts/ServerContext';
import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext';
import { useTranslation } from '../../../contexts/TranslationContext';
import { useRecordList } from '../../../hooks/lists/useRecordList';
import { useComponentDidUpdate } from '../../../hooks/useComponentDidUpdate';
import { useEndpointAction } from '../../../hooks/useEndpointAction';
import { useEndpointData } from '../../../hooks/useEndpointData';
import { useForm } from '../../../hooks/useForm';
import { AsyncStatePhase } from '../../../lib/asyncState';
import { formsSubscription } from '../additionalForms';
import DepartmentsAgentsTable from './DepartmentsAgentsTable';

const useQuery = ({ name }) => useMemo(() => ({ selector: JSON.stringify({ name }) }), [name]);

function EditDepartment({ data, id, title, reload, allowedToForwardData }) {
const t = useTranslation();
const agentsRoute = useRoute('omnichannel-departments');
Expand Down Expand Up @@ -113,6 +113,12 @@ function EditDepartment({ data, id, title, reload, allowedToForwardData }) {
departmentsAllowedToForward,
} = values;

const { itemsList: RoomsList, loadMoreItems: loadMoreRooms } = useRoomsList(
useMemo(() => ({ text: offlineMessageChannelName }), [offlineMessageChannelName]),
);

const { phase: roomsPhase, items: roomsItems, itemCount: roomsTotal } = useRecordList(RoomsList);

const handleTagChipClick = (tag) => () => {
setTags((tags) => tags.filter((_tag) => _tag !== tag));
};
Expand All @@ -128,21 +134,6 @@ function EditDepartment({ data, id, title, reload, allowedToForwardData }) {
setTagsText(e.target.value);
});

const query = useQuery({ offlineMessageChannelName });

const { value: autoCompleteChannels = {} } = useEndpointData(
'rooms.autocomplete.channelAndPrivate',
query,
);

const channelOpts = useMemo(
() =>
autoCompleteChannels && autoCompleteChannels.items
? autoCompleteChannels.items.map(({ name }) => [name, name])
: [],
[autoCompleteChannels],
);

const saveDepartmentInfo = useMethod('livechat:saveDepartment');
const saveDepartmentAgentsInfoOnEdit = useEndpointAction(
'POST',
Expand Down Expand Up @@ -356,12 +347,19 @@ function EditDepartment({ data, id, title, reload, allowedToForwardData }) {
<Field>
<Field.Label>{t('Livechat_DepartmentOfflineMessageToChannel')}</Field.Label>
<Field.Row>
<SelectFiltered
flexGrow={1}
options={channelOpts}
<PaginatedSelectFiltered
value={offlineMessageChannelName}
onChange={handleOfflineMessageChannelName}
flexShrink={0}
filter={offlineMessageChannelName}
setFilter={handleOfflineMessageChannelName}
options={roomsItems}
placeholder={t('Channel_name')}
endReached={
roomsPhase === AsyncStatePhase.LOADING
? () => {}
: (start) => loadMoreRooms(start, Math.min(50, roomsTotal))
}
/>
</Field.Row>
</Field>
Expand Down

0 comments on commit 995c558

Please sign in to comment.