nodebb-plugin-niki-loyalty 1.0.2 → 1.0.4
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 +12 -36
- package/package.json +1 -1
- package/static/lib/client.js +57 -32
package/library.js
CHANGED
|
@@ -2,83 +2,58 @@
|
|
|
2
2
|
|
|
3
3
|
const db = require.main.require('./src/database');
|
|
4
4
|
const user = require.main.require('./src/user');
|
|
5
|
-
const routeHelpers = require.main.require('./src/controllers/helpers');
|
|
6
5
|
|
|
7
6
|
const Plugin = {};
|
|
8
7
|
|
|
9
8
|
// --- AYARLAR ---
|
|
10
9
|
const SETTINGS = {
|
|
11
|
-
pointsPerHeartbeat: 5,
|
|
12
|
-
|
|
13
|
-
dailyCap: 250 // Günlük maksimum puan
|
|
10
|
+
pointsPerHeartbeat: 5,
|
|
11
|
+
dailyCap: 250
|
|
14
12
|
};
|
|
15
13
|
|
|
16
14
|
Plugin.init = async function (params) {
|
|
17
15
|
const router = params.router;
|
|
18
16
|
const middleware = params.middleware;
|
|
19
|
-
|
|
20
|
-
// Konsolda bu yazıyı görmelisin, görmüyorsan plugin aktif değildir.
|
|
21
|
-
console.log('✅ Niki Loyalty Plugin: Başlatılıyor...');
|
|
22
17
|
|
|
23
|
-
// API: Kalp Atışı (Puan Kazanma)
|
|
18
|
+
// 1. API: Kalp Atışı (Puan Kazanma - Client.js burayı kullanır)
|
|
24
19
|
router.post('/api/niki-loyalty/heartbeat', middleware.ensureLoggedIn, async (req, res) => {
|
|
25
20
|
const uid = req.uid;
|
|
26
|
-
const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
27
|
-
|
|
28
|
-
// 1. Günlük Limiti Kontrol Et
|
|
21
|
+
const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
29
22
|
const dailyKey = `niki:daily:${uid}:${today}`;
|
|
23
|
+
|
|
30
24
|
const currentDailyScore = await db.getObjectField(dailyKey, 'score') || 0;
|
|
31
25
|
|
|
32
26
|
if (parseInt(currentDailyScore) >= SETTINGS.dailyCap) {
|
|
33
27
|
return res.json({ earned: false, reason: 'daily_cap' });
|
|
34
28
|
}
|
|
35
29
|
|
|
36
|
-
// 2. Puan Ver
|
|
37
30
|
await user.incrementUserFieldBy(uid, 'niki_points', SETTINGS.pointsPerHeartbeat);
|
|
38
31
|
await db.incrObjectFieldBy(dailyKey, 'score', SETTINGS.pointsPerHeartbeat);
|
|
39
32
|
|
|
40
|
-
// 3. Güncel Bakiyeyi Dön
|
|
41
33
|
const newBalance = await user.getUserField(uid, 'niki_points');
|
|
42
|
-
return res.json({
|
|
43
|
-
earned: true,
|
|
44
|
-
points: SETTINGS.pointsPerHeartbeat,
|
|
45
|
-
total: newBalance,
|
|
46
|
-
daily: parseInt(currentDailyScore) + SETTINGS.pointsPerHeartbeat
|
|
47
|
-
});
|
|
34
|
+
return res.json({ earned: true, points: SETTINGS.pointsPerHeartbeat, total: newBalance });
|
|
48
35
|
});
|
|
49
36
|
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
routeHelpers.setupPageRoute(router, '/niki-wallet', middleware, [], async (req, res) => {
|
|
37
|
+
// 2. YENİ API: Cüzdan Verisi Çekme (Custom Page burayı kullanacak)
|
|
38
|
+
router.get('/api/niki-loyalty/wallet-data', middleware.ensureLoggedIn, async (req, res) => {
|
|
53
39
|
const uid = req.uid;
|
|
54
|
-
|
|
55
|
-
// Giriş yapmamışsa login sayfasına at
|
|
56
|
-
if (!uid) return res.redirect('/login');
|
|
57
|
-
|
|
58
40
|
const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
|
|
59
41
|
|
|
60
|
-
// Verileri Çek
|
|
61
42
|
const [userData, dailyData] = await Promise.all([
|
|
62
|
-
user.getUserFields(uid, ['
|
|
43
|
+
user.getUserFields(uid, ['niki_points']),
|
|
63
44
|
db.getObject(`niki:daily:${uid}:${today}`)
|
|
64
45
|
]);
|
|
65
46
|
|
|
66
47
|
const currentPoints = parseInt(userData.niki_points) || 0;
|
|
67
48
|
const dailyScore = parseInt(dailyData ? dailyData.score : 0) || 0;
|
|
68
|
-
|
|
69
|
-
// Yüzdelik Hesapla (Bar için)
|
|
70
49
|
let dailyPercent = (dailyScore / SETTINGS.dailyCap) * 100;
|
|
71
50
|
if (dailyPercent > 100) dailyPercent = 100;
|
|
72
51
|
|
|
73
|
-
|
|
74
|
-
res.render('niki-wallet', {
|
|
75
|
-
title: 'Niki Cüzdan',
|
|
52
|
+
res.json({
|
|
76
53
|
points: currentPoints,
|
|
77
54
|
dailyScore: dailyScore,
|
|
78
55
|
dailyCap: SETTINGS.dailyCap,
|
|
79
|
-
dailyPercent: dailyPercent
|
|
80
|
-
user: userData,
|
|
81
|
-
breadcrumbs: routeHelpers.buildBreadcrumbs([{ text: 'Niki Cüzdan' }]) // Breadcrumb ekledim (opsiyonel şıklık)
|
|
56
|
+
dailyPercent: dailyPercent
|
|
82
57
|
});
|
|
83
58
|
});
|
|
84
59
|
};
|
|
@@ -88,6 +63,7 @@ Plugin.addScripts = async function (scripts) {
|
|
|
88
63
|
return scripts;
|
|
89
64
|
};
|
|
90
65
|
|
|
66
|
+
// Menüye eklemeye devam edelim, Custom Page ile aynı linki vereceğiz
|
|
91
67
|
Plugin.addNavigation = async function (nav) {
|
|
92
68
|
nav.push({
|
|
93
69
|
"route": "/niki-wallet",
|
package/package.json
CHANGED
package/static/lib/client.js
CHANGED
|
@@ -1,52 +1,76 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
$(document).ready(function () {
|
|
4
|
-
// ---
|
|
5
|
-
// Buraya
|
|
6
|
-
const NIKI_LOGO_URL = 'https://
|
|
4
|
+
// --- AYARLAR ---
|
|
5
|
+
// Buraya logonun linkini koy. Eğer link yoksa geçici bir kedi ikonu koydum.
|
|
6
|
+
const NIKI_LOGO_URL = 'https://cdn-icons-png.flaticon.com/512/616/616554.png';
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<div
|
|
12
|
-
<
|
|
13
|
-
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
<span class="niki-val" id="niki-live-points">...</span>
|
|
17
|
-
</div>
|
|
8
|
+
// Widget HTML Şablonu
|
|
9
|
+
const widgetHtml = `
|
|
10
|
+
<div id="niki-floating-widget" class="niki-hidden">
|
|
11
|
+
<div class="niki-widget-content" onclick="ajaxify.go('niki-wallet')">
|
|
12
|
+
<img src="${NIKI_LOGO_URL}" class="niki-widget-logo" alt="Niki">
|
|
13
|
+
<div class="niki-widget-text">
|
|
14
|
+
<span class="niki-lbl">PUANIM</span>
|
|
15
|
+
<span class="niki-val" id="niki-live-points">...</span>
|
|
18
16
|
</div>
|
|
19
17
|
</div>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
</div>
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
// 1. Widget'ı Ekrana Koyma ve Veri Çekme Fonksiyonu
|
|
22
|
+
function initNikiWidget() {
|
|
23
|
+
// Eğer giriş yapmamışsa çalışma
|
|
24
|
+
if (!app.user.uid || app.user.uid <= 0) return;
|
|
25
|
+
|
|
26
|
+
// Widget zaten varsa tekrar ekleme, sadece veriyi güncelle
|
|
27
|
+
if ($('#niki-floating-widget').length === 0) {
|
|
28
|
+
$('body').append(widgetHtml);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// VERİYİ DOĞRU YERDEN ÇEK: wallet-data API'si (Kesin çözüm)
|
|
32
|
+
$.get('/api/niki-loyalty/wallet-data', function(data) {
|
|
33
|
+
// Puanı güncelle
|
|
34
|
+
$('#niki-live-points').text(data.points || 0);
|
|
35
|
+
|
|
36
|
+
// Widget'ı görünür yap
|
|
37
|
+
$('#niki-floating-widget').removeClass('niki-hidden');
|
|
38
|
+
}).fail(function() {
|
|
39
|
+
// Hata olursa 0 yaz ama widget'ı yine de göster
|
|
40
|
+
$('#niki-live-points').text('0');
|
|
41
|
+
$('#niki-floating-widget').removeClass('niki-hidden');
|
|
26
42
|
});
|
|
27
43
|
}
|
|
28
44
|
|
|
29
|
-
// 2.
|
|
45
|
+
// 2. Sayfa İlk Açıldığında Çalıştır
|
|
46
|
+
initNikiWidget();
|
|
47
|
+
|
|
48
|
+
// 3. Sayfa Değiştiğinde (Menülerde gezerken) Tekrar Çalıştır
|
|
49
|
+
$(window).on('action:ajaxify.end', function () {
|
|
50
|
+
initNikiWidget();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// --- AKTİFLİK VE PUAN KAZANMA SİSTEMİ ---
|
|
30
54
|
let activeSeconds = 0;
|
|
31
55
|
let isUserActive = false;
|
|
32
56
|
let idleTimer;
|
|
33
57
|
|
|
34
|
-
// Hareket
|
|
58
|
+
// Hareket algılayınca sayacı sıfırla
|
|
35
59
|
function resetIdleTimer() {
|
|
36
60
|
isUserActive = true;
|
|
37
61
|
clearTimeout(idleTimer);
|
|
38
|
-
idleTimer = setTimeout(() => { isUserActive = false; }, 30000); // 30sn hareketsizse
|
|
62
|
+
idleTimer = setTimeout(() => { isUserActive = false; }, 30000); // 30sn hareketsizse pasif ol
|
|
39
63
|
}
|
|
40
|
-
$(window).on('mousemove scroll keydown click', resetIdleTimer);
|
|
64
|
+
$(window).on('mousemove scroll keydown click touchstart', resetIdleTimer);
|
|
41
65
|
|
|
42
|
-
// Her saniye kontrol
|
|
66
|
+
// Her saniye kontrol et
|
|
43
67
|
setInterval(() => {
|
|
44
|
-
// Sadece Topic
|
|
68
|
+
// Sadece "Topic" sayfalarında, sekme görünürse ve kullanıcı aktifse say
|
|
45
69
|
if (ajaxify.data.template.topic && document.visibilityState === 'visible' && isUserActive) {
|
|
46
70
|
activeSeconds++;
|
|
47
71
|
}
|
|
48
72
|
|
|
49
|
-
// 60 saniye
|
|
73
|
+
// 60 saniye dolunca sunucuya bildir
|
|
50
74
|
if (activeSeconds >= 60) {
|
|
51
75
|
sendHeartbeat();
|
|
52
76
|
activeSeconds = 0;
|
|
@@ -54,21 +78,22 @@ $(document).ready(function () {
|
|
|
54
78
|
}, 1000);
|
|
55
79
|
|
|
56
80
|
function sendHeartbeat() {
|
|
57
|
-
|
|
58
|
-
$.post('/api/niki-loyalty/heartbeat', { tid: topicId, _csrf: config.csrf_token }, function(res) {
|
|
81
|
+
$.post('/api/niki-loyalty/heartbeat', { _csrf: config.csrf_token }, function(res) {
|
|
59
82
|
if (res.earned) {
|
|
60
|
-
//
|
|
83
|
+
// Puanı anlık güncelle
|
|
61
84
|
$('#niki-live-points').text(res.total);
|
|
62
|
-
showNikiToast(`+${res.points} Puan! ☕`);
|
|
63
85
|
|
|
64
|
-
//
|
|
86
|
+
// Bildirim göster
|
|
87
|
+
showNikiToast(`+${res.points} Puan Kazandın! ☕`);
|
|
88
|
+
|
|
89
|
+
// Widget'ı zıplat
|
|
65
90
|
$('#niki-floating-widget').addClass('niki-bounce');
|
|
66
91
|
setTimeout(() => $('#niki-floating-widget').removeClass('niki-bounce'), 500);
|
|
67
92
|
}
|
|
68
93
|
});
|
|
69
94
|
}
|
|
70
95
|
|
|
71
|
-
// Özel Bildirim (Toast)
|
|
96
|
+
// Özel Bildirim (Toast) Fonksiyonu
|
|
72
97
|
function showNikiToast(msg) {
|
|
73
98
|
$('.niki-toast').remove();
|
|
74
99
|
const toast = $(`<div class="niki-toast"><i class="fa fa-paw"></i> ${msg}</div>`);
|