From 8a6a033cfdb9bb63ad14fff3f5d5654a9f863305 Mon Sep 17 00:00:00 2001 From: Nutcake Date: Fri, 12 May 2023 19:29:23 +0200 Subject: [PATCH] Add active session name and headless indicator to friend tile --- lib/models/friend.dart | 49 ++++++++++++++--------- lib/models/session.dart | 29 +++++++++++++- lib/widgets/friends/friend_list_tile.dart | 23 ++++++++--- lib/widgets/friends/friends_list.dart | 1 + lib/widgets/messages/messages_list.dart | 5 ++- 5 files changed, 81 insertions(+), 26 deletions(-) diff --git a/lib/models/friend.dart b/lib/models/friend.dart index 99b4cf6..8125bd5 100644 --- a/lib/models/friend.dart +++ b/lib/models/friend.dart @@ -16,6 +16,8 @@ class Friend implements Comparable { required this.friendStatus, required this.latestMessageTime, }); + bool get isHeadless => userStatus.activeSessions.any((session) => session.headlessHost == true && session.hostUserId == id); + factory Friend.fromMap(Map map) { final userStatus = UserStatus.fromMap(map["userStatus"]); return Friend( @@ -116,19 +118,23 @@ enum OnlineStatus { class UserStatus { final OnlineStatus onlineStatus; final DateTime lastStatusChange; + final Session currentSession; final List activeSessions; final String neosVersion; - const UserStatus({required this.onlineStatus, required this.lastStatusChange, required this.activeSessions, - required this.neosVersion, - }); + const UserStatus( + {required this.onlineStatus, required this.lastStatusChange, required this.currentSession, required this.activeSessions, + required this.neosVersion, + }); - factory UserStatus.empty() => UserStatus( - onlineStatus: OnlineStatus.offline, - lastStatusChange: DateTime.now(), - activeSessions: [], - neosVersion: "", - ); + factory UserStatus.empty() => + UserStatus( + onlineStatus: OnlineStatus.offline, + lastStatusChange: DateTime.now(), + activeSessions: [], + currentSession: Session.none(), + neosVersion: "", + ); factory UserStatus.fromMap(Map map) { final statusString = map["onlineStatus"] as String?; @@ -136,27 +142,34 @@ class UserStatus { return UserStatus( onlineStatus: status, lastStatusChange: DateTime.parse(map["lastStatusChange"]), + currentSession: Session.fromMap(map["currentSession"]), activeSessions: (map["activeSessions"] as List? ?? []).map((e) => Session.fromMap(e)).toList(), neosVersion: map["neosVersion"] ?? "", ); } - Map toMap({bool shallow=false}) { + Map toMap({bool shallow = false}) { return { "onlineStatus": onlineStatus.index, "lastStatusChange": lastStatusChange.toIso8601String(), + "currentSession": currentSession.isNone || shallow ? null : currentSession.toMap(), "activeSessions": shallow ? [] : activeSessions.map((e) => e.toMap(),), "neosVersion": neosVersion, }; } - UserStatus copyWith({OnlineStatus? onlineStatus, DateTime? lastStatusChange, List? activeSessions, + UserStatus copyWith({ + OnlineStatus? onlineStatus, + DateTime? lastStatusChange, + Session? currentSession, + List? activeSessions, String? neosVersion - }) - => UserStatus( - onlineStatus: onlineStatus ?? this.onlineStatus, - lastStatusChange: lastStatusChange ?? this.lastStatusChange, - activeSessions: activeSessions ?? this.activeSessions, - neosVersion: neosVersion ?? this.neosVersion, - ); + }) => + UserStatus( + onlineStatus: onlineStatus ?? this.onlineStatus, + lastStatusChange: lastStatusChange ?? this.lastStatusChange, + currentSession: currentSession ?? this.currentSession, + activeSessions: activeSessions ?? this.activeSessions, + neosVersion: neosVersion ?? this.neosVersion, + ); } \ No newline at end of file diff --git a/lib/models/session.dart b/lib/models/session.dart index 253d017..475ce21 100644 --- a/lib/models/session.dart +++ b/lib/models/session.dart @@ -13,15 +13,38 @@ class Session { 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.thumbnail, required this.maxUsers, required this.hasEnded, required this.isValid, required this.description, - required this.tags, required this.headlessHost, required this.hostUsername, required this.accessLevel, + 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.fromMap(Map map) { + factory Session.none() { + return Session( + id: "", + name: "", + sessionUsers: const [], + thumbnail: "", + maxUsers: 0, + hasEnded: true, + isValid: false, + description: "", + tags: const [], + headlessHost: false, + hostUserId: "", + hostUsername: "", + accessLevel: SessionAccessLevel.unknown + ); + } + + bool get isNone => id.isEmpty && isValid == false; + + factory Session.fromMap(Map? map) { + if (map == null) return Session.none(); return Session( id: map["sessionId"], name: map["name"], @@ -33,6 +56,7 @@ class Session { 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"]), ); @@ -50,6 +74,7 @@ class Session { "description": description, "tags": shallow ? [] : throw UnimplementedError(), "headlessHost": headlessHost, + "hostUserId": hostUserId, "hostUsername": hostUsername, "accessLevel": accessLevel.name, // This probably wont work, the API usually expects integers. }; diff --git a/lib/widgets/friends/friend_list_tile.dart b/lib/widgets/friends/friend_list_tile.dart index 2df5d17..b0c2db7 100644 --- a/lib/widgets/friends/friend_list_tile.dart +++ b/lib/widgets/friends/friend_list_tile.dart @@ -2,19 +2,19 @@ import 'package:contacts_plus_plus/auxiliary.dart'; import 'package:contacts_plus_plus/clients/messaging_client.dart'; import 'package:contacts_plus_plus/models/friend.dart'; import 'package:contacts_plus_plus/models/message.dart'; +import 'package:contacts_plus_plus/widgets/formatted_text.dart'; import 'package:contacts_plus_plus/widgets/friends/friend_online_status_indicator.dart'; import 'package:contacts_plus_plus/widgets/generic_avatar.dart'; import 'package:contacts_plus_plus/widgets/messages/messages_list.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; -import 'package:package_info_plus/package_info_plus.dart'; import 'package:provider/provider.dart'; class FriendListTile extends StatelessWidget { - const FriendListTile({required this.friend, this.unreads, this.onTap, super.key}); + const FriendListTile({required this.friend, required this.unreads, this.onTap, super.key}); final Friend friend; - final int? unreads; + final int unreads; final Function? onTap; @override @@ -23,10 +23,18 @@ class FriendListTile extends StatelessWidget { final theme = Theme.of(context); return ListTile( leading: GenericAvatar(imageUri: imageUri,), - trailing: unreads != null && unreads != 0 + trailing: unreads != 0 ? Text("+$unreads", style: theme.textTheme.bodyMedium?.copyWith(color: theme.colorScheme.primary),) : null, - title: Text(friend.username), + title: Row( + children: [ + Text(friend.username), + if (friend.isHeadless) Padding( + padding: const EdgeInsets.only(left: 8), + child: Icon(Icons.dns, size: 12, color: theme.colorScheme.onSecondaryContainer.withAlpha(150),), + ), + ], + ), subtitle: Row( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, @@ -34,6 +42,11 @@ class FriendListTile extends StatelessWidget { FriendOnlineStatusIndicator(userStatus: friend.userStatus), const SizedBox(width: 4,), Text(toBeginningOfSentenceCase(friend.userStatus.onlineStatus.name) ?? "Unknown"), + if (!friend.userStatus.currentSession.isNone) + ...[ + const Text(" in "), + Expanded(child: FormattedText(friend.userStatus.currentSession.formattedName, overflow: TextOverflow.ellipsis, maxLines: 1,)) + ] ], ), onTap: () async { diff --git a/lib/widgets/friends/friends_list.dart b/lib/widgets/friends/friends_list.dart index f4591e9..f7d50a6 100644 --- a/lib/widgets/friends/friends_list.dart +++ b/lib/widgets/friends/friends_list.dart @@ -6,6 +6,7 @@ import 'package:contacts_plus_plus/clients/messaging_client.dart'; import 'package:contacts_plus_plus/models/friend.dart'; import 'package:contacts_plus_plus/models/personal_profile.dart'; import 'package:contacts_plus_plus/widgets/default_error_widget.dart'; +import 'package:contacts_plus_plus/widgets/formatted_text.dart'; import 'package:contacts_plus_plus/widgets/friends/expanding_input_fab.dart'; import 'package:contacts_plus_plus/widgets/friends/friend_list_tile.dart'; import 'package:contacts_plus_plus/widgets/my_profile_dialog.dart'; diff --git a/lib/widgets/messages/messages_list.dart b/lib/widgets/messages/messages_list.dart index e88c708..345834e 100644 --- a/lib/widgets/messages/messages_list.dart +++ b/lib/widgets/messages/messages_list.dart @@ -72,7 +72,10 @@ class _MessagesListState extends State { FriendOnlineStatusIndicator(userStatus: widget.friend.userStatus), const SizedBox(width: 8,), Text(widget.friend.username), - + if (widget.friend.isHeadless) Padding( + padding: const EdgeInsets.only(left: 12), + child: Icon(Icons.dns, size: 18, color: Theme.of(context).colorScheme.onSecondaryContainer.withAlpha(150),), + ), ], ), scrolledUnderElevation: 0.0,