@nodebb/nodebb-plugin-reactions 2.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/languages/en-GB/reactions.json +2 -0
- package/library.js +110 -68
- package/package.json +1 -1
- package/plugin.json +3 -0
- package/public/client.js +76 -11
- package/templates/admin/plugins/reactions.tpl +9 -1
- package/templates/partials/chats/add-reaction.tpl +2 -0
- package/templates/partials/chats/reaction.tpl +1 -1
- package/templates/partials/chats/reactions.tpl +3 -1
- package/templates/partials/topic/reaction.tpl +1 -1
- package/templates/partials/topic/reactions.tpl +3 -1
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
"error.maximum-reached": "Maximum reactions reached",
|
|
6
6
|
"error.maximum-per-user-per-post-reached": "Maximum reactions per user per post reached",
|
|
7
7
|
"settings.title": "Reactions plugin settings",
|
|
8
|
+
"settings.enable-post-reactions": "Enable post reactions",
|
|
8
9
|
"settings.max-reactions-per-post": "Maximum unique reactions per post (0 for unlimited, default: 4)",
|
|
9
10
|
"settings.max-reactions-per-user-per-post": "Maximum reactions per user per post (0 for unlimited)",
|
|
10
11
|
"settings.max-reactions-per-user-per-post-help": "The limit is enforced only when adding a new reaction and not when joining an existing reaction.",
|
|
12
|
+
"settings.enable-message-reactions": "Enable message reactions",
|
|
11
13
|
"settings.max-reactions-per-message": "Maximum unique reactions per message (0 for unlimited, default: 4)",
|
|
12
14
|
"settings.max-reactions-per-user-per-message": "Maximum reactions per user per message (0 for unlimited)",
|
|
13
15
|
"settings.max-reactions-per-user-per-message-help": "The limit is enforced only when adding a new reaction and not when joining an existing reaction.",
|
package/library.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const _ = require.main.require('lodash');
|
|
4
3
|
const meta = require.main.require('./src/meta');
|
|
5
4
|
const user = require.main.require('./src/user');
|
|
6
5
|
const posts = require.main.require('./src/posts');
|
|
7
6
|
const messaging = require.main.require('./src/messaging');
|
|
7
|
+
const privileges = require.main.require('./src/privileges');
|
|
8
8
|
const db = require.main.require('./src/database');
|
|
9
9
|
const routesHelpers = require.main.require('./src/routes/helpers');
|
|
10
10
|
const websockets = require.main.require('./src/socket.io/index');
|
|
@@ -45,12 +45,27 @@ ReactionsPlugin.getPluginConfig = async function (config) {
|
|
|
45
45
|
config.maximumReactions = settings.maximumReactions ? parseInt(settings.maximumReactions, 10) : DEFAULT_MAX_EMOTES;
|
|
46
46
|
config.maximumReactionsPerMessage = settings.maximumReactionsPerMessage ?
|
|
47
47
|
parseInt(settings.maximumReactionsPerMessage, 10) : DEFAULT_MAX_EMOTES;
|
|
48
|
+
config.enablePostReactions = settings.enablePostReactions === 'on';
|
|
49
|
+
config.enableMessageReactions = settings.enableMessageReactions === 'on';
|
|
48
50
|
} catch (e) {
|
|
49
51
|
console.error(e);
|
|
50
52
|
}
|
|
51
53
|
return config;
|
|
52
54
|
};
|
|
53
55
|
|
|
56
|
+
ReactionsPlugin.filterSettingsGet = async function (hookData) {
|
|
57
|
+
if (hookData.plugin === 'reactions') {
|
|
58
|
+
const { values } = hookData;
|
|
59
|
+
if (!values.hasOwnProperty('enablePostReactions')) {
|
|
60
|
+
values.enablePostReactions = 'on';
|
|
61
|
+
}
|
|
62
|
+
if (!values.hasOwnProperty('enableMessageReactions')) {
|
|
63
|
+
values.enableMessageReactions = 'on';
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return hookData;
|
|
67
|
+
};
|
|
68
|
+
|
|
54
69
|
ReactionsPlugin.getPostReactions = async function (data) {
|
|
55
70
|
if (data.uid === 0) {
|
|
56
71
|
return data;
|
|
@@ -58,6 +73,9 @@ ReactionsPlugin.getPostReactions = async function (data) {
|
|
|
58
73
|
|
|
59
74
|
try {
|
|
60
75
|
const settings = await meta.settings.get('reactions');
|
|
76
|
+
if (settings.enablePostReactions === 'off') {
|
|
77
|
+
return data;
|
|
78
|
+
}
|
|
61
79
|
const maximumReactions = settings.maximumReactions || DEFAULT_MAX_EMOTES;
|
|
62
80
|
|
|
63
81
|
const pids = data.posts.map(post => post && parseInt(post.pid, 10));
|
|
@@ -83,42 +101,24 @@ ReactionsPlugin.getPostReactions = async function (data) {
|
|
|
83
101
|
}
|
|
84
102
|
}
|
|
85
103
|
|
|
86
|
-
const reactionSetToUsersMap =
|
|
87
|
-
if (reactionSets.length > 0) {
|
|
88
|
-
const uidsForReactions = await db.getSetsMembers(reactionSets);
|
|
89
|
-
const allUids = _.union(...uidsForReactions).filter(Boolean);
|
|
90
|
-
const usersData = await user.getUsersFields(allUids, ['uid', 'username']);
|
|
91
|
-
const uidToUserdataMap = _.keyBy(usersData, 'uid');
|
|
92
|
-
|
|
93
|
-
for (let i = 0, len = reactionSets.length; i < len; i++) {
|
|
94
|
-
const uidsForReaction = uidsForReactions[i];
|
|
95
|
-
if (uidsForReaction && uidsForReaction.length > 0) {
|
|
96
|
-
const usersData = uidsForReaction.map(uid => uidToUserdataMap[uid]).filter(Boolean);
|
|
97
|
-
reactionSetToUsersMap.set(reactionSets[i], usersData);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
104
|
+
const reactionSetToUsersMap = await getReactionSetsUidsMap(reactionSets);
|
|
101
105
|
|
|
102
106
|
for (const post of data.posts) {
|
|
103
107
|
post.maxReactionsReached = pidToIsMaxReactionsReachedMap.get(post.pid);
|
|
104
108
|
post.reactions = [];
|
|
105
109
|
|
|
106
|
-
|
|
107
|
-
|
|
110
|
+
const reactions = pidToReactionsMap.get(post.pid);
|
|
111
|
+
if (reactions) {
|
|
112
|
+
for (const reaction of reactions) {
|
|
108
113
|
const reactionSet = `pid:${post.pid}:reaction:${reaction}`;
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const reactionCount = usersData.length;
|
|
112
|
-
const reactedUsernames = usersData.map(userData => userData.username).join(', ');
|
|
113
|
-
const reactedUids = usersData.map(userData => userData.uid);
|
|
114
|
-
|
|
114
|
+
const uids = reactionSetToUsersMap.get(reactionSet);
|
|
115
|
+
if (Array.isArray(uids)) {
|
|
115
116
|
post.reactions.push({
|
|
116
117
|
pid: post.pid,
|
|
117
|
-
reacted:
|
|
118
|
+
reacted: uids.includes(String(data.uid)),
|
|
118
119
|
reaction,
|
|
119
|
-
usernames: reactedUsernames,
|
|
120
120
|
reactionImage: parse(reaction),
|
|
121
|
-
reactionCount,
|
|
121
|
+
reactionCount: uids.length,
|
|
122
122
|
});
|
|
123
123
|
}
|
|
124
124
|
}
|
|
@@ -137,6 +137,9 @@ ReactionsPlugin.getMessageReactions = async function (data) {
|
|
|
137
137
|
|
|
138
138
|
try {
|
|
139
139
|
const settings = await meta.settings.get('reactions');
|
|
140
|
+
if (settings.enableMessageReactions === 'off') {
|
|
141
|
+
return data;
|
|
142
|
+
}
|
|
140
143
|
const maximumReactionsPerMessage = settings.maximumReactionsPerMessage || DEFAULT_MAX_EMOTES;
|
|
141
144
|
|
|
142
145
|
const mids = data.messages.map(message => message && parseInt(message.mid, 10));
|
|
@@ -158,42 +161,23 @@ ReactionsPlugin.getMessageReactions = async function (data) {
|
|
|
158
161
|
}
|
|
159
162
|
}
|
|
160
163
|
|
|
161
|
-
const reactionSetToUsersMap =
|
|
162
|
-
if (reactionSets.length > 0) {
|
|
163
|
-
const uidsForReactions = await db.getSetsMembers(reactionSets);
|
|
164
|
-
const allUids = _.union(...uidsForReactions).filter(Boolean);
|
|
165
|
-
const usersData = await user.getUsersFields(allUids, ['uid', 'username']);
|
|
166
|
-
const uidToUserdataMap = _.keyBy(usersData, 'uid');
|
|
167
|
-
|
|
168
|
-
for (let i = 0, len = reactionSets.length; i < len; i++) {
|
|
169
|
-
const uidsForReaction = uidsForReactions[i];
|
|
170
|
-
if (uidsForReaction && uidsForReaction.length > 0) {
|
|
171
|
-
const usersData = uidsForReaction.map(uid => uidToUserdataMap[uid]).filter(Boolean);
|
|
172
|
-
reactionSetToUsersMap.set(reactionSets[i], usersData);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
164
|
+
const reactionSetToUsersMap = await getReactionSetsUidsMap(reactionSets);
|
|
176
165
|
|
|
177
166
|
for (const msg of data.messages) {
|
|
178
167
|
msg.maxReactionsReached = midToIsMaxReactionsReachedMap.get(msg.mid);
|
|
179
168
|
msg.reactions = [];
|
|
180
|
-
|
|
181
|
-
if (
|
|
182
|
-
for (const reaction of
|
|
169
|
+
const reactions = midToReactionsMap.get(msg.mid);
|
|
170
|
+
if (reactions) {
|
|
171
|
+
for (const reaction of reactions) {
|
|
183
172
|
const reactionSet = `mid:${msg.mid}:reaction:${reaction}`;
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const reactionCount = usersData.length;
|
|
187
|
-
const reactedUsernames = usersData.map(userData => userData.username).join(', ');
|
|
188
|
-
const reactedUids = usersData.map(userData => userData.uid);
|
|
189
|
-
|
|
173
|
+
const uids = reactionSetToUsersMap.get(reactionSet);
|
|
174
|
+
if (Array.isArray(uids)) {
|
|
190
175
|
msg.reactions.push({
|
|
191
176
|
mid: msg.mid,
|
|
192
|
-
reacted:
|
|
177
|
+
reacted: uids.includes(String(data.uid)),
|
|
193
178
|
reaction,
|
|
194
|
-
usernames: reactedUsernames,
|
|
195
179
|
reactionImage: parse(reaction),
|
|
196
|
-
reactionCount,
|
|
180
|
+
reactionCount: uids.length,
|
|
197
181
|
});
|
|
198
182
|
}
|
|
199
183
|
}
|
|
@@ -205,6 +189,22 @@ ReactionsPlugin.getMessageReactions = async function (data) {
|
|
|
205
189
|
return data;
|
|
206
190
|
};
|
|
207
191
|
|
|
192
|
+
|
|
193
|
+
async function getReactionSetsUidsMap(reactionSets) {
|
|
194
|
+
const reactionSetToUsersMap = new Map(); // reactionSet -> uids
|
|
195
|
+
if (reactionSets.length > 0) {
|
|
196
|
+
const uidsForReactions = await db.getSetsMembers(reactionSets);
|
|
197
|
+
|
|
198
|
+
for (let i = 0, len = reactionSets.length; i < len; i++) {
|
|
199
|
+
const uidsForReaction = uidsForReactions[i];
|
|
200
|
+
if (uidsForReaction && uidsForReaction.length > 0) {
|
|
201
|
+
reactionSetToUsersMap.set(reactionSets[i], uidsForReaction);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return reactionSetToUsersMap;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
208
|
ReactionsPlugin.onReply = async function (data) {
|
|
209
209
|
if (data.uid !== 0) {
|
|
210
210
|
data.reactions = [];
|
|
@@ -229,15 +229,11 @@ ReactionsPlugin.deleteReactions = async function (hookData) {
|
|
|
229
229
|
|
|
230
230
|
async function sendPostEvent(data, eventName) {
|
|
231
231
|
try {
|
|
232
|
-
const [reactionCount, totalReactions
|
|
232
|
+
const [reactionCount, totalReactions] = await Promise.all([
|
|
233
233
|
db.setCount(`pid:${data.pid}:reaction:${data.reaction}`),
|
|
234
234
|
db.setCount(`pid:${data.pid}:reactions`),
|
|
235
|
-
db.getSetMembers(`pid:${data.pid}:reaction:${data.reaction}`),
|
|
236
235
|
]);
|
|
237
236
|
|
|
238
|
-
const userdata = await user.getUsersFields(uids, ['uid', 'username']);
|
|
239
|
-
const usernames = userdata.map(user => user.username).join(', ');
|
|
240
|
-
|
|
241
237
|
if (parseInt(reactionCount, 10) === 0) {
|
|
242
238
|
await db.setRemove(`pid:${data.pid}:reactions`, data.reaction);
|
|
243
239
|
}
|
|
@@ -248,7 +244,6 @@ async function sendPostEvent(data, eventName) {
|
|
|
248
244
|
reaction: data.reaction,
|
|
249
245
|
reactionCount,
|
|
250
246
|
totalReactions,
|
|
251
|
-
usernames,
|
|
252
247
|
reactionImage: parse(data.reaction),
|
|
253
248
|
});
|
|
254
249
|
} catch (e) {
|
|
@@ -258,15 +253,11 @@ async function sendPostEvent(data, eventName) {
|
|
|
258
253
|
|
|
259
254
|
async function sendMessageEvent(data, eventName) {
|
|
260
255
|
try {
|
|
261
|
-
const [reactionCount, totalReactions
|
|
256
|
+
const [reactionCount, totalReactions] = await Promise.all([
|
|
262
257
|
db.setCount(`mid:${data.mid}:reaction:${data.reaction}`),
|
|
263
258
|
db.setCount(`mid:${data.mid}:reactions`),
|
|
264
|
-
db.getSetMembers(`mid:${data.mid}:reaction:${data.reaction}`),
|
|
265
259
|
]);
|
|
266
260
|
|
|
267
|
-
const userdata = await user.getUsersFields(uids, ['uid', 'username']);
|
|
268
|
-
const usernames = userdata.map(user => user.username).join(', ');
|
|
269
|
-
|
|
270
261
|
if (parseInt(reactionCount, 10) === 0) {
|
|
271
262
|
await db.setRemove(`mid:${data.mid}:reactions`, data.reaction);
|
|
272
263
|
}
|
|
@@ -277,7 +268,6 @@ async function sendMessageEvent(data, eventName) {
|
|
|
277
268
|
reaction: data.reaction,
|
|
278
269
|
reactionCount,
|
|
279
270
|
totalReactions,
|
|
280
|
-
usernames,
|
|
281
271
|
reactionImage: parse(data.reaction),
|
|
282
272
|
});
|
|
283
273
|
} catch (e) {
|
|
@@ -310,6 +300,9 @@ SocketPlugins.reactions = {
|
|
|
310
300
|
}
|
|
311
301
|
|
|
312
302
|
const settings = await meta.settings.get('reactions');
|
|
303
|
+
if (settings.enablePostReactions === 'off') {
|
|
304
|
+
throw new Error('[[error:post-reactions-disabled]]');
|
|
305
|
+
}
|
|
313
306
|
const maximumReactions = settings.maximumReactions || DEFAULT_MAX_EMOTES;
|
|
314
307
|
const [tid, totalReactions, emojiIsAlreadyExist, alreadyReacted, reactionReputation] = await Promise.all([
|
|
315
308
|
posts.getPostField(data.pid, 'tid'),
|
|
@@ -360,11 +353,15 @@ SocketPlugins.reactions = {
|
|
|
360
353
|
throw new Error('[[reactions:error.invalid-reaction]]');
|
|
361
354
|
}
|
|
362
355
|
|
|
363
|
-
const [tid, hasReacted, reactionReputation] = await Promise.all([
|
|
356
|
+
const [settings, tid, hasReacted, reactionReputation] = await Promise.all([
|
|
357
|
+
meta.settings.get('reactions'),
|
|
364
358
|
posts.getPostField(data.pid, 'tid'),
|
|
365
359
|
db.isSetMember(`pid:${data.pid}:reaction:${data.reaction}`, socket.uid),
|
|
366
360
|
getReactionReputation(data.reaction),
|
|
367
361
|
]);
|
|
362
|
+
if (settings.enablePostReactions === 'off') {
|
|
363
|
+
throw new Error('[[error:post-reactions-disabled]]');
|
|
364
|
+
}
|
|
368
365
|
if (!tid) {
|
|
369
366
|
throw new Error('[[error:no-post]]');
|
|
370
367
|
}
|
|
@@ -395,6 +392,9 @@ SocketPlugins.reactions = {
|
|
|
395
392
|
}
|
|
396
393
|
|
|
397
394
|
const settings = await meta.settings.get('reactions');
|
|
395
|
+
if (settings.enableMessageReactions === 'off') {
|
|
396
|
+
throw new Error('[[error:post-reactions-disabled]]');
|
|
397
|
+
}
|
|
398
398
|
const maximumReactionsPerMessage = settings.maximumReactionsPerMessage || DEFAULT_MAX_EMOTES;
|
|
399
399
|
const [roomId, totalReactions, emojiIsAlreadyExist] = await Promise.all([
|
|
400
400
|
messaging.getMessageField(data.mid, 'roomId'),
|
|
@@ -442,10 +442,14 @@ SocketPlugins.reactions = {
|
|
|
442
442
|
throw new Error('[[reactions:error.invalid-reaction]]');
|
|
443
443
|
}
|
|
444
444
|
|
|
445
|
-
const [roomId, hasReacted] = await Promise.all([
|
|
445
|
+
const [settings, roomId, hasReacted] = await Promise.all([
|
|
446
|
+
meta.settings.get('reactions'),
|
|
446
447
|
messaging.getMessageField(data.mid, 'roomId'),
|
|
447
448
|
db.isSetMember(`mid:${data.mid}:reaction:${data.reaction}`, socket.uid),
|
|
448
449
|
]);
|
|
450
|
+
if (settings.enableMessageReactions === 'off') {
|
|
451
|
+
throw new Error('[[error:post-reactions-disabled]]');
|
|
452
|
+
}
|
|
449
453
|
if (!roomId) {
|
|
450
454
|
throw new Error('[[error:no-message]]');
|
|
451
455
|
}
|
|
@@ -462,5 +466,43 @@ SocketPlugins.reactions = {
|
|
|
462
466
|
|
|
463
467
|
await sendMessageEvent(data, 'event:reactions.removeMessageReaction');
|
|
464
468
|
},
|
|
469
|
+
getReactionUsernames: async function (socket, data) {
|
|
470
|
+
if (!socket.uid) {
|
|
471
|
+
throw new Error('[[error:not-logged-in]]');
|
|
472
|
+
}
|
|
473
|
+
if (!emojiTable[data.reaction]) {
|
|
474
|
+
throw new Error('[[reactions:error.invalid-reaction]]');
|
|
475
|
+
}
|
|
476
|
+
let set = '';
|
|
477
|
+
if (data.type === 'post') {
|
|
478
|
+
if (!await privileges.posts.can('topics:read', data.pid, socket.uid)) {
|
|
479
|
+
throw new Error('[[error:not-allowed]]');
|
|
480
|
+
}
|
|
481
|
+
set = `pid:${data.pid}:reaction:${data.reaction}`;
|
|
482
|
+
} else if (data.type === 'message') {
|
|
483
|
+
const roomId = await messaging.getMessageField(data.mid, 'roomId');
|
|
484
|
+
if (!await messaging.canViewMessage(data.mid, roomId, socket.uid)) {
|
|
485
|
+
throw new Error('[[error:not-allowed]]');
|
|
486
|
+
}
|
|
487
|
+
set = `mid:${data.mid}:reaction:${data.reaction}`;
|
|
488
|
+
} else {
|
|
489
|
+
throw new Error('[[error:invalid-data]]');
|
|
490
|
+
}
|
|
491
|
+
let uids = await db.getSetMembers(set);
|
|
492
|
+
const cutoff = 6;
|
|
493
|
+
|
|
494
|
+
let otherCount = 0;
|
|
495
|
+
if (uids.length > cutoff) {
|
|
496
|
+
otherCount = uids.length - (cutoff - 1);
|
|
497
|
+
uids = uids.slice(0, cutoff - 1);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
const usernames = await user.getUsernamesByUids(uids);
|
|
501
|
+
return {
|
|
502
|
+
cutoff: cutoff,
|
|
503
|
+
otherCount,
|
|
504
|
+
usernames,
|
|
505
|
+
};
|
|
506
|
+
},
|
|
465
507
|
};
|
|
466
508
|
|
package/package.json
CHANGED
package/plugin.json
CHANGED
package/public/client.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
$(document).ready(function () {
|
|
4
4
|
setupReactions();
|
|
5
5
|
let alerts;
|
|
6
|
+
let mouseOverReactionEl;
|
|
7
|
+
let tooltipTimeoutId = 0;
|
|
6
8
|
function setupReactions() {
|
|
7
9
|
createReactionTooltips();
|
|
8
10
|
require(['hooks', 'alerts'], function (hooks, _alerts) {
|
|
@@ -10,7 +12,11 @@ $(document).ready(function () {
|
|
|
10
12
|
hooks.on('action:ajaxify.end', function () {
|
|
11
13
|
if (ajaxify.data.template.topic) {
|
|
12
14
|
setupPostReactions();
|
|
13
|
-
}
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
// switchChat uses action:chat.loaded and not action:ajaxify.end
|
|
18
|
+
hooks.on('action:chat.loaded', function () {
|
|
19
|
+
if (ajaxify.data.template.chats && ajaxify.data.roomId) {
|
|
14
20
|
setupMessageReactions();
|
|
15
21
|
}
|
|
16
22
|
});
|
|
@@ -186,11 +192,18 @@ $(document).ready(function () {
|
|
|
186
192
|
mid: data.mid,
|
|
187
193
|
reaction: data.reaction,
|
|
188
194
|
reactionCount: data.reactionCount,
|
|
189
|
-
usernames: data.usernames,
|
|
190
195
|
reacted: isSelf && type === 'add',
|
|
191
196
|
reactionImage: data.reactionImage,
|
|
192
197
|
}, function (html) {
|
|
193
|
-
|
|
198
|
+
require(['forum/chats/messages'], function (messages) {
|
|
199
|
+
const reactionEl = $('[component="message/reactions"][data-mid="' + data.mid + '"]');
|
|
200
|
+
const chatContentEl = reactionEl.parents('[component="chat/message/content"]');
|
|
201
|
+
const isAtBottom = messages.isAtBottom(chatContentEl);
|
|
202
|
+
reactionEl.append(html);
|
|
203
|
+
if (isAtBottom || isSelf) {
|
|
204
|
+
messages.scrollToBottom(chatContentEl);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
194
207
|
});
|
|
195
208
|
} else {
|
|
196
209
|
reactionEl.find('.reaction-emoji-count').attr('data-count', data.reactionCount);
|
|
@@ -203,13 +216,65 @@ $(document).ready(function () {
|
|
|
203
216
|
}
|
|
204
217
|
|
|
205
218
|
function createReactionTooltips() {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
219
|
+
require(['bootstrap', 'translator'], function (bootstrap, translator) {
|
|
220
|
+
async function createTooltip(data) {
|
|
221
|
+
if (!mouseOverReactionEl || !mouseOverReactionEl.length) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const el = mouseOverReactionEl;
|
|
225
|
+
let usernames = data.usernames.filter(name => name !== '[[global:former_user]]');
|
|
226
|
+
if (!usernames.length) {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (usernames.length + data.otherCount > data.cutoff) {
|
|
230
|
+
usernames = usernames.join(', ').replace(/,/g, '|');
|
|
231
|
+
usernames = await translator.translate('[[topic:users_and_others, ' + usernames + ', ' + data.otherCount + ']]');
|
|
232
|
+
usernames = usernames.replace(/\|/g, ',');
|
|
233
|
+
} else {
|
|
234
|
+
usernames = usernames.join(', ');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
el.attr('title', usernames);
|
|
238
|
+
(new bootstrap.Tooltip(el, {
|
|
239
|
+
container: '#content',
|
|
240
|
+
html: true,
|
|
241
|
+
placement: 'top',
|
|
242
|
+
animation: false,
|
|
243
|
+
})).show();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (!utils.isTouchDevice()) {
|
|
247
|
+
$('#content').on('mouseenter', '.reaction', function () {
|
|
248
|
+
const $this = $(this);
|
|
249
|
+
mouseOverReactionEl = $this;
|
|
250
|
+
const mid = $this.attr('data-mid');
|
|
251
|
+
const pid = $this.attr('data-pid');
|
|
252
|
+
tooltipTimeoutId = setTimeout(async () => {
|
|
253
|
+
if (mouseOverReactionEl && mouseOverReactionEl.length) {
|
|
254
|
+
const d = await socket.emit('plugins.reactions.getReactionUsernames', {
|
|
255
|
+
type: pid ? 'post' : 'message',
|
|
256
|
+
mid: mid,
|
|
257
|
+
pid: pid,
|
|
258
|
+
reaction: $this.attr('data-reaction'),
|
|
259
|
+
});
|
|
260
|
+
createTooltip(d);
|
|
261
|
+
}
|
|
262
|
+
}, 200);
|
|
263
|
+
});
|
|
264
|
+
$('#content').on('mouseleave', '.reaction', function () {
|
|
265
|
+
if (tooltipTimeoutId) {
|
|
266
|
+
clearTimeout(tooltipTimeoutId);
|
|
267
|
+
tooltipTimeoutId = 0;
|
|
268
|
+
}
|
|
269
|
+
mouseOverReactionEl = null;
|
|
270
|
+
const $this = $(this);
|
|
271
|
+
const tooltip = bootstrap.Tooltip.getInstance(this);
|
|
272
|
+
if (tooltip) {
|
|
273
|
+
tooltip.dispose();
|
|
274
|
+
$this.attr('title', '');
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
});
|
|
214
279
|
}
|
|
215
280
|
});
|
|
@@ -7,10 +7,13 @@
|
|
|
7
7
|
<form role="form" class="reactions-settings">
|
|
8
8
|
<div class="mb-3">
|
|
9
9
|
<h5 class="fw-bold tracking-tight settings-header">[[reactions:settings.title]]</h5>
|
|
10
|
+
<div class="form-check form-switch mb-3">
|
|
11
|
+
<input id="enablePostReactions" name="enablePostReactions" type="checkbox" class="form-check-input">
|
|
12
|
+
<label for="enablePostReactions" class="form-check-label">[[reactions:settings.enable-post-reactions]]</label>
|
|
13
|
+
</div>
|
|
10
14
|
<div class="mb-3">
|
|
11
15
|
<label class="form-label">[[reactions:settings.max-reactions-per-post]]</label>
|
|
12
16
|
<input type="number" min="0" class="form-control" id="maximumReactions" name="maximumReactions">
|
|
13
|
-
|
|
14
17
|
</div>
|
|
15
18
|
<div class="mb-3">
|
|
16
19
|
<label class="form-label">[[reactions:settings.max-reactions-per-user-per-post]]</label>
|
|
@@ -19,6 +22,11 @@
|
|
|
19
22
|
[[reactions:settings.max-reactions-per-user-per-post-help]]
|
|
20
23
|
</p>
|
|
21
24
|
</div>
|
|
25
|
+
<hr/>
|
|
26
|
+
<div class="form-check form-switch mb-3">
|
|
27
|
+
<input id="enableMessageReactions" name="enableMessageReactions" type="checkbox" class="form-check-input">
|
|
28
|
+
<label for="enableMessageReactions" class="form-check-label">[[reactions:settings.enable-message-reactions]]</label>
|
|
29
|
+
</div>
|
|
22
30
|
<div class="mb-3">
|
|
23
31
|
<label class="form-label">[[reactions:settings.max-reactions-per-message]]</label>
|
|
24
32
|
<input type="number" min="0" class="form-control" id="maximumReactionsPerMessage" name="maximumReactionsPerMessage">
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
{{{ if config.enableMessageReactions }}}
|
|
1
2
|
<button class="reaction-add btn btn-sm btn-link {{{ if ./maxReactionsReached }}}max-reactions{{{ end }}}" component="message/reaction/add" data-mid="{./mid}" title="[[reactions:add-reaction]]">
|
|
2
3
|
<i class="fa fa-face-smile"></i>
|
|
3
4
|
</button>
|
|
5
|
+
{{{ end }}}
|
|
4
6
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<span class="reaction mb-2 {{{ if ./reacted }}}reacted{{{ end }}}" component="message/reaction" data-mid="{./mid}" data-reaction="{./reaction}"
|
|
1
|
+
<span class="reaction mb-2 {{{ if ./reacted }}}reacted{{{ end }}}" component="message/reaction" data-mid="{./mid}" data-reaction="{./reaction}">
|
|
2
2
|
{./reactionImage}
|
|
3
3
|
<small class="reaction-emoji-count" data-count="{./reactionCount}"></small>
|
|
4
4
|
</span>
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
{{{ if config.enableMessageReactions }}}
|
|
1
2
|
<div class="reactions {{{ if ./deleted}}}hidden{{{ end }}}" component="message/reactions" data-mid="{./mid}">
|
|
2
3
|
{{{ each ./reactions }}}
|
|
3
4
|
<!-- IMPORT partials/chats/reaction.tpl -->
|
|
4
5
|
{{{ end }}}
|
|
5
|
-
</div>
|
|
6
|
+
</div>
|
|
7
|
+
{{{ end }}}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<span class="reaction {{{ if ./reacted }}}reacted{{{ end }}}" component="post/reaction" data-pid="{./pid}" data-reaction="{./reaction}"
|
|
1
|
+
<span class="reaction {{{ if ./reacted }}}reacted{{{ end }}}" component="post/reaction" data-pid="{./pid}" data-reaction="{./reaction}">
|
|
2
2
|
{./reactionImage}
|
|
3
3
|
<small class="reaction-emoji-count" data-count="{./reactionCount}"></small>
|
|
4
4
|
</span>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
{{{ if config.enablePostReactions }}}
|
|
1
2
|
<span class="reactions" component="post/reactions" data-pid="{./pid}">
|
|
2
3
|
<span class="reaction-add d-inline-block px-2 mx-1 btn-ghost-sm {{{ if ./maxReactionsReached }}}max-reactions{{{ end }}}" component="post/reaction/add" data-pid="{./pid}" title="[[reactions:add-reaction]]">
|
|
3
4
|
<i class="fa fa-face-smile text-primary"></i>
|
|
@@ -5,4 +6,5 @@
|
|
|
5
6
|
{{{ each ./reactions }}}
|
|
6
7
|
<!-- IMPORT partials/topic/reaction.tpl -->
|
|
7
8
|
{{{ end }}}
|
|
8
|
-
</span>
|
|
9
|
+
</span>
|
|
10
|
+
{{{ end }}}
|