From 3d779b56e4555312d93de5bf1e1dd48607e8e1fa Mon Sep 17 00:00:00 2001 From: Riyanshi Gupta Date: Sun, 14 Jun 2026 19:19:49 +0530 Subject: [PATCH 1/6] feat: replace letter badges with emoji icons (Closes #520) --- src/components/dashboard/RankPreview.jsx | 4 ++-- src/constants/index.js | 10 +++++----- src/pages/Achievements.jsx | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/dashboard/RankPreview.jsx b/src/components/dashboard/RankPreview.jsx index b228f9b..3ee2136 100644 --- a/src/components/dashboard/RankPreview.jsx +++ b/src/components/dashboard/RankPreview.jsx @@ -81,11 +81,11 @@ export const RankPreview = () => {
{/* Badge Icon */}
- {badge.name[0]} + {badge.icon}
{/* Badge Info */} diff --git a/src/constants/index.js b/src/constants/index.js index 4719c1e..fae4e20 100644 --- a/src/constants/index.js +++ b/src/constants/index.js @@ -44,9 +44,9 @@ export const uiGradients = { }; export const systemBadges = [ - { id: "b1", name: "Pioneer", description: "First 100 users", color: "from-amber-500 to-orange-500" }, - { id: "b2", name: "Code Warrior", description: "100+ contributions", color: "from-blue-500 to-indigo-500" }, - { id: "b3", name: "Streak Master", description: "10+ day streak", color: "from-red-500 to-pink-500" }, - { id: "b4", name: "CSS Sorceress", description: "UI layout designer", color: "from-purple-500 to-violet-500" }, - { id: "b5", name: "Ranker Ambassador", description: "10+ successful referrals", color: "from-emerald-500 to-teal-500" } + { id: "b1", name: "Pioneer", icon: "🦅", description: "First 100 users", color: "from-amber-500 to-orange-500" }, + { id: "b2", name: "Code Warrior", icon: "⚔️", description: "100+ contributions", color: "from-blue-500 to-indigo-500" }, + { id: "b3", name: "Streak Master", icon: "🔥", description: "10+ day streak", color: "from-red-500 to-pink-500" }, + { id: "b4", name: "CSS Sorceress", icon: "🎨", description: "UI layout designer", color: "from-purple-500 to-violet-500" }, + { id: "b5", name: "Ranker Ambassador", icon: "🦈", description: "10+ successful referrals", color: "from-emerald-500 to-teal-500" } ]; diff --git a/src/pages/Achievements.jsx b/src/pages/Achievements.jsx index 0033101..b9a92d7 100644 --- a/src/pages/Achievements.jsx +++ b/src/pages/Achievements.jsx @@ -173,7 +173,7 @@ export const Achievements = () => { !info.unlocked ? "brightness-75 saturate-50" : "" }`} > - {badge.name[0]} + {badge.icon} {/* Locked / Unlocked Indicator */}
@@ -311,7 +311,7 @@ export const Achievements = () => {
{/* Glowing Big Badge representation */}
- {selectedBadge.name[0]} + {selectedBadge.icon} {/* Orbit Ring */}
From 0e56d65e97c19a11bd4765c680af061cd871157a Mon Sep 17 00:00:00 2001 From: Riyanshi Gupta Date: Sun, 14 Jun 2026 19:31:36 +0530 Subject: [PATCH 2/6] fix: prevent theme flash on refresh by applying theme before first paint (Closes #208) --- index.html | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/index.html b/index.html index 84baee7..a36ddf5 100644 --- a/index.html +++ b/index.html @@ -14,6 +14,22 @@ RankerHub — Gamified Developer Leaderboard + + + From 1c9f4d4ff930177d8b1faa2f18002772ae4f0fc5 Mon Sep 17 00:00:00 2001 From: Riyanshi Gupta Date: Sun, 14 Jun 2026 23:50:11 +0530 Subject: [PATCH 3/6] fix: prevent silent batch failure on unfollow action (Closes #519) --- src/services/friendsService.js | 39 ++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/services/friendsService.js b/src/services/friendsService.js index 1630c1e..44e21db 100644 --- a/src/services/friendsService.js +++ b/src/services/friendsService.js @@ -9,7 +9,9 @@ import { orderBy, documentId, writeBatch, - increment + increment, + deleteDoc, + setDoc } from "firebase/firestore"; import { db } from "../lib/firebase"; @@ -113,36 +115,37 @@ export const toggleFollowStatus = async (currentUserId, developerId, isFollowing const followDocId = `${currentUserId}_${developerId}`; const followRef = doc(db, "follows", followDocId); - // 1. Initialize atomic write batch - const batch = writeBatch(db); - - // 2. Select a randomized shard document to spread lock contention uniformly - const randomShardId = Math.floor(Math.random() * SHARD_COUNT).toString(); - const globalConnectionsShardRef = doc(db, "aggregates", "global_connections", "shards", randomShardId); - + // 1. Perform the core follow/unfollow write on its own. + // This must succeed independently of the analytics shard counter below, + // otherwise a permission error on the shard write silently blocks the + // entire unfollow action (the original batch.commit() would throw and + // roll back, including the delete, with no visible error to the user). try { if (isFollowing) { // Unfollow - batch.delete(followRef); - // Decrement the distributed shard counter atomically - batch.set(globalConnectionsShardRef, { count: increment(-1) }, { merge: true }); + await deleteDoc(followRef); } else { // Follow - batch.set(followRef, { + await setDoc(followRef, { followerId: currentUserId, followedId: developerId, createdAt: new Date().toISOString() }); - // Increment the distributed shard counter atomically - batch.set(globalConnectionsShardRef, { count: increment(1) }, { merge: true }); } - - // Commit all updates atomically in a single network transaction write - await batch.commit(); } catch (error) { - console.error("Error toggling follow status with batch:", error); + console.error("Error toggling follow status:", error); throw error; } + + // 2. Best-effort update of the distributed shard counter for global stats. + // Failures here should not affect the user's follow/unfollow state. + try { + const randomShardId = Math.floor(Math.random() * SHARD_COUNT).toString(); + const globalConnectionsShardRef = doc(db, "aggregates", "global_connections", "shards", randomShardId); + await setDoc(globalConnectionsShardRef, { count: increment(isFollowing ? -1 : 1) }, { merge: true }); + } catch (error) { + console.error("Error updating connection shard counter:", error); + } }; /** From 4cb9b8e2aebf5eb63b1ada3d658520f28eae4c2f Mon Sep 17 00:00:00 2001 From: Riyanshi Gupta Date: Mon, 15 Jun 2026 00:00:38 +0530 Subject: [PATCH 4/6] feat: add visible scrollbar to Recent Activity feed (Closes #518) --- src/components/dashboard/ActivityFeed.jsx | 3 +-- tailwind.config.js | 8 +++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/dashboard/ActivityFeed.jsx b/src/components/dashboard/ActivityFeed.jsx index c71913b..c9acfea 100644 --- a/src/components/dashboard/ActivityFeed.jsx +++ b/src/components/dashboard/ActivityFeed.jsx @@ -29,8 +29,7 @@ export const ActivityFeed = () => {
{/* Timeline items */} -
- Date: Mon, 15 Jun 2026 20:54:33 +0530 Subject: [PATCH 5/6] fix: install tailwind-scrollbar v3 and fix ESM config for build/lint errors --- package-lock.json | 33 +++++++++++++++++++++++++++++++++ package.json | 1 + tailwind.config.js | 2 ++ 3 files changed, 36 insertions(+) diff --git a/package-lock.json b/package-lock.json index dfc01b7..62c3380 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,7 @@ "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.6.0", "postcss": "^8.5.15", + "tailwind-scrollbar": "^3.1.0", "tailwindcss": "^3.4.19", "typescript": "^6.0.3", "vite": "^8.0.16", @@ -87,6 +88,7 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -302,6 +304,7 @@ "integrity": "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" @@ -313,6 +316,7 @@ "integrity": "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -518,6 +522,7 @@ "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.14.13.tgz", "integrity": "sha512-H89Jeyp31+EZk9GPu6vaeL9mEmoXgM3nASB7UPBYYS/lqAks21mO1BU1dF8NbsVTL6tgGZkGUtiGJgxtDiwHkw==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@firebase/component": "0.7.3", "@firebase/logger": "0.5.1", @@ -584,6 +589,7 @@ "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.5.13.tgz", "integrity": "sha512-pn3FvXwUR34kWPccDQfCKsNZcM2wD1OS+J1jeEgzM1ZNXoxR2NaF6e5DjDuRrnTwR6LN2XQQt0IqE6yKmgpCQg==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@firebase/app": "0.14.13", "@firebase/component": "0.7.3", @@ -600,6 +606,7 @@ "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.5.tgz", "integrity": "sha512-YevqTjvo7Iujsa9Dwowmd6dSoElhzmD63ZSrq6bzjvQ6POjYgNjOFHLmNIgJs48eNO093NCERibuFnxbfOvU7A==", "license": "Apache-2.0", + "peer": true, "dependencies": { "@firebase/logger": "0.5.1" } @@ -1053,6 +1060,7 @@ "integrity": "sha512-LUdM4Wg7YM9Pq/49nGYySJA0CSQEKnGffFzWV8+6gXN7mGxn+FL1IqvFbuZUtAQcfZgHYDwCE1wwlK7rB7gl2g==", "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "tslib": "^2.1.0" }, @@ -2210,6 +2218,7 @@ "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -2385,6 +2394,7 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2639,6 +2649,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -3089,6 +3100,7 @@ "integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", @@ -3856,6 +3868,7 @@ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, "license": "MIT", + "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -4645,6 +4658,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.12", "picocolors": "^1.1.1", @@ -4866,6 +4880,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -4875,6 +4890,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -5325,12 +5341,26 @@ "node": ">= 4.7.0" } }, + "node_modules/tailwind-scrollbar": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tailwind-scrollbar/-/tailwind-scrollbar-3.1.0.tgz", + "integrity": "sha512-pmrtDIZeHyu2idTejfV59SbaJyvp1VRjYxAjZBH0jnyrPRo6HL1kD5Glz8VPagasqr6oAx6M05+Tuw429Z8jxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "tailwindcss": "3.x" + } + }, "node_modules/tailwindcss": { "version": "3.4.19", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -5450,6 +5480,7 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -5590,6 +5621,7 @@ "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", @@ -5935,6 +5967,7 @@ "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/package.json b/package.json index 24d32ae..13db45c 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "eslint-plugin-react-refresh": "^0.5.2", "globals": "^17.6.0", "postcss": "^8.5.15", + "tailwind-scrollbar": "^3.1.0", "tailwindcss": "^3.4.19", "typescript": "^6.0.3", "vite": "^8.0.16", diff --git a/tailwind.config.js b/tailwind.config.js index e4a3fd6..dd88295 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,3 +1,5 @@ +import tailwindScrollbar from 'tailwind-scrollbar'; + /** @type {import('tailwindcss').Config} */ export default { content: [ From 11c500b1a8035d4e5d8cb7c63b52b86349639dd1 Mon Sep 17 00:00:00 2001 From: Riyanshi Gupta Date: Mon, 15 Jun 2026 21:04:17 +0530 Subject: [PATCH 6/6] fix: replace require with tailwindScrollbar import in plugins array --- tailwind.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tailwind.config.js b/tailwind.config.js index dd88295..7951005 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -35,6 +35,6 @@ export default { } }, plugins: [ - require('tailwind-scrollbar'), + tailwindScrollbar, ], } \ No newline at end of file