import 'package:recon/string_formatter.dart'; class Session { final String id; final String name; final FormatNode formattedName; final List sessionUsers; final String thumbnailUrl; final int maxUsers; final bool hasEnded; final bool isValid; final String description; final FormatNode formattedDescription; final List tags; final bool headlessHost; final String hostUserId; final String hostUsername; final SessionAccessLevel accessLevel; Session({ required this.id, required this.name, required this.sessionUsers, required this.thumbnailUrl, required this.maxUsers, required this.hasEnded, required this.isValid, required this.description, required this.tags, required this.headlessHost, required this.hostUserId, required this.hostUsername, required this.accessLevel, }) : formattedName = FormatNode.fromText(name), formattedDescription = FormatNode.fromText(description); factory Session.none() { return Session( id: "", name: "", sessionUsers: const [], thumbnailUrl: "", maxUsers: 0, hasEnded: true, isValid: false, description: "", tags: const [], headlessHost: false, hostUserId: "", hostUsername: "", accessLevel: SessionAccessLevel.unknown, ); } bool get isVisible => name.isNotEmpty && accessLevel != SessionAccessLevel.unknown; factory Session.fromMap(Map? map) { if (map == null) return Session.none(); return Session( id: map["sessionId"], name: map["name"], sessionUsers: (map["sessionUsers"] as List? ?? []).map((entry) => SessionUser.fromMap(entry)).toList(), thumbnailUrl: map["thumbnailUrl"] ?? "", maxUsers: map["maxUsers"] ?? 0, hasEnded: map["hasEnded"] ?? false, isValid: map["isValid"] ?? true, description: map["description"] ?? "", tags: ((map["tags"] as List?) ?? []).map((e) => e.toString()).toList(), headlessHost: map["headlessHost"] ?? false, hostUserId: map["hostUserId"] ?? "", hostUsername: map["hostUsername"] ?? "", accessLevel: SessionAccessLevel.fromName(map["accessLevel"]), ); } Map toMap({bool shallow = false}) { return { "sessionId": id, "name": name, "sessionUsers": shallow ? [] : sessionUsers.map((e) => e.toMap()).toList(), "thumbnail": thumbnailUrl, "maxUsers": maxUsers, "hasEnded": hasEnded, "isValid": isValid, "description": description, "tags": shallow ? [] : tags, "headlessHost": headlessHost, "hostUserId": hostUserId, "hostUsername": hostUsername, "accessLevel": accessLevel.name, // This probably wont work, the API usually expects integers. }; } Session copyWith({ String? id, String? name, FormatNode? formattedName, List? sessionUsers, String? thumbnailUrl, int? maxUsers, bool? hasEnded, bool? isValid, String? description, FormatNode? formattedDescription, List? tags, bool? headlessHost, String? hostUserId, String? hostUsername, SessionAccessLevel? accessLevel, }) { return Session( id: id ?? this.id, name: name ?? this.name, sessionUsers: sessionUsers ?? this.sessionUsers, thumbnailUrl: thumbnailUrl ?? this.thumbnailUrl, maxUsers: maxUsers ?? this.maxUsers, hasEnded: hasEnded ?? this.hasEnded, isValid: isValid ?? this.isValid, description: description ?? this.description, tags: tags ?? this.tags, headlessHost: headlessHost ?? this.headlessHost, hostUserId: hostUserId ?? this.hostUserId, hostUsername: hostUsername ?? this.hostUsername, accessLevel: accessLevel ?? this.accessLevel, ); } bool get isLive => !hasEnded && isValid; } enum SessionAccessLevel { unknown, private, contacts, contactsPlus, registeredUsers, anyone; static const _readableNamesMap = { SessionAccessLevel.unknown: "Unknown", SessionAccessLevel.private: "Private", SessionAccessLevel.contacts: "Contacts only", SessionAccessLevel.contactsPlus: "Contacts+", SessionAccessLevel.registeredUsers: "Registered users", SessionAccessLevel.anyone: "Public", }; factory SessionAccessLevel.fromName(String? name) { return SessionAccessLevel.values.firstWhere( (element) => element.name.toLowerCase() == name?.toLowerCase(), orElse: () => SessionAccessLevel.unknown, ); } String toReadableString() { return SessionAccessLevel._readableNamesMap[this] ?? SessionAccessLevel.unknown.toReadableString(); } } class SessionUser { final String id; final String username; final bool isPresent; final int outputDevice; SessionUser({required this.id, required this.username, required this.isPresent, required this.outputDevice}); factory SessionUser.fromMap(Map map) { return SessionUser( id: map["userID"] ?? "", username: map["username"] ?? "Unknown", isPresent: map["isPresent"] ?? false, outputDevice: map["outputDevice"] ?? 0, ); } Map toMap() { return { "userID": id, "username": username, "isPresent": isPresent, "outputDevice": outputDevice, }; } } class SessionFilterSettings { final String name; final bool includeEnded; final bool includeIncompatible; final String hostName; final int minActiveUsers; final bool includeEmptyHeadless; const SessionFilterSettings({ required this.name, required this.includeEnded, required this.includeIncompatible, required this.hostName, required this.minActiveUsers, required this.includeEmptyHeadless, }); factory SessionFilterSettings.empty() => const SessionFilterSettings( name: "", includeEnded: false, includeIncompatible: false, hostName: "", minActiveUsers: 0, includeEmptyHeadless: true, ); String buildRequestString() => "?includeEmptyHeadless=$includeEmptyHeadless" "${"&includeEnded=$includeEnded"}" "${name.isNotEmpty ? "&name=$name" : ""}" "${hostName.isNotEmpty ? "&hostName=$hostName" : ""}" "${minActiveUsers > 0 ? "&minActiveUsers=$minActiveUsers" : ""}"; SessionFilterSettings copyWith({ String? name, bool? includeEnded, bool? includeIncompatible, String? hostName, int? minActiveUsers, bool? includeEmptyHeadless, }) { return SessionFilterSettings( name: name ?? this.name, includeEnded: includeEnded ?? this.includeEnded, includeIncompatible: includeIncompatible ?? this.includeIncompatible, hostName: hostName ?? this.hostName, minActiveUsers: minActiveUsers ?? this.minActiveUsers, includeEmptyHeadless: includeEmptyHeadless ?? this.includeEmptyHeadless, ); } }