162 lines
4.5 KiB
Dart
162 lines
4.5 KiB
Dart
import 'dart:async';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
|
|
/// Service to track and manage user online presence
|
|
class UserPresenceService {
|
|
static const String _presenceTableName = 'user_presence';
|
|
static const int _presenceTimeout = 60; // seconds
|
|
|
|
final _supabase = Supabase.instance.client;
|
|
Timer? _heartbeatTimer;
|
|
String? _userId;
|
|
bool _isInitialized = false;
|
|
|
|
final StreamController<int> _onlineUsersController =
|
|
StreamController<int>.broadcast();
|
|
|
|
/// Stream of currently online users count
|
|
Stream<int> get onlineUsersStream => _onlineUsersController.stream;
|
|
|
|
/// Current count of online users
|
|
int _onlineUsersCount = 0;
|
|
int get onlineUsersCount => _onlineUsersCount;
|
|
|
|
/// Initialize the presence service
|
|
Future<void> initialize() async {
|
|
if (_isInitialized) return;
|
|
|
|
try {
|
|
final currentUser = _supabase.auth.currentUser;
|
|
if (currentUser == null) {
|
|
debugPrint('UserPresence: No authenticated user');
|
|
return;
|
|
}
|
|
|
|
_userId = currentUser.id;
|
|
|
|
// Start heartbeat timer
|
|
_startHeartbeat();
|
|
|
|
// Initial presence update
|
|
await updatePresence();
|
|
|
|
// Get initial online users count
|
|
await _fetchOnlineUsersCount();
|
|
|
|
_isInitialized = true;
|
|
debugPrint('UserPresence: Service initialized for user $_userId');
|
|
} catch (e) {
|
|
debugPrint('UserPresence: Error initializing - $e');
|
|
}
|
|
}
|
|
|
|
/// Start heartbeat timer to keep presence alive
|
|
void _startHeartbeat() {
|
|
_heartbeatTimer?.cancel();
|
|
|
|
// Update presence every 30 seconds
|
|
_heartbeatTimer = Timer.periodic(const Duration(seconds: 30), (timer) {
|
|
updatePresence();
|
|
_fetchOnlineUsersCount();
|
|
});
|
|
}
|
|
|
|
/// Update user presence
|
|
Future<void> updatePresence() async {
|
|
if (_userId == null) return;
|
|
|
|
try {
|
|
final currentUser = _supabase.auth.currentUser;
|
|
if (currentUser == null) return;
|
|
|
|
final now = DateTime.now().toIso8601String();
|
|
|
|
// Update the database record
|
|
await _supabase.from(_presenceTableName).upsert({
|
|
'id': _userId!,
|
|
'user_id': _userId!,
|
|
'email': currentUser.email ?? '',
|
|
'last_seen_at': now,
|
|
'is_online': true,
|
|
});
|
|
|
|
debugPrint('UserPresence: Updated presence for user $_userId');
|
|
} catch (e) {
|
|
debugPrint('UserPresence: Error updating presence - $e');
|
|
}
|
|
}
|
|
|
|
/// Fetch the count of online users
|
|
Future<void> _fetchOnlineUsersCount() async {
|
|
try {
|
|
final now = DateTime.now();
|
|
final cutoffTime = now.subtract(Duration(seconds: _presenceTimeout));
|
|
|
|
// First, get all online users
|
|
final response = await _supabase
|
|
.from(_presenceTableName)
|
|
.select()
|
|
.gt('last_seen_at', cutoffTime.toIso8601String())
|
|
.eq('is_online', true);
|
|
|
|
// Calculate count from the response
|
|
final List<dynamic> users = response;
|
|
final count = users.length;
|
|
|
|
if (_onlineUsersCount != count) {
|
|
_onlineUsersCount = count;
|
|
_onlineUsersController.add(count);
|
|
debugPrint(
|
|
'UserPresence: Online users count updated: $_onlineUsersCount',
|
|
);
|
|
}
|
|
} catch (e) {
|
|
debugPrint('UserPresence: Error fetching online users count - $e');
|
|
}
|
|
}
|
|
|
|
/// Get all currently online users
|
|
Future<List<Map<String, dynamic>>> getOnlineUsers() async {
|
|
try {
|
|
final now = DateTime.now();
|
|
final cutoffTime = now.subtract(Duration(seconds: _presenceTimeout));
|
|
|
|
final response = await _supabase
|
|
.from(_presenceTableName)
|
|
.select('id, user_id, email, last_seen_at')
|
|
.gt('last_seen_at', cutoffTime.toIso8601String())
|
|
.eq('is_online', true);
|
|
|
|
return List<Map<String, dynamic>>.from(response);
|
|
} catch (e) {
|
|
debugPrint('UserPresence: Error getting online users - $e');
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/// Clean up resources
|
|
void dispose() {
|
|
_heartbeatTimer?.cancel();
|
|
_onlineUsersController.close();
|
|
_isInitialized = false;
|
|
|
|
// Set user as offline in the database
|
|
if (_userId != null) {
|
|
try {
|
|
_supabase
|
|
.from(_presenceTableName)
|
|
.update({'is_online': false})
|
|
.eq('id', _userId!)
|
|
.then((_) {
|
|
debugPrint('UserPresence: User set to offline');
|
|
});
|
|
} catch (e) {
|
|
debugPrint('UserPresence: Error setting user offline - $e');
|
|
}
|
|
}
|
|
|
|
debugPrint('UserPresence: Service disposed');
|
|
}
|
|
}
|