noah33565 commited on
Commit
9928a35
Β·
verified Β·
1 Parent(s): 239f717

Update app.js

Browse files
Files changed (1) hide show
  1. app.js +77 -9
app.js CHANGED
@@ -18,6 +18,21 @@ const OWNER_NAME = 'Noah';
18
  const OWNER_PASS = 'Noah100419!';
19
  const POLL_MS = 3000; // Polling-Intervall in ms
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  // ─── STATE ────────────────────────────────────────────────────
22
  let currentUser = null;
23
  let currentChannel = 'allgemein';
@@ -67,11 +82,21 @@ function getFingerprint() {
67
  }
68
  const MY_FP = getFingerprint();
69
 
 
 
 
 
 
 
 
 
 
 
70
  // ─── JSONBIN API ──────────────────────────────────────────────
71
  const isConfigured = () =>
72
  !JSONBIN_KEY.includes('DEIN') && !JSONBIN_BIN.includes('DEINE');
73
 
74
- async function apiLoad() {
75
  if (!isConfigured()) {
76
  const v = localStorage.getItem('nc_db');
77
  DB = v ? JSON.parse(v) : DB_DEFAULT();
@@ -83,12 +108,35 @@ async function apiLoad() {
83
  headers: { 'X-Master-Key': JSONBIN_KEY, 'X-Bin-Meta': 'false' }
84
  });
85
  if (!r.ok) throw new Error(r.status);
86
- DB = await r.json();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
  ensureFields();
88
- localStorage.setItem('nc_db', JSON.stringify(DB)); // Lokaler Backup
89
  } catch(e) {
90
  console.warn('Load fehlgeschlagen:', e);
91
- // Fallback: letzten lokalen Stand nehmen
92
  const v = localStorage.getItem('nc_db');
93
  if (v) DB = JSON.parse(v);
94
  else DB = DB_DEFAULT();
@@ -240,6 +288,7 @@ async function doLogin() {
240
  if (decode(entry.password)!==p)return err('Falscher Username oder Passwort');
241
  if (DB.moderation.bans?.[u.toLowerCase()]) return err('🚫 Du bist von NoahsChat gebannt.');
242
  currentUser={...entry};
 
243
  refreshCaptcha('login');
244
  dbSet(`users/${u.toLowerCase()}/_fp`, MY_FP);
245
  dbSet(`online/${entry.username}`, Date.now());
@@ -251,6 +300,7 @@ async function doLogin() {
251
  // ─── LOGOUT ───────────────────────────────────────────────────
252
  async function doLogout() {
253
  if (!currentUser) return;
 
254
  dbDelete(`online/${currentUser.username}`);
255
  if (voiceActive) leaveVoice();
256
  clearInterval(pollTimer);
@@ -285,9 +335,9 @@ function startPolling() {
285
  async function pollTick() {
286
  if (!currentUser) return;
287
 
288
- // DB neu laden β†’ bekommt Γ„nderungen anderer User
289
  setSyncStatus('sync');
290
- await apiLoad();
291
  setSyncStatus('ok');
292
 
293
  // Eigene Daten aktualisieren (Rolle kΓΆnnte geΓ€ndert worden sein)
@@ -299,9 +349,11 @@ async function pollTick() {
299
  translationEnabled = me.settings?.translate||false;
300
  }
301
 
302
- // Heartbeat
 
303
  DB.online[currentUser.username] = Date.now();
304
- apiSave();
 
305
 
306
  // Moderation-Checks
307
  const mod = DB.moderation;
@@ -1347,7 +1399,23 @@ window.addEventListener('DOMContentLoaded', async () => {
1347
  // Loading entfernen
1348
  loadEl.remove();
1349
 
1350
- // Captchas initialisieren
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1351
  refreshCaptcha('login');
1352
  refreshCaptcha('reg');
1353
  });
 
18
  const OWNER_PASS = 'Noah100419!';
19
  const POLL_MS = 3000; // Polling-Intervall in ms
20
 
21
+ // ─── SESSION PERSISTENCE ──────────────────────────────────────
22
+ function saveSession(user) {
23
+ if (user) localStorage.setItem('nc_session', JSON.stringify({ username: user.username, ts: Date.now() }));
24
+ else localStorage.removeItem('nc_session');
25
+ }
26
+ function loadSession() {
27
+ try {
28
+ const s = localStorage.getItem('nc_session');
29
+ if (!s) return null;
30
+ const parsed = JSON.parse(s);
31
+ if (Date.now() - parsed.ts > 7 * 86400000) { localStorage.removeItem('nc_session'); return null; }
32
+ return parsed;
33
+ } catch { return null; }
34
+ }
35
+
36
  // ─── STATE ────────────────────────────────────────────────────
37
  let currentUser = null;
38
  let currentChannel = 'allgemein';
 
82
  }
83
  const MY_FP = getFingerprint();
84
 
85
+ let heartbeatTimer = null;
86
+ function scheduleHeartbeatSave() {
87
+ // Nur alle 6 Sekunden wirklich speichern (Polling ist 3s, also jeden 2. Tick)
88
+ if (heartbeatTimer) return;
89
+ heartbeatTimer = setTimeout(() => {
90
+ heartbeatTimer = null;
91
+ apiSave();
92
+ }, 6000);
93
+ }
94
+
95
  // ─── JSONBIN API ──────────────────────────────────────────────
96
  const isConfigured = () =>
97
  !JSONBIN_KEY.includes('DEIN') && !JSONBIN_BIN.includes('DEINE');
98
 
99
+ async function apiLoad(isMerge = false) {
100
  if (!isConfigured()) {
101
  const v = localStorage.getItem('nc_db');
102
  DB = v ? JSON.parse(v) : DB_DEFAULT();
 
108
  headers: { 'X-Master-Key': JSONBIN_KEY, 'X-Bin-Meta': 'false' }
109
  });
110
  if (!r.ok) throw new Error(r.status);
111
+ const remote = await r.json();
112
+
113
+ if (isMerge && DB) {
114
+ // Smart merge: Remote-Daten haben Vorrang, aber lokale Γ„nderungen die
115
+ // noch nicht gepusht wurden (pending saves) bleiben erhalten.
116
+ // Nachrichten: Union beider Sets (remote + lokal), remote gewinnt bei Konflikt
117
+ const mergedMsgs = {};
118
+ const allChannels = new Set([...Object.keys(DB.msgs || {}), ...Object.keys(remote.msgs || {})]);
119
+ for (const ch of allChannels) {
120
+ const localCh = DB.msgs?.[ch] || {};
121
+ const remoteCh = remote.msgs?.[ch] || {};
122
+ mergedMsgs[ch] = { ...localCh, ...remoteCh }; // Remote ΓΌberschreibt lokal bei gleichem Key
123
+ }
124
+ const mergedDms = {};
125
+ const allDms = new Set([...Object.keys(DB.dms || {}), ...Object.keys(remote.dms || {})]);
126
+ for (const dm of allDms) {
127
+ const localDm = DB.dms?.[dm] || {};
128
+ const remoteDm = remote.dms?.[dm] || {};
129
+ mergedDms[dm] = { ...localDm, ...remoteDm };
130
+ }
131
+ DB = { ...remote, msgs: mergedMsgs, dms: mergedDms };
132
+ } else {
133
+ DB = remote;
134
+ }
135
+
136
  ensureFields();
137
+ localStorage.setItem('nc_db', JSON.stringify(DB));
138
  } catch(e) {
139
  console.warn('Load fehlgeschlagen:', e);
 
140
  const v = localStorage.getItem('nc_db');
141
  if (v) DB = JSON.parse(v);
142
  else DB = DB_DEFAULT();
 
288
  if (decode(entry.password)!==p)return err('Falscher Username oder Passwort');
289
  if (DB.moderation.bans?.[u.toLowerCase()]) return err('🚫 Du bist von NoahsChat gebannt.');
290
  currentUser={...entry};
291
+ saveSession(currentUser);
292
  refreshCaptcha('login');
293
  dbSet(`users/${u.toLowerCase()}/_fp`, MY_FP);
294
  dbSet(`online/${entry.username}`, Date.now());
 
300
  // ─── LOGOUT ───────────────────────────────────────────────────
301
  async function doLogout() {
302
  if (!currentUser) return;
303
+ saveSession(null);
304
  dbDelete(`online/${currentUser.username}`);
305
  if (voiceActive) leaveVoice();
306
  clearInterval(pollTimer);
 
335
  async function pollTick() {
336
  if (!currentUser) return;
337
 
338
+ // DB neu laden β†’ bekommt Γ„nderungen anderer User (mit Merge, damit lokale Msgs nicht verloren gehen)
339
  setSyncStatus('sync');
340
+ await apiLoad(true);
341
  setSyncStatus('ok');
342
 
343
  // Eigene Daten aktualisieren (Rolle kΓΆnnte geΓ€ndert worden sein)
 
349
  translationEnabled = me.settings?.translate||false;
350
  }
351
 
352
+ // Heartbeat β€” NUR den eigenen Online-Eintrag setzen, NICHT die komplette DB speichern
353
+ // Direkt im Objekt setzen und gezielt speichern um Race-Conditions zu vermeiden
354
  DB.online[currentUser.username] = Date.now();
355
+ // Heartbeat-only save: kleines Debounce damit nicht zu viele Requests entstehen
356
+ scheduleHeartbeatSave();
357
 
358
  // Moderation-Checks
359
  const mod = DB.moderation;
 
1399
  // Loading entfernen
1400
  loadEl.remove();
1401
 
1402
+ // Session wiederherstellen (Auto-Login nach Reload)
1403
+ const session = loadSession();
1404
+ if (session) {
1405
+ const entry = DB.users[session.username.toLowerCase()];
1406
+ if (entry && !DB.moderation?.bans?.[session.username.toLowerCase()] && !DB.moderation?.banned_fps?.[MY_FP]) {
1407
+ currentUser = { ...entry };
1408
+ userLanguage = entry.settings?.lang || 'de';
1409
+ translationEnabled = entry.settings?.translate || false;
1410
+ saveSession(currentUser); // Timestamp erneuern
1411
+ launchApp();
1412
+ return; // Auth-Screen nicht zeigen
1413
+ } else {
1414
+ saveSession(null); // UngΓΌltige Session lΓΆschen
1415
+ }
1416
+ }
1417
+
1418
+ // Captchas immer initialisieren (auch falls Session nicht klappt)
1419
  refreshCaptcha('login');
1420
  refreshCaptcha('reg');
1421
  });