@nodebb/nodebb-plugin-reactions 2.1.0 → 2.1.1
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 +104 -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;
|
|
@@ -83,42 +98,24 @@ ReactionsPlugin.getPostReactions = async function (data) {
|
|
|
83
98
|
}
|
|
84
99
|
}
|
|
85
100
|
|
|
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
|
-
}
|
|
101
|
+
const reactionSetToUsersMap = await getReactionSetsUidsMap(reactionSets);
|
|
101
102
|
|
|
102
103
|
for (const post of data.posts) {
|
|
103
104
|
post.maxReactionsReached = pidToIsMaxReactionsReachedMap.get(post.pid);
|
|
104
105
|
post.reactions = [];
|
|
105
106
|
|
|
106
|
-
|
|
107
|
-
|
|
107
|
+
const reactions = pidToReactionsMap.get(post.pid);
|
|
108
|
+
if (reactions) {
|
|
109
|
+
for (const reaction of reactions) {
|
|
108
110
|
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
|
-
|
|
111
|
+
const uids = reactionSetToUsersMap.get(reactionSet);
|
|
112
|
+
if (Array.isArray(uids)) {
|
|
115
113
|
post.reactions.push({
|
|
116
114
|
pid: post.pid,
|
|
117
|
-
reacted:
|
|
115
|
+
reacted: uids.includes(String(data.uid)),
|
|
118
116
|
reaction,
|
|
119
|
-
usernames: reactedUsernames,
|
|
120
117
|
reactionImage: parse(reaction),
|
|
121
|
-
reactionCount,
|
|
118
|
+
reactionCount: uids.length,
|
|
122
119
|
});
|
|
123
120
|
}
|
|
124
121
|
}
|
|
@@ -158,42 +155,23 @@ ReactionsPlugin.getMessageReactions = async function (data) {
|
|
|
158
155
|
}
|
|
159
156
|
}
|
|
160
157
|
|
|
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
|
-
}
|
|
158
|
+
const reactionSetToUsersMap = await getReactionSetsUidsMap(reactionSets);
|
|
176
159
|
|
|
177
160
|
for (const msg of data.messages) {
|
|
178
161
|
msg.maxReactionsReached = midToIsMaxReactionsReachedMap.get(msg.mid);
|
|
179
162
|
msg.reactions = [];
|
|
180
|
-
|
|
181
|
-
if (
|
|
182
|
-
for (const reaction of
|
|
163
|
+
const reactions = midToReactionsMap.get(msg.mid);
|
|
164
|
+
if (reactions) {
|
|
165
|
+
for (const reaction of reactions) {
|
|
183
166
|
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
|
-
|
|
167
|
+
const uids = reactionSetToUsersMap.get(reactionSet);
|
|
168
|
+
if (Array.isArray(uids)) {
|
|
190
169
|
msg.reactions.push({
|
|
191
170
|
mid: msg.mid,
|
|
192
|
-
reacted:
|
|
171
|
+
reacted: uids.includes(String(data.uid)),
|
|
193
172
|
reaction,
|
|
194
|
-
usernames: reactedUsernames,
|
|
195
173
|
reactionImage: parse(reaction),
|
|
196
|
-
reactionCount,
|
|
174
|
+
reactionCount: uids.length,
|
|
197
175
|
});
|
|
198
176
|
}
|
|
199
177
|
}
|
|
@@ -205,6 +183,22 @@ ReactionsPlugin.getMessageReactions = async function (data) {
|
|
|
205
183
|
return data;
|
|
206
184
|
};
|
|
207
185
|
|
|
186
|
+
|
|
187
|
+
async function getReactionSetsUidsMap(reactionSets) {
|
|
188
|
+
const reactionSetToUsersMap = new Map(); // reactionSet -> uids
|
|
189
|
+
if (reactionSets.length > 0) {
|
|
190
|
+
const uidsForReactions = await db.getSetsMembers(reactionSets);
|
|
191
|
+
|
|
192
|
+
for (let i = 0, len = reactionSets.length; i < len; i++) {
|
|
193
|
+
const uidsForReaction = uidsForReactions[i];
|
|
194
|
+
if (uidsForReaction && uidsForReaction.length > 0) {
|
|
195
|
+
reactionSetToUsersMap.set(reactionSets[i], uidsForReaction);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
return reactionSetToUsersMap;
|
|
200
|
+
}
|
|
201
|
+
|
|
208
202
|
ReactionsPlugin.onReply = async function (data) {
|
|
209
203
|
if (data.uid !== 0) {
|
|
210
204
|
data.reactions = [];
|
|
@@ -229,15 +223,11 @@ ReactionsPlugin.deleteReactions = async function (hookData) {
|
|
|
229
223
|
|
|
230
224
|
async function sendPostEvent(data, eventName) {
|
|
231
225
|
try {
|
|
232
|
-
const [reactionCount, totalReactions
|
|
226
|
+
const [reactionCount, totalReactions] = await Promise.all([
|
|
233
227
|
db.setCount(`pid:${data.pid}:reaction:${data.reaction}`),
|
|
234
228
|
db.setCount(`pid:${data.pid}:reactions`),
|
|
235
|
-
db.getSetMembers(`pid:${data.pid}:reaction:${data.reaction}`),
|
|
236
229
|
]);
|
|
237
230
|
|
|
238
|
-
const userdata = await user.getUsersFields(uids, ['uid', 'username']);
|
|
239
|
-
const usernames = userdata.map(user => user.username).join(', ');
|
|
240
|
-
|
|
241
231
|
if (parseInt(reactionCount, 10) === 0) {
|
|
242
232
|
await db.setRemove(`pid:${data.pid}:reactions`, data.reaction);
|
|
243
233
|
}
|
|
@@ -248,7 +238,6 @@ async function sendPostEvent(data, eventName) {
|
|
|
248
238
|
reaction: data.reaction,
|
|
249
239
|
reactionCount,
|
|
250
240
|
totalReactions,
|
|
251
|
-
usernames,
|
|
252
241
|
reactionImage: parse(data.reaction),
|
|
253
242
|
});
|
|
254
243
|
} catch (e) {
|
|
@@ -258,15 +247,11 @@ async function sendPostEvent(data, eventName) {
|
|
|
258
247
|
|
|
259
248
|
async function sendMessageEvent(data, eventName) {
|
|
260
249
|
try {
|
|
261
|
-
const [reactionCount, totalReactions
|
|
250
|
+
const [reactionCount, totalReactions] = await Promise.all([
|
|
262
251
|
db.setCount(`mid:${data.mid}:reaction:${data.reaction}`),
|
|
263
252
|
db.setCount(`mid:${data.mid}:reactions`),
|
|
264
|
-
db.getSetMembers(`mid:${data.mid}:reaction:${data.reaction}`),
|
|
265
253
|
]);
|
|
266
254
|
|
|
267
|
-
const userdata = await user.getUsersFields(uids, ['uid', 'username']);
|
|
268
|
-
const usernames = userdata.map(user => user.username).join(', ');
|
|
269
|
-
|
|
270
255
|
if (parseInt(reactionCount, 10) === 0) {
|
|
271
256
|
await db.setRemove(`mid:${data.mid}:reactions`, data.reaction);
|
|
272
257
|
}
|
|
@@ -277,7 +262,6 @@ async function sendMessageEvent(data, eventName) {
|
|
|
277
262
|
reaction: data.reaction,
|
|
278
263
|
reactionCount,
|
|
279
264
|
totalReactions,
|
|
280
|
-
usernames,
|
|
281
265
|
reactionImage: parse(data.reaction),
|
|
282
266
|
});
|
|
283
267
|
} catch (e) {
|
|
@@ -310,6 +294,9 @@ SocketPlugins.reactions = {
|
|
|
310
294
|
}
|
|
311
295
|
|
|
312
296
|
const settings = await meta.settings.get('reactions');
|
|
297
|
+
if (settings.enablePostReactions === 'off') {
|
|
298
|
+
throw new Error('[[error:post-reactions-disabled]]');
|
|
299
|
+
}
|
|
313
300
|
const maximumReactions = settings.maximumReactions || DEFAULT_MAX_EMOTES;
|
|
314
301
|
const [tid, totalReactions, emojiIsAlreadyExist, alreadyReacted, reactionReputation] = await Promise.all([
|
|
315
302
|
posts.getPostField(data.pid, 'tid'),
|
|
@@ -360,11 +347,15 @@ SocketPlugins.reactions = {
|
|
|
360
347
|
throw new Error('[[reactions:error.invalid-reaction]]');
|
|
361
348
|
}
|
|
362
349
|
|
|
363
|
-
const [tid, hasReacted, reactionReputation] = await Promise.all([
|
|
350
|
+
const [settings, tid, hasReacted, reactionReputation] = await Promise.all([
|
|
351
|
+
meta.settings.get('reactions'),
|
|
364
352
|
posts.getPostField(data.pid, 'tid'),
|
|
365
353
|
db.isSetMember(`pid:${data.pid}:reaction:${data.reaction}`, socket.uid),
|
|
366
354
|
getReactionReputation(data.reaction),
|
|
367
355
|
]);
|
|
356
|
+
if (settings.enablePostReactions === 'off') {
|
|
357
|
+
throw new Error('[[error:post-reactions-disabled]]');
|
|
358
|
+
}
|
|
368
359
|
if (!tid) {
|
|
369
360
|
throw new Error('[[error:no-post]]');
|
|
370
361
|
}
|
|
@@ -395,6 +386,9 @@ SocketPlugins.reactions = {
|
|
|
395
386
|
}
|
|
396
387
|
|
|
397
388
|
const settings = await meta.settings.get('reactions');
|
|
389
|
+
if (settings.enableMessageReactions === 'off') {
|
|
390
|
+
throw new Error('[[error:post-reactions-disabled]]');
|
|
391
|
+
}
|
|
398
392
|
const maximumReactionsPerMessage = settings.maximumReactionsPerMessage || DEFAULT_MAX_EMOTES;
|
|
399
393
|
const [roomId, totalReactions, emojiIsAlreadyExist] = await Promise.all([
|
|
400
394
|
messaging.getMessageField(data.mid, 'roomId'),
|
|
@@ -442,10 +436,14 @@ SocketPlugins.reactions = {
|
|
|
442
436
|
throw new Error('[[reactions:error.invalid-reaction]]');
|
|
443
437
|
}
|
|
444
438
|
|
|
445
|
-
const [roomId, hasReacted] = await Promise.all([
|
|
439
|
+
const [settings, roomId, hasReacted] = await Promise.all([
|
|
440
|
+
meta.settings.get('reactions'),
|
|
446
441
|
messaging.getMessageField(data.mid, 'roomId'),
|
|
447
442
|
db.isSetMember(`mid:${data.mid}:reaction:${data.reaction}`, socket.uid),
|
|
448
443
|
]);
|
|
444
|
+
if (settings.enableMessageReactions === 'off') {
|
|
445
|
+
throw new Error('[[error:post-reactions-disabled]]');
|
|
446
|
+
}
|
|
449
447
|
if (!roomId) {
|
|
450
448
|
throw new Error('[[error:no-message]]');
|
|
451
449
|
}
|
|
@@ -462,5 +460,43 @@ SocketPlugins.reactions = {
|
|
|
462
460
|
|
|
463
461
|
await sendMessageEvent(data, 'event:reactions.removeMessageReaction');
|
|
464
462
|
},
|
|
463
|
+
getReactionUsernames: async function (socket, data) {
|
|
464
|
+
if (!socket.uid) {
|
|
465
|
+
throw new Error('[[error:not-logged-in]]');
|
|
466
|
+
}
|
|
467
|
+
if (!emojiTable[data.reaction]) {
|
|
468
|
+
throw new Error('[[reactions:error.invalid-reaction]]');
|
|
469
|
+
}
|
|
470
|
+
let set = '';
|
|
471
|
+
if (data.type === 'post') {
|
|
472
|
+
if (!await privileges.posts.can('topics:read', data.pid, socket.uid)) {
|
|
473
|
+
throw new Error('[[error:not-allowed]]');
|
|
474
|
+
}
|
|
475
|
+
set = `pid:${data.pid}:reaction:${data.reaction}`;
|
|
476
|
+
} else if (data.type === 'message') {
|
|
477
|
+
const roomId = await messaging.getMessageField(data.mid, 'roomId');
|
|
478
|
+
if (!await messaging.canViewMessage(data.mid, roomId, socket.uid)) {
|
|
479
|
+
throw new Error('[[error:not-allowed]]');
|
|
480
|
+
}
|
|
481
|
+
set = `mid:${data.mid}:reaction:${data.reaction}`;
|
|
482
|
+
} else {
|
|
483
|
+
throw new Error('[[error:invalid-data]]');
|
|
484
|
+
}
|
|
485
|
+
let uids = await db.getSetMembers(set);
|
|
486
|
+
const cutoff = 6;
|
|
487
|
+
|
|
488
|
+
let otherCount = 0;
|
|
489
|
+
if (uids.length > cutoff) {
|
|
490
|
+
otherCount = uids.length - (cutoff - 1);
|
|
491
|
+
uids = uids.slice(0, cutoff - 1);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const usernames = await user.getUsernamesByUids(uids);
|
|
495
|
+
return {
|
|
496
|
+
cutoff: cutoff,
|
|
497
|
+
otherCount,
|
|
498
|
+
usernames,
|
|
499
|
+
};
|
|
500
|
+
},
|
|
465
501
|
};
|
|
466
502
|
|
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 }}}
|