4sp-dv-latest 1.0.4 → 1.0.5

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.
@@ -6,13 +6,71 @@
6
6
  <title>4SP - VERSION 5 CLIENT</title>
7
7
  <link rel="icon" type="image/png" href="https://cdn.jsdelivr.net/npm/4sp-dv@latest/images/logo.png">
8
8
 
9
- <base href="https://cdn.jsdelivr.net/npm/4sp-dv@1.0.45/logged-in/">
9
+ <base href="https://cdn.jsdelivr.net/npm/4sp-dv@1.0.48/logged-in/">
10
10
  <script src="https://cdn.tailwindcss.com"></script>
11
11
  <link href="https://fonts.googleapis.com/css2?family=Geist:wght@100..900&display=swap" rel="stylesheet">
12
12
 
13
13
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css">
14
14
  <style>
15
- body {
15
+ /* --- PRELOADER --- */
16
+ #app-preloader {
17
+ position: fixed;
18
+ inset: 0;
19
+ background-color: #000000;
20
+ z-index: 99999;
21
+ display: flex;
22
+ flex-direction: column;
23
+ align-items: flex-end;
24
+ justify-content: flex-end;
25
+ padding: 3rem;
26
+ transition: opacity 0.5s ease-out, visibility 0.5s;
27
+ }
28
+ #app-preloader.fade-out {
29
+ opacity: 0;
30
+ visibility: hidden;
31
+ }
32
+
33
+ .preloader-logo {
34
+ position: absolute;
35
+ top: 1.5rem;
36
+ right: 1.5rem;
37
+ height: 3rem;
38
+ width: auto;
39
+ opacity: 0.5; /* Initial opacity per universal style, not darkened */
40
+ }
41
+
42
+ .preloader-content {
43
+ display: flex;
44
+ flex-direction: column;
45
+ align-items: flex-end;
46
+ gap: 0.5rem;
47
+ }
48
+
49
+ .preloader-title {
50
+ color: #fff;
51
+ font-size: 1.875rem; /* 3xl */
52
+ font-weight: 300;
53
+ font-style: italic;
54
+ font-family: 'Geist', sans-serif;
55
+ white-space: nowrap;
56
+ }
57
+
58
+ .preloader-bar-container {
59
+ width: 16rem; /* 64 */
60
+ height: 0.25rem; /* 1 */
61
+ background-color: #333;
62
+ border-radius: 9999px;
63
+ overflow: hidden;
64
+ }
65
+
66
+ .preloader-bar {
67
+ height: 100%;
68
+ background-color: #fff;
69
+ width: 0%;
70
+ transition: width 0.5s ease-out;
71
+ }
72
+
73
+ body {
16
74
  background-color: #040404;
17
75
  color: #c0c0c0;
18
76
  font-family: 'Geist', sans-serif;
@@ -236,13 +294,21 @@
236
294
  transform: translateY(0) scale(0.98);
237
295
  }
238
296
 
239
- .auth-menu-item .fa-gear, .nav-tab .fa-gear {
240
- display: inline-block;
241
- transform-origin: center;
242
- /* Quick transition for each "click" step */
243
- transition: transform 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
297
+ .auth-menu-item i, .nav-tab i, .settings-tab i {
298
+ display: inline-flex;
299
+ align-items: center;
300
+ justify-content: center;
301
+ width: 1.1em;
302
+ height: 1.1em;
303
+ line-height: 1;
304
+ position: relative;
305
+ top: 1px;
306
+ flex-shrink: 0;
307
+ margin: 0;
244
308
  }
245
309
 
310
+
311
+
246
312
  .auth-header {
247
313
  padding: 0.5rem 1rem;
248
314
  border-bottom: 1px solid var(--menu-divider, #333);
@@ -549,6 +615,17 @@
549
615
  </style>
550
616
  </head>
551
617
  <body>
618
+ <!-- PRELOADER -->
619
+ <div id="app-preloader">
620
+ <img src="https://cdn.jsdelivr.net/npm/4sp-asset-library@latest/logo.png" class="preloader-logo" alt="Logo" onerror="this.style.display='none'">
621
+ <div class="preloader-content">
622
+ <h2 class="preloader-title">4simpleproblems Version 5 Client</h2>
623
+ <div class="preloader-bar-container">
624
+ <div id="preloader-bar" class="preloader-bar"></div>
625
+ </div>
626
+ </div>
627
+ </div>
628
+
552
629
  <div id="lock-screen" class="w-full h-full flex flex-col items-center justify-center p-4">
553
630
  <div class="max-w-5xl w-full bg-[#040404] flex flex-col md:flex-row border border-[#252525] rounded-3xl overflow-hidden shadow-2xl">
554
631
  <div class="flex-1 p-8 flex flex-col justify-center items-center border-b md:border-b-0 md:border-r border-[#252525]">
@@ -872,7 +949,7 @@
872
949
  const displayUsername = document.getElementById('display-username');
873
950
  const displayEmail = document.getElementById('display-email');
874
951
 
875
- const BASE_URL = 'https://cdn.jsdelivr.net/npm/4sp-dv@1.0.45/logged-in/';
952
+ const BASE_URL = 'https://cdn.jsdelivr.net/npm/4sp-dv@1.0.48/logged-in/';
876
953
 
877
954
  // Preload Logos
878
955
  const preloadImgs = [
@@ -1375,9 +1452,19 @@
1375
1452
  unlockBtn.classList.add('hidden');
1376
1453
  codeInput.classList.add('hidden');
1377
1454
 
1455
+ // Timeout Race to prevent infinite "Verifying..."
1456
+ const timeoutPromise = new Promise((_, reject) =>
1457
+ setTimeout(() => reject(new Error("Verification timed out.")), 10000)
1458
+ );
1459
+
1378
1460
  try {
1379
1461
  const docRef = doc(db, "access_codes", savedCode);
1380
- const docSnap = await getDoc(docRef);
1462
+
1463
+ // Race the Firestore connection
1464
+ const docSnap = await Promise.race([
1465
+ getDoc(docRef),
1466
+ timeoutPromise
1467
+ ]);
1381
1468
 
1382
1469
  if (docSnap.exists() && docSnap.data().active) {
1383
1470
  const codeData = docSnap.data();
@@ -1393,9 +1480,12 @@
1393
1480
  currentUser = userData;
1394
1481
  }
1395
1482
 
1396
- // Always fetch fresh to re-verify admin status
1483
+ // Always fetch fresh to re-verify admin status (also raced)
1397
1484
  if (codeData.ownerUid) {
1398
- await fetchUserData(codeData.ownerUid);
1485
+ await Promise.race([
1486
+ fetchUserData(codeData.ownerUid),
1487
+ timeoutPromise
1488
+ ]);
1399
1489
  }
1400
1490
 
1401
1491
  launchApp();
@@ -1406,7 +1496,7 @@
1406
1496
  }
1407
1497
  } catch(e) {
1408
1498
  console.error("Auto-login error:", e);
1409
- resetLockScreen("Connection failed. Retrying...");
1499
+ resetLockScreen("Connection issue. Please login again.");
1410
1500
  }
1411
1501
  }
1412
1502
  }
@@ -2709,60 +2799,7 @@
2709
2799
  osc.stop(now + 0.02);
2710
2800
  };
2711
2801
 
2712
- // Gear Clicking Sequence
2713
- let gearHoverStartTime = 0;
2714
- let currentGearRotation = 0;
2715
- let currentAnimatingGear = null;
2716
- const gearClickDelays = [0, 120, 240, 360]; // Adjusted for 0.5s total
2717
-
2718
- const handleGearMouseOver = (e) => {
2719
- const target = e.target.closest('#auth-settings-btn, #nav-settings-tab');
2720
- if (target && currentAnimatingGear !== target) {
2721
- currentAnimatingGear = target;
2722
- const icon = target.querySelector('.fa-gear');
2723
- if (!icon) return;
2724
-
2725
- gearHoverStartTime = Date.now();
2726
- gearClickDelays.forEach((delay, index) => {
2727
- setTimeout(() => {
2728
- if (target.matches(':hover')) {
2729
- if (window.areAnimationsEnabled) {
2730
- currentGearRotation = (index + 1) * 45;
2731
- icon.style.transform = `rotate(${currentGearRotation}deg)`;
2732
- }
2733
- window.playMechClick(false);
2734
- }
2735
- }, delay);
2736
- });
2737
- }
2738
- };
2739
2802
 
2740
- const handleGearMouseOut = (e) => {
2741
- const target = e.target.closest('#auth-settings-btn, #nav-settings-tab');
2742
- if (target && !target.contains(e.relatedTarget)) {
2743
- currentAnimatingGear = null;
2744
- const icon = target.querySelector('.fa-gear');
2745
- if (!icon) return;
2746
-
2747
- const elapsed = Date.now() - gearHoverStartTime;
2748
- const clickCount = gearClickDelays.filter(d => d <= elapsed).length;
2749
-
2750
- for (let i = 0; i < clickCount; i++) {
2751
- setTimeout(() => {
2752
- if (!target.matches(':hover')) {
2753
- if (window.areAnimationsEnabled) {
2754
- currentGearRotation -= 45;
2755
- icon.style.transform = `rotate(${currentGearRotation}deg)`;
2756
- }
2757
- window.playMechClick(true);
2758
- }
2759
- }, gearClickDelays[i]);
2760
- }
2761
- }
2762
- };
2763
-
2764
- document.addEventListener('mouseover', handleGearMouseOver);
2765
- document.addEventListener('mouseout', handleGearMouseOut);
2766
2803
 
2767
2804
  const notificationContainer = document.getElementById('notification-container');
2768
2805
  function showNotification(message, iconClass = 'fa-solid fa-info-circle', type = 'info') {
@@ -2802,6 +2839,27 @@
2802
2839
  document.addEventListener('paste', (e) => {
2803
2840
  if (e.target.closest('input, textarea')) showNotification('Pasted from clipboard', 'fa-solid fa-paste', 'info');
2804
2841
  });
2842
+
2843
+ // --- PRELOADER REMOVAL ---
2844
+ window.addEventListener('load', () => {
2845
+ const preloader = document.getElementById('app-preloader');
2846
+ const bar = document.getElementById('preloader-bar');
2847
+
2848
+ if (preloader && bar) {
2849
+ // Animate bar to 100%
2850
+ setTimeout(() => {
2851
+ bar.style.width = '100%';
2852
+
2853
+ // Then fade out
2854
+ setTimeout(() => {
2855
+ preloader.classList.add('fade-out');
2856
+ setTimeout(() => {
2857
+ preloader.remove();
2858
+ }, 500);
2859
+ }, 600); // Wait for bar animation
2860
+ }, 100);
2861
+ }
2862
+ });
2805
2863
  </script>
2806
2864
  </body>
2807
2865
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "4sp-dv-latest",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "The latest HTML file of the 4SP Version 5 Client.",
5
5
  "license": "ISC",
6
6
  "author": "",