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 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, // Her vuruşta kaç puan?
12
- heartbeatInterval: 60, // Kaç saniyede bir
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, ''); // 20251214 formatı
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
- // SAYFA: Cüzdan (/niki-wallet)
51
- // setupPageRoute: NodeBB'nin yeni sayfa oluşturma yardımcısıdır.
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, ['username', 'userslug', 'picture', 'niki_points']),
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
- // niki-wallet.tpl dosyasını render et
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-niki-loyalty",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "Niki The Cat Coffee Loyalty System - Earn points while studying on IEU Forum.",
5
5
  "main": "library.js",
6
6
  "nbbpm": {
@@ -1,52 +1,76 @@
1
1
  'use strict';
2
2
 
3
3
  $(document).ready(function () {
4
- // --- NIKI LOGO URL ---
5
- // Buraya Niki'nin transparent PNG logosunu koymalısın.
6
- const NIKI_LOGO_URL = 'https://i.imgur.com/kXUe4M6.png'; // Örnek kedi logosu
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
- // 1. Widget Ekrana Bas (Eğer giriş yapmışsa)
9
- if (app.user.uid > 0 && $('#niki-floating-widget').length === 0) {
10
- const widgetHtml = `
11
- <div id="niki-floating-widget">
12
- <div class="niki-widget-content" onclick="ajaxify.go('niki-wallet')">
13
- <img src="${NIKI_LOGO_URL}" class="niki-widget-logo" alt="Niki">
14
- <div class="niki-widget-text">
15
- <span class="niki-lbl">PUANIM</span>
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
- $('body').append(widgetHtml);
22
-
23
- // Açılışta puanı çek
24
- $.get('/api/user/' + app.user.userslug, function(data) {
25
- $('#niki-live-points').text(data.niki_points || 0);
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. Aktiflik Takibi (Time-on-Site)
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 algılayıcı
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 dur
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 sayfalarındaysak say
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 doldu mu?
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
- const topicId = ajaxify.data.tid;
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
- // UI Güncelle
83
+ // Puanı anlık güncelle
61
84
  $('#niki-live-points').text(res.total);
62
- showNikiToast(`+${res.points} Puan! ☕`);
63
85
 
64
- // Widget'a ufak bir "bounce" efekti ver
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>`);