improve best friends dialog

This commit is contained in:
otsmr 2025-02-01 11:39:34 +01:00
parent ceb6c7b362
commit b466b6a8cb
4 changed files with 118 additions and 55 deletions

View file

@ -2,6 +2,7 @@ import 'dart:collection';
import 'package:fixnum/fixnum.dart'; import 'package:fixnum/fixnum.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:twonly/src/components/flame.dart';
import 'package:twonly/src/components/headline.dart'; import 'package:twonly/src/components/headline.dart';
import 'package:twonly/src/components/initialsavatar.dart'; import 'package:twonly/src/components/initialsavatar.dart';
import 'package:twonly/src/model/contacts_model.dart'; import 'package:twonly/src/model/contacts_model.dart';
@ -10,10 +11,12 @@ class BestFriendsSelector extends StatelessWidget {
final List<Contact> users; final List<Contact> users;
final Function(Int64, bool) updateStatus; final Function(Int64, bool) updateStatus;
final HashSet<Int64> selectedUserIds; final HashSet<Int64> selectedUserIds;
final int maxTotalMediaCounter;
const BestFriendsSelector({ const BestFriendsSelector({
super.key, super.key,
required this.users, required this.users,
required this.maxTotalMediaCounter,
required this.updateStatus, required this.updateStatus,
required this.selectedUserIds, required this.selectedUserIds,
}); });
@ -24,14 +27,13 @@ class BestFriendsSelector extends StatelessWidget {
return Container(); return Container();
} }
final limitedUsers = users.length > 8 ? users.sublist(0, 8) : users;
return Column( return Column(
children: [ children: [
HeadLineComponent(AppLocalizations.of(context)!.shareImageBestFriends), HeadLineComponent(AppLocalizations.of(context)!.shareImageBestFriends),
Column( Column(
spacing: 8, spacing: 8,
children: List.generate( children: List.generate(
(limitedUsers.length + 1) ~/ 2, (users.length + 1) ~/ 2,
(rowIndex) { (rowIndex) {
final firstUserIndex = rowIndex * 2; final firstUserIndex = rowIndex * 2;
final secondUserIndex = firstUserIndex + 1; final secondUserIndex = firstUserIndex + 1;
@ -41,19 +43,20 @@ class BestFriendsSelector extends StatelessWidget {
Expanded( Expanded(
child: UserCheckbox( child: UserCheckbox(
isChecked: selectedUserIds isChecked: selectedUserIds
.contains(limitedUsers[firstUserIndex].userId), .contains(users[firstUserIndex].userId),
user: limitedUsers[firstUserIndex], user: users[firstUserIndex],
onChanged: updateStatus, onChanged: updateStatus,
maxTotalMediaCounter: maxTotalMediaCounter,
), ),
), ),
(secondUserIndex < limitedUsers.length) (secondUserIndex < users.length)
? Expanded( ? Expanded(
child: UserCheckbox( child: UserCheckbox(
isChecked: selectedUserIds isChecked: selectedUserIds
.contains(limitedUsers[secondUserIndex].userId), .contains(users[secondUserIndex].userId),
user: limitedUsers[secondUserIndex], user: users[secondUserIndex],
onChanged: updateStatus, onChanged: updateStatus,
), maxTotalMediaCounter: maxTotalMediaCounter),
) )
: Expanded( : Expanded(
child: Container(), child: Container(),
@ -72,10 +75,12 @@ class UserCheckbox extends StatelessWidget {
final Contact user; final Contact user;
final Function(Int64, bool) onChanged; final Function(Int64, bool) onChanged;
final bool isChecked; final bool isChecked;
final int maxTotalMediaCounter;
const UserCheckbox({ const UserCheckbox({
super.key, super.key,
required this.user, required this.user,
required this.maxTotalMediaCounter,
required this.onChanged, required this.onChanged,
required this.isChecked, required this.isChecked,
}); });
@ -105,14 +110,15 @@ class UserCheckbox extends StatelessWidget {
displayName: user.displayName, displayName: user.displayName,
), ),
SizedBox(width: 8), SizedBox(width: 8),
Expanded( Text(
child: Text( user.displayName.length > 10
user.displayName.length > 10 ? '${user.displayName.substring(0, 10)}...'
? '${user.displayName.substring(0, 10)}...' : user.displayName,
: user.displayName, overflow: TextOverflow.ellipsis,
overflow: TextOverflow.ellipsis,
),
), ),
if (user.flameCounter > 0)
FlameCounterWidget(user, maxTotalMediaCounter),
Expanded(child: Container()),
Checkbox( Checkbox(
value: isChecked, value: isChecked,
onChanged: (bool? value) { onChanged: (bool? value) {

View file

@ -0,0 +1,32 @@
import 'package:flutter/material.dart';
import 'package:twonly/src/model/contacts_model.dart';
class FlameCounterWidget extends StatelessWidget {
final Contact user;
final int maxTotalMediaCounter;
const FlameCounterWidget(
this.user,
this.maxTotalMediaCounter, {
super.key,
});
@override
Widget build(BuildContext context) {
return Row(
children: [
const SizedBox(width: 5),
Text(""),
const SizedBox(width: 5),
Text(
user.flameCounter.toString(),
style: const TextStyle(fontSize: 12),
),
Text(
(maxTotalMediaCounter == user.totalMediaCounter) ? "❤️‍🔥" : "🔥",
style: const TextStyle(fontSize: 10),
),
],
);
}
}

View file

@ -2,6 +2,7 @@ import 'dart:math';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:twonly/src/components/flame.dart';
import 'package:twonly/src/components/initialsavatar.dart'; import 'package:twonly/src/components/initialsavatar.dart';
import 'package:twonly/src/components/message_send_state_icon.dart'; import 'package:twonly/src/components/message_send_state_icon.dart';
import 'package:twonly/src/components/notification_badge.dart'; import 'package:twonly/src/components/notification_badge.dart';
@ -187,25 +188,7 @@ class _UserListItem extends State<UserListItem> {
style: TextStyle(fontSize: 12), style: TextStyle(fontSize: 12),
), ),
if (widget.user.flameCounter > 0) if (widget.user.flameCounter > 0)
Row( FlameCounterWidget(widget.user, widget.maxTotalMediaCounter),
children: [
const SizedBox(width: 5),
Text(""),
const SizedBox(width: 5),
Text(
widget.user.flameCounter.toString(),
style: TextStyle(fontSize: 12),
),
const SizedBox(width: 3),
Text(
(widget.maxTotalMediaCounter ==
widget.user.totalMediaCounter)
? "❤️‍🔥"
: "🔥",
style: TextStyle(fontSize: 10),
)
],
),
], ],
), ),
leading: InitialsAvatar(displayName: widget.user.displayName), leading: InitialsAvatar(displayName: widget.user.displayName),

View file

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:twonly/src/components/best_friends_selector.dart'; import 'package:twonly/src/components/best_friends_selector.dart';
import 'package:twonly/src/components/flame.dart';
import 'package:twonly/src/components/headline.dart'; import 'package:twonly/src/components/headline.dart';
import 'package:twonly/src/components/initialsavatar.dart'; import 'package:twonly/src/components/initialsavatar.dart';
import 'package:twonly/src/model/contacts_model.dart'; import 'package:twonly/src/model/contacts_model.dart';
@ -21,7 +22,9 @@ class ShareImageView extends StatefulWidget {
class _ShareImageView extends State<ShareImageView> { class _ShareImageView extends State<ShareImageView> {
List<Contact> _users = []; List<Contact> _users = [];
List<Contact> _usersFiltered = []; List<Contact> _otherUsers = [];
List<Contact> _bestFriends = [];
int maxTotalMediaCounter = 0;
final HashSet<Int64> _selectedUserIds = HashSet<Int64>(); final HashSet<Int64> _selectedUserIds = HashSet<Int64>();
String _lastSearchQuery = ''; String _lastSearchQuery = '';
final TextEditingController searchUserName = TextEditingController(); final TextEditingController searchUserName = TextEditingController();
@ -36,26 +39,57 @@ class _ShareImageView extends State<ShareImageView> {
final users = await DbContacts.getActiveUsers(); final users = await DbContacts.getActiveUsers();
setState(() { setState(() {
_users = users; _users = users;
_usersFiltered = _users; _updateUsers(_users);
});
}
Future _updateUsers(List<Contact> users) async {
// Sort contacts by flameCounter and then by totalMediaCounter
users.sort((a, b) {
// First, compare by flameCounter
int flameComparison = b.flameCounter.compareTo(a.flameCounter);
if (flameComparison != 0) {
return flameComparison; // Sort by flameCounter in descending order
}
// If flameCounter is the same, compare by totalMediaCounter
return b.totalMediaCounter.compareTo(
a.totalMediaCounter); // Sort by totalMediaCounter in descending order
});
maxTotalMediaCounter = 0;
if (users.isNotEmpty) {
maxTotalMediaCounter =
users.map((x) => x.totalMediaCounter).reduce((a, b) => a > b ? a : b);
}
// Separate best friends and other users
List<Contact> bestFriends = [];
List<Contact> otherUsers = [];
for (var contact in users) {
if (contact.flameCounter > 0 && bestFriends.length < 6) {
bestFriends.add(contact);
} else {
otherUsers.add(contact);
}
}
setState(() {
_bestFriends = bestFriends;
_otherUsers = otherUsers;
}); });
} }
Future _filterUsers(String query) async { Future _filterUsers(String query) async {
if (query.isEmpty) { if (query.isEmpty) {
_usersFiltered = _users; _updateUsers(_users);
return; return;
} }
if (_lastSearchQuery.length < query.length) { List<Contact> usersFiltered = _users
_usersFiltered = _users .where((user) =>
.where((user) => user.displayName.toLowerCase().contains(query.toLowerCase()))
user.displayName.toLowerCase().contains(query.toLowerCase())) .toList();
.toList(); _updateUsers(usersFiltered);
} else {
_usersFiltered = _usersFiltered
.where((user) =>
user.displayName.toLowerCase().contains(query.toLowerCase()))
.toList();
}
_lastSearchQuery = query; _lastSearchQuery = query;
} }
@ -77,8 +111,9 @@ class _ShareImageView extends State<ShareImageView> {
AppLocalizations.of(context)!.searchUsernameInput))), AppLocalizations.of(context)!.searchUsernameInput))),
const SizedBox(height: 10), const SizedBox(height: 10),
BestFriendsSelector( BestFriendsSelector(
users: _usersFiltered, users: _bestFriends,
selectedUserIds: _selectedUserIds, selectedUserIds: _selectedUserIds,
maxTotalMediaCounter: maxTotalMediaCounter,
updateStatus: (userId, checked) { updateStatus: (userId, checked) {
if (checked) { if (checked) {
_selectedUserIds.add(userId); _selectedUserIds.add(userId);
@ -88,12 +123,13 @@ class _ShareImageView extends State<ShareImageView> {
}, },
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
if (_usersFiltered.isNotEmpty) if (_otherUsers.isNotEmpty)
HeadLineComponent( HeadLineComponent(
AppLocalizations.of(context)!.shareImageAllUsers), AppLocalizations.of(context)!.shareImageAllUsers),
Expanded( Expanded(
child: UserList( child: UserList(
List.from(_usersFiltered), List.from(_otherUsers),
maxTotalMediaCounter,
selectedUserIds: _selectedUserIds, selectedUserIds: _selectedUserIds,
), ),
) )
@ -136,8 +172,10 @@ class _ShareImageView extends State<ShareImageView> {
} }
class UserList extends StatelessWidget { class UserList extends StatelessWidget {
const UserList(this.users, {super.key, required this.selectedUserIds}); const UserList(this.users, this.maxTotalMediaCounter,
{super.key, required this.selectedUserIds});
final List<Contact> users; final List<Contact> users;
final int maxTotalMediaCounter;
final HashSet<Int64> selectedUserIds; final HashSet<Int64> selectedUserIds;
@override @override
@ -151,7 +189,11 @@ class UserList extends StatelessWidget {
itemBuilder: (BuildContext context, int i) { itemBuilder: (BuildContext context, int i) {
Contact user = users[i]; Contact user = users[i];
return ListTile( return ListTile(
title: Text(user.displayName), title: Row(children: [
Text(user.displayName),
if (user.flameCounter > 0)
FlameCounterWidget(user, maxTotalMediaCounter),
]),
leading: InitialsAvatar( leading: InitialsAvatar(
displayName: user.displayName, displayName: user.displayName,
fontSize: 15, fontSize: 15,