mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 16:46:12 +01:00
refactor: announces
store number of announces on post hash, show announces like votes, with tooltip and a way to see all, remove them from topic.events so they dont load all tid:<tid>:posts everytime topic is loaded
This commit is contained in:
@@ -13,6 +13,6 @@
|
|||||||
"help.federating": "Likewise, if users from outside of this forum start following <em>you</em>, then your posts will start appearing on those apps and websites as well.",
|
"help.federating": "Likewise, if users from outside of this forum start following <em>you</em>, then your posts will start appearing on those apps and websites as well.",
|
||||||
"help.next-generation": "This is the next generation of social media, start contributing today!",
|
"help.next-generation": "This is the next generation of social media, start contributing today!",
|
||||||
|
|
||||||
"topic-event-announce-ago": "%1 shared <a href=\"%2\">this post</a> %3",
|
"announcers": "Announcers",
|
||||||
"topic-event-announce-on": "%1 shared <a href=\"%2\">this post</a> on %3"
|
"announcers-x": "Announcers (%1)"
|
||||||
}
|
}
|
||||||
@@ -186,6 +186,10 @@ paths:
|
|||||||
$ref: 'write/posts/pid/voters.yaml'
|
$ref: 'write/posts/pid/voters.yaml'
|
||||||
/posts/{pid}/upvoters:
|
/posts/{pid}/upvoters:
|
||||||
$ref: 'write/posts/pid/upvoters.yaml'
|
$ref: 'write/posts/pid/upvoters.yaml'
|
||||||
|
/posts/{pid}/announcers:
|
||||||
|
$ref: 'write/posts/pid/announcers.yaml'
|
||||||
|
/posts/{pid}/announcers/tooltip:
|
||||||
|
$ref: 'write/posts/pid/announcers-tooltip.yaml'
|
||||||
/posts/{pid}/bookmark:
|
/posts/{pid}/bookmark:
|
||||||
$ref: 'write/posts/pid/bookmark.yaml'
|
$ref: 'write/posts/pid/bookmark.yaml'
|
||||||
/posts/{pid}/diffs:
|
/posts/{pid}/diffs:
|
||||||
|
|||||||
33
public/openapi/write/posts/pid/announcers-tooltip.yaml
Normal file
33
public/openapi/write/posts/pid/announcers-tooltip.yaml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- posts
|
||||||
|
summary: get announcers of a post
|
||||||
|
description: This is used for getting a list of usernames for the announcers tooltip
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: pid
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: a valid post id
|
||||||
|
example: 2
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Usernames of announcers of post
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
$ref: ../../../components/schemas/Status.yaml#/Status
|
||||||
|
response:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
otherCount:
|
||||||
|
type: number
|
||||||
|
usernames:
|
||||||
|
type: array
|
||||||
|
cutoff:
|
||||||
|
type: number
|
||||||
|
|
||||||
32
public/openapi/write/posts/pid/announcers.yaml
Normal file
32
public/openapi/write/posts/pid/announcers.yaml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- posts
|
||||||
|
summary: get announcers of a post
|
||||||
|
description: This returns the announcers of a post if the user has permission to view them
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: pid
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
required: true
|
||||||
|
description: a valid post id
|
||||||
|
example: 2
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Data about announcers of this post
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
$ref: ../../../components/schemas/Status.yaml#/Status
|
||||||
|
response:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
announceCount:
|
||||||
|
type: number
|
||||||
|
announcers:
|
||||||
|
type: array
|
||||||
|
|
||||||
|
|
||||||
@@ -141,6 +141,10 @@ define('forum/topic/postTools', [
|
|||||||
votes.showVotes(getData($(this), 'data-pid'));
|
votes.showVotes(getData($(this), 'data-pid'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
postContainer.on('click', '[component="post/announce-count"]', function () {
|
||||||
|
votes.showAnnouncers(getData($(this), 'data-pid'));
|
||||||
|
});
|
||||||
|
|
||||||
postContainer.on('click', '[component="post/flag"]', function () {
|
postContainer.on('click', '[component="post/flag"]', function () {
|
||||||
const pid = getData($(this), 'data-pid');
|
const pid = getData($(this), 'data-pid');
|
||||||
require(['flags'], function (flags) {
|
require(['flags'], function (flags) {
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ define('forum/topic/votes', [
|
|||||||
components.get('topic').on('mouseenter', '[data-pid] [component="post/vote-count"]', loadDataAndCreateTooltip);
|
components.get('topic').on('mouseenter', '[data-pid] [component="post/vote-count"]', loadDataAndCreateTooltip);
|
||||||
components.get('topic').on('mouseleave', '[data-pid] [component="post/vote-count"]', destroyTooltip);
|
components.get('topic').on('mouseleave', '[data-pid] [component="post/vote-count"]', destroyTooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
components.get('topic').on('mouseenter', '[data-pid] [component="post/announce-count"]', loadDataAndCreateTooltip);
|
||||||
|
components.get('topic').on('mouseleave', '[data-pid] [component="post/announce-count"]', destroyTooltip);
|
||||||
};
|
};
|
||||||
|
|
||||||
function canSeeVotes() {
|
function canSeeVotes() {
|
||||||
@@ -43,8 +46,11 @@ define('forum/topic/votes', [
|
|||||||
tooltip.dispose();
|
tooltip.dispose();
|
||||||
$this.attr('title', '');
|
$this.attr('title', '');
|
||||||
}
|
}
|
||||||
|
const path = $this.attr('component') === 'post/vote-count' ?
|
||||||
|
`/posts/${encodeURIComponent(pid)}/upvoters` :
|
||||||
|
`/posts/${encodeURIComponent(pid)}/announcers/tooltip`;
|
||||||
|
|
||||||
api.get(`/posts/${encodeURIComponent(pid)}/upvoters`, {}, function (err, data) {
|
api.get(path, {}, function (err, data) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return alerts.error(err);
|
return alerts.error(err);
|
||||||
}
|
}
|
||||||
@@ -132,6 +138,24 @@ define('forum/topic/votes', [
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Votes.showAnnouncers = async function (pid) {
|
||||||
|
const data = await api.get(`/posts/${encodeURIComponent(pid)}/announcers`, {})
|
||||||
|
.catch(err => alerts.error(err));
|
||||||
|
|
||||||
|
const html = await app.parseAndTranslate('modals/announcers', data);
|
||||||
|
const dialog = bootbox.dialog({
|
||||||
|
title: `[[activitypub:announcers-x, ${data.announceCount}]]`,
|
||||||
|
message: html,
|
||||||
|
className: 'announce-modal',
|
||||||
|
show: true,
|
||||||
|
onEscape: true,
|
||||||
|
backdrop: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.on('click', function () {
|
||||||
|
dialog.modal('hide');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return Votes;
|
return Votes;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -365,14 +365,24 @@ Notes.announce.list = async ({ pid, tid }) => {
|
|||||||
|
|
||||||
Notes.announce.add = async (pid, actor, timestamp = Date.now()) => {
|
Notes.announce.add = async (pid, actor, timestamp = Date.now()) => {
|
||||||
await db.sortedSetAdd(`pid:${pid}:announces`, timestamp, actor);
|
await db.sortedSetAdd(`pid:${pid}:announces`, timestamp, actor);
|
||||||
|
await posts.setPostField(pid, 'announces', await db.sortedSetCard(`pid:${pid}:announces`));
|
||||||
};
|
};
|
||||||
|
|
||||||
Notes.announce.remove = async (pid, actor) => {
|
Notes.announce.remove = async (pid, actor) => {
|
||||||
await db.sortedSetRemove(`pid:${pid}:announces`, actor);
|
await db.sortedSetRemove(`pid:${pid}:announces`, actor);
|
||||||
|
const count = await db.sortedSetCard(`pid:${pid}:announces`);
|
||||||
|
if (count > 0) {
|
||||||
|
await posts.setPostField(pid, 'announces', count);
|
||||||
|
} else {
|
||||||
|
await db.deleteObjectField(`post:${pid}`, 'announces');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Notes.announce.removeAll = async (pid) => {
|
Notes.announce.removeAll = async (pid) => {
|
||||||
await db.delete(`pid:${pid}:announces`);
|
await Promise.all([
|
||||||
|
db.delete(`pid:${pid}:announces`),
|
||||||
|
db.deleteObjectField(`post:${pid}`, 'announces'),
|
||||||
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
Notes.delete = async (pids) => {
|
Notes.delete = async (pids) => {
|
||||||
|
|||||||
@@ -364,9 +364,13 @@ postsAPI.getUpvoters = async function (caller, data) {
|
|||||||
throw new Error('[[error:no-privileges]]');
|
throw new Error('[[error:no-privileges]]');
|
||||||
}
|
}
|
||||||
|
|
||||||
let upvotedUids = (await posts.getUpvotedUidsByPids([pid]))[0];
|
const upvotedUids = (await posts.getUpvotedUidsByPids([pid]))[0];
|
||||||
|
return await getTooltipData(upvotedUids);
|
||||||
|
};
|
||||||
|
|
||||||
|
async function getTooltipData(uids) {
|
||||||
const cutoff = 6;
|
const cutoff = 6;
|
||||||
if (!upvotedUids.length) {
|
if (!uids.length) {
|
||||||
return {
|
return {
|
||||||
otherCount: 0,
|
otherCount: 0,
|
||||||
usernames: [],
|
usernames: [],
|
||||||
@@ -374,17 +378,41 @@ postsAPI.getUpvoters = async function (caller, data) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
let otherCount = 0;
|
let otherCount = 0;
|
||||||
if (upvotedUids.length > cutoff) {
|
if (uids.length > cutoff) {
|
||||||
otherCount = upvotedUids.length - (cutoff - 1);
|
otherCount = uids.length - (cutoff - 1);
|
||||||
upvotedUids = upvotedUids.slice(0, cutoff - 1);
|
uids = uids.slice(0, cutoff - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const usernames = await user.getUsernamesByUids(upvotedUids);
|
const usernames = await user.getUsernamesByUids(uids);
|
||||||
return {
|
return {
|
||||||
otherCount,
|
otherCount,
|
||||||
usernames,
|
usernames,
|
||||||
cutoff,
|
cutoff,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
postsAPI.getAnnouncers = async (caller, data) => {
|
||||||
|
if (!data.pid) {
|
||||||
|
throw new Error('[[error:invalid-data]]');
|
||||||
|
}
|
||||||
|
if (!meta.config.activitypubEnabled) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const { pid } = data;
|
||||||
|
const cid = await posts.getCidByPid(pid);
|
||||||
|
if (!await privileges.categories.isUserAllowedTo('topics:read', cid, caller.uid)) {
|
||||||
|
throw new Error('[[error:no-privileges]]');
|
||||||
|
}
|
||||||
|
const notes = require('../activitypub/notes');
|
||||||
|
const announcers = await notes.announce.list({ pid });
|
||||||
|
const uids = announcers.map(ann => ann.actor);
|
||||||
|
if (data.tooltip) {
|
||||||
|
return await getTooltipData(uids);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
announceCount: uids.length,
|
||||||
|
announcers: await user.getUsersFields(uids, ['username', 'userslug', 'picture']),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
async function canSeeVotes(uid, cids) {
|
async function canSeeVotes(uid, cids) {
|
||||||
|
|||||||
@@ -141,6 +141,16 @@ Posts.getUpvoters = async (req, res) => {
|
|||||||
helpers.formatApiResponse(200, res, data);
|
helpers.formatApiResponse(200, res, data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Posts.getAnnouncers = async (req, res) => {
|
||||||
|
const data = await api.posts.getAnnouncers(req, { pid: req.params.pid, tooltip: 0 });
|
||||||
|
helpers.formatApiResponse(200, res, data);
|
||||||
|
};
|
||||||
|
|
||||||
|
Posts.getAnnouncersTooltip = async (req, res) => {
|
||||||
|
const data = await api.posts.getAnnouncers(req, { pid: req.params.pid, tooltip: 1 });
|
||||||
|
helpers.formatApiResponse(200, res, data);
|
||||||
|
};
|
||||||
|
|
||||||
Posts.bookmark = async (req, res) => {
|
Posts.bookmark = async (req, res) => {
|
||||||
const data = await mock(req);
|
const data = await mock(req);
|
||||||
await api.posts.bookmark(req, data);
|
await api.posts.bookmark(req, data);
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ module.exports = function () {
|
|||||||
setupApiRoute(router, 'get', '/:pid/voters', [middleware.assert.post], controllers.write.posts.getVoters);
|
setupApiRoute(router, 'get', '/:pid/voters', [middleware.assert.post], controllers.write.posts.getVoters);
|
||||||
setupApiRoute(router, 'get', '/:pid/upvoters', [middleware.assert.post], controllers.write.posts.getUpvoters);
|
setupApiRoute(router, 'get', '/:pid/upvoters', [middleware.assert.post], controllers.write.posts.getUpvoters);
|
||||||
|
|
||||||
|
setupApiRoute(router, 'get', '/:pid/announcers', [middleware.assert.post], controllers.write.posts.getAnnouncers);
|
||||||
|
setupApiRoute(router, 'get', '/:pid/announcers/tooltip', [middleware.assert.post], controllers.write.posts.getAnnouncersTooltip);
|
||||||
setupApiRoute(router, 'put', '/:pid/bookmark', middlewares, controllers.write.posts.bookmark);
|
setupApiRoute(router, 'put', '/:pid/bookmark', middlewares, controllers.write.posts.bookmark);
|
||||||
setupApiRoute(router, 'delete', '/:pid/bookmark', middlewares, controllers.write.posts.unbookmark);
|
setupApiRoute(router, 'delete', '/:pid/bookmark', middlewares, controllers.write.posts.unbookmark);
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ const categories = require('../categories');
|
|||||||
const plugins = require('../plugins');
|
const plugins = require('../plugins');
|
||||||
const translator = require('../translator');
|
const translator = require('../translator');
|
||||||
const privileges = require('../privileges');
|
const privileges = require('../privileges');
|
||||||
const activitypub = require('../activitypub');
|
|
||||||
const utils = require('../utils');
|
const utils = require('../utils');
|
||||||
const helpers = require('../helpers');
|
const helpers = require('../helpers');
|
||||||
|
|
||||||
@@ -69,10 +68,6 @@ Events._types = {
|
|||||||
icon: 'fa-code-fork',
|
icon: 'fa-code-fork',
|
||||||
translation: async (event, language) => translateEventArgs(event, language, 'topic:user-forked-topic', renderUser(event), `${relative_path}${event.href}`, renderTimeago(event)),
|
translation: async (event, language) => translateEventArgs(event, language, 'topic:user-forked-topic', renderUser(event), `${relative_path}${event.href}`, renderTimeago(event)),
|
||||||
},
|
},
|
||||||
announce: {
|
|
||||||
icon: 'fa-share-alt',
|
|
||||||
translation: async (event, language) => translateEventArgs(event, language, 'activitypub:topic-event-announce', renderUser(event), `${relative_path}${event.href}`, renderTimeago(event)),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Events.init = async () => {
|
Events.init = async () => {
|
||||||
@@ -175,19 +170,6 @@ async function modifyEvent({ tid, uid, eventIds, timestamps, events }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add post announces
|
|
||||||
const announces = await activitypub.notes.announce.list({ tid });
|
|
||||||
announces.forEach(({ actor, pid, timestamp }) => {
|
|
||||||
events.push({
|
|
||||||
type: 'announce',
|
|
||||||
uid: actor,
|
|
||||||
href: `/post/${encodeURIComponent(pid)}`,
|
|
||||||
pid,
|
|
||||||
timestamp,
|
|
||||||
});
|
|
||||||
timestamps.push(timestamp);
|
|
||||||
});
|
|
||||||
|
|
||||||
const [users, fromCategories, userSettings] = await Promise.all([
|
const [users, fromCategories, userSettings] = await Promise.all([
|
||||||
getUserInfo(events.map(event => event.uid).filter(Boolean)),
|
getUserInfo(events.map(event => event.uid).filter(Boolean)),
|
||||||
getCategoryInfo(events.map(event => event.fromCid).filter(Boolean)),
|
getCategoryInfo(events.map(event => event.fromCid).filter(Boolean)),
|
||||||
|
|||||||
5
src/views/modals/announcers.tpl
Normal file
5
src/views/modals/announcers.tpl
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<div class="mb-3">
|
||||||
|
{{{ each announcers }}}
|
||||||
|
<a class="text-decoration-none" href="{config.relative_path}/user/{./userslug}">{buildAvatar(@value, "24px", true)}</a>
|
||||||
|
{{{ end }}}
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user