nodebb-plugin-chat-search 0.0.2 → 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/package.json +1 -1
- package/static/lib/main.js +173 -27
package/package.json
CHANGED
package/static/lib/main.js
CHANGED
|
@@ -1,52 +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
22
|
$(window).on('action:ajaxify.end', function (ev, data) {
|
|
7
|
-
|
|
8
|
-
|
|
23
|
+
// אם יש observer ישן, ננתק אותו כדי לא להעמיס
|
|
24
|
+
if (observer) observer.disconnect();
|
|
25
|
+
|
|
9
26
|
const isChatUrl = data.url.match(/^(user\/[^\/]+\/)?chats/);
|
|
10
27
|
const isChatTemplate = data.template && data.template.name === 'chats';
|
|
11
28
|
|
|
12
29
|
if (isChatUrl || isChatTemplate) {
|
|
13
|
-
|
|
30
|
+
initFastInjection();
|
|
31
|
+
} else {
|
|
32
|
+
// יציאה מאזור הצ'אטים - איפוס הזיכרון
|
|
33
|
+
window.chatSearchState = { query: '', resultsHtml: '', isOpen: false, lastScroll: 0 };
|
|
14
34
|
}
|
|
15
35
|
});
|
|
16
36
|
|
|
17
37
|
$(window).on('action:chat.loaded', function (ev, data) {
|
|
38
|
+
highlightActiveChat();
|
|
18
39
|
handleScrollToMessage();
|
|
19
40
|
});
|
|
20
41
|
|
|
42
|
+
// בדיקה ראשונית
|
|
21
43
|
if (ajaxify.data && ajaxify.data.template && ajaxify.data.template.name === 'chats') {
|
|
22
|
-
|
|
44
|
+
initFastInjection();
|
|
23
45
|
}
|
|
24
46
|
|
|
25
|
-
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 פספס
|
|
26
70
|
let attempts = 0;
|
|
27
|
-
const interval = setInterval(
|
|
71
|
+
const interval = setInterval(() => {
|
|
28
72
|
attempts++;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
addGlobalSearchBar(container);
|
|
34
|
-
clearInterval(interval);
|
|
35
|
-
} else if (attempts >= 20) {
|
|
73
|
+
if (tryInject()) {
|
|
74
|
+
// לא עוצרים את האינטרוול מיד, כי לפעמים NodeBB מרענן פעמיים
|
|
75
|
+
if (attempts > 20) clearInterval(interval);
|
|
76
|
+
} else if (attempts > 40) { // 2 שניות
|
|
36
77
|
clearInterval(interval);
|
|
37
78
|
}
|
|
38
|
-
},
|
|
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;
|
|
39
96
|
}
|
|
40
97
|
|
|
41
98
|
function addGlobalSearchBar(container) {
|
|
42
99
|
if ($('#global-chat-search-container').length > 0) return;
|
|
43
100
|
|
|
101
|
+
// ה-HTML של התיבה
|
|
44
102
|
const searchHtml = `
|
|
45
103
|
<div id="global-chat-search-container" style="padding: 10px; background: #fff; border-bottom: 1px solid #ddd; margin-bottom: 10px;">
|
|
46
104
|
<div class="input-group">
|
|
47
|
-
<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;">
|
|
48
106
|
<span class="input-group-btn">
|
|
49
|
-
<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>
|
|
50
108
|
</span>
|
|
51
109
|
</div>
|
|
52
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>
|
|
@@ -55,10 +113,39 @@ $(document).ready(function () {
|
|
|
55
113
|
|
|
56
114
|
container.prepend(searchHtml);
|
|
57
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)
|
|
58
135
|
$('#btn-chat-search').off('click').on('click', executeSearch);
|
|
59
|
-
|
|
136
|
+
|
|
137
|
+
// שמירת גלילה
|
|
138
|
+
results.on('scroll', function() {
|
|
139
|
+
window.chatSearchState.lastScroll = $(this).scrollTop();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
input.off('keypress').on('keypress', function (e) {
|
|
60
143
|
if (e.which === 13) executeSearch();
|
|
61
144
|
});
|
|
145
|
+
|
|
146
|
+
input.on('input', function() {
|
|
147
|
+
window.chatSearchState.query = $(this).val();
|
|
148
|
+
});
|
|
62
149
|
}
|
|
63
150
|
|
|
64
151
|
function executeSearch() {
|
|
@@ -67,12 +154,15 @@ $(document).ready(function () {
|
|
|
67
154
|
|
|
68
155
|
if (!query) {
|
|
69
156
|
resultsContainer.hide();
|
|
157
|
+
window.chatSearchState.isOpen = false;
|
|
158
|
+
window.chatSearchState.resultsHtml = '';
|
|
70
159
|
return;
|
|
71
160
|
}
|
|
72
161
|
|
|
73
162
|
let targetUid = ajaxify.data.uid || app.user.uid;
|
|
74
163
|
|
|
75
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;
|
|
76
166
|
|
|
77
167
|
socket.emit('plugins.chatSearch.searchGlobal', {
|
|
78
168
|
query: query,
|
|
@@ -80,12 +170,14 @@ $(document).ready(function () {
|
|
|
80
170
|
}, function (err, messages) {
|
|
81
171
|
if (err) {
|
|
82
172
|
console.error(err);
|
|
83
|
-
resultsContainer.html('<div class="alert alert-danger" style="margin:5px;"
|
|
173
|
+
resultsContainer.html('<div class="alert alert-danger" style="margin:5px;">שגיאה</div>');
|
|
84
174
|
return;
|
|
85
175
|
}
|
|
86
176
|
|
|
87
177
|
if (!messages || messages.length === 0) {
|
|
88
|
-
|
|
178
|
+
const noRes = '<div class="text-center" style="padding:10px; color:#777;">לא נמצאו תוצאות.</div>';
|
|
179
|
+
resultsContainer.html(noRes);
|
|
180
|
+
window.chatSearchState.resultsHtml = noRes;
|
|
89
181
|
return;
|
|
90
182
|
}
|
|
91
183
|
|
|
@@ -93,17 +185,71 @@ $(document).ready(function () {
|
|
|
93
185
|
|
|
94
186
|
messages.forEach(msg => {
|
|
95
187
|
const date = new Date(msg.timestamp).toLocaleDateString();
|
|
96
|
-
|
|
97
|
-
let baseUrl = window.location.pathname;
|
|
188
|
+
let baseUrl = window.location.pathname.replace(/\/chats\/.*$/, '/chats');
|
|
98
189
|
if (baseUrl.endsWith('/')) baseUrl = baseUrl.slice(0, -1);
|
|
99
|
-
if (baseUrl.match(/\/[0-9]+$/)) baseUrl = baseUrl.replace(/\/[0-9]+$/, '');
|
|
100
190
|
|
|
101
191
|
const chatLink = baseUrl + '/' + msg.roomId + '?mid=' + msg.mid;
|
|
102
192
|
const senderName = (msg.user && msg.user.username) ? msg.user.username : 'Unknown';
|
|
103
|
-
|
|
104
|
-
// הסרנו את השורה של "עם: ..."
|
|
105
193
|
|
|
106
194
|
html += `
|
|
107
|
-
<li class="list-group-item search-result" style="cursor:pointer; border-bottom: 1px solid #f0f0f0; padding: 10px 8px;" onclick="ajaxify.go('${chatLink}')">
|
|
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}')">
|
|
108
196
|
<div style="font-size:13px; color:#333; margin-bottom:5px;">
|
|
109
|
-
<span class="pull-left text-muted"
|
|
197
|
+
<span class="pull-left text-muted" style="font-size:10px; margin-right: 8px;">${date}</span>
|
|
198
|
+
<strong>${msg.roomName}</strong>
|
|
199
|
+
</div>
|
|
200
|
+
<div style="font-size:12px; color:#444; background: #f9f9f9; padding: 6px; border-radius: 4px; border-right: 3px solid #007bff;">
|
|
201
|
+
<strong>${senderName}:</strong> ${msg.content}
|
|
202
|
+
</div>
|
|
203
|
+
</li>
|
|
204
|
+
`;
|
|
205
|
+
});
|
|
206
|
+
html += '</ul>';
|
|
207
|
+
|
|
208
|
+
resultsContainer.html(html);
|
|
209
|
+
|
|
210
|
+
// עדכון הזיכרון
|
|
211
|
+
window.chatSearchState.resultsHtml = html;
|
|
212
|
+
window.chatSearchState.lastScroll = 0;
|
|
213
|
+
|
|
214
|
+
highlightActiveChat();
|
|
215
|
+
});
|
|
216
|
+
}
|
|
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
|
+
|
|
232
|
+
function handleScrollToMessage() {
|
|
233
|
+
const params = new URLSearchParams(window.location.search);
|
|
234
|
+
const mid = params.get('mid');
|
|
235
|
+
if (!mid) return;
|
|
236
|
+
|
|
237
|
+
scrollToId(mid);
|
|
238
|
+
let attempts = 0;
|
|
239
|
+
const scrollInt = setInterval(() => {
|
|
240
|
+
attempts++;
|
|
241
|
+
if (scrollToId(mid) || attempts > 15) clearInterval(scrollInt);
|
|
242
|
+
}, 300);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function scrollToId(mid) {
|
|
246
|
+
const el = $('[data-mid="' + mid + '"]');
|
|
247
|
+
if (el.length > 0) {
|
|
248
|
+
el[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
249
|
+
el.css('background', '#fffeca').css('transition', 'background 1s');
|
|
250
|
+
setTimeout(() => el.css('background', ''), 2000);
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
});
|