Hibok
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 
 
 
 

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