Add active session name and headless indicator to friend tile

This commit is contained in:
Nutcake 2023-05-12 19:29:23 +02:00
parent 801881cc07
commit 8a6a033cfd
5 changed files with 81 additions and 26 deletions

View file

@ -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<Session> 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<Session>? activeSessions,
UserStatus copyWith({
OnlineStatus? onlineStatus,
DateTime? lastStatusChange,
Session? currentSession,
List<Session>? 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,
);
}

View file

@ -13,15 +13,38 @@ class Session {
final FormatNode formattedDescription;
final List<String> 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.
};

View file

@ -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 {

View file

@ -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';

View file

@ -72,7 +72,10 @@ class _MessagesListState extends State<MessagesList> {
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,