nodebb-plugin-niki-loyalty 1.0.20 → 1.0.21
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 +2 -2
- package/package.json +1 -1
- package/static/lib/client.js +177 -57
package/library.js
CHANGED
package/package.json
CHANGED
package/static/lib/client.js
CHANGED
|
@@ -1,84 +1,181 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
/* globals document, $, window, ajaxify, app, localStorage */
|
|
4
|
+
|
|
3
5
|
$(document).ready(function () {
|
|
4
|
-
|
|
5
|
-
//
|
|
6
|
-
const
|
|
6
|
+
|
|
7
|
+
// --- GENEL AYARLAR ---
|
|
8
|
+
const config = {
|
|
9
|
+
// Widget Görünümü
|
|
10
|
+
logoUrl: "https://i.ibb.co/nZvtpss/logo-placeholder.png",
|
|
11
|
+
defaultLabel: "PUANIM",
|
|
12
|
+
|
|
13
|
+
// Topic Notification Ayarları
|
|
14
|
+
targetCategoryId: 1, // Takip edilecek Kategori ID
|
|
15
|
+
checkInterval: 300000, // 5 Dakika (ms)
|
|
16
|
+
showDuration: 15000, // Konu ekranda kaç sn kalsın?
|
|
17
|
+
maxShowCount: 5, // Bir konu bir kişiye max kaç kez gösterilsin?
|
|
18
|
+
topicValidity: 86400000, // Konu 24 saatten eskiyse gösterme (ms)
|
|
19
|
+
|
|
20
|
+
// API Yolları
|
|
21
|
+
walletRoute: 'niki-wallet',
|
|
22
|
+
csrf_token: config.csrf_token
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Global Durum Değişkenleri
|
|
26
|
+
let currentPoints = "..."; // Başlangıçta boş
|
|
27
|
+
let isTopicAnimating = false; // Şu an konu mu gösteriliyor?
|
|
7
28
|
|
|
8
|
-
//
|
|
29
|
+
// --- 1. WIDGET HTML ŞABLONU ---
|
|
9
30
|
const widgetHtml = `
|
|
10
31
|
<div id="niki-floating-widget" class="niki-hidden">
|
|
11
|
-
<div
|
|
12
|
-
<img src="${
|
|
32
|
+
<div id="niki-widget-content" class="niki-widget-content">
|
|
33
|
+
<img src="${config.logoUrl}" class="niki-widget-logo" alt="Niki">
|
|
13
34
|
<div class="niki-widget-text">
|
|
14
|
-
<span class="niki-lbl"
|
|
15
|
-
<span
|
|
35
|
+
<span id="niki-lbl" class="niki-lbl">${config.defaultLabel}</span>
|
|
36
|
+
<span id="niki-val" class="niki-val">${currentPoints}</span>
|
|
16
37
|
</div>
|
|
17
38
|
</div>
|
|
18
39
|
</div>
|
|
19
40
|
`;
|
|
20
41
|
|
|
21
|
-
//
|
|
42
|
+
// --- 2. BAŞLATMA FONKSİYONU ---
|
|
22
43
|
function initNikiWidget() {
|
|
44
|
+
// Kullanıcı giriş yapmamışsa widget'ı gösterme
|
|
23
45
|
if (!app.user.uid || app.user.uid <= 0) return;
|
|
24
46
|
|
|
25
|
-
//
|
|
47
|
+
// HTML Enjeksiyonu
|
|
26
48
|
if ($('#niki-floating-widget').length === 0) {
|
|
27
49
|
$('body').append(widgetHtml);
|
|
28
50
|
}
|
|
29
51
|
|
|
30
|
-
|
|
31
|
-
|
|
52
|
+
const $widget = $('#niki-floating-widget');
|
|
53
|
+
const $val = $('#niki-val');
|
|
54
|
+
|
|
55
|
+
// CACHE KONTROLÜ (Hızlı açılış için)
|
|
32
56
|
const cachedPoints = localStorage.getItem('niki_last_points');
|
|
33
57
|
if (cachedPoints !== null) {
|
|
34
|
-
|
|
35
|
-
$
|
|
58
|
+
currentPoints = cachedPoints;
|
|
59
|
+
$val.text(currentPoints);
|
|
60
|
+
$widget.removeClass('niki-hidden');
|
|
36
61
|
}
|
|
37
62
|
|
|
38
|
-
//
|
|
39
|
-
fixLogo();
|
|
40
|
-
|
|
41
|
-
// --- GÜNCEL VERİ ÇEKME ---
|
|
42
|
-
// Arka planda sunucuya sor: "Puan değişti mi?"
|
|
63
|
+
// GÜNCEL VERİ ÇEKME (Sunucudan)
|
|
43
64
|
$.get('/api/niki-loyalty/wallet-data', function(data) {
|
|
44
65
|
const freshPoints = data.points || 0;
|
|
66
|
+
currentPoints = freshPoints;
|
|
45
67
|
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// Logoyu tekrar kontrol et (Resim geç yüklendiyse)
|
|
68
|
+
// Eğer şu an konu animasyonu oynamıyorsa puanı güncelle
|
|
69
|
+
if (!isTopicAnimating) {
|
|
70
|
+
$val.text(freshPoints);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
$widget.removeClass('niki-hidden');
|
|
74
|
+
localStorage.setItem('niki_last_points', freshPoints); // Cache güncelle
|
|
54
75
|
fixLogo();
|
|
55
76
|
}).fail(function() {
|
|
56
|
-
// Hata olursa ve cache yoksa 0
|
|
77
|
+
// Hata olursa ve cache yoksa 0 göster
|
|
57
78
|
if (cachedPoints === null) {
|
|
58
|
-
|
|
59
|
-
$
|
|
79
|
+
currentPoints = "0";
|
|
80
|
+
$val.text("0");
|
|
81
|
+
$widget.removeClass('niki-hidden');
|
|
60
82
|
}
|
|
61
83
|
});
|
|
84
|
+
|
|
85
|
+
// Tıklama Olayı (Event Delegation)
|
|
86
|
+
$('body').off('click', '#niki-widget-content').on('click', '#niki-widget-content', function () {
|
|
87
|
+
const link = $(this).data('link') || config.walletRoute;
|
|
88
|
+
ajaxify.go(link);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
// Topic Notification Kontrolünü Başlat
|
|
92
|
+
startTopicCheck();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// --- 3. TOPIC NOTIFICATION SİSTEMİ ---
|
|
96
|
+
function startTopicCheck() {
|
|
97
|
+
// Eski interval varsa temizle
|
|
98
|
+
if (window.nikiTopicInterval) clearInterval(window.nikiTopicInterval);
|
|
99
|
+
|
|
100
|
+
// Fonksiyon: Kontrol Et ve Göster
|
|
101
|
+
const checkAndShow = function() {
|
|
102
|
+
$.get('/api/category/' + config.targetCategoryId, function (data) {
|
|
103
|
+
if (data && data.topics && data.topics.length > 0) {
|
|
104
|
+
const topic = data.topics[0];
|
|
105
|
+
const now = Date.now();
|
|
106
|
+
|
|
107
|
+
// KURAL 1: Süre Kontrolü (24 Saat)
|
|
108
|
+
if (now - topic.timestamp > config.topicValidity) return;
|
|
109
|
+
|
|
110
|
+
// KURAL 2: Gösterim Sayısı Kontrolü (LocalStorage)
|
|
111
|
+
let storageData = JSON.parse(localStorage.getItem('niki_topic_tracking') || '{}');
|
|
112
|
+
|
|
113
|
+
// Konu değişmişse sayacı sıfırla
|
|
114
|
+
if (storageData.tid !== topic.tid) {
|
|
115
|
+
storageData = { tid: topic.tid, count: 0 };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// KURAL 3: Limit Dolmadıysa Göster
|
|
119
|
+
if (storageData.count < config.maxShowCount) {
|
|
120
|
+
animateTopicNotification(topic);
|
|
121
|
+
|
|
122
|
+
// Sayacı artır ve kaydet
|
|
123
|
+
storageData.count++;
|
|
124
|
+
localStorage.setItem('niki_topic_tracking', JSON.stringify(storageData));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Sayfa açılır açılmaz bir kez kontrol et
|
|
131
|
+
checkAndShow();
|
|
132
|
+
|
|
133
|
+
// Sonra 5 dakikada bir devam et
|
|
134
|
+
window.nikiTopicInterval = setInterval(checkAndShow, config.checkInterval);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function animateTopicNotification(topic) {
|
|
138
|
+
const $val = $('#niki-val');
|
|
139
|
+
const $lbl = $('#niki-lbl');
|
|
140
|
+
const $container = $('#niki-widget-content');
|
|
141
|
+
|
|
142
|
+
if ($val.length > 0) {
|
|
143
|
+
isTopicAnimating = true; // Flag'i kaldır (Puan güncellemesi araya girmesin)
|
|
144
|
+
|
|
145
|
+
$val.fadeOut(200, function () {
|
|
146
|
+
$lbl.text('YENİ KONU:');
|
|
147
|
+
$(this).text(topic.title).fadeIn(200);
|
|
148
|
+
|
|
149
|
+
// Linki ve Stili Değiştir
|
|
150
|
+
$container.data('link', 'topic/' + topic.slug);
|
|
151
|
+
$container.addClass('highlight-topic');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// 15 Saniye sonra eski haline dön
|
|
155
|
+
setTimeout(function () {
|
|
156
|
+
$val.fadeOut(200, function () {
|
|
157
|
+
$lbl.text(config.defaultLabel);
|
|
158
|
+
$(this).text(currentPoints).fadeIn(200); // Güncel puanı yaz
|
|
159
|
+
|
|
160
|
+
// Linki ve Stili Sıfırla
|
|
161
|
+
$container.data('link', config.walletRoute);
|
|
162
|
+
$container.removeClass('highlight-topic');
|
|
163
|
+
|
|
164
|
+
isTopicAnimating = false; // Flag'i indir
|
|
165
|
+
});
|
|
166
|
+
}, config.showDuration);
|
|
167
|
+
}
|
|
62
168
|
}
|
|
63
169
|
|
|
64
|
-
//
|
|
170
|
+
// --- 4. YARDIMCI FONKSİYONLAR ---
|
|
65
171
|
function fixLogo() {
|
|
66
172
|
const img = document.querySelector("img.niki-widget-logo");
|
|
67
|
-
if (img && img.src !==
|
|
68
|
-
img.src =
|
|
173
|
+
if (img && img.src !== config.logoUrl) {
|
|
174
|
+
img.src = config.logoUrl;
|
|
69
175
|
}
|
|
70
176
|
}
|
|
71
177
|
|
|
72
|
-
//
|
|
73
|
-
initNikiWidget();
|
|
74
|
-
|
|
75
|
-
// Sayfa Geçişlerinde Tekrar Çalıştır
|
|
76
|
-
$(window).on('action:ajaxify.end', function () {
|
|
77
|
-
initNikiWidget();
|
|
78
|
-
setTimeout(fixLogo, 500); // 0.5sn sonra son bir kontrol
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
// --- AKTİFLİK SİSTEMİ (Heartbeat) ---
|
|
178
|
+
// --- 5. AKTİFLİK SİSTEMİ (HEARTBEAT) ---
|
|
82
179
|
let activeSeconds = 0;
|
|
83
180
|
let isUserActive = false;
|
|
84
181
|
let idleTimer;
|
|
@@ -88,29 +185,42 @@ $(document).ready(function () {
|
|
|
88
185
|
clearTimeout(idleTimer);
|
|
89
186
|
idleTimer = setTimeout(() => { isUserActive = false; }, 30000);
|
|
90
187
|
}
|
|
188
|
+
|
|
189
|
+
// Kullanıcı hareketlerini dinle
|
|
91
190
|
$(window).on('mousemove scroll keydown click touchstart', resetIdleTimer);
|
|
92
191
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
192
|
+
// Her saniye kontrol et
|
|
193
|
+
if (!window.nikiHeartbeatInterval) {
|
|
194
|
+
window.nikiHeartbeatInterval = setInterval(() => {
|
|
195
|
+
// Sadece bir konunun içindeysek (ajaxify.data.template.topic) ve sekme açıksa say
|
|
196
|
+
if (ajaxify.data.template.topic && document.visibilityState === 'visible' && isUserActive) {
|
|
197
|
+
activeSeconds++;
|
|
198
|
+
}
|
|
199
|
+
// 60 saniye dolduysa sunucuya bildir
|
|
200
|
+
if (activeSeconds >= 60) {
|
|
201
|
+
sendHeartbeat();
|
|
202
|
+
activeSeconds = 0;
|
|
203
|
+
}
|
|
204
|
+
}, 1000);
|
|
205
|
+
}
|
|
102
206
|
|
|
103
207
|
function sendHeartbeat() {
|
|
104
208
|
$.post('/api/niki-loyalty/heartbeat', { _csrf: config.csrf_token }, function(res) {
|
|
105
209
|
if (res.earned) {
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
// Hafızayı da güncelle
|
|
210
|
+
// Global puan değişkenini güncelle
|
|
211
|
+
currentPoints = res.total;
|
|
109
212
|
localStorage.setItem('niki_last_points', res.total);
|
|
110
213
|
|
|
214
|
+
// Eğer o sırada Konu Animasyonu oynamıyorsa, ekrandaki puanı da güncelle
|
|
215
|
+
if (!isTopicAnimating) {
|
|
216
|
+
$('#niki-val').text(res.total);
|
|
217
|
+
|
|
218
|
+
// Ufak bir zıplama efekti
|
|
219
|
+
$('#niki-floating-widget').addClass('niki-bounce');
|
|
220
|
+
setTimeout(() => $('#niki-floating-widget').removeClass('niki-bounce'), 500);
|
|
221
|
+
}
|
|
222
|
+
|
|
111
223
|
showNikiToast(`+${res.points} Puan Kazandın! ☕`);
|
|
112
|
-
$('#niki-floating-widget').addClass('niki-bounce');
|
|
113
|
-
setTimeout(() => $('#niki-floating-widget').removeClass('niki-bounce'), 500);
|
|
114
224
|
}
|
|
115
225
|
});
|
|
116
226
|
}
|
|
@@ -125,4 +235,14 @@ $(document).ready(function () {
|
|
|
125
235
|
setTimeout(() => toast.remove(), 3000);
|
|
126
236
|
}, 3000);
|
|
127
237
|
}
|
|
238
|
+
|
|
239
|
+
// --- BAŞLATMA ---
|
|
240
|
+
initNikiWidget();
|
|
241
|
+
|
|
242
|
+
// Sayfa geçişlerinde widget'ı koru
|
|
243
|
+
$(window).on('action:ajaxify.end', function () {
|
|
244
|
+
initNikiWidget();
|
|
245
|
+
setTimeout(fixLogo, 500);
|
|
246
|
+
});
|
|
247
|
+
|
|
128
248
|
});
|