2025-04-02 21:11:25 -04:00
# Follower Statistics
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
**Last Updated:** 2025-04-02
**Status:** Implemented
**Related To:** [Profile Tab ](./tabs/overview_tab.md ), [NostrBand Integration ](../../technical/nostr/nostr_band_integration.md )
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
## Introduction
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
The follower statistics feature provides real-time information about a user's Nostr followers and following counts. This document outlines the technical implementation of follower statistics, which is primarily displayed in the Profile tab.
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
## Implementation Overview
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
Follower statistics are implemented through integration with the NostrBand API, which provides network-wide statistics for Nostr users, including follower counts. This integration is handled through the NostrBandService and exposed via the useProfileStats hook.
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
### Key Components
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
- **NostrBandService**: Core service that interfaces with the NostrBand API
- **useProfileStats Hook**: React hook that provides access to follower statistics
- **ProfileFollowerStats Component**: UI component for displaying follower statistics
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
## NostrBand Service Implementation
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
The NostrBandService (implemented in `lib/services/NostrBandService.ts` ) provides methods for fetching follower statistics:
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
```typescript
// NostrBand service for fetching follower statistics
export class NostrBandService {
private baseUrl = 'https://api.nostr.band/v0';
// Fetch follower statistics for a given public key
public async getProfileStats(pubkey: string): Promise< ProfileStats > {
if (!pubkey) {
return { followersCount: 0, followingCount: 0 };
}
try {
const response = await fetch(`${this.baseUrl}/profile/${pubkey}/stats` );
if (!response.ok) {
throw new Error(`NostrBand API error: ${response.status}` );
}
const data = await response.json();
return {
followersCount: data.followers_count || 0,
followingCount: data.following_count || 0
};
} catch (error) {
console.error('Error fetching profile stats:', error);
// Return default values on error
return { followersCount: 0, followingCount: 0 };
}
}
}
// Singleton instance
export const nostrBandService = new NostrBandService();
2025-03-28 10:18:44 -07:00
```
2025-04-02 21:11:25 -04:00
### ProfileStats Interface
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
The ProfileStats interface defines the structure of follower statistics:
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
```typescript
export interface ProfileStats {
followersCount: number;
followingCount: number;
}
2025-03-28 10:18:44 -07:00
```
2025-04-02 21:11:25 -04:00
## useProfileStats Hook
The useProfileStats hook (implemented in `lib/hooks/useProfileStats.ts` ) provides a React interface for accessing follower statistics:
```typescript
// Hook for accessing profile statistics
export function useProfileStats({
pubkey,
refreshInterval = 0
}: {
pubkey: string;
refreshInterval?: number;
}): {
followersCount: number;
followingCount: number;
isLoading: boolean;
refresh: () => Promise< void > ;
} {
const [stats, setStats] = useState< ProfileStats > ({ followersCount: 0, followingCount: 0 });
const [isLoading, setIsLoading] = useState(true);
// Fetch profile stats
const fetchStats = useCallback(async () => {
if (!pubkey) {
setStats({ followersCount: 0, followingCount: 0 });
setIsLoading(false);
return;
}
try {
setIsLoading(true);
const profileStats = await nostrBandService.getProfileStats(pubkey);
setStats(profileStats);
} catch (error) {
console.error('Error in useProfileStats:', error);
} finally {
setIsLoading(false);
}
}, [pubkey]);
// Initial fetch and refresh interval
useEffect(() => {
fetchStats();
if (refreshInterval > 0) {
const interval = setInterval(fetchStats, refreshInterval);
return () => clearInterval(interval);
}
}, [fetchStats, refreshInterval]);
// Return stats and loading state
return {
...stats,
isLoading,
refresh: fetchStats
};
}
```
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
## Profile Integration
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
The follower statistics are displayed in the Profile tab using the ProfileFollowerStats component:
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
```jsx
2025-03-28 10:18:44 -07:00
// Profile follower stats component
2025-04-02 21:11:25 -04:00
const ProfileFollowerStats = React.memo(() => {
2025-03-28 10:18:44 -07:00
return (
< View className = "flex-row mb-2" >
< TouchableOpacity className = "mr-4" >
< Text >
2025-04-02 21:11:25 -04:00
< Text className = "font-bold" > {statsLoading ? '...' : followingCount.toLocaleString()}< / Text >
2025-03-28 10:18:44 -07:00
< Text className = "text-muted-foreground" > following< / Text >
< / Text >
< / TouchableOpacity >
< TouchableOpacity >
< Text >
2025-04-02 21:11:25 -04:00
< Text className = "font-bold" > {statsLoading ? '...' : followersCount.toLocaleString()}< / Text >
2025-03-28 10:18:44 -07:00
< Text className = "text-muted-foreground" > followers< / Text >
< / Text >
< / TouchableOpacity >
< / View >
);
});
```
2025-04-02 21:11:25 -04:00
This component is called in the Profile tab header:
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
```jsx
// In the Profile header
const { followersCount, followingCount, isLoading: statsLoading } = useProfileStats({
pubkey: pubkey || '',
refreshInterval: 60000 * 15 // refresh every 15 minutes
});
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
// Later in the JSX
< ProfileFollowerStats / >
2025-03-28 10:18:44 -07:00
```
2025-04-02 21:11:25 -04:00
## Hook Ordering Consistency
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
To maintain React hook ordering consistency regardless of authentication state, the useProfileStats hook is always called, even when the user is not authenticated:
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
```jsx
// Always call useProfileStats hook, even if isAuthenticated is false
// This ensures consistent hook ordering regardless of authentication state
const { followersCount, followingCount, isLoading: statsLoading } = useProfileStats({
pubkey: pubkey || '',
refreshInterval: 60000 * 15
});
2025-03-28 10:18:44 -07:00
```
2025-04-02 21:11:25 -04:00
When not authenticated or when the pubkey is empty, the hook returns default values (zero counts).
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
## Caching Strategy
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
Follower statistics are cached to reduce API calls and improve performance:
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
```typescript
// In NostrBandService
private cache = new Map< string , { stats: ProfileStats ; timestamp: number } > ();
private cacheTTL = 15 * 60 * 1000; // 15 minutes
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
public async getProfileStats(pubkey: string): Promise< ProfileStats > {
// Check cache first
const cached = this.cache.get(pubkey);
if (cached & & Date.now() - cached.timestamp < this.cacheTTL ) {
return cached.stats;
}
// Fetch from API if not cached or expired
try {
const stats = await this.fetchProfileStats(pubkey);
// Update cache
this.cache.set(pubkey, {
stats,
timestamp: Date.now()
});
return stats;
} catch (error) {
// Error handling...
}
}
```
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
## Offline Support
The follower statistics feature gracefully handles offline states:
```typescript
public async getProfileStats(pubkey: string): Promise< ProfileStats > {
// Check connectivity
if (!this.connectivityService.isOnline()) {
// Return cached data if available
const cached = this.cache.get(pubkey);
if (cached) {
return cached.stats;
}
// Return default values if no cached data
return { followersCount: 0, followingCount: 0 };
}
// Proceed with normal API call if online
// ...
}
```
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
## Performance Considerations
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
To ensure good performance, several optimizations are implemented:
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
1. **Memoization** : The ProfileFollowerStats component is memoized to prevent unnecessary re-renders
2. **Refresh Interval** : Stats are only refreshed at specific intervals (default: 15 minutes)
3. **Caching** : Follower statistics are cached to reduce API calls
4. **Lazy Loading** : Stats are loaded after the profile is rendered to prioritize UI responsiveness
2025-03-28 10:18:44 -07:00
## Future Enhancements
2025-04-02 21:11:25 -04:00
Future enhancements to follower statistics include:
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
1. **Follower List** : Display a list of followers/following with profile information
2. **Interaction Analytics** : Show interaction statistics with followers
3. **Growth Tracking** : Track follower growth over time
4. **Network Visualization** : Visualize the user's Nostr network
5. **Notification Integration** : Notify users of new followers
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
## Related Documentation
2025-03-28 10:18:44 -07:00
2025-04-02 21:11:25 -04:00
- [Profile Tab ](./tabs/overview_tab.md ) - UI implementation of profile display
- [NostrBand Integration ](../../technical/nostr/nostr_band_integration.md ) - Technical details of the NostrBand API integration
- [Authentication Patterns ](./authentication_patterns.md ) - How authentication affects hook ordering