From 49e0e1ab2d9ef485abfa52e143308a1aeb1db7cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bar=C4=B1=C5=9F=20Soner=20U=C5=9Fakl=C4=B1?= Date: Sun, 24 Nov 2024 11:36:02 -0500 Subject: [PATCH] feat: add pagination to groups page, api routes use page instead of after --- public/openapi/read/groups.yaml | 2 - public/openapi/write/groups.yaml | 8 +-- public/src/admin/manage/groups.js | 36 ++++++-------- public/src/client/groups/list.js | 81 ++++++++++--------------------- public/src/client/users.js | 8 +-- src/api/groups.js | 5 +- src/controllers/groups.js | 35 +++++++++++-- src/groups/index.js | 12 ++++- 8 files changed, 95 insertions(+), 92 deletions(-) diff --git a/public/openapi/read/groups.yaml b/public/openapi/read/groups.yaml index 68173fed4f..dfb6e16c68 100644 --- a/public/openapi/read/groups.yaml +++ b/public/openapi/read/groups.yaml @@ -103,8 +103,6 @@ get: type: boolean sort: type: string - nextStart: - type: number title: type: string - $ref: ../components/schemas/Breadcrumbs.yaml#/Breadcrumbs diff --git a/public/openapi/write/groups.yaml b/public/openapi/write/groups.yaml index 432e474f8a..0e8ac45374 100644 --- a/public/openapi/write/groups.yaml +++ b/public/openapi/write/groups.yaml @@ -2,15 +2,15 @@ get: tags: - groups summary: list groups - description: This operation returns a list of user groups. The number of groups returned is hardcoded to 10. + description: This operation returns a list of user groups. The number of groups returned is hardcoded to 15 per page. parameters: - in: query - name: 'after' + name: 'page' schema: type: number required: false - description: An offset used to display a different subset of groups. - example: '0' + description: Used for pagination + example: '1' - in: query name: 'sort' schema: diff --git a/public/src/admin/manage/groups.js b/public/src/admin/manage/groups.js index 682dcadf21..bc9fbf5f39 100644 --- a/public/src/admin/manage/groups.js +++ b/public/src/admin/manage/groups.js @@ -1,12 +1,11 @@ 'use strict'; define('admin/manage/groups', [ - 'categorySelector', 'slugify', 'api', 'bootbox', 'alerts', -], function (categorySelector, slugify, api, bootbox, alerts) { +], function (slugify, api, bootbox, alerts) { const Groups = {}; Groups.init = function () { @@ -88,30 +87,27 @@ define('admin/manage/groups', [ return ajaxify.refresh(); } $('.pagination').addClass('hide'); - const groupsEl = $('.groups-list'); - socket.emit('groups.search', { + api.get('/api/groups', { query: queryEl.val(), - options: { - sort: 'date', - }, - }, function (err, groups) { - if (err) { - return alerts.error(err); - } - - app.parseAndTranslate('admin/manage/groups', 'groups', { - groups: groups, - categories: ajaxify.data.categories, - }, function (html) { - groupsEl.find('[data-groupname]').remove(); - groupsEl.find('tbody').append(html); - }); - }); + sort: 'date', + hideEphemeralGroups: true, + }).then(renderSearchResults) + .catch(alerts.error); } queryEl.on('keyup', utils.debounce(doSearch, 200)); } + function renderSearchResults(data) { + const groupsEl = $('.groups-list'); + app.parseAndTranslate('admin/manage/groups', 'groups', { + groups: data.groups, + categories: ajaxify.data.categories, + }, function (html) { + groupsEl.find('[data-groupname]').remove(); + groupsEl.find('tbody').append(html); + }); + } return Groups; }); diff --git a/public/src/client/groups/list.js b/public/src/client/groups/list.js index aa270bf7f4..bd13996044 100644 --- a/public/src/client/groups/list.js +++ b/public/src/client/groups/list.js @@ -1,13 +1,11 @@ 'use strict'; define('forum/groups/list', [ - 'forum/infinitescroll', 'benchpress', 'api', 'bootbox', 'alerts', -], function (infinitescroll, Benchpress, api, bootbox, alerts) { + 'api', 'bootbox', 'alerts', +], function (api, bootbox, alerts) { const Groups = {}; Groups.init = function () { - infinitescroll.init(Groups.loadMoreGroups); - // Group creation $('button[data-action="new"]').on('click', function () { bootbox.prompt('[[groups:new-group.group-name]]', function (name) { @@ -24,67 +22,40 @@ define('forum/groups/list', [ $('#search-sort').val(params.sort || 'alpha'); // Group searching - $('#search-text').on('keyup', Groups.search); + $('#search-text').on('keyup', utils.debounce(Groups.search, 200)); $('#search-button').on('click', Groups.search); $('#search-sort').on('change', function () { ajaxify.go('groups?sort=' + $('#search-sort').val()); }); }; - Groups.loadMoreGroups = function (direction) { - if (direction < 0) { - return; - } - - infinitescroll.loadMore('/groups', { - sort: $('#search-sort').val(), - after: $('[component="groups/container"]').attr('data-nextstart'), - }, function (data, done) { - if (data && data.groups.length) { - Benchpress.render('partials/groups/list', { - groups: data.groups, - }).then(function (html) { - $('#groups-list').append(html); - done(); - }); - } else { - done(); - } - - if (data && data.nextStart) { - $('[component="groups/container"]').attr('data-nextstart', data.nextStart); - } - }); - }; - Groups.search = function () { - const groupsEl = $('#groups-list'); - const queryEl = $('#search-text'); - const sortEl = $('#search-sort'); + api.get('/api/groups', { + query: $('#search-text').val(), + sort: $('#search-sort').val(), + filterHidden: true, + showMembers: true, + hideEphemeralGroups: true, + }).then(renderSearchResults) + .catch(alerts.error); - socket.emit('groups.search', { - query: queryEl.val(), - options: { - sort: sortEl.val(), - filterHidden: true, - showMembers: true, - hideEphemeralGroups: true, - }, - }, function (err, groups) { - if (err) { - return alerts.error(err); - } - groups = groups.filter(function (group) { - return group.name !== 'registered-users' && group.name !== 'guests'; - }); - Benchpress.render('partials/groups/list', { - groups: groups, - }).then(function (html) { - groupsEl.empty().append(html); - }); - }); return false; }; + function renderSearchResults(data) { + app.parseAndTranslate('partials/paginator', { + pagination: data.pagination, + }).then(function (html) { + $('.pagination-container').replaceWith(html); + }); + + const groupsEl = $('#groups-list'); + app.parseAndTranslate('partials/groups/list', { + groups: data.groups, + }).then(function (html) { + groupsEl.empty().append(html); + }); + } + return Groups; }); diff --git a/public/src/client/users.js b/public/src/client/users.js index d9bc4be595..cb6322ba74 100644 --- a/public/src/client/users.js +++ b/public/src/client/users.js @@ -2,8 +2,8 @@ define('forum/users', [ - 'benchpress', 'api', 'alerts', 'accounts/invite', -], function (Benchpress, api, alerts, AccountInvite) { + 'api', 'alerts', 'accounts/invite', +], function (api, alerts, AccountInvite) { const Users = {}; let searchResultCount = 0; @@ -88,7 +88,9 @@ define('forum/users', [ } function renderSearchResults(data) { - Benchpress.render('partials/paginator', { pagination: data.pagination }).then(function (html) { + app.parseAndTranslate('partials/paginator', { + pagination: data.pagination, + }).then(function (html) { $('.pagination-container').replaceWith(html); }); diff --git a/src/api/groups.js b/src/api/groups.js index 95074c4b6a..a75d4b45db 100644 --- a/src/api/groups.js +++ b/src/api/groups.js @@ -13,8 +13,9 @@ const slugify = require('../slugify'); const groupsAPI = module.exports; groupsAPI.list = async (caller, data) => { - const groupsPerPage = 10; - const start = parseInt(data.after || 0, 10); + const page = parseInt(data.page, 10) || 1; + const groupsPerPage = 15; + const start = Math.max(0, page - 1) * groupsPerPage; const stop = start + groupsPerPage - 1; const groupData = await groups.getGroupsBySort(data.sort, start, stop); diff --git a/src/controllers/groups.js b/src/controllers/groups.js index 6a21610748..4e9a65f433 100644 --- a/src/controllers/groups.js +++ b/src/controllers/groups.js @@ -14,22 +14,49 @@ const groupsController = module.exports; groupsController.list = async function (req, res) { const sort = req.query.sort || 'alpha'; - - const [groupData, allowGroupCreation] = await Promise.all([ - groups.getGroupsBySort(sort, 0, 14), + const page = parseInt(req.query.page, 10) || 1; + const [allowGroupCreation, [groupData, pageCount]] = await Promise.all([ privileges.global.can('group:create', req.uid), + getGroups(req, sort, page), ]); res.render('groups/list', { groups: groupData, allowGroupCreation: allowGroupCreation, sort: validator.escape(String(sort)), - nextStart: 15, + pagination: pagination.create(page, pageCount, req.query), title: '[[pages:groups]]', breadcrumbs: helpers.buildBreadcrumbs([{ text: '[[pages:groups]]' }]), }); }; +async function getGroups(req, sort, page) { + const resultsPerPage = req.query.query ? 100 : 15; + const start = Math.max(0, page - 1) * resultsPerPage; + const stop = start + resultsPerPage - 1; + + if (req.query.query) { + const filterHidden = req.query.filterHidden === 'true' || !await user.isAdministrator(req.uid); + const groupData = await groups.search(req.query.query, { + sort, + filterHidden: filterHidden, + showMembers: req.query.showMembers === 'true', + hideEphemeralGroups: req.query.hideEphemeralGroups === 'true', + }); + const pageCount = Math.ceil(groupData.length / resultsPerPage); + + return [groupData.slice(start, stop + 1), pageCount]; + } + + const [groupData, groupCount] = await Promise.all([ + groups.getGroupsBySort(sort, start, stop), + groups.getGroupCountBySort(sort), + ]); + + const pageCount = Math.ceil(groupCount / resultsPerPage); + return [groupData, pageCount]; +} + groupsController.details = async function (req, res, next) { const lowercaseSlug = req.params.slug.toLowerCase(); if (req.params.slug !== lowercaseSlug) { diff --git a/src/groups/index.js b/src/groups/index.js index 8aef1a7b51..7a9b8a731e 100644 --- a/src/groups/index.js +++ b/src/groups/index.js @@ -76,14 +76,22 @@ Groups.getGroupsFromSet = async function (set, start, stop) { }; Groups.getGroupsBySort = async function (sort, start, stop) { + return await Groups.getGroupsFromSet(sortToSet(sort), start, stop); +}; + +Groups.getGroupCountBySort = async function (sort) { + return await db.sortedSetCard(sortToSet(sort)); +}; + +function sortToSet(sort) { let set = 'groups:visible:name'; if (sort === 'count') { set = 'groups:visible:memberCount'; } else if (sort === 'date') { set = 'groups:visible:createtime'; } - return await Groups.getGroupsFromSet(set, start, stop); -}; + return set; +} Groups.getNonPrivilegeGroups = async function (set, start, stop, flags) { if (!flags) {