Add active session name and headless indicator to friend tile
This commit is contained in:
parent
801881cc07
commit
8a6a033cfd
5 changed files with 81 additions and 26 deletions
|
@ -16,6 +16,8 @@ class Friend implements Comparable {
|
||||||
required this.friendStatus, required this.latestMessageTime,
|
required this.friendStatus, required this.latestMessageTime,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
bool get isHeadless => userStatus.activeSessions.any((session) => session.headlessHost == true && session.hostUserId == id);
|
||||||
|
|
||||||
factory Friend.fromMap(Map map) {
|
factory Friend.fromMap(Map map) {
|
||||||
final userStatus = UserStatus.fromMap(map["userStatus"]);
|
final userStatus = UserStatus.fromMap(map["userStatus"]);
|
||||||
return Friend(
|
return Friend(
|
||||||
|
@ -116,17 +118,21 @@ enum OnlineStatus {
|
||||||
class UserStatus {
|
class UserStatus {
|
||||||
final OnlineStatus onlineStatus;
|
final OnlineStatus onlineStatus;
|
||||||
final DateTime lastStatusChange;
|
final DateTime lastStatusChange;
|
||||||
|
final Session currentSession;
|
||||||
final List<Session> activeSessions;
|
final List<Session> activeSessions;
|
||||||
final String neosVersion;
|
final String neosVersion;
|
||||||
|
|
||||||
const UserStatus({required this.onlineStatus, required this.lastStatusChange, required this.activeSessions,
|
const UserStatus(
|
||||||
|
{required this.onlineStatus, required this.lastStatusChange, required this.currentSession, required this.activeSessions,
|
||||||
required this.neosVersion,
|
required this.neosVersion,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory UserStatus.empty() => UserStatus(
|
factory UserStatus.empty() =>
|
||||||
|
UserStatus(
|
||||||
onlineStatus: OnlineStatus.offline,
|
onlineStatus: OnlineStatus.offline,
|
||||||
lastStatusChange: DateTime.now(),
|
lastStatusChange: DateTime.now(),
|
||||||
activeSessions: [],
|
activeSessions: [],
|
||||||
|
currentSession: Session.none(),
|
||||||
neosVersion: "",
|
neosVersion: "",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -136,26 +142,33 @@ class UserStatus {
|
||||||
return UserStatus(
|
return UserStatus(
|
||||||
onlineStatus: status,
|
onlineStatus: status,
|
||||||
lastStatusChange: DateTime.parse(map["lastStatusChange"]),
|
lastStatusChange: DateTime.parse(map["lastStatusChange"]),
|
||||||
|
currentSession: Session.fromMap(map["currentSession"]),
|
||||||
activeSessions: (map["activeSessions"] as List? ?? []).map((e) => Session.fromMap(e)).toList(),
|
activeSessions: (map["activeSessions"] as List? ?? []).map((e) => Session.fromMap(e)).toList(),
|
||||||
neosVersion: map["neosVersion"] ?? "",
|
neosVersion: map["neosVersion"] ?? "",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map toMap({bool shallow=false}) {
|
Map toMap({bool shallow = false}) {
|
||||||
return {
|
return {
|
||||||
"onlineStatus": onlineStatus.index,
|
"onlineStatus": onlineStatus.index,
|
||||||
"lastStatusChange": lastStatusChange.toIso8601String(),
|
"lastStatusChange": lastStatusChange.toIso8601String(),
|
||||||
|
"currentSession": currentSession.isNone || shallow ? null : currentSession.toMap(),
|
||||||
"activeSessions": shallow ? [] : activeSessions.map((e) => e.toMap(),),
|
"activeSessions": shallow ? [] : activeSessions.map((e) => e.toMap(),),
|
||||||
"neosVersion": neosVersion,
|
"neosVersion": neosVersion,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
UserStatus copyWith({OnlineStatus? onlineStatus, DateTime? lastStatusChange, List<Session>? activeSessions,
|
UserStatus copyWith({
|
||||||
|
OnlineStatus? onlineStatus,
|
||||||
|
DateTime? lastStatusChange,
|
||||||
|
Session? currentSession,
|
||||||
|
List<Session>? activeSessions,
|
||||||
String? neosVersion
|
String? neosVersion
|
||||||
})
|
}) =>
|
||||||
=> UserStatus(
|
UserStatus(
|
||||||
onlineStatus: onlineStatus ?? this.onlineStatus,
|
onlineStatus: onlineStatus ?? this.onlineStatus,
|
||||||
lastStatusChange: lastStatusChange ?? this.lastStatusChange,
|
lastStatusChange: lastStatusChange ?? this.lastStatusChange,
|
||||||
|
currentSession: currentSession ?? this.currentSession,
|
||||||
activeSessions: activeSessions ?? this.activeSessions,
|
activeSessions: activeSessions ?? this.activeSessions,
|
||||||
neosVersion: neosVersion ?? this.neosVersion,
|
neosVersion: neosVersion ?? this.neosVersion,
|
||||||
);
|
);
|
||||||
|
|
|
@ -13,15 +13,38 @@ class Session {
|
||||||
final FormatNode formattedDescription;
|
final FormatNode formattedDescription;
|
||||||
final List<String> tags;
|
final List<String> tags;
|
||||||
final bool headlessHost;
|
final bool headlessHost;
|
||||||
|
final String hostUserId;
|
||||||
final String hostUsername;
|
final String hostUsername;
|
||||||
final SessionAccessLevel accessLevel;
|
final SessionAccessLevel accessLevel;
|
||||||
|
|
||||||
Session({required this.id, required this.name, required this.sessionUsers, required this.thumbnail,
|
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.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);
|
}) : 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(
|
return Session(
|
||||||
id: map["sessionId"],
|
id: map["sessionId"],
|
||||||
name: map["name"],
|
name: map["name"],
|
||||||
|
@ -33,6 +56,7 @@ class Session {
|
||||||
description: map["description"] ?? "",
|
description: map["description"] ?? "",
|
||||||
tags: ((map["tags"] as List?) ?? []).map((e) => e.toString()).toList(),
|
tags: ((map["tags"] as List?) ?? []).map((e) => e.toString()).toList(),
|
||||||
headlessHost: map["headlessHost"] ?? false,
|
headlessHost: map["headlessHost"] ?? false,
|
||||||
|
hostUserId: map["hostUserId"] ?? "",
|
||||||
hostUsername: map["hostUsername"] ?? "",
|
hostUsername: map["hostUsername"] ?? "",
|
||||||
accessLevel: SessionAccessLevel.fromName(map["accessLevel"]),
|
accessLevel: SessionAccessLevel.fromName(map["accessLevel"]),
|
||||||
);
|
);
|
||||||
|
@ -50,6 +74,7 @@ class Session {
|
||||||
"description": description,
|
"description": description,
|
||||||
"tags": shallow ? [] : throw UnimplementedError(),
|
"tags": shallow ? [] : throw UnimplementedError(),
|
||||||
"headlessHost": headlessHost,
|
"headlessHost": headlessHost,
|
||||||
|
"hostUserId": hostUserId,
|
||||||
"hostUsername": hostUsername,
|
"hostUsername": hostUsername,
|
||||||
"accessLevel": accessLevel.name, // This probably wont work, the API usually expects integers.
|
"accessLevel": accessLevel.name, // This probably wont work, the API usually expects integers.
|
||||||
};
|
};
|
||||||
|
|
|
@ -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/clients/messaging_client.dart';
|
||||||
import 'package:contacts_plus_plus/models/friend.dart';
|
import 'package:contacts_plus_plus/models/friend.dart';
|
||||||
import 'package:contacts_plus_plus/models/message.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/friends/friend_online_status_indicator.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/generic_avatar.dart';
|
import 'package:contacts_plus_plus/widgets/generic_avatar.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/messages/messages_list.dart';
|
import 'package:contacts_plus_plus/widgets/messages/messages_list.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class FriendListTile extends StatelessWidget {
|
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 Friend friend;
|
||||||
final int? unreads;
|
final int unreads;
|
||||||
final Function? onTap;
|
final Function? onTap;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -23,10 +23,18 @@ class FriendListTile extends StatelessWidget {
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
return ListTile(
|
return ListTile(
|
||||||
leading: GenericAvatar(imageUri: imageUri,),
|
leading: GenericAvatar(imageUri: imageUri,),
|
||||||
trailing: unreads != null && unreads != 0
|
trailing: unreads != 0
|
||||||
? Text("+$unreads", style: theme.textTheme.bodyMedium?.copyWith(color: theme.colorScheme.primary),)
|
? Text("+$unreads", style: theme.textTheme.bodyMedium?.copyWith(color: theme.colorScheme.primary),)
|
||||||
: null,
|
: 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(
|
subtitle: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
@ -34,6 +42,11 @@ class FriendListTile extends StatelessWidget {
|
||||||
FriendOnlineStatusIndicator(userStatus: friend.userStatus),
|
FriendOnlineStatusIndicator(userStatus: friend.userStatus),
|
||||||
const SizedBox(width: 4,),
|
const SizedBox(width: 4,),
|
||||||
Text(toBeginningOfSentenceCase(friend.userStatus.onlineStatus.name) ?? "Unknown"),
|
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 {
|
onTap: () async {
|
||||||
|
|
|
@ -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/friend.dart';
|
||||||
import 'package:contacts_plus_plus/models/personal_profile.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/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/expanding_input_fab.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/friends/friend_list_tile.dart';
|
import 'package:contacts_plus_plus/widgets/friends/friend_list_tile.dart';
|
||||||
import 'package:contacts_plus_plus/widgets/my_profile_dialog.dart';
|
import 'package:contacts_plus_plus/widgets/my_profile_dialog.dart';
|
||||||
|
|
|
@ -72,7 +72,10 @@ class _MessagesListState extends State<MessagesList> {
|
||||||
FriendOnlineStatusIndicator(userStatus: widget.friend.userStatus),
|
FriendOnlineStatusIndicator(userStatus: widget.friend.userStatus),
|
||||||
const SizedBox(width: 8,),
|
const SizedBox(width: 8,),
|
||||||
Text(widget.friend.username),
|
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,
|
scrolledUnderElevation: 0.0,
|
||||||
|
|
Loading…
Reference in a new issue