Browse Source

add friend

ignalxy 4 years ago
parent
commit
d1d93484a8

+ 2 - 2
lib/common/api.dart

@@ -163,7 +163,7 @@ class Api {
   }
 
   Future<bool> addFriend(String username) async {
-    debug("add friend begin");
+    debug("add friend begin: add $username");
     bool _result = true;
     try {
       await dio.post('/friends/add/', data: FormData.fromMap({"username": username}));
@@ -337,7 +337,7 @@ class Api {
     return _result;
   }
 
- connectWebSocket(String username) async {
+ void connectWebSocket() {
     debug("delete friend begin");
     final channel=IOWebSocketChannel.connect(WebSocketUrl);
     channel.stream.listen((message) {

+ 1 - 0
lib/l10n/localization_intl.dart

@@ -29,6 +29,7 @@ class GmLocalizations {
   String get search => Intl.message('Search', name: 'search');
   String get searchUser => Intl.message('Find User', name: 'find user');
   String get addFriendFailed => Intl.message('Add friend failed', name: 'add friend failed');
+  String get friendRequest => Intl.message('Friends Request', name: 'friend request');
 }
 
 //Locale代理类

+ 3 - 1
lib/main.dart

@@ -11,6 +11,7 @@ import 'l10n/localization_intl.dart';
 import 'presenter/locale.dart';
 import 'presenter/theme.dart';
 import 'presenter/login.dart';
+import 'view/friend_request.dart';
 import 'view/language.dart';
 import 'view/login.dart';
 import 'view/theme.dart';
@@ -71,7 +72,8 @@ class MyApp extends StatelessWidget {
               "themes": (context) => ThemeRoute(),
               "language": (context) => LanguageRoute(),
               "add friend": (context) => AddFriendView(),
-              "user chat": (context) => UserChatView()
+              "user chat": (context) => UserChatView(),
+              "friend request": (context) => FriendRequestListView()
             },
           );
         },

+ 24 - 0
lib/model/contact_info.dart

@@ -0,0 +1,24 @@
+import 'package:e2ee_chat/azlistview/azlistview.dart';
+import 'package:flutter/material.dart';
+import 'package:lpinyin/lpinyin.dart';
+
+class ContactInfo extends ISuspensionBean {
+  ContactInfo({required this.name, required this.tag, this.bgColor, this.iconData});
+
+  final String name;
+  final String tag;
+  final Color? bgColor;
+  final IconData? iconData;
+
+  @override
+  String getSuspensionTag() => tag;
+
+  static String getTag(String name) {
+    String pinyin = PinyinHelper.getPinyinE(name);
+    String tag = pinyin.substring(0, 1).toUpperCase();
+    if (!RegExp("[A-Z]").hasMatch(tag)) {
+      tag = "#";
+    }
+    return tag;
+  }
+}

+ 3 - 23
lib/presenter/contact_list.dart

@@ -1,5 +1,6 @@
 import 'package:e2ee_chat/azlistview/azlistview.dart';
 import 'package:e2ee_chat/common/global.dart';
+import 'package:e2ee_chat/model/contact_info.dart';
 import 'package:e2ee_chat/model/user.dart';
 import 'package:e2ee_chat/presenter/contact.dart';
 import 'package:e2ee_chat/presenter/profile.dart';
@@ -11,22 +12,13 @@ import 'package:e2ee_chat/common/api.dart';
 import '../objectbox.g.dart';
 import 'login.dart';
 
-String _getTag(String name) {
-  String pinyin = PinyinHelper.getPinyinE(name);
-  String tag = pinyin.substring(0, 1).toUpperCase();
-  if (!RegExp("[A-Z]").hasMatch(tag)) {
-    tag = "#";
-  }
-  return tag;
-}
-
 class ContactListPresenter extends ChangeNotifier {
   List<ContactInfo> get contacts {
     final User? user = LoginPresenter().user;
     final list = <ContactInfo>[];
     try {
       for (var i in user!.friends) {
-        list.add(ContactInfo(name: i.username, tag: _getTag(i.username)));
+        list.add(ContactInfo(name: i.username, tag: ContactInfo.getTag(i.username)));
       }
     } catch (e) {
       debug(e);
@@ -60,16 +52,4 @@ class ContactListPresenter extends ChangeNotifier {
     debug('contact list presenter fresh contacts end');
     return _result;
   }
-}
-
-class ContactInfo extends ISuspensionBean {
-  ContactInfo({required this.name, required this.tag, this.bgColor, this.iconData});
-
-  final String name;
-  final String tag;
-  final Color? bgColor;
-  final IconData? iconData;
-
-  @override
-  String getSuspensionTag() => tag;
-}
+}

+ 76 - 0
lib/presenter/friend_request.dart

@@ -0,0 +1,76 @@
+import 'dart:async';
+import 'dart:convert';
+
+import 'package:dash_chat/dash_chat.dart';
+import 'package:e2ee_chat/azlistview/azlistview.dart';
+import 'package:e2ee_chat/common/api.dart';
+import 'package:e2ee_chat/common/global.dart';
+import 'package:e2ee_chat/model/contact_info.dart';
+import 'package:e2ee_chat/model/friendship.dart';
+import 'package:e2ee_chat/model/message.dart';
+import 'package:e2ee_chat/model/user.dart';
+import 'package:e2ee_chat/objectbox.g.dart';
+import 'package:e2ee_chat/presenter/login.dart';
+import 'package:flutter/material.dart';
+import 'package:objectbox/objectbox.dart';
+import 'package:rxdart/rxdart.dart';
+
+import 'chat_list.dart';
+
+class FriendRequestPresenter extends ChatListPresenter {
+  FriendRequestPresenter({this.from}) {
+    _listener = _listenFriendRequest();
+  }
+
+  User? get user => LoginPresenter().user;
+  User? from;
+
+  List<ContactInfo> requests = [];
+
+  bool _disposed = false;
+  Future<void>? _listener;
+
+
+  @override
+  void dispose() {
+    _disposed = true;
+    _listener?.then((e) => super.dispose()) ?? super.dispose();
+  }
+
+
+  Future<void> _listenFriendRequest() async {
+    while (!_disposed) {
+      if (from != null) {
+          await flushFriendRequest();
+      }
+      await Future.delayed(Duration(seconds: 1));
+    }
+  }
+
+  Future<void> acceptFriend(String username) async {
+    bool _result = await Api().acceptFriend(username);
+    if (_result) {
+      requests.removeWhere((r) => r.name == username);
+      notifyListeners();
+    }
+  }
+
+  Future<void> flushFriendRequest() async {
+    final _user = user;
+    if (_user != null) {
+      List<String>? _list = await Api().friendRequest();
+      if (_list != null && !requests.toSet().containsAll(_list)) {
+        // TODO: 如果列表一样则不刷新
+        debug("flush friend request: $_list");
+        requests.clear();
+        for (final i in _list) {
+          requests.add(ContactInfo(name: i, tag: ContactInfo.getTag(i)));
+        }
+        SuspensionUtil.sortListBySuspensionTag(requests);
+        SuspensionUtil.setShowSuspensionStatus(requests);
+        notifyListeners();
+      }
+    }
+    // messages.forEach((message) => list.add(message.chatMessage));
+  }
+}

+ 3 - 2
lib/presenter/user_chat.dart

@@ -17,7 +17,7 @@ import 'chat_list.dart';
 
 class UserChatPresenter extends ChatListPresenter {
   UserChatPresenter({this.to}) {
-    _listenUserChat();
+    _listener = _listenUserChat();
   }
 
   User? get from => LoginPresenter().user;
@@ -33,12 +33,13 @@ class UserChatPresenter extends ChatListPresenter {
   List<ChatMessage> get chatMessages => _chatMessages ?? [];
 
   bool _disposed = false;
+  Future<void>? _listener;
 
 
   @override
   void dispose() {
     _disposed = true;
-    super.dispose();
+    _listener?.then((e) => super.dispose()) ?? super.dispose();
   }
 
 

+ 11 - 4
lib/view/contact_list.dart

@@ -1,5 +1,7 @@
 import 'package:e2ee_chat/azlistview/azlistview.dart';
+import 'package:e2ee_chat/common/api.dart';
 import 'package:e2ee_chat/common/global.dart';
+import 'package:e2ee_chat/model/contact_info.dart';
 import 'package:e2ee_chat/model/message.dart';
 import 'package:e2ee_chat/model/user.dart';
 import 'package:e2ee_chat/presenter/contact_list.dart';
@@ -17,16 +19,17 @@ class ContactListView extends StatefulWidget {
 }
 
 class _ContactListViewState extends State<ContactListView> {
+  final presenter = ContactListPresenter();
   @override
   void initState() {
     super.initState();
-    ContactListPresenter().freshContacts().then((_) => setState(() {}));
+    presenter.freshContacts().then((_) => setState(() {}));
   }
 
   @override
   Widget build(BuildContext context) {
     return ChangeNotifierProvider<ContactListPresenter>(
-      create: (context) => ContactListPresenter(),
+      create: (context) => presenter,
       child: Builder(
         builder: (context) {
           final provider = Provider.of<ContactListPresenter>(context);
@@ -38,13 +41,17 @@ class _ContactListViewState extends State<ContactListView> {
                 itemBuilder: (context, index) {
                   ContactInfo info = contacts[index];
                   return Utils.getWeChatListItem(context, info, defHeaderBgColor: Color(0xFFE5E5E5), onTap: () {
-                    // TODO: fuck
-                    Navigator.of(context).pushNamed("user chat", arguments: info.name);
+                    if (info.getSuspensionTag() == '↑') {
+                      Navigator.of(context).pushNamed("friend request");
+                    } else {
+                      Navigator.of(context).pushNamed("user chat", arguments: info.name);
+                    }
                   });
                 },
                 physics: BouncingScrollPhysics(),
                 susItemBuilder: (BuildContext context, int index) {
                   ContactInfo model = contacts[index];
+                  debug('sus item build contact info: name ${model.name} tag ${model.getSuspensionTag()}');
                   if ('↑' == model.getSuspensionTag()) {
                     return Container();
                   }

+ 114 - 0
lib/view/friend_request.dart

@@ -0,0 +1,114 @@
+import 'package:e2ee_chat/azlistview/azlistview.dart';
+import 'package:e2ee_chat/common/api.dart';
+import 'package:e2ee_chat/common/global.dart';
+import 'package:e2ee_chat/l10n/localization_intl.dart';
+import 'package:e2ee_chat/model/contact_info.dart';
+import 'package:e2ee_chat/model/message.dart';
+import 'package:e2ee_chat/model/user.dart';
+import 'package:e2ee_chat/presenter/contact_list.dart';
+import 'package:e2ee_chat/presenter/chat_list.dart';
+import 'package:e2ee_chat/presenter/friend_request.dart';
+import 'package:e2ee_chat/presenter/login.dart';
+import 'package:e2ee_chat/presenter/user_chat.dart';
+import 'package:e2ee_chat/view/user_chat.dart';
+import 'package:e2ee_chat/widgets/utils.dart';
+import 'package:flutter/material.dart';
+import 'package:provider/provider.dart';
+
+
+class FriendRequestListView extends StatefulWidget {
+  @override
+  _FriendRequestListViewState createState() => _FriendRequestListViewState();
+}
+
+class _FriendRequestListViewState extends State<FriendRequestListView> {
+  final presenter = FriendRequestPresenter();
+  String _choice = 'Nothing';
+
+  @override
+  void initState() {
+    super.initState();
+    presenter.flushFriendRequest().then((_) => setState(() {}));
+  }
+
+  @override
+  Widget build(BuildContext context) {
+    return ChangeNotifierProvider<FriendRequestPresenter>(
+        create: (context) => presenter,
+        child: Scaffold(
+          appBar: AppBar(
+            title: Text(GmLocalizations.of(context).friendRequest),
+          ),
+          body: Builder(
+            builder: (context) {
+              final provider = Provider.of<FriendRequestPresenter>(context);
+              final requests = provider.requests;
+              return RefreshIndicator(
+                  child: AzListView(
+                    data: requests,
+                    itemCount: requests.length,
+                    itemBuilder: (context, index) {
+                      ContactInfo info = requests[index];
+                      return Utils.getWeChatListItem(context, info, defHeaderBgColor: Color(0xFFE5E5E5), onTap: () async {
+                        //TODO: 弹窗 接受 拒绝
+                        await showDialog(
+                          context: context,
+                          barrierDismissible: false,//// user must tap button!
+                          builder: (BuildContext context) {
+                            return AlertDialog(
+                              title: Text('Confirm'),
+                              content: Text('Accept?'),
+                              actions: <Widget>[
+                                TextButton(
+                                  child: Text('No'),
+                                  onPressed: () {
+                                    Navigator.pop(context);
+                                  },
+                                ),
+                                TextButton(
+                                  child: Text('Yes'),
+                                  onPressed: () {
+                                    provider.acceptFriend(info.name);
+                                    Navigator.pop(context);
+                                  },
+                                ),
+                              ],
+                            );
+                          },
+                        );
+                      });
+                    },
+                    physics: BouncingScrollPhysics(),
+                    susItemBuilder: (BuildContext context, int index) {
+                      ContactInfo model = requests[index];
+                      debug('sus item build contact info: name ${model.name} tag ${model.getSuspensionTag()}');
+                      if ('↑' == model.getSuspensionTag()) {
+                        return Container();
+                      }
+                      return Utils.getSusItem(context, model.getSuspensionTag());
+                    },
+                    indexBarData: ['↑', '☆', ...kIndexBarData],
+                    indexBarOptions: IndexBarOptions(
+                      needRebuild: true,
+                      ignoreDragCancel: true,
+                      downTextStyle: TextStyle(fontSize: 12, color: Colors.white),
+                      downItemDecoration: BoxDecoration(shape: BoxShape.circle, color: Colors.green),
+                      indexHintWidth: 120 / 2,
+                      indexHintHeight: 100 / 2,
+                      indexHintDecoration: BoxDecoration(
+                        image: DecorationImage(
+                          image: AssetImage(Utils.getImgPath('ic_index_bar_bubble_gray')),
+                          fit: BoxFit.contain,
+                        ),
+                      ),
+                      indexHintAlignment: Alignment.centerRight,
+                      indexHintChildAlignment: Alignment(-0.25, 0.0),
+                      indexHintOffset: Offset(-20, 0),
+                    ),
+                  ),
+                  onRefresh: () async {});
+            },
+          ),
+        ));
+  }
+}

+ 1 - 1
lib/view/home.dart

@@ -3,7 +3,7 @@ import 'package:e2ee_chat/l10n/localization_intl.dart';
 import 'package:e2ee_chat/common/api.dart';
 import 'package:e2ee_chat/presenter/theme.dart';
 import 'package:e2ee_chat/widgets/empty.dart';
-import 'package:e2ee_chat/widgets/mydrawer.dart';
+import 'package:e2ee_chat/view/mydrawer.dart';
 import 'package:flutter/material.dart';
 import 'package:provider/provider.dart';
 

+ 0 - 0
lib/widgets/mydrawer.dart → lib/view/mydrawer.dart


+ 1 - 0
lib/widgets/utils.dart

@@ -1,3 +1,4 @@
+import 'package:e2ee_chat/model/contact_info.dart';
 import 'package:e2ee_chat/presenter/contact_list.dart';
 import 'package:flutter/material.dart';
 import 'package:common_utils/common_utils.dart';