icon things
This commit is contained in:
parent
a91654df03
commit
8bc45ad6fd
50 changed files with 592 additions and 213 deletions
|
@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:network_info_plus/network_info_plus.dart';
|
||||
import 'package:multicast_dns/multicast_dns.dart';
|
||||
import 'music_queue_service.dart';
|
||||
|
||||
class NetworkUser {
|
||||
final String id;
|
||||
|
@ -15,6 +16,11 @@ class NetworkUser {
|
|||
final int votes;
|
||||
bool isOnline;
|
||||
DateTime lastSeen;
|
||||
String? currentTrackId;
|
||||
String? currentTrackName;
|
||||
String? currentArtist;
|
||||
String? currentTrackImage;
|
||||
bool isListening;
|
||||
|
||||
NetworkUser({
|
||||
required this.id,
|
||||
|
@ -24,6 +30,11 @@ class NetworkUser {
|
|||
this.votes = 0,
|
||||
this.isOnline = true,
|
||||
DateTime? lastSeen,
|
||||
this.currentTrackId,
|
||||
this.currentTrackName,
|
||||
this.currentArtist,
|
||||
this.currentTrackImage,
|
||||
this.isListening = false,
|
||||
}) : lastSeen = lastSeen ?? DateTime.now();
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
|
@ -34,6 +45,11 @@ class NetworkUser {
|
|||
'votes': votes,
|
||||
'isOnline': isOnline,
|
||||
'lastSeen': lastSeen.toIso8601String(),
|
||||
'currentTrackId': currentTrackId,
|
||||
'currentTrackName': currentTrackName,
|
||||
'currentArtist': currentArtist,
|
||||
'currentTrackImage': currentTrackImage,
|
||||
'isListening': isListening,
|
||||
};
|
||||
|
||||
factory NetworkUser.fromJson(Map<String, dynamic> json) => NetworkUser(
|
||||
|
@ -44,6 +60,11 @@ class NetworkUser {
|
|||
votes: json['votes'] ?? 0,
|
||||
isOnline: json['isOnline'] ?? true,
|
||||
lastSeen: DateTime.parse(json['lastSeen']),
|
||||
currentTrackId: json['currentTrackId'],
|
||||
currentTrackName: json['currentTrackName'],
|
||||
currentArtist: json['currentArtist'],
|
||||
currentTrackImage: json['currentTrackImage'],
|
||||
isListening: json['isListening'] ?? false,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -66,6 +87,7 @@ class NetworkGroupService extends ChangeNotifier {
|
|||
|
||||
final Map<String, NetworkUser> _networkUsers = {};
|
||||
late NetworkUser _currentUser;
|
||||
MusicQueueService? _musicService;
|
||||
|
||||
// Getters
|
||||
bool get isConnectedToWifi => _isConnectedToWifi;
|
||||
|
@ -79,6 +101,8 @@ class NetworkGroupService extends ChangeNotifier {
|
|||
NetworkGroupService() {
|
||||
_initializeCurrentUser();
|
||||
_startNetworkMonitoring();
|
||||
// Initialize music service reference
|
||||
_musicService = MusicQueueService();
|
||||
}
|
||||
|
||||
void _initializeCurrentUser() {
|
||||
|
@ -232,6 +256,8 @@ class NetworkGroupService extends ChangeNotifier {
|
|||
response.headers.set('Access-Control-Allow-Origin', '*');
|
||||
|
||||
if (request.method == 'GET' && request.uri.path == '/user') {
|
||||
// Update current user with latest listening info before sending
|
||||
await _updateCurrentUserListeningInfo();
|
||||
// Return current user info
|
||||
response.write(jsonEncode(_currentUser.toJson()));
|
||||
} else if (request.method == 'POST' && request.uri.path == '/heartbeat') {
|
||||
|
@ -244,6 +270,41 @@ class NetworkGroupService extends ChangeNotifier {
|
|||
notifyListeners();
|
||||
|
||||
response.write(jsonEncode({'status': 'ok'}));
|
||||
} else if (request.method == 'POST' && request.uri.path == '/join-session') {
|
||||
// Handle request to join this user's listening session
|
||||
final body = await utf8.decoder.bind(request).join();
|
||||
// Parse request data for future use (logging, analytics, etc.)
|
||||
jsonDecode(body);
|
||||
|
||||
// Get current track info to send back
|
||||
final currentTrackInfo = _musicService?.currentTrackInfo;
|
||||
if (currentTrackInfo != null) {
|
||||
response.write(jsonEncode({
|
||||
'status': 'ok',
|
||||
'trackInfo': currentTrackInfo,
|
||||
'message': 'Successfully joined listening session'
|
||||
}));
|
||||
} else {
|
||||
response.write(jsonEncode({
|
||||
'status': 'no_track',
|
||||
'message': 'No track currently playing'
|
||||
}));
|
||||
}
|
||||
} else if (request.method == 'GET' && request.uri.path == '/current-track') {
|
||||
// Get current track info without joining
|
||||
await _updateCurrentUserListeningInfo();
|
||||
final currentTrackInfo = _musicService?.currentTrackInfo;
|
||||
if (currentTrackInfo != null) {
|
||||
response.write(jsonEncode({
|
||||
'status': 'ok',
|
||||
'trackInfo': currentTrackInfo
|
||||
}));
|
||||
} else {
|
||||
response.write(jsonEncode({
|
||||
'status': 'no_track',
|
||||
'message': 'No track currently playing'
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
response.statusCode = 404;
|
||||
response.write(jsonEncode({'error': 'Not found'}));
|
||||
|
@ -395,6 +456,39 @@ class NetworkGroupService extends ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _updateCurrentUserListeningInfo() async {
|
||||
final currentTrackInfo = _musicService?.currentTrackInfo;
|
||||
final currentTrack = _musicService?.currentTrack;
|
||||
|
||||
if (currentTrack != null && currentTrackInfo != null) {
|
||||
_currentUser = NetworkUser(
|
||||
id: _currentUser.id,
|
||||
name: _currentUser.name,
|
||||
ipAddress: _currentUser.ipAddress,
|
||||
joinedAt: _currentUser.joinedAt,
|
||||
votes: _currentUser.votes,
|
||||
isOnline: true,
|
||||
currentTrackId: currentTrack.id,
|
||||
currentTrackName: currentTrack.name,
|
||||
currentArtist: currentTrack.artistNames,
|
||||
currentTrackImage: currentTrack.imageUrl,
|
||||
isListening: currentTrackInfo['isPlaying'] ?? false,
|
||||
);
|
||||
} else {
|
||||
_currentUser = NetworkUser(
|
||||
id: _currentUser.id,
|
||||
name: _currentUser.name,
|
||||
ipAddress: _currentUser.ipAddress,
|
||||
joinedAt: _currentUser.joinedAt,
|
||||
votes: _currentUser.votes,
|
||||
isOnline: true,
|
||||
isListening: false,
|
||||
);
|
||||
}
|
||||
|
||||
_networkUsers[_currentUser.id] = _currentUser;
|
||||
}
|
||||
|
||||
// Public methods for UI interaction
|
||||
Future<void> refreshNetwork() async {
|
||||
await _checkConnectivity();
|
||||
|
@ -426,6 +520,45 @@ class NetworkGroupService extends ChangeNotifier {
|
|||
return 'Connected to $_currentNetworkName';
|
||||
}
|
||||
|
||||
// Join another user's listening session
|
||||
Future<bool> joinListeningSession(NetworkUser user) async {
|
||||
if (!user.isListening || user.currentTrackId == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
final client = HttpClient();
|
||||
client.connectionTimeout = const Duration(seconds: 5);
|
||||
|
||||
final request = await client.postUrl(
|
||||
Uri.parse('http://${user.ipAddress}:$_discoveryPort/join-session')
|
||||
);
|
||||
request.headers.set('Content-Type', 'application/json');
|
||||
request.write(jsonEncode({'userId': _currentUser.id}));
|
||||
|
||||
final response = await request.close().timeout(const Duration(seconds: 5));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final body = await utf8.decoder.bind(response).join();
|
||||
final responseData = jsonDecode(body);
|
||||
|
||||
if (responseData['status'] == 'ok' && responseData['trackInfo'] != null) {
|
||||
// Here you could sync the track with your local player
|
||||
// For now, we'll just return success
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
client.close();
|
||||
return false;
|
||||
} catch (e) {
|
||||
if (kDebugMode) {
|
||||
print('Error joining listening session: $e');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Demo methods for testing
|
||||
void simulateNetworkConnection() {
|
||||
_isConnectedToWifi = true;
|
||||
|
|
|
@ -34,7 +34,13 @@ class SpamProtectionService extends ChangeNotifier {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Check cooldown
|
||||
// Allow first vote without cooldown for smooth user experience
|
||||
final votes = _userVotes[userId] ?? [];
|
||||
if (votes.isEmpty) {
|
||||
return true; // First vote is always allowed
|
||||
}
|
||||
|
||||
// Check cooldown for subsequent votes
|
||||
if (_isOnCooldown(userId, _lastVoteTime, voteCooldown)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -54,7 +60,13 @@ class SpamProtectionService extends ChangeNotifier {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Check cooldown
|
||||
// Allow first suggestion without cooldown for smooth user experience
|
||||
final suggestions = _userSuggestions[userId] ?? [];
|
||||
if (suggestions.isEmpty) {
|
||||
return true; // First suggestion is always allowed
|
||||
}
|
||||
|
||||
// Check cooldown for subsequent suggestions
|
||||
if (_isOnCooldown(userId, _lastSuggestionTime, suggestionCooldown)) {
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue