Hibok
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 
 

558 строки
19 KiB

  1. import 'package:chat/data/UserData.dart';
  2. import 'package:chat/data/WebData.dart';
  3. import 'package:chat/data/constants.dart';
  4. import 'package:chat/generated/i18n.dart';
  5. import 'package:chat/models/ref_name_provider.dart';
  6. import 'package:chat/utils/CustomUI.dart';
  7. import 'package:chat/utils/HttpUtil.dart';
  8. import 'package:chat/utils/MessageMgr.dart';
  9. import 'package:chat/utils/TokenMgr.dart';
  10. import 'package:chat/utils/conversation_table.dart';
  11. import 'package:chat/utils/screen.dart';
  12. import 'package:dio/dio.dart';
  13. import 'package:cached_network_image/cached_network_image.dart';
  14. import 'package:flutter/material.dart';
  15. import 'package:flutter/services.dart';
  16. import 'package:oktoast/oktoast.dart';
  17. import 'package:provider/provider.dart';
  18. import 'package:chat/models/friends_info.dart';
  19. import 'ProfilePage.dart';
  20. import 'package:permission_handler/permission_handler.dart';
  21. import 'dart:io';
  22. import 'address_book.dart';
  23. import 'new_addfriends.dart';
  24. class _ContactItem extends StatelessWidget {
  25. _ContactItem(
  26. {@required this.avatar,
  27. @required this.title,
  28. @required this.userId,
  29. this.groupTitle,
  30. this.onPressed,
  31. this.gradient,
  32. this.iconCode,
  33. this.applyId,
  34. this.isShowDivder: true,
  35. this.state});
  36. final int userId;
  37. final int iconCode;
  38. final String avatar;
  39. final String title;
  40. final String groupTitle;
  41. final VoidCallback onPressed;
  42. final Gradient gradient;
  43. final bool isShowDivder;
  44. final int state;
  45. final int applyId;
  46. static const double MARGIN_VERTICAL = 10.0;
  47. static const double GROUP_TITLE_HEIGHT = 24.0;
  48. @override
  49. Widget build(BuildContext context) {
  50. Widget _avatarIcon;
  51. if (iconCode == null) {
  52. _avatarIcon = ClipRRect(
  53. borderRadius: BorderRadius.circular(6),
  54. child: CachedNetworkImage(
  55. imageUrl: this.avatar,
  56. placeholder: (context, url) => Image.asset(
  57. Constants.DefaultHeadImgUrl,
  58. width: Constants.ContactAvatarSize,
  59. height: Constants.ContactAvatarSize,
  60. ),
  61. width: Constants.ContactAvatarSize,
  62. height: Constants.ContactAvatarSize,
  63. ));
  64. } else {
  65. _avatarIcon = Container(
  66. width: Constants.ContactAvatarSize,
  67. height: Constants.ContactAvatarSize,
  68. decoration: BoxDecoration(
  69. gradient: gradient, borderRadius: BorderRadius.circular(6)),
  70. child: Icon(
  71. IconData(this.iconCode, fontFamily: Constants.IconFontFamily),
  72. color: Colors.white,
  73. ),
  74. );
  75. }
  76. Widget _button = Container(
  77. padding: const EdgeInsets.symmetric(
  78. vertical: MARGIN_VERTICAL, horizontal: 16.0),
  79. decoration: BoxDecoration(color: Colors.white),
  80. child: Row(
  81. children: <Widget>[
  82. _avatarIcon,
  83. SizedBox(width: 10.0),
  84. Expanded(
  85. child: Text(
  86. title,
  87. textScaleFactor: 1.0,
  88. )),
  89. state == 1
  90. ? Container(
  91. padding: EdgeInsets.symmetric(horizontal: 21, vertical: 7),
  92. child: Text(
  93. I18n.of(context).added,
  94. textScaleFactor: 1.0,
  95. style: TextStyle(color: const Color(0xFF8A8B8B)),
  96. ),
  97. )
  98. : (state == 0
  99. ? Container(
  100. padding:
  101. EdgeInsets.symmetric(horizontal: 21, vertical: 7),
  102. child: Text(
  103. I18n.of(context).check,
  104. textScaleFactor: 1.0,
  105. style: TextStyle(color: Constants.BlueTextColor),
  106. ),
  107. )
  108. : Container(
  109. padding:
  110. EdgeInsets.symmetric(horizontal: 21, vertical: 7),
  111. child: Text(
  112. I18n.of(context).rejected,
  113. textScaleFactor: 1.0,
  114. style: TextStyle(color: const Color(0xFF8A8B8B)),
  115. ),
  116. )),
  117. ],
  118. ),
  119. );
  120. _button = Dismissible(
  121. key: Key(userId.toString()),
  122. child: _button,
  123. direction: DismissDirection.endToStart,
  124. background: Container(
  125. color: Colors.red,
  126. child: Row(
  127. mainAxisAlignment: MainAxisAlignment.end,
  128. children: <Widget>[
  129. fixedText(I18n.of(context).delete, color: Colors.white),
  130. Icon(
  131. Icons.delete,
  132. color: Colors.white,
  133. ),
  134. SizedBox(width: 20),
  135. ],
  136. ),
  137. ),
  138. confirmDismiss: (direction) async {
  139. var _confirmContent = I18n.of(context).confirm_delete;
  140. bool isDismiss =
  141. await CustomUI.showIosDialog(context, _confirmContent, () async {
  142. Map data = {
  143. "userId": UserData().basicInfo.userId,
  144. "aUserId": userId,
  145. };
  146. data['sign'] = TokenMgr().getSign(data);
  147. Response res = await HttpUtil().post(
  148. 'friendship/delete/applicationRecord',
  149. data: data,
  150. isShowLoading: true);
  151. Map resData = res.data;
  152. //showToast(resData['msg']);
  153. if (resData['code'] == 0) {
  154. MessageMgr().emit('delete_friend_record', userId);
  155. }
  156. Navigator.of(context).pop(true);
  157. }, () {
  158. Navigator.of(context).pop(false);
  159. });
  160. return isDismiss;
  161. });
  162. //分组标签
  163. Widget _itemBody;
  164. if (this.groupTitle != null) {
  165. _itemBody = Column(
  166. children: <Widget>[
  167. Container(
  168. height: GROUP_TITLE_HEIGHT,
  169. padding: EdgeInsets.only(left: 16.0, right: 16.0),
  170. color: const Color(AppColors.ContactGroupTitleBgColor),
  171. alignment: Alignment.centerLeft,
  172. child: Text(
  173. this.groupTitle,
  174. style: AppStyles.GroupTitleItemTextStyle,
  175. textScaleFactor: 1.0,
  176. ),
  177. ),
  178. _button,
  179. ],
  180. );
  181. } else {
  182. _itemBody = _button;
  183. }
  184. return InkWell(
  185. onTap: () {
  186. Navigator.of(context).push(
  187. new MaterialPageRoute(
  188. builder: (context) {
  189. return ProfilePage(
  190. userId: userId,
  191. addMode: state == 0 ? 2 : 0,
  192. applyId: applyId,
  193. );
  194. },
  195. ),
  196. );
  197. },
  198. child: Container(
  199. color: Colors.white,
  200. child: Column(
  201. children: <Widget>[
  202. _itemBody,
  203. isShowDivder
  204. ? Container(
  205. height: 1,
  206. color: const Color(0xFFF3F3F3),
  207. margin: EdgeInsets.only(
  208. left: 26 + Constants.ContactAvatarSize),
  209. )
  210. : Container()
  211. ],
  212. )));
  213. }
  214. }
  215. class NewFriendsPage extends StatefulWidget {
  216. @override
  217. _NewFriendsPageState createState() => _NewFriendsPageState();
  218. }
  219. class _NewFriendsPageState extends State<NewFriendsPage> {
  220. String _currentLetter = '';
  221. ScrollController _scrollController;
  222. TextEditingController _txtCtrl = new TextEditingController();
  223. bool _hasdeleteIcon = false;
  224. List<FriendModel> searchList = [];
  225. List<FriendModel> friendList = [];
  226. List<Widget> _functionButtons = [];
  227. @override
  228. void initState() {
  229. super.initState();
  230. print('NewFriendsPage initState');
  231. getNewFriendList();
  232. _scrollController = new ScrollController();
  233. MessageMgr().on('do_friend_apply', messageApply);
  234. MessageMgr().on('delete_friend_record', deleteFriendRecord);
  235. _functionButtons = [
  236. FriendsInfo(
  237. avatar: '',
  238. userId: 0,
  239. iconCode: 0xe67a,
  240. title: I18n.of(Constants.getCurrentContext()).contact_add,
  241. gradient: LinearGradient(
  242. begin: Alignment.topCenter,
  243. end: Alignment.bottomCenter,
  244. colors: <Color>[
  245. const Color(0xFF1AC59E),
  246. const Color(0xFF088E76),
  247. ]),
  248. onPressed: () async {
  249. final PermissionStatus addStatus = await PermissionHandler()
  250. .checkPermissionStatus(PermissionGroup.contacts);
  251. if ((addStatus == PermissionStatus.unknown ||
  252. addStatus == PermissionStatus.denied) &&
  253. Platform.isIOS) {
  254. CustomUI.buildContacts(context, I18n.of(context).privacyAgreement,
  255. I18n.of(context).determine, () {
  256. Navigator.of(context).pop();
  257. showPermission();
  258. }, title: I18n.of(context).tip);
  259. } else {
  260. showPermission();
  261. }
  262. }),
  263. ];
  264. }
  265. showPermission() async {
  266. if (await CustomUI.showPermissionSetting(
  267. Constants.getCurrentContext(),
  268. PermissionGroup.contacts,
  269. I18n.of(Constants.getCurrentContext()).contact_permission)) {
  270. MessageMgr().emit('PostContact');
  271. Navigator.push(Constants.getCurrentContext(),
  272. MaterialPageRoute<void>(builder: (BuildContext context) {
  273. return ContactsPage();
  274. }));
  275. }
  276. }
  277. messageApply(data) {
  278. for (int i = 0; i < friendList.length; i++) {
  279. if (friendList[i].friendId == data['userId']) {
  280. setState(() {
  281. friendList[i].state = data['state'];
  282. });
  283. break;
  284. }
  285. }
  286. }
  287. deleteFriendRecord(userId) {
  288. for (int i = 0; i < friendList.length; i++) {
  289. if (friendList[i].friendId == userId) {
  290. setState(() {
  291. friendList.removeAt(i);
  292. });
  293. break;
  294. }
  295. }
  296. }
  297. getNewFriendList() async {
  298. Map data = {
  299. "userId": UserData().basicInfo.userId,
  300. "type": 2,
  301. };
  302. data['sign'] = TokenMgr().getSign(data);
  303. Response res =
  304. await HttpUtil().post('friendship/newFriends/record', data: data);
  305. Map resData = res.data;
  306. print(resData['data']);
  307. if (resData['code'] == 0 && resData['data'] != null) {
  308. resData['data'].forEach((f) {
  309. var friend = FriendModel.fromApplyServerJson(f);
  310. if (friend.friendId != UserData().basicInfo.userId)
  311. friendList.add(friend);
  312. });
  313. setState(() {});
  314. }
  315. }
  316. @override
  317. void dispose() {
  318. _scrollController.dispose();
  319. MessageMgr().off('do_friend_apply', messageApply);
  320. MessageMgr().off('delete_friend_record', deleteFriendRecord);
  321. super.dispose();
  322. }
  323. @override
  324. Widget build(BuildContext context) {
  325. final List<Widget> _body = [];
  326. if (!_hasdeleteIcon) {
  327. _body.add(ListView.builder(
  328. controller: _scrollController,
  329. itemBuilder: (BuildContext context, int index) {
  330. if (index < _functionButtons.length) {
  331. if (index == 0) {
  332. return Padding(
  333. padding: EdgeInsets.only(top: 7),
  334. child: _functionButtons[index],
  335. );
  336. } else {
  337. return _functionButtons[index];
  338. }
  339. }
  340. int _contactIndex = index - _functionButtons.length;
  341. bool _isGroupTitle = true;
  342. FriendModel _contact = friendList[_contactIndex];
  343. bool isThreeDay = WebData().isThreeDayAgo(_contact.creatTime);
  344. if (_contactIndex >= 1 &&
  345. WebData().isThreeDayAgo(_contact.creatTime) ==
  346. WebData()
  347. .isThreeDayAgo(friendList[_contactIndex - 1].creatTime)) {
  348. _isGroupTitle = false;
  349. }
  350. return _ContactItem(
  351. userId: _contact.friendId,
  352. avatar: _contact.avatar,
  353. title: Provider.of<RefNameProvider>(context)
  354. .getRefName(_contact.friendId, _contact.name),
  355. state: _contact.state,
  356. isShowDivder: true,
  357. applyId: _contact.applyId,
  358. groupTitle: _isGroupTitle
  359. ? (isThreeDay
  360. ? I18n.of(context).before_three_day
  361. : I18n.of(context).after_three_day)
  362. : null);
  363. },
  364. itemCount: friendList.length + _functionButtons.length,
  365. ));
  366. } else {
  367. _body.add(ListView.builder(
  368. controller: _scrollController,
  369. itemBuilder: (BuildContext context, int index) {
  370. FriendModel _contact = searchList[index];
  371. return _ContactItem(
  372. userId: _contact.friendId,
  373. avatar: _contact.avatar,
  374. title: _contact.name,
  375. state: _contact.state,
  376. isShowDivder: true,
  377. applyId: _contact.applyId,
  378. groupTitle: null);
  379. },
  380. itemCount: searchList.length,
  381. ));
  382. }
  383. if (_currentLetter != null &&
  384. _currentLetter.isNotEmpty &&
  385. !_hasdeleteIcon) {
  386. _body.add(Center(
  387. child: Container(
  388. width: Constants.IndexLetterBoxSize,
  389. height: Constants.IndexLetterBoxSize,
  390. decoration: BoxDecoration(
  391. color: AppColors.IndexLetterBoxBgColor,
  392. borderRadius: BorderRadius.all(
  393. Radius.circular(Constants.IndexLetterBoxRadius)),
  394. ),
  395. child: Center(
  396. child: Text(
  397. _currentLetter,
  398. style: AppStyles.IndexLetterBoxTextStyle,
  399. textScaleFactor: 1.0,
  400. ),
  401. ),
  402. ),
  403. ));
  404. }
  405. return Scaffold(
  406. resizeToAvoidBottomPadding: false,
  407. appBar: AppBar(
  408. backgroundColor: AppColors.NewAppbarBgColor,
  409. title: Text(
  410. I18n.of(context).new_friends,
  411. textScaleFactor: 1.0,
  412. style: TextStyle(color: AppColors.NewAppbarTextColor),
  413. ),
  414. centerTitle: true,
  415. leading: CustomUI.buildCustomLeading(context),
  416. elevation: 1,
  417. actions: <Widget>[
  418. Container(
  419. child: IconButton(
  420. icon: CircleAvatar(
  421. backgroundColor: Constants.GreyBackgroundColor,
  422. radius: 13.75,
  423. child: Padding(
  424. padding: EdgeInsets.only(bottom: 1.5),
  425. child: Icon(
  426. IconData(0xe662,
  427. fontFamily: Constants.IconFontFamily),
  428. color: Constants.BlackTextColor,
  429. size: 21,
  430. ))),
  431. onPressed: () {
  432. Navigator.of(context).push(
  433. new MaterialPageRoute(
  434. builder: (context) {
  435. return NewAddFriendsPage();
  436. },
  437. ),
  438. );
  439. },
  440. ),
  441. ),
  442. ],
  443. bottom: PreferredSize(
  444. preferredSize: Size.fromHeight(49),
  445. child: Container(
  446. alignment: Alignment.center,
  447. margin: EdgeInsets.only(bottom: 14, left: 12.5, right: 12.5),
  448. height: 35,
  449. decoration: BoxDecoration(
  450. color: const Color(0xFFEEEEEE),
  451. borderRadius: BorderRadius.all(Radius.circular(8))),
  452. child: TextField(
  453. keyboardAppearance: Brightness.light,
  454. keyboardType: TextInputType.text,
  455. textInputAction: TextInputAction.search,
  456. controller: _txtCtrl,
  457. maxLines: 1,
  458. style: TextStyle(
  459. textBaseline: TextBaseline.alphabetic, fontSize: 14.5),
  460. autofocus: false,
  461. inputFormatters: [
  462. LengthLimitingTextInputFormatter(50),
  463. ],
  464. decoration: InputDecoration(
  465. hintText: I18n.of(context).search,
  466. hintStyle: TextStyle(fontSize: 14.5),
  467. contentPadding: EdgeInsets.only(
  468. left: 20,
  469. top: (UserData().language == LanguageType.English ||
  470. UserData().language ==
  471. LanguageType.Vietnamese)
  472. ? 3
  473. : 10.5,
  474. bottom: 10.5),
  475. prefixIcon: Icon(
  476. IconData(
  477. 0xe664,
  478. fontFamily: Constants.IconFontFamily,
  479. ),
  480. color: const Color(0xFFA0A0A0),
  481. size: 18,
  482. ),
  483. suffixIcon: Padding(
  484. padding: EdgeInsetsDirectional.only(
  485. start: 2.0, end: _hasdeleteIcon ? 20.0 : 0),
  486. child: _hasdeleteIcon
  487. ? new InkWell(
  488. onTap: (() {
  489. setState(() {
  490. WidgetsBinding.instance
  491. .addPostFrameCallback(
  492. (_) => _txtCtrl.clear());
  493. _hasdeleteIcon = false;
  494. });
  495. }),
  496. child: Icon(
  497. Icons.clear,
  498. size: 18.0,
  499. color: Constants.BlackTextColor,
  500. ))
  501. : new Text('')),
  502. filled: true,
  503. fillColor: Colors.transparent,
  504. border: InputBorder.none,
  505. ),
  506. onChanged: (str) async {
  507. setState(() {
  508. if (str.isEmpty) {
  509. _hasdeleteIcon = false;
  510. } else {
  511. _hasdeleteIcon = true;
  512. searchList = CustomUI().getSearchResult(
  513. str, friendList == null ? [] : friendList);
  514. }
  515. });
  516. },
  517. onEditingComplete: () {}),
  518. )),
  519. ),
  520. body: Stack(
  521. children: _body,
  522. ));
  523. }
  524. }