diff --git a/public/openapi/write.yaml b/public/openapi/write.yaml index f62d1faca0..63c52430b8 100644 --- a/public/openapi/write.yaml +++ b/public/openapi/write.yaml @@ -118,6 +118,8 @@ paths: $ref: 'write/categories/cid/posts.yaml' /categories/{cid}/children: $ref: 'write/categories/cid/children.yaml' + /categories/{cid}/topics: + $ref: 'write/categories/cid/topics.yaml' /categories/{cid}/watch: $ref: 'write/categories/cid/watch.yaml' /categories/{cid}/privileges: diff --git a/public/openapi/write/categories/cid/topics.yaml b/public/openapi/write/categories/cid/topics.yaml new file mode 100644 index 0000000000..a14664b20c --- /dev/null +++ b/public/openapi/write/categories/cid/topics.yaml @@ -0,0 +1,75 @@ +get: + tags: + - categories + summary: get topics + description: | + This operation returns a set of topics in the requested category. + + The number of topics returned is defined by the "Topics per Page" (`topicsPerPage`) setting under ACP > Settings > Pagination. + parameters: + - in: path + name: cid + schema: + type: string + required: true + description: a valid category id, `0` for global privileges, `admin` for admin privileges + example: 1 + - in: query + name: 'query' + schema: + type: string + required: false + description: Likely unused — a URI-encoded JSON string containing values that are passed to `getCategoryTopics`. + example: '' + - in: query + name: 'after' + schema: + type: string + required: false + description: The index to start at when querying for the next set of topics. This parameter would be more aptly named `start`. + example: '0' + - in: query + name: 'sort' + schema: + type: string + required: false + description: Likely deprecated — the sorting method of topics (use `categoryTopicSort` instead.) + example: '' + - in: query + name: 'categoryTopicSort' + schema: + type: string + required: false + description: The sorting method of topics + example: 'newest_to_oldest' + - in: query + name: 'direction' + schema: + type: string + required: false + description: The sorting of returned results (if you scroll up you want the topics reversed). Set to "-1" for reversed results. + example: '1' + responses: + '200': + description: categories topics successfully retrieved + content: + application/json: + schema: + type: object + properties: + status: + $ref: ../../../components/schemas/Status.yaml#/Status + response: + type: object + properties: + topics: + type: array + items: + $ref: ../../../components/schemas/TopicObject.yaml#/TopicObject + nextStart: + type: number + privileges: + type: object + additionalProperties: + type: boolean + description: A set of privileges with either true or false \ No newline at end of file diff --git a/public/src/client/category.js b/public/src/client/category.js index b14a80d0c9..e1ab97431f 100644 --- a/public/src/client/category.js +++ b/public/src/client/category.js @@ -123,7 +123,7 @@ define('forum/category', [ hooks.fire('action:topics.loading'); const params = utils.params(); - infinitescroll.loadMore('categories.loadMore', { + infinitescroll.loadMore(`/categories/${ajaxify.data.cid}/topics`, { cid: ajaxify.data.cid, after: after, direction: direction, diff --git a/public/src/client/infinitescroll.js b/public/src/client/infinitescroll.js index bd6f98d178..838f164f32 100644 --- a/public/src/client/infinitescroll.js +++ b/public/src/client/infinitescroll.js @@ -1,7 +1,7 @@ 'use strict'; -define('forum/infinitescroll', ['hooks', 'alerts'], function (hooks, alerts) { +define('forum/infinitescroll', ['hooks', 'alerts', 'api'], function (hooks, alerts, api) { const scroll = {}; let callback; let previousScrollTop = 0; @@ -72,7 +72,9 @@ define('forum/infinitescroll', ['hooks', 'alerts'], function (hooks, alerts) { const hookData = { method: method, data: data }; hooks.fire('action:infinitescroll.loadmore', hookData); - socket.emit(hookData.method, hookData.data, function (err, data) { + const call = hookData.method.startsWith('/') ? api.get : socket.emit; + + call(hookData.method, hookData.data, function (err, data) { if (err) { loadingMore = false; return alerts.error(err); diff --git a/src/api/categories.js b/src/api/categories.js index 7cb7a17f69..774091fd61 100644 --- a/src/api/categories.js +++ b/src/api/categories.js @@ -1,5 +1,6 @@ 'use strict'; +const meta = require('../meta'); const categories = require('../categories'); const topics = require('../topics'); const events = require('../events'); @@ -106,6 +107,47 @@ categoriesAPI.getChildren = async (caller, { cid, start }) => { return { categories: payload }; }; +categoriesAPI.getTopics = async (caller, data) => { + data.query = data.query || {}; + const [userPrivileges, settings, targetUid] = await Promise.all([ + privileges.categories.get(data.cid, caller.uid), + user.getSettings(caller.uid), + user.getUidByUserslug(data.query.author), + ]); + + if (!userPrivileges.read) { + throw new Error('[[error:no-privileges]]'); + } + + const infScrollTopicsPerPage = 20; + const sort = data.sort || data.categoryTopicSort || meta.config.categoryTopicSort || 'newest_to_oldest'; + + let start = Math.max(0, parseInt(data.after || 0, 10)); + + if (data.direction === -1) { + start -= infScrollTopicsPerPage; + } + + let stop = start + infScrollTopicsPerPage - 1; + + start = Math.max(0, start); + stop = Math.max(0, stop); + const result = await categories.getCategoryTopics({ + uid: caller.uid, + cid: data.cid, + start, + stop, + sort, + settings, + query: data.query, + tag: data.query.tag, + targetUid, + }); + categories.modifyTopicsByPrivilege(result.topics, userPrivileges); + + return { ...result, privileges: userPrivileges }; +}; + categoriesAPI.setWatchState = async (caller, { cid, state, uid }) => { let targetUid = caller.uid; const cids = Array.isArray(cid) ? cid.map(cid => parseInt(cid, 10)) : [parseInt(cid, 10)]; diff --git a/src/controllers/write/categories.js b/src/controllers/write/categories.js index 133ec2c055..80ee961fbf 100644 --- a/src/controllers/write/categories.js +++ b/src/controllers/write/categories.js @@ -51,6 +51,13 @@ Categories.getChildren = async (req, res) => { helpers.formatApiResponse(200, res, await api.categories.getChildren(req, { cid, start })); }; +Categories.getTopics = async (req, res) => { + const { cid } = req.params; + const result = await api.categories.getTopics(req, { ...req.query, cid }); + + helpers.formatApiResponse(200, res, result); +}; + Categories.setWatchState = async (req, res) => { const { cid } = req.params; let { uid, state } = req.body; diff --git a/src/routes/write/categories.js b/src/routes/write/categories.js index 9121ee9a7f..ca149a54da 100644 --- a/src/routes/write/categories.js +++ b/src/routes/write/categories.js @@ -19,6 +19,7 @@ module.exports = function () { setupApiRoute(router, 'get', '/:cid/count', [...middlewares, middleware.assert.category], controllers.write.categories.getTopicCount); setupApiRoute(router, 'get', '/:cid/posts', [...middlewares, middleware.assert.category], controllers.write.categories.getPosts); setupApiRoute(router, 'get', '/:cid/children', [...middlewares, middleware.assert.category], controllers.write.categories.getChildren); + setupApiRoute(router, 'get', '/:cid/topics', [...middlewares, middleware.assert.category], controllers.write.categories.getTopics); setupApiRoute(router, 'put', '/:cid/watch', [...middlewares, middleware.assert.category], controllers.write.categories.setWatchState); setupApiRoute(router, 'delete', '/:cid/watch', [...middlewares, middleware.assert.category], controllers.write.categories.setWatchState); diff --git a/src/socket.io/categories.js b/src/socket.io/categories.js index a62d55edfe..934defaeac 100644 --- a/src/socket.io/categories.js +++ b/src/socket.io/categories.js @@ -1,7 +1,6 @@ 'use strict'; const categories = require('../categories'); -const privileges = require('../privileges'); const user = require('../user'); const topics = require('../topics'); const api = require('../api'); @@ -38,47 +37,15 @@ SocketCategories.loadMore = async function (socket, data) { throw new Error('[[error:invalid-data]]'); } data.query = data.query || {}; - const [userPrivileges, settings, targetUid] = await Promise.all([ - privileges.categories.get(data.cid, socket.uid), - user.getSettings(socket.uid), - user.getUidByUserslug(data.query.author), - ]); - if (!userPrivileges.read) { - throw new Error('[[error:no-privileges]]'); - } + const result = await api.categories.getTopics(socket, data); - const infScrollTopicsPerPage = 20; - const sort = data.sort || data.categoryTopicSort; - - let start = Math.max(0, parseInt(data.after, 10)); - - if (data.direction === -1) { - start -= infScrollTopicsPerPage; - } - - let stop = start + infScrollTopicsPerPage - 1; - - start = Math.max(0, start); - stop = Math.max(0, stop); - const result = await categories.getCategoryTopics({ - uid: socket.uid, - cid: data.cid, - start: start, - stop: stop, - sort: sort, - settings: settings, - query: data.query, - tag: data.query.tag, - targetUid: targetUid, - }); - categories.modifyTopicsByPrivilege(result.topics, userPrivileges); - - result.privileges = userPrivileges; + // Backwards compatibility — unsure of current usage. result.template = { category: true, name: 'category', }; + return result; };