Encrypted messaging app
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

227 lines
5.9 KiB

  1. import 'package:Envelope/database/repositories/friends_repository.dart';
  2. import 'package:Envelope/services/friends_service.dart';
  3. import 'package:flutter/material.dart';
  4. import '/components/custom_title_bar.dart';
  5. import '/components/qr_reader.dart';
  6. import '/components/custom_expandable_fab.dart';
  7. import '/database/models/friends.dart';
  8. import '/views/main/friend/list_item.dart';
  9. import '/views/main/friend/add_search.dart';
  10. import '/views/main/friend/request_list_item.dart';
  11. class FriendList extends StatefulWidget {
  12. final List<Friend> friends;
  13. final Function callback;
  14. const FriendList({
  15. Key? key,
  16. required this.friends,
  17. required this.callback,
  18. }) : super(key: key);
  19. @override
  20. State<FriendList> createState() => _FriendListState();
  21. }
  22. class _FriendListState extends State<FriendList> {
  23. final GlobalKey<RefreshIndicatorState> _refreshIndicatorKey = GlobalKey<RefreshIndicatorState>();
  24. late ScrollController _scrollController;
  25. List<Friend> friends = [];
  26. List<Friend> friendsDuplicate = [];
  27. @override
  28. Widget build(BuildContext context) {
  29. return Scaffold(
  30. appBar: const CustomTitleBar(
  31. title: Text(
  32. 'Friends',
  33. style: TextStyle(
  34. fontSize: 32,
  35. fontWeight: FontWeight.bold
  36. )
  37. ),
  38. showBack: false,
  39. backgroundColor: Colors.transparent,
  40. ),
  41. body: Padding(
  42. padding: const EdgeInsets.only(top: 16,left: 16,right: 16, bottom: 65),
  43. child: Stack(
  44. children: <Widget>[
  45. Padding(
  46. padding: const EdgeInsets.only(top: 50),
  47. child: RefreshIndicator(
  48. key: _refreshIndicatorKey,
  49. onRefresh: _refresh,
  50. child: list(),
  51. )
  52. ),
  53. TextField(
  54. decoration: const InputDecoration(
  55. hintText: 'Search...',
  56. prefixIcon: Icon(
  57. Icons.search,
  58. size: 20
  59. ),
  60. ),
  61. onChanged: (value) => filterSearchResults(value.toLowerCase())
  62. ),
  63. ],
  64. ),
  65. ),
  66. floatingActionButton: Padding(
  67. padding: const EdgeInsets.only(right: 10, bottom: 60),
  68. child: ExpandableFab(
  69. icon: Icon(
  70. Icons.add,
  71. size: 30,
  72. color: Theme.of(context).colorScheme.onPrimary,
  73. ),
  74. distance: 90.0,
  75. children: [
  76. ActionButton(
  77. onPressed: () {
  78. Navigator.of(context).push(
  79. MaterialPageRoute(builder: (context) => const QrReader())
  80. );//.then(onGoBack); // TODO
  81. },
  82. icon: const Icon(Icons.qr_code_2, size: 25),
  83. ),
  84. ActionButton(
  85. onPressed: () {
  86. Navigator.of(context).push(
  87. MaterialPageRoute(builder: (context) => const FriendAddSearch())
  88. );//.then(onGoBack); // TODO
  89. },
  90. icon: const Icon(Icons.search, size: 25),
  91. ),
  92. ],
  93. )
  94. )
  95. );
  96. }
  97. void filterSearchResults(String query) {
  98. List<Friend> dummyFriendsList = [];
  99. dummyFriendsList.addAll(friends);
  100. if (query.isNotEmpty) {
  101. List<Friend> dummyFriendData = [];
  102. for (Friend item in dummyFriendsList) {
  103. if(item.username.toLowerCase().contains(query)) {
  104. dummyFriendData.add(item);
  105. }
  106. }
  107. setState(() {
  108. friends.clear();
  109. friends.addAll(dummyFriendData);
  110. });
  111. return;
  112. }
  113. setState(() {
  114. friends.clear();
  115. friends.addAll(widget.friends);
  116. });
  117. }
  118. @override
  119. void initState() {
  120. _scrollController = ScrollController();
  121. _scrollController.addListener(_scrollListener);
  122. super.initState();
  123. friends.addAll(widget.friends);
  124. }
  125. Future<void> initFriends() async {
  126. friends = await FriendsRepository.getFriends();
  127. setState(() {});
  128. widget.callback();
  129. }
  130. Widget _heading(String heading) {
  131. return Padding(
  132. padding: const EdgeInsets.only(top: 5, bottom: 10),
  133. child: Align(
  134. alignment: Alignment.centerLeft,
  135. child: Text(
  136. heading,
  137. style: TextStyle(
  138. fontSize: 16,
  139. color: Theme.of(context).hintColor,
  140. ),
  141. ),
  142. )
  143. );
  144. }
  145. Widget list() {
  146. if (friends.isEmpty) {
  147. return const Center(
  148. child: Text('No Friends'),
  149. );
  150. }
  151. return ListView.separated(
  152. controller: _scrollController,
  153. itemCount: friends.length,
  154. shrinkWrap: false,
  155. physics: const AlwaysScrollableScrollPhysics(),
  156. padding: const EdgeInsets.only(top: 10),
  157. separatorBuilder: (context, i) {
  158. if (friends[i].acceptedAt == null) {
  159. return FriendRequestListItem(
  160. friend: friends[i],
  161. callback: initFriends,
  162. );
  163. }
  164. return FriendListItem(
  165. friend: friends[i],
  166. );
  167. },
  168. itemBuilder: (context, i) {
  169. if (i == 0 && friends[i].acceptedAt == null) {
  170. return _heading('Friend Requests');
  171. }
  172. if ((i == 0 || friends[i - 1].acceptedAt == null) && friends[i].acceptedAt != null) {
  173. return _heading('Friends');
  174. }
  175. return const SizedBox.shrink();
  176. },
  177. );
  178. }
  179. Future<void> _refresh() async {
  180. DateTime? acceptedAt = await FriendsRepository.getLatestAcceptedAt();
  181. if (acceptedAt == null) {
  182. return;
  183. }
  184. FriendsService.updateFriends(acceptedAt: acceptedAt);
  185. friends = await FriendsRepository.getFriends();
  186. setState(() {});
  187. }
  188. Future<void> _scrollListener() async {
  189. if (!(_scrollController.offset >= _scrollController.position.maxScrollExtent)) {
  190. return;
  191. }
  192. int page = 0;
  193. if (friends.length > 19) {
  194. page = friends.length ~/ 20;
  195. }
  196. await FriendsService.updateFriends(page: page);
  197. friends = await FriendsRepository.getFriends();
  198. setState(() {});
  199. }
  200. }