mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-01-15 13:08:42 +00:00
improve best friends dialog
This commit is contained in:
parent
ceb6c7b362
commit
b466b6a8cb
4 changed files with 118 additions and 55 deletions
|
|
@ -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) {
|
||||||
|
|
|
||||||
32
lib/src/components/flame.dart
Normal file
32
lib/src/components/flame.dart
Normal 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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
} else {
|
_updateUsers(usersFiltered);
|
||||||
_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,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue