nodebb-plugin-niki-loyalty 1.0.6 → 1.0.8

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
@@ -1,78 +1,128 @@
1
1
  'use strict';
2
2
 
3
- const db = require.main.require('./src/database');
4
- const user = require.main.require('./src/user');
3
+ $(document).ready(function () {
4
+ // --- AYARLAR ---
5
+ // 1. Logo Ayarı (Senin çalışan linkin)
6
+ const NIKI_LOGO_URL = "https://i.ibb.co/nZvtpss/logo-placeholder.png";
5
7
 
6
- const Plugin = {};
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>
16
+ </div>
17
+ </div>
18
+ </div>
19
+ `;
7
20
 
8
- // --- AYARLAR ---
9
- const SETTINGS = {
10
- pointsPerHeartbeat: 5,
11
- dailyCap: 250
12
- };
21
+ // 1. Widget Başlatma ve Veri Yönetimi
22
+ function initNikiWidget() {
23
+ if (!app.user.uid || app.user.uid <= 0) return;
13
24
 
14
- Plugin.init = async function (params) {
15
- const router = params.router;
16
- const middleware = params.middleware;
17
-
18
- // 1. API: Kalp Atışı (Puan Kazanma - Client.js burayı kullanır)
19
- router.post('/api/niki-loyalty/heartbeat', middleware.ensureLoggedIn, async (req, res) => {
20
- const uid = req.uid;
21
- const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
22
- const dailyKey = `niki:daily:${uid}:${today}`;
23
-
24
- const currentDailyScore = await db.getObjectField(dailyKey, 'score') || 0;
25
+ // Widget yoksa ekle
26
+ if ($('#niki-floating-widget').length === 0) {
27
+ $('body').append(widgetHtml);
28
+ }
25
29
 
26
- if (parseInt(currentDailyScore) >= SETTINGS.dailyCap) {
27
- return res.json({ earned: false, reason: 'daily_cap' });
30
+ // --- HIZLI YÜKLEME (CACHE) ---
31
+ // Önce hafızadaki son puanı hemen göster (Bekletme yapmaz)
32
+ const cachedPoints = localStorage.getItem('niki_last_points');
33
+ if (cachedPoints !== null) {
34
+ $('#niki-live-points').text(cachedPoints);
35
+ $('#niki-floating-widget').removeClass('niki-hidden');
28
36
  }
29
37
 
30
- await user.incrementUserFieldBy(uid, 'niki_points', SETTINGS.pointsPerHeartbeat);
31
- await db.incrObjectFieldBy(dailyKey, 'score', SETTINGS.pointsPerHeartbeat);
38
+ // Logo Kontrolü (Garanti olsun)
39
+ fixLogo();
32
40
 
33
- const newBalance = await user.getUserField(uid, 'niki_points');
34
- return res.json({ earned: true, points: SETTINGS.pointsPerHeartbeat, total: newBalance });
35
- });
41
+ // --- GÜNCEL VERİ ÇEKME ---
42
+ // Arka planda sunucuya sor: "Puan değişti mi?"
43
+ $.get('/api/niki-loyalty/wallet-data', function(data) {
44
+ const freshPoints = data.points || 0;
45
+
46
+ // Puanı güncelle
47
+ $('#niki-live-points').text(freshPoints);
48
+ $('#niki-floating-widget').removeClass('niki-hidden'); // İlk kez açılıyorsa göster
49
+
50
+ // Yeni puanı hafızaya at (Bir sonraki giriş için)
51
+ localStorage.setItem('niki_last_points', freshPoints);
52
+
53
+ // Logoyu tekrar kontrol et (Resim geç yüklendiyse)
54
+ fixLogo();
55
+ }).fail(function() {
56
+ // Hata olursa ve cache yoksa 0 yaz
57
+ if (cachedPoints === null) {
58
+ $('#niki-live-points').text('0');
59
+ $('#niki-floating-widget').removeClass('niki-hidden');
60
+ }
61
+ });
62
+ }
36
63
 
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) => {
39
- const uid = req.uid;
40
- const today = new Date().toISOString().slice(0, 10).replace(/-/g, '');
41
-
42
- const [userData, dailyData] = await Promise.all([
43
- user.getUserFields(uid, ['niki_points']),
44
- db.getObject(`niki:daily:${uid}:${today}`)
45
- ]);
64
+ // Logo Düzeltici (Senin çalışan kodun entegresi)
65
+ function fixLogo() {
66
+ const img = document.querySelector("img.niki-widget-logo");
67
+ if (img && img.src !== NIKI_LOGO_URL) {
68
+ img.src = NIKI_LOGO_URL;
69
+ }
70
+ }
46
71
 
47
- const currentPoints = parseInt(userData.niki_points) || 0;
48
- const dailyScore = parseInt(dailyData ? dailyData.score : 0) || 0;
49
- let dailyPercent = (dailyScore / SETTINGS.dailyCap) * 100;
50
- if (dailyPercent > 100) dailyPercent = 100;
72
+ // Başlat
73
+ initNikiWidget();
51
74
 
52
- res.json({
53
- points: currentPoints,
54
- dailyScore: dailyScore,
55
- dailyCap: SETTINGS.dailyCap,
56
- dailyPercent: dailyPercent
57
- });
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
58
79
  });
59
- };
60
80
 
61
- Plugin.addScripts = async function (scripts) {
62
- scripts.push('plugins/nodebb-plugin-niki-loyalty/static/lib/client.js');
63
- return scripts;
64
- };
81
+ // --- AKTİFLİK SİSTEMİ (Heartbeat) ---
82
+ let activeSeconds = 0;
83
+ let isUserActive = false;
84
+ let idleTimer;
85
+
86
+ function resetIdleTimer() {
87
+ isUserActive = true;
88
+ clearTimeout(idleTimer);
89
+ idleTimer = setTimeout(() => { isUserActive = false; }, 30000);
90
+ }
91
+ $(window).on('mousemove scroll keydown click touchstart', resetIdleTimer);
65
92
 
66
- // Menüye eklemeye devam edelim, Custom Page ile aynı linki vereceğiz
67
- Plugin.addNavigation = async function (nav) {
68
- nav.push({
69
- "route": "/niki-wallet",
70
- "title": "Niki Cüzdan",
71
- "enabled": true,
72
- "iconClass": "fa-coffee",
73
- "text": "Niki Cüzdan"
74
- });
75
- return nav;
76
- };
93
+ setInterval(() => {
94
+ if (ajaxify.data.template.topic && document.visibilityState === 'visible' && isUserActive) {
95
+ activeSeconds++;
96
+ }
97
+ if (activeSeconds >= 60) {
98
+ sendHeartbeat();
99
+ activeSeconds = 0;
100
+ }
101
+ }, 1000);
102
+
103
+ function sendHeartbeat() {
104
+ $.post('/api/niki-loyalty/heartbeat', { _csrf: config.csrf_token }, function(res) {
105
+ if (res.earned) {
106
+ // Puanı güncelle
107
+ $('#niki-live-points').text(res.total);
108
+ // Hafızayı da güncelle
109
+ localStorage.setItem('niki_last_points', res.total);
110
+
111
+ showNikiToast(`+${res.points} Puan Kazandın! ☕`);
112
+ $('#niki-floating-widget').addClass('niki-bounce');
113
+ setTimeout(() => $('#niki-floating-widget').removeClass('niki-bounce'), 500);
114
+ }
115
+ });
116
+ }
77
117
 
78
- module.exports = Plugin;
118
+ function showNikiToast(msg) {
119
+ $('.niki-toast').remove();
120
+ const toast = $(`<div class="niki-toast"><i class="fa fa-paw"></i> ${msg}</div>`);
121
+ $('body').append(toast);
122
+ setTimeout(() => { toast.addClass('show'); }, 100);
123
+ setTimeout(() => {
124
+ toast.removeClass('show');
125
+ setTimeout(() => toast.remove(), 3000);
126
+ }, 3000);
127
+ }
128
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-niki-loyalty",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Niki The Cat Coffee Loyalty System - Earn points while studying on IEU Forum.",
5
5
  "main": "library.js",
6
6
  "nbbpm": {
@@ -2,15 +2,14 @@
2
2
 
3
3
  $(document).ready(function () {
4
4
  // --- AYARLAR ---
5
- // Logoyu yerel dosyadan çekiyoruz (İnternet linki değil)
6
- // Bu sayede "resim yüklenmedi" hatası almazsın.
7
- const NIKI_LOGO_PATH = config.relative_path + '/plugins/nodebb-plugin-niki-loyalty/static/logo.png';
5
+ // 1. Logo Ayarı (Senin çalışan linkin)
6
+ const NIKI_LOGO_URL = "https://i.ibb.co/nZvtpss/logo-placeholder.png";
8
7
 
9
8
  // Widget HTML Şablonu
10
9
  const widgetHtml = `
11
10
  <div id="niki-floating-widget" class="niki-hidden">
12
11
  <div class="niki-widget-content" onclick="ajaxify.go('niki-wallet')">
13
- <img src="${NIKI_LOGO_PATH}" class="niki-widget-logo" alt="Niki">
12
+ <img src="${NIKI_LOGO_URL}" class="niki-widget-logo" alt="Niki">
14
13
  <div class="niki-widget-text">
15
14
  <span class="niki-lbl">PUANIM</span>
16
15
  <span class="niki-val" id="niki-live-points">...</span>
@@ -19,59 +18,82 @@ $(document).ready(function () {
19
18
  </div>
20
19
  `;
21
20
 
22
- // 1. Widget Ekrana Koyma ve Veri Çekme Fonksiyonu
21
+ // 1. Widget Başlatma ve Veri Yönetimi
23
22
  function initNikiWidget() {
24
- // Eğer giriş yapmamışsa çalışma
25
23
  if (!app.user.uid || app.user.uid <= 0) return;
26
24
 
27
- // Widget zaten varsa tekrar ekleme
25
+ // Widget yoksa ekle
28
26
  if ($('#niki-floating-widget').length === 0) {
29
27
  $('body').append(widgetHtml);
30
28
  }
31
29
 
32
- // VERİYİ DOĞRU YERDEN ÇEK: wallet-data API'si
30
+ // --- HIZLI YÜKLEME (CACHE) ---
31
+ // Önce hafızadaki son puanı hemen göster (Bekletme yapmaz)
32
+ const cachedPoints = localStorage.getItem('niki_last_points');
33
+ if (cachedPoints !== null) {
34
+ $('#niki-live-points').text(cachedPoints);
35
+ $('#niki-floating-widget').removeClass('niki-hidden');
36
+ }
37
+
38
+ // Logo Kontrolü (Garanti olsun)
39
+ fixLogo();
40
+
41
+ // --- GÜNCEL VERİ ÇEKME ---
42
+ // Arka planda sunucuya sor: "Puan değişti mi?"
33
43
  $.get('/api/niki-loyalty/wallet-data', function(data) {
44
+ const freshPoints = data.points || 0;
45
+
34
46
  // Puanı güncelle
35
- $('#niki-live-points').text(data.points || 0);
47
+ $('#niki-live-points').text(freshPoints);
48
+ $('#niki-floating-widget').removeClass('niki-hidden'); // İlk kez açılıyorsa göster
36
49
 
37
- // Widget'ı görünür yap
38
- $('#niki-floating-widget').removeClass('niki-hidden');
50
+ // Yeni puanı hafızaya at (Bir sonraki giriş için)
51
+ localStorage.setItem('niki_last_points', freshPoints);
52
+
53
+ // Logoyu tekrar kontrol et (Resim geç yüklendiyse)
54
+ fixLogo();
39
55
  }).fail(function() {
40
- // Hata olursa 0 yaz ama widget'ı yine de göster
41
- $('#niki-live-points').text('0');
42
- $('#niki-floating-widget').removeClass('niki-hidden');
56
+ // Hata olursa ve cache yoksa 0 yaz
57
+ if (cachedPoints === null) {
58
+ $('#niki-live-points').text('0');
59
+ $('#niki-floating-widget').removeClass('niki-hidden');
60
+ }
43
61
  });
44
62
  }
45
63
 
46
- // 2. Sayfa İlk Açıldığında Çalıştır
64
+ // Logo Düzeltici (Senin çalışan kodun entegresi)
65
+ function fixLogo() {
66
+ const img = document.querySelector("img.niki-widget-logo");
67
+ if (img && img.src !== NIKI_LOGO_URL) {
68
+ img.src = NIKI_LOGO_URL;
69
+ }
70
+ }
71
+
72
+ // Başlat
47
73
  initNikiWidget();
48
74
 
49
- // 3. Sayfa Değiştiğinde (Menülerde gezerken) Tekrar Çalıştır
75
+ // Sayfa Geçişlerinde Tekrar Çalıştır
50
76
  $(window).on('action:ajaxify.end', function () {
51
77
  initNikiWidget();
78
+ setTimeout(fixLogo, 500); // 0.5sn sonra son bir kontrol
52
79
  });
53
80
 
54
- // --- AKTİFLİK VE PUAN KAZANMA SİSTEMİ ---
81
+ // --- AKTİFLİK SİSTEMİ (Heartbeat) ---
55
82
  let activeSeconds = 0;
56
83
  let isUserActive = false;
57
84
  let idleTimer;
58
85
 
59
- // Hareket algılayınca sayacı sıfırla
60
86
  function resetIdleTimer() {
61
87
  isUserActive = true;
62
88
  clearTimeout(idleTimer);
63
- idleTimer = setTimeout(() => { isUserActive = false; }, 30000); // 30sn hareketsizse pasif ol
89
+ idleTimer = setTimeout(() => { isUserActive = false; }, 30000);
64
90
  }
65
91
  $(window).on('mousemove scroll keydown click touchstart', resetIdleTimer);
66
92
 
67
- // Her saniye kontrol et
68
93
  setInterval(() => {
69
- // Sadece "Topic" sayfalarında, sekme görünürse ve kullanıcı aktifse say
70
94
  if (ajaxify.data.template.topic && document.visibilityState === 'visible' && isUserActive) {
71
95
  activeSeconds++;
72
96
  }
73
-
74
- // 60 saniye dolunca sunucuya bildir
75
97
  if (activeSeconds >= 60) {
76
98
  sendHeartbeat();
77
99
  activeSeconds = 0;
@@ -81,20 +103,18 @@ $(document).ready(function () {
81
103
  function sendHeartbeat() {
82
104
  $.post('/api/niki-loyalty/heartbeat', { _csrf: config.csrf_token }, function(res) {
83
105
  if (res.earned) {
84
- // Puanı anlık güncelle
106
+ // Puanı güncelle
85
107
  $('#niki-live-points').text(res.total);
86
-
87
- // Bildirim göster
108
+ // Hafızayı da güncelle
109
+ localStorage.setItem('niki_last_points', res.total);
110
+
88
111
  showNikiToast(`+${res.points} Puan Kazandın! ☕`);
89
-
90
- // Widget'ı zıplat
91
112
  $('#niki-floating-widget').addClass('niki-bounce');
92
113
  setTimeout(() => $('#niki-floating-widget').removeClass('niki-bounce'), 500);
93
114
  }
94
115
  });
95
116
  }
96
117
 
97
- // Özel Bildirim (Toast) Fonksiyonu
98
118
  function showNikiToast(msg) {
99
119
  $('.niki-toast').remove();
100
120
  const toast = $(`<div class="niki-toast"><i class="fa fa-paw"></i> ${msg}</div>`);
@@ -102,7 +122,7 @@ $(document).ready(function () {
102
122
  setTimeout(() => { toast.addClass('show'); }, 100);
103
123
  setTimeout(() => {
104
124
  toast.removeClass('show');
105
- setTimeout(() => toast.remove(), 300);
125
+ setTimeout(() => toast.remove(), 3000);
106
126
  }, 3000);
107
127
  }
108
128
  });