Hibok
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.
 
 
 
 
 
 

538 lines
17 KiB

  1. import 'dart:io';
  2. import 'package:cached_network_image/cached_network_image.dart';
  3. import 'package:chat/home/global_search.dart';
  4. import 'package:chat/home/new_friends.dart';
  5. import 'package:chat/home/search_newfriend.dart';
  6. import 'package:chat/models/friends_info.dart';
  7. import 'package:flutter/material.dart';
  8. import 'package:chat/data/UserData.dart';
  9. import 'package:chat/data/constants.dart';
  10. import 'package:chat/generated/i18n.dart';
  11. import 'package:chat/models/ref_name_provider.dart';
  12. import 'package:chat/utils/CustomUI.dart';
  13. import 'package:chat/utils/HttpUtil.dart';
  14. import 'package:chat/utils/LoadingDialog.dart';
  15. import 'package:chat/utils/MessageMgr.dart';
  16. import 'package:chat/utils/TokenMgr.dart';
  17. import 'package:chat/utils/conversation_table.dart';
  18. import 'package:chat/utils/friend_list_mgr.dart';
  19. import 'package:chat/utils/screen.dart';
  20. import 'package:dio/dio.dart';
  21. import 'package:oktoast/oktoast.dart';
  22. import 'package:permission_handler/permission_handler.dart';
  23. import 'package:provider/provider.dart';
  24. import 'package:shared_preferences/shared_preferences.dart';
  25. import 'address_book.dart';
  26. List showUserList = [];
  27. bool isHaveNewFriends() {
  28. return showUserList.length > 0;
  29. }
  30. class FriendPage extends StatefulWidget {
  31. @override
  32. _FriendPageState createState() => _FriendPageState();
  33. }
  34. class _FriendPageState extends State<FriendPage> {
  35. String _currentLetter = '';
  36. ScrollController _scrollController;
  37. List<FriendModel> friendList = [];
  38. List<Widget> _functionButtons = [];
  39. final Map _letterPosMap = {INDEX_BAR_WORDS[0]: 0.0};
  40. Set<String> userIdSet = new Set();
  41. //var _contactsFuture;
  42. @override
  43. void initState() {
  44. super.initState();
  45. print('FriendPage initState');
  46. initNewFriendsList([]);
  47. getFriendList();
  48. getNewFriendList(null);
  49. _scrollController = new ScrollController();
  50. MessageMgr().on('Add friend', msgAddfrend);
  51. MessageMgr().on('Delete friend', msgDeletefrend);
  52. MessageMgr().on('do_friend_apply', messageApply);
  53. MessageMgr().on('goto_new_friends', messageNew);
  54. }
  55. messageApply(data) {
  56. getNewFriendList(data);
  57. }
  58. messageNew(data) {
  59. gotoNewFriendsPage();
  60. }
  61. gotoNewFriendsPage() async {
  62. Navigator.of(context).push(
  63. new MaterialPageRoute(
  64. builder: (context) {
  65. return NewFriendsPage();
  66. },
  67. ),
  68. );
  69. SharedPreferences prefs = await SharedPreferences.getInstance();
  70. prefs.setStringList(Constants.NewFriendsList, userIdSet.toList());
  71. showUserList = [];
  72. initNewFriendsList([]);
  73. setState(() {});
  74. }
  75. Widget _buildNewFriends(list) {
  76. if (list.length == 0) {
  77. return FriendsInfo(
  78. avatar: '',
  79. userId: 0,
  80. iconCode: 0xe66c,
  81. title: I18n.of(LoadingManage.context).new_friends,
  82. gradient: LinearGradient(
  83. begin: Alignment.topCenter,
  84. end: Alignment.bottomCenter,
  85. colors: <Color>[
  86. const Color(0xFF58B7F5),
  87. const Color(0xFF1874C9),
  88. ]),
  89. onPressed: gotoNewFriendsPage);
  90. } else {
  91. return _buildFriendsItem(list);
  92. }
  93. }
  94. Widget _buildAvatar(url) {
  95. return ClipRRect(
  96. borderRadius: BorderRadius.circular(6),
  97. child: CachedNetworkImage(
  98. imageUrl: url,
  99. width: Constants.ContactAvatarSize,
  100. height: Constants.ContactAvatarSize,
  101. ));
  102. }
  103. Widget _buildFriendsItem(list) {
  104. Widget unreadRedDot = Container(
  105. margin: EdgeInsets.only(right: 15),
  106. width: Constants.UnReadMsgNotifyDotSize,
  107. height: Constants.UnReadMsgNotifyDotSize,
  108. alignment: Alignment.center,
  109. decoration: BoxDecoration(
  110. borderRadius:
  111. BorderRadius.circular(Constants.UnReadMsgNotifyDotSize / 2.0),
  112. color: Color(0xFFFF5454),
  113. ),
  114. child: Text(list.length > 99 ? "99+" : list.length.toString(),
  115. textScaleFactor: 1.0, style: AppStyles.UnreadMsgCountDotStyle),
  116. );
  117. var left;
  118. if (list.length == 1) {
  119. left = Row(
  120. children: <Widget>[
  121. _buildAvatar(list[0]['ApplyUrl']),
  122. SizedBox(width: 10.0),
  123. Expanded(
  124. child: Column(
  125. crossAxisAlignment: CrossAxisAlignment.start,
  126. children: <Widget>[
  127. Text(list[0]['ApplyName'], style: TextStyle(fontSize: 12.66)),
  128. SizedBox(
  129. height: 5,
  130. ),
  131. Text(I18n.of(context).apply_fro_friends,
  132. style:
  133. TextStyle(fontSize: 10, color: const Color(0xFF6A6A6A))),
  134. ],
  135. )),
  136. unreadRedDot,
  137. ],
  138. );
  139. } else {
  140. left = Row(
  141. children: <Widget>[
  142. Expanded(
  143. child: Container(
  144. height: Constants.ContactAvatarSize,
  145. constraints: BoxConstraints(maxWidth: Screen.width - 100),
  146. child: ListView(
  147. scrollDirection: Axis.horizontal,
  148. children: list
  149. .map<Widget>((f) => Container(
  150. child: _buildAvatar(f['ApplyUrl']),
  151. margin: EdgeInsets.only(right: 5),
  152. ))
  153. .toList(),
  154. ))),
  155. unreadRedDot,
  156. ],
  157. );
  158. }
  159. return InkWell(
  160. onTap: gotoNewFriendsPage,
  161. child: Container(
  162. padding: const EdgeInsets.symmetric(
  163. vertical: MARGIN_VERTICAL, horizontal: 16.0),
  164. decoration: BoxDecoration(color: Colors.white),
  165. child: left,
  166. ));
  167. }
  168. showPermission() async {
  169. if (await CustomUI.showPermissionSetting(
  170. LoadingManage.context,
  171. PermissionGroup.contacts,
  172. I18n.of(LoadingManage.context).contact_permission)) {
  173. MessageMgr().emit('PostContact');
  174. Navigator.push(LoadingManage.context,
  175. MaterialPageRoute<void>(builder: (BuildContext context) {
  176. return ContactsPage();
  177. }));
  178. }
  179. }
  180. getFriendList() async {
  181. friendList = await FriendListMgr().getFriendList();
  182. if (mounted) {
  183. setState(() {});
  184. }
  185. }
  186. getNewFriendList(userdata) async {
  187. Map data = {
  188. "userId": UserData().basicInfo.userId,
  189. "type": 1,
  190. };
  191. data['sign'] = TokenMgr().getSign(data);
  192. Response res =
  193. await HttpUtil().post('friendship/newFriends/record', data: data);
  194. if (res == null) {
  195. return;
  196. }
  197. var resData = res.data;
  198. if (resData['code'] == 0) {
  199. if (resData['data'] != null) {
  200. SharedPreferences prefs = await SharedPreferences.getInstance();
  201. List<String> list = prefs.getStringList(Constants.NewFriendsList);
  202. if (list != null) {
  203. userIdSet = new Set.from(list);
  204. } else {
  205. userIdSet = new Set();
  206. }
  207. if (userdata != null && userIdSet.contains(userdata['userId'])) {
  208. userIdSet.remove(userdata['userId']);
  209. }
  210. showUserList = [];
  211. for (int i = 0; i < resData['data'].length; i++) {
  212. if (!userIdSet
  213. .contains(resData['data'][i]['ApplyUserId'].toString())) {
  214. showUserList.add(resData['data'][i]);
  215. userIdSet.add(resData['data'][i]['ApplyUserId'].toString());
  216. }
  217. }
  218. initNewFriendsList(showUserList);
  219. if (mounted) {
  220. setState(() {});
  221. }
  222. }
  223. } else {
  224. showToast(resData['msg']);
  225. }
  226. }
  227. initNewFriendsList(showUserList) {
  228. _functionButtons = [
  229. FriendsInfo(
  230. avatar: '',
  231. userId: 0,
  232. iconCode: 0xe659,
  233. title: I18n.of(LoadingManage.context).scan,
  234. gradient: LinearGradient(
  235. begin: Alignment.topCenter,
  236. end: Alignment.bottomCenter,
  237. colors: <Color>[
  238. const Color(0xFF6171F1),
  239. const Color(0xFF3441C1),
  240. ]),
  241. onPressed: () {
  242. CustomUI().goScanPage(LoadingManage.context);
  243. }),
  244. FriendsInfo(
  245. avatar: '',
  246. userId: 0,
  247. iconCode: 0xe67a,
  248. isShowDivder: true,
  249. title: I18n.of(LoadingManage.context).contact_add,
  250. gradient: LinearGradient(
  251. begin: Alignment.topCenter,
  252. end: Alignment.bottomCenter,
  253. colors: <Color>[
  254. const Color(0xFF1AC59E),
  255. const Color(0xFF088E76),
  256. ]),
  257. onPressed: () async {
  258. final PermissionStatus addStatus = await PermissionHandler()
  259. .checkPermissionStatus(PermissionGroup.contacts);
  260. if ((addStatus == PermissionStatus.unknown ||
  261. addStatus == PermissionStatus.denied) &&
  262. Platform.isIOS) {
  263. CustomUI.buildContacts(context, I18n.of(context).privacyAgreement,
  264. I18n.of(context).determine, () {
  265. Navigator.of(context).pop();
  266. showPermission();
  267. }, title: I18n.of(context).tip);
  268. } else {
  269. showPermission();
  270. }
  271. }),
  272. FriendsInfo(
  273. avatar: '',
  274. userId: 0,
  275. iconCode: 0xe662,
  276. isShowDivder: true,
  277. title: I18n.of(LoadingManage.context).add_friends,
  278. gradient: LinearGradient(
  279. begin: Alignment.topCenter,
  280. end: Alignment.bottomCenter,
  281. colors: <Color>[
  282. const Color(0xFF9E58E5),
  283. const Color(0xFF6118A6),
  284. ]),
  285. onPressed: () async {
  286. Navigator.push(LoadingManage.context,
  287. MaterialPageRoute<void>(builder: (BuildContext context) {
  288. return SearchNewFriendsPage();
  289. }));
  290. }),
  291. ];
  292. _functionButtons.insert(0, _buildNewFriends(showUserList));
  293. }
  294. msgAddfrend(data) {
  295. // setState(() {
  296. // _contactsFuture = FriendListMgr().getFriendList();
  297. // });
  298. getFriendList();
  299. }
  300. msgDeletefrend(data) {
  301. // setState(() {
  302. // _contactsFuture = FriendListMgr().getFriendList();
  303. // });
  304. getFriendList();
  305. }
  306. updateIndexPos(List<FriendModel> friendList) {
  307. //计算用于 IndexBar 进行定位的关键通讯录列表项的位置
  308. var _totalPos = _functionButtons.length * FriendsInfo.height(false);
  309. for (var i = 0; i < friendList.length; i++) {
  310. bool _hasGroupTitle = true;
  311. if (i > 0 &&
  312. friendList[i].nameTag.compareTo(friendList[i - 1].nameTag) == 0) {
  313. _hasGroupTitle = false;
  314. }
  315. if (_hasGroupTitle) {
  316. _letterPosMap[friendList[i].nameTag] = _totalPos;
  317. }
  318. _totalPos += FriendsInfo.height(_hasGroupTitle);
  319. }
  320. }
  321. @override
  322. void dispose() {
  323. _scrollController.dispose();
  324. print('FriendPage dispose');
  325. MessageMgr().off('Add friend', msgAddfrend);
  326. MessageMgr().off('Delete friend', msgDeletefrend);
  327. MessageMgr().off('do_friend_apply', messageApply);
  328. MessageMgr().off('goto_new_friends', messageNew);
  329. super.dispose();
  330. }
  331. String getLetter(BuildContext context, double tileHeight, Offset globalPos) {
  332. RenderBox _box = context.findRenderObject();
  333. var local = _box.globalToLocal(globalPos);
  334. int index = (local.dy ~/ tileHeight).clamp(0, INDEX_BAR_WORDS.length - 1);
  335. return INDEX_BAR_WORDS[index];
  336. }
  337. void _jumpToIndex(String letter) {
  338. if (_letterPosMap.isNotEmpty) {
  339. final _pos = _letterPosMap[letter];
  340. if (_pos != null) {
  341. _scrollController.animateTo(_letterPosMap[letter],
  342. curve: Curves.easeInOut, duration: Duration(microseconds: 200));
  343. }
  344. }
  345. }
  346. Widget _buildIndexBar(BuildContext context, BoxConstraints constraints) {
  347. final List<Widget> _letters = INDEX_BAR_WORDS.map((String word) {
  348. return Expanded(
  349. child: Container(
  350. margin: EdgeInsets.only(right: 5),
  351. decoration: BoxDecoration(
  352. shape: BoxShape.circle,
  353. color:
  354. _currentLetter == word ? Colors.blue : Colors.transparent,
  355. ),
  356. alignment: Alignment.center,
  357. padding: EdgeInsets.all(2),
  358. width: 20,
  359. child: Text(
  360. word,
  361. style: TextStyle(
  362. fontSize: 10,
  363. color:
  364. _currentLetter == word ? Colors.white : Colors.black),
  365. )));
  366. }).toList();
  367. final _totalHeight = constraints.biggest.height;
  368. final _tileHeight = _totalHeight / _letters.length;
  369. return GestureDetector(
  370. onVerticalDragDown: (DragDownDetails details) {
  371. setState(() {
  372. _currentLetter =
  373. getLetter(context, _tileHeight, details.globalPosition);
  374. _jumpToIndex(_currentLetter);
  375. });
  376. },
  377. onVerticalDragEnd: (DragEndDetails details) {
  378. setState(() {
  379. //_indexBarBgColor = Colors.transparent;
  380. _currentLetter = null;
  381. });
  382. },
  383. onVerticalDragCancel: () {
  384. setState(() {
  385. //_indexBarBgColor = Colors.transparent;
  386. _currentLetter = null;
  387. });
  388. },
  389. onVerticalDragUpdate: (DragUpdateDetails details) {
  390. setState(() {
  391. //var _letter = getLetter(context, _tileHeight, details.globalPosition);
  392. _currentLetter =
  393. getLetter(context, _tileHeight, details.globalPosition);
  394. _jumpToIndex(_currentLetter);
  395. });
  396. },
  397. child: Column(
  398. children: _letters,
  399. ),
  400. );
  401. }
  402. @override
  403. Widget build(BuildContext context) {
  404. final List<Widget> _body = [];
  405. friendList.sort((a, b) => a.nameTag.compareTo(b.nameTag));
  406. updateIndexPos(friendList);
  407. _body.addAll([
  408. ListView.builder(
  409. controller: _scrollController,
  410. itemBuilder: (BuildContext context, int index) {
  411. if (index < _functionButtons.length) {
  412. if (index == 0) {
  413. return Padding(
  414. padding: EdgeInsets.only(top: 7),
  415. child: _functionButtons[index],
  416. );
  417. } else {
  418. return _functionButtons[index];
  419. }
  420. }
  421. int _contactIndex = index - _functionButtons.length;
  422. bool _isGroupTitle = true;
  423. FriendModel _contact = friendList[_contactIndex];
  424. if (_contactIndex >= 1 &&
  425. _contact.nameTag == friendList[_contactIndex - 1].nameTag) {
  426. _isGroupTitle = false;
  427. }
  428. return FriendsInfo(
  429. userId: _contact.friendId,
  430. avatar: _contact.avatar,
  431. title: Provider.of<RefNameProvider>(context)
  432. .getRefName(_contact.friendId, _contact.name),
  433. isShowDivder: _isGroupTitle,
  434. groupTitle: _isGroupTitle ? _contact.nameTag : null);
  435. },
  436. itemCount: friendList.length + _functionButtons.length,
  437. ),
  438. Positioned(
  439. width: Constants.IndexBarWidth,
  440. right: 0.0,
  441. top: 0.0,
  442. bottom: 0.0,
  443. child: Container(
  444. //color: _indexBarBgColor,
  445. child: LayoutBuilder(
  446. builder: _buildIndexBar,
  447. ),
  448. ),
  449. )
  450. ]);
  451. if (_currentLetter != null && _currentLetter.isNotEmpty) {
  452. _body.add(Center(
  453. child: Container(
  454. width: Constants.IndexLetterBoxSize,
  455. height: Constants.IndexLetterBoxSize,
  456. decoration: BoxDecoration(
  457. color: AppColors.IndexLetterBoxBgColor,
  458. borderRadius: BorderRadius.all(
  459. Radius.circular(Constants.IndexLetterBoxRadius)),
  460. ),
  461. child: Center(
  462. child:
  463. Text(_currentLetter, style: AppStyles.IndexLetterBoxTextStyle),
  464. ),
  465. ),
  466. ));
  467. }
  468. return Scaffold(
  469. resizeToAvoidBottomPadding: false,
  470. appBar: AppBar(
  471. backgroundColor: AppColors.NewAppbarBgColor,
  472. title: Text(I18n.of(context).contact,
  473. textScaleFactor: 1.0, style: Constants.MainTitleStyle),
  474. centerTitle: false,
  475. elevation: 1,
  476. bottom: CustomUI.buildSearchButton(context, () {
  477. Navigator.of(context).push(
  478. new MaterialPageRoute(
  479. builder: (context) {
  480. return GlobalSearchPage(
  481. type: GlobalSearchPageType.SearchMyFriends,
  482. );
  483. },
  484. ),
  485. );
  486. })),
  487. body: Stack(
  488. children: _body,
  489. ));
  490. }
  491. }