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.
 
 
 
 
 
 

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