nodebb-plugin-chat-search 0.0.1 → 0.0.3
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/library.js +23 -17
- package/package.json +1 -1
- package/static/lib/main.js +141 -43
package/library.js
CHANGED
|
@@ -23,7 +23,6 @@ async function searchGlobal(socket, data) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
let targetUid = socket.uid;
|
|
26
|
-
// בדיקת הרשאות (אדמין צופה במישהו אחר)
|
|
27
26
|
if (data.targetUid && parseInt(data.targetUid, 10) !== parseInt(socket.uid, 10)) {
|
|
28
27
|
const isAdmin = await user.isAdministrator(socket.uid);
|
|
29
28
|
if (!isAdmin) {
|
|
@@ -38,12 +37,10 @@ async function searchGlobal(socket, data) {
|
|
|
38
37
|
let allResults = [];
|
|
39
38
|
|
|
40
39
|
for (const roomId of roomIds) {
|
|
41
|
-
// בדיקת חברות
|
|
42
40
|
const inRoom = await messaging.isUserInRoom(targetUid, roomId);
|
|
43
41
|
if (!inRoom) continue;
|
|
44
42
|
|
|
45
43
|
try {
|
|
46
|
-
// 1. שליפת הודעות
|
|
47
44
|
const messages = await messaging.getMessages({
|
|
48
45
|
callerUid: socket.uid,
|
|
49
46
|
uid: targetUid,
|
|
@@ -60,32 +57,41 @@ async function searchGlobal(socket, data) {
|
|
|
60
57
|
);
|
|
61
58
|
|
|
62
59
|
if (matches.length > 0) {
|
|
63
|
-
//
|
|
64
|
-
// במקום להסתמך על roomData, נשלוף UIDs ואז Users
|
|
60
|
+
// שליפת משתמשים
|
|
65
61
|
const uids = await messaging.getUidsInRoom(roomId, 0, -1);
|
|
66
|
-
const usersData = await user.getUsersFields(uids, ['uid', 'username'
|
|
62
|
+
const usersData = await user.getUsersFields(uids, ['uid', 'username']);
|
|
67
63
|
|
|
68
|
-
// סינון המשתמש
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
64
|
+
// סינון המשתמש הנוכחי (משאירים רק את הפרטנרים)
|
|
65
|
+
const otherUsers = usersData.filter(u => parseInt(u.uid, 10) !== parseInt(targetUid, 10));
|
|
66
|
+
|
|
67
|
+
// --- לוגיקת הקיצור (2 שמות + ועוד X) ---
|
|
68
|
+
let displayName = '';
|
|
69
|
+
|
|
70
|
+
if (otherUsers.length === 0) {
|
|
71
|
+
displayName = 'צ\'אט עצמי'; // או משתמש מחוק
|
|
72
|
+
} else if (otherUsers.length <= 2) {
|
|
73
|
+
// אם יש 1 או 2, מציגים את כולם
|
|
74
|
+
displayName = otherUsers.map(u => u.username).join(', ');
|
|
75
|
+
} else {
|
|
76
|
+
// אם יש יותר מ-2, לוקחים את ה-2 הראשונים ומוסיפים את היתרה
|
|
77
|
+
const firstTwo = otherUsers.slice(0, 2).map(u => u.username).join(', ');
|
|
78
|
+
const remaining = otherUsers.length - 2;
|
|
79
|
+
displayName = `${firstTwo} ועוד ${remaining} משתמשים`;
|
|
80
|
+
}
|
|
81
|
+
// ----------------------------------------
|
|
73
82
|
|
|
74
|
-
// --- תיקון שליפת שם החדר ---
|
|
75
83
|
const roomData = await messaging.getRoomData(roomId);
|
|
76
|
-
|
|
84
|
+
// שם החדר: אם יש שם מוגדר לקבוצה - קח אותו, אחרת קח את השמות שיצרנו
|
|
85
|
+
let roomName = (roomData && roomData.roomName) || displayName;
|
|
77
86
|
|
|
78
|
-
// --- תיקון שם השולח (במקרה שחסר בהודעה) ---
|
|
79
|
-
// אם חסר אובייקט user בהודעה, נשלים אותו מתוך usersData ששלפנו הרגע
|
|
80
87
|
matches.forEach(m => {
|
|
81
88
|
if (!m.user || !m.user.username) {
|
|
82
89
|
const sender = usersData.find(u => parseInt(u.uid, 10) === parseInt(m.fromuid, 10));
|
|
83
90
|
m.user = sender || { username: 'Unknown' };
|
|
84
91
|
}
|
|
85
|
-
|
|
86
92
|
m.roomName = roomName;
|
|
87
|
-
m.chatWith = participants;
|
|
88
93
|
m.targetUid = targetUid;
|
|
94
|
+
// מחקנו את chatWith כי כבר לא צריך אותו
|
|
89
95
|
});
|
|
90
96
|
|
|
91
97
|
allResults = allResults.concat(matches);
|
package/package.json
CHANGED
package/static/lib/main.js
CHANGED
|
@@ -1,59 +1,110 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// מצב גלובלי לזיכרון בין מעברי דפים
|
|
4
|
+
window.chatSearchState = window.chatSearchState || {
|
|
5
|
+
query: '',
|
|
6
|
+
resultsHtml: '',
|
|
7
|
+
isOpen: false,
|
|
8
|
+
lastScroll: 0
|
|
9
|
+
};
|
|
10
|
+
|
|
3
11
|
$(document).ready(function () {
|
|
4
|
-
console.log('[Chat Search] Loaded (
|
|
12
|
+
console.log('[Chat Search] Loaded (Instant No-Flicker).');
|
|
13
|
+
|
|
14
|
+
let observer = null;
|
|
15
|
+
|
|
16
|
+
// האזנה למעבר דפים
|
|
17
|
+
$(window).on('action:ajaxify.start', function () {
|
|
18
|
+
// לפני שהדף מתחלף, ננסה לשמור את האלמנט בזיכרון אם נרצה להחזירו (אופציונלי)
|
|
19
|
+
// כרגע אנו מסתמכים על בנייה מחדש מהירה מאוד
|
|
20
|
+
});
|
|
5
21
|
|
|
6
|
-
// בכל מעבר דף, אנחנו מוחקים את הישן ומתחילים חיפוש חדש של הקונטיינר
|
|
7
22
|
$(window).on('action:ajaxify.end', function (ev, data) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
// גם לפי URL וגם לפי Template
|
|
23
|
+
// אם יש observer ישן, ננתק אותו כדי לא להעמיס
|
|
24
|
+
if (observer) observer.disconnect();
|
|
25
|
+
|
|
12
26
|
const isChatUrl = data.url.match(/^(user\/[^\/]+\/)?chats/);
|
|
13
27
|
const isChatTemplate = data.template && data.template.name === 'chats';
|
|
14
28
|
|
|
15
29
|
if (isChatUrl || isChatTemplate) {
|
|
16
|
-
|
|
30
|
+
initFastInjection();
|
|
31
|
+
} else {
|
|
32
|
+
// יציאה מאזור הצ'אטים - איפוס הזיכרון
|
|
33
|
+
window.chatSearchState = { query: '', resultsHtml: '', isOpen: false, lastScroll: 0 };
|
|
17
34
|
}
|
|
18
35
|
});
|
|
19
36
|
|
|
20
37
|
$(window).on('action:chat.loaded', function (ev, data) {
|
|
38
|
+
highlightActiveChat();
|
|
21
39
|
handleScrollToMessage();
|
|
22
40
|
});
|
|
23
41
|
|
|
24
|
-
//
|
|
25
|
-
if (ajaxify.data.template && ajaxify.data.template.name === 'chats') {
|
|
26
|
-
|
|
42
|
+
// בדיקה ראשונית
|
|
43
|
+
if (ajaxify.data && ajaxify.data.template && ajaxify.data.template.name === 'chats') {
|
|
44
|
+
initFastInjection();
|
|
27
45
|
}
|
|
28
46
|
|
|
29
|
-
function
|
|
47
|
+
function initFastInjection() {
|
|
48
|
+
// 1. ניסיון הזרקה מיידי
|
|
49
|
+
tryInject();
|
|
50
|
+
|
|
51
|
+
// 2. הפעלת MutationObserver לזיהוי שינויים ב-DOM בזמן אמת (מונע קפיצות)
|
|
52
|
+
const targetNode = document.body;
|
|
53
|
+
const config = { childList: true, subtree: true };
|
|
54
|
+
|
|
55
|
+
observer = new MutationObserver(function(mutationsList) {
|
|
56
|
+
for(let mutation of mutationsList) {
|
|
57
|
+
if (mutation.type === 'childList') {
|
|
58
|
+
// אם נוספו אלמנטים לדף, נבדוק אם הסרגל הגיע
|
|
59
|
+
const container = findContainer();
|
|
60
|
+
if (container.length > 0 && container.find('#global-chat-search-container').length === 0) {
|
|
61
|
+
addGlobalSearchBar(container);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
observer.observe(targetNode, config);
|
|
68
|
+
|
|
69
|
+
// 3. גיבוי: Interval מהיר מאוד (50ms) למקרה שה-Observer פספס
|
|
30
70
|
let attempts = 0;
|
|
31
|
-
const interval = setInterval(
|
|
71
|
+
const interval = setInterval(() => {
|
|
32
72
|
attempts++;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (
|
|
37
|
-
|
|
38
|
-
// ברגע שמצאנו - מזריקים ועוצרים
|
|
39
|
-
if (container.length > 0) {
|
|
40
|
-
addGlobalSearchBar(container);
|
|
41
|
-
clearInterval(interval);
|
|
42
|
-
} else if (attempts >= 20) { // מנסים במשך 4 שניות
|
|
73
|
+
if (tryInject()) {
|
|
74
|
+
// לא עוצרים את האינטרוול מיד, כי לפעמים NodeBB מרענן פעמיים
|
|
75
|
+
if (attempts > 20) clearInterval(interval);
|
|
76
|
+
} else if (attempts > 40) { // 2 שניות
|
|
43
77
|
clearInterval(interval);
|
|
44
78
|
}
|
|
45
|
-
},
|
|
79
|
+
}, 50);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function findContainer() {
|
|
83
|
+
// סדר עדיפויות למציאת הקונטיינר
|
|
84
|
+
let container = $('[component="chat/nav-wrapper"]');
|
|
85
|
+
if (container.length === 0) container = $('.chats-page').find('.col-md-4').first();
|
|
86
|
+
return container;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function tryInject() {
|
|
90
|
+
const container = findContainer();
|
|
91
|
+
if (container.length > 0) {
|
|
92
|
+
addGlobalSearchBar(container);
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
46
96
|
}
|
|
47
97
|
|
|
48
98
|
function addGlobalSearchBar(container) {
|
|
49
99
|
if ($('#global-chat-search-container').length > 0) return;
|
|
50
100
|
|
|
101
|
+
// ה-HTML של התיבה
|
|
51
102
|
const searchHtml = `
|
|
52
103
|
<div id="global-chat-search-container" style="padding: 10px; background: #fff; border-bottom: 1px solid #ddd; margin-bottom: 10px;">
|
|
53
104
|
<div class="input-group">
|
|
54
|
-
<input type="text" id="global-chat-search" class="form-control" placeholder="חפש הודעה..." style="font-size: 14px;">
|
|
105
|
+
<input type="text" id="global-chat-search" class="form-control" placeholder="חפש הודעה..." style="font-size: 14px; height: 34px;">
|
|
55
106
|
<span class="input-group-btn">
|
|
56
|
-
<button class="btn btn-primary" id="btn-chat-search" type="button"><i class="fa fa-search"></i></button>
|
|
107
|
+
<button class="btn btn-primary" id="btn-chat-search" type="button" style="height: 34px;"><i class="fa fa-search"></i></button>
|
|
57
108
|
</span>
|
|
58
109
|
</div>
|
|
59
110
|
<div id="global-search-results" style="margin-top: 5px; max-height: 400px; overflow-y: auto; background: white; border: 1px solid #eee; display:none;"></div>
|
|
@@ -62,10 +113,39 @@ $(document).ready(function () {
|
|
|
62
113
|
|
|
63
114
|
container.prepend(searchHtml);
|
|
64
115
|
|
|
116
|
+
// --- שחזור מיידי של המצב (מונע הבהוב של התוכן) ---
|
|
117
|
+
const input = $('#global-chat-search');
|
|
118
|
+
const results = $('#global-search-results');
|
|
119
|
+
|
|
120
|
+
if (window.chatSearchState.query) {
|
|
121
|
+
input.val(window.chatSearchState.query);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (window.chatSearchState.isOpen && window.chatSearchState.resultsHtml) {
|
|
125
|
+
results.html(window.chatSearchState.resultsHtml).show();
|
|
126
|
+
// שחזור מיקום הגלילה
|
|
127
|
+
if (window.chatSearchState.lastScroll > 0) {
|
|
128
|
+
results.scrollTop(window.chatSearchState.lastScroll);
|
|
129
|
+
}
|
|
130
|
+
highlightActiveChat();
|
|
131
|
+
}
|
|
132
|
+
// ------------------------------------------------
|
|
133
|
+
|
|
134
|
+
// הגדרת אירועים (Events)
|
|
65
135
|
$('#btn-chat-search').off('click').on('click', executeSearch);
|
|
66
|
-
|
|
136
|
+
|
|
137
|
+
// שמירת גלילה
|
|
138
|
+
results.on('scroll', function() {
|
|
139
|
+
window.chatSearchState.lastScroll = $(this).scrollTop();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
input.off('keypress').on('keypress', function (e) {
|
|
67
143
|
if (e.which === 13) executeSearch();
|
|
68
144
|
});
|
|
145
|
+
|
|
146
|
+
input.on('input', function() {
|
|
147
|
+
window.chatSearchState.query = $(this).val();
|
|
148
|
+
});
|
|
69
149
|
}
|
|
70
150
|
|
|
71
151
|
function executeSearch() {
|
|
@@ -74,12 +154,15 @@ $(document).ready(function () {
|
|
|
74
154
|
|
|
75
155
|
if (!query) {
|
|
76
156
|
resultsContainer.hide();
|
|
157
|
+
window.chatSearchState.isOpen = false;
|
|
158
|
+
window.chatSearchState.resultsHtml = '';
|
|
77
159
|
return;
|
|
78
160
|
}
|
|
79
161
|
|
|
80
162
|
let targetUid = ajaxify.data.uid || app.user.uid;
|
|
81
163
|
|
|
82
164
|
resultsContainer.show().html('<div class="text-center" style="padding:10px;"><i class="fa fa-spinner fa-spin"></i> מחפש...</div>');
|
|
165
|
+
window.chatSearchState.isOpen = true;
|
|
83
166
|
|
|
84
167
|
socket.emit('plugins.chatSearch.searchGlobal', {
|
|
85
168
|
query: query,
|
|
@@ -87,12 +170,14 @@ $(document).ready(function () {
|
|
|
87
170
|
}, function (err, messages) {
|
|
88
171
|
if (err) {
|
|
89
172
|
console.error(err);
|
|
90
|
-
resultsContainer.html('<div class="alert alert-danger" style="margin:5px;"
|
|
173
|
+
resultsContainer.html('<div class="alert alert-danger" style="margin:5px;">שגיאה</div>');
|
|
91
174
|
return;
|
|
92
175
|
}
|
|
93
176
|
|
|
94
177
|
if (!messages || messages.length === 0) {
|
|
95
|
-
|
|
178
|
+
const noRes = '<div class="text-center" style="padding:10px; color:#777;">לא נמצאו תוצאות.</div>';
|
|
179
|
+
resultsContainer.html(noRes);
|
|
180
|
+
window.chatSearchState.resultsHtml = noRes;
|
|
96
181
|
return;
|
|
97
182
|
}
|
|
98
183
|
|
|
@@ -100,37 +185,50 @@ $(document).ready(function () {
|
|
|
100
185
|
|
|
101
186
|
messages.forEach(msg => {
|
|
102
187
|
const date = new Date(msg.timestamp).toLocaleDateString();
|
|
103
|
-
|
|
104
|
-
let baseUrl = window.location.pathname;
|
|
188
|
+
let baseUrl = window.location.pathname.replace(/\/chats\/.*$/, '/chats');
|
|
105
189
|
if (baseUrl.endsWith('/')) baseUrl = baseUrl.slice(0, -1);
|
|
106
|
-
if (baseUrl.match(/\/[0-9]+$/)) baseUrl = baseUrl.replace(/\/[0-9]+$/, '');
|
|
107
190
|
|
|
108
191
|
const chatLink = baseUrl + '/' + msg.roomId + '?mid=' + msg.mid;
|
|
109
192
|
const senderName = (msg.user && msg.user.username) ? msg.user.username : 'Unknown';
|
|
110
|
-
|
|
111
|
-
let participantsHtml = '';
|
|
112
|
-
if (msg.chatWith) {
|
|
113
|
-
participantsHtml = `<div style="font-size:11px; color:#005999; margin-bottom:2px;"><i class="fa fa-users"></i> עם: <strong>${msg.chatWith}</strong></div>`;
|
|
114
|
-
}
|
|
115
193
|
|
|
116
194
|
html += `
|
|
117
|
-
<li class="list-group-item search-result" style="cursor:pointer; border-bottom: 1px solid #f0f0f0; padding: 8px;" onclick="ajaxify.go('${chatLink}')">
|
|
118
|
-
<div style="font-size:13px; color:#333; margin-bottom:
|
|
195
|
+
<li class="list-group-item search-result" data-roomid="${msg.roomId}" style="cursor:pointer; border-bottom: 1px solid #f0f0f0; padding: 10px 8px;" onclick="ajaxify.go('${chatLink}')">
|
|
196
|
+
<div style="font-size:13px; color:#333; margin-bottom:5px;">
|
|
197
|
+
<span class="pull-left text-muted" style="font-size:10px; margin-right: 8px;">${date}</span>
|
|
119
198
|
<strong>${msg.roomName}</strong>
|
|
120
|
-
<span class="pull-left text-muted" style="font-size:10px;">${date}</span>
|
|
121
199
|
</div>
|
|
122
|
-
|
|
123
|
-
<div style="font-size:12px; color:#444; background: #f9f9f9; padding: 4px; border-radius: 3px; border-right: 2px solid #007bff;">
|
|
200
|
+
<div style="font-size:12px; color:#444; background: #f9f9f9; padding: 6px; border-radius: 4px; border-right: 3px solid #007bff;">
|
|
124
201
|
<strong>${senderName}:</strong> ${msg.content}
|
|
125
202
|
</div>
|
|
126
203
|
</li>
|
|
127
204
|
`;
|
|
128
205
|
});
|
|
129
206
|
html += '</ul>';
|
|
207
|
+
|
|
130
208
|
resultsContainer.html(html);
|
|
209
|
+
|
|
210
|
+
// עדכון הזיכרון
|
|
211
|
+
window.chatSearchState.resultsHtml = html;
|
|
212
|
+
window.chatSearchState.lastScroll = 0;
|
|
213
|
+
|
|
214
|
+
highlightActiveChat();
|
|
131
215
|
});
|
|
132
216
|
}
|
|
133
217
|
|
|
218
|
+
function highlightActiveChat() {
|
|
219
|
+
// מנסים לקחת RoomID מה-URL או מהמידע של NodeBB
|
|
220
|
+
let currentRoomId = ajaxify.data.roomId;
|
|
221
|
+
if (!currentRoomId) {
|
|
222
|
+
const match = window.location.pathname.match(/chats\/(\d+)/);
|
|
223
|
+
if (match) currentRoomId = match[1];
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (!currentRoomId) return;
|
|
227
|
+
|
|
228
|
+
$('.search-result').css('background-color', '');
|
|
229
|
+
$('.search-result[data-roomid="' + currentRoomId + '"]').css('background-color', '#eef6ff');
|
|
230
|
+
}
|
|
231
|
+
|
|
134
232
|
function handleScrollToMessage() {
|
|
135
233
|
const params = new URLSearchParams(window.location.search);
|
|
136
234
|
const mid = params.get('mid');
|
|
@@ -140,8 +238,8 @@ $(document).ready(function () {
|
|
|
140
238
|
let attempts = 0;
|
|
141
239
|
const scrollInt = setInterval(() => {
|
|
142
240
|
attempts++;
|
|
143
|
-
if (scrollToId(mid) || attempts >
|
|
144
|
-
},
|
|
241
|
+
if (scrollToId(mid) || attempts > 15) clearInterval(scrollInt);
|
|
242
|
+
}, 300);
|
|
145
243
|
}
|
|
146
244
|
|
|
147
245
|
function scrollToId(mid) {
|