diff --git a/index.html b/index.html
index 84baee7..a36ddf5 100644
--- a/index.html
+++ b/index.html
@@ -14,6 +14,22 @@
{/* 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 */}
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);
+ }
};
/**