Kaynağa Gözat

[fix]修复一些bug

Destiny 11 ay önce
ebeveyn
işleme
58d1f99db5

+ 2 - 2
lib/dialog/loading_dialog.dart

@@ -2,8 +2,8 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
 
 class LoadingDialog {
 
-  static void show() {
-    SmartDialog.showLoading();
+  static void show({int displayTime = 0}) {
+    SmartDialog.showLoading(displayTime: Duration(milliseconds: displayTime));
   }
 
   static void showText(String msg, {bool backDismiss = false}) {

+ 8 - 0
lib/module/contact/backup/controller.dart

@@ -2,6 +2,7 @@ import 'dart:convert';
 import 'dart:io';
 
 import 'package:clean/base/base_controller.dart';
+import 'package:clean/dialog/loading_dialog.dart';
 import 'package:clean/utils/expand.dart';
 import 'package:clean/utils/toast_util.dart';
 import 'package:flutter/cupertino.dart';
@@ -122,6 +123,7 @@ class ContactBackUpController extends BaseController {
 
   // 备份通讯录
   Future<void> backupContacts() async {
+    LoadingDialog.show(displayTime: 100);
     try {
       if (!await FlutterContacts.requestPermission()) {
         throw Exception('通讯录权限被拒绝');
@@ -151,8 +153,14 @@ class ContactBackUpController extends BaseController {
       // 刷新备份文件列表
       loadBackupFiles();
 
+      // LoadingDialog.hide();
+      ToastUtil.show("Successful");
       print('备份成功: ${file.path}');
+
     } catch (e) {
+
+      LoadingDialog.hide();
+      ToastUtil.show("Failed");
       print('备份失败: $e');
     }
   }

+ 1 - 1
lib/module/contact/backup/view.dart

@@ -111,7 +111,7 @@ class ContactBackUpPage extends BasePage<ContactBackUpController> {
                         ),
                         GestureDetector(
                           onTap: () {
-                            // controller.toggleSelectAll();
+                            controller.toggleSelectAll();
                           },
                           child: Text(
                             controller.isAllSelected.value

+ 190 - 41
lib/module/contact/duplicate/controller.dart

@@ -1,5 +1,6 @@
 import 'package:clean/base/base_controller.dart';
 import 'package:clean/data/repositories/user_repository.dart';
+import 'package:clean/dialog/loading_dialog.dart';
 import 'package:clean/module/contact/contact_state.dart';
 import 'package:clean/module/store/store_view.dart';
 import 'package:clean/utils/toast_util.dart';
@@ -8,14 +9,14 @@ import 'package:flutter_contacts/flutter_contacts.dart';
 import 'package:get/get_rx/src/rx_types/rx_types.dart';
 
 class ContactDuplicateController extends BaseController {
-
   // 是否为编辑状态
   RxBool isEdit = false.obs;
 
   // 是否全选
   RxBool isAllSelected = false.obs;
 
-  RxMap<String, List<Contact>> contactsByPhoneNumber = <String, List<Contact>>{}.obs;
+  RxMap<Contact, List<Contact>> contactsByPhoneNumber =
+      <Contact, List<Contact>>{}.obs;
 
   var contactCount = 0;
 
@@ -32,35 +33,41 @@ class ContactDuplicateController extends BaseController {
     loadDuplicateContacts();
   }
 
-  Future<void> loadDuplicateContacts() async {
+// 按共享电话号码分组联系人
+  Map<Contact, List<Contact>> groupContactsBySharedPhones(
+      List<Contact> contacts) {
+    final UnionFind uf = UnionFind();
+    final Map<String, List<Contact>> phoneToContactsMap = {};
 
-    Map<String, List<Contact>> groupedContacts = {};
-
-    // 获取所有联系人
-    List<Contact> contacts = await FlutterContacts.getContacts(
-      withProperties: true,
-      withPhoto: true,
-    );
+    // 遍历所有联系人,建立电话号码与联系人的映射
+    for (var contact in contacts) {
+      for (var phone in contact.phones) {
+        if (!phoneToContactsMap.containsKey(phone.number)) {
+          phoneToContactsMap[phone.number] = [];
+        }
+        phoneToContactsMap[phone.number]!.add(contact);
+      }
+    }
 
-    // 按名字的首字母排序
-    contacts.sort((a, b) => (a.displayName ?? '').compareTo(b.displayName ?? ''));
+    // 合并拥有相同电话号码的联系人
+    for (var phone in phoneToContactsMap.keys) {
+      final contactList = phoneToContactsMap[phone]!;
+      for (var i = 1; i < contactList.length; i++) {
+        uf.union(contactList[0], contactList[i]);
+      }
+    }
 
+    // 整理分组结果
+    final Map<Contact, List<Contact>> groupedContacts = {};
     for (var contact in contacts) {
-      if (contact.phones.isNotEmpty) {
-        for (var phone in contact.phones) {
-          final phoneNumber = phone.number;
-
-          if (phoneNumber.isNotEmpty) {
-            if (!groupedContacts.containsKey(phoneNumber)) {
-              groupedContacts[phoneNumber] = [];
-            }
-            groupedContacts[phoneNumber]!.add(contact);
-          }
-        }
+      final root = uf.find(contact);
+      if (!groupedContacts.containsKey(root)) {
+        groupedContacts[root] = [];
       }
+      groupedContacts[root]!.add(contact);
     }
 
-    Map<String, List<Contact>> tempContacts = {};
+    Map<Contact, List<Contact>> tempContacts = {};
     tempContacts.addAll(groupedContacts);
     for (var key in groupedContacts.keys) {
       if (groupedContacts[key]?.length == 1) {
@@ -68,7 +75,48 @@ class ContactDuplicateController extends BaseController {
       }
     }
 
-    contactsByPhoneNumber.value = tempContacts;
+    return tempContacts;
+  }
+
+  Future<void> loadDuplicateContacts() async {
+    Map<String, List<Contact>> groupedContacts = {};
+
+    // 获取所有联系人
+    List<Contact> contacts = await FlutterContacts.getContacts(
+      withProperties: true,
+      withPhoto: true,
+    );
+
+    // 按名字的首字母排序
+    contacts
+        .sort((a, b) => (a.displayName ?? '').compareTo(b.displayName ?? ''));
+
+    final duplicateContact = groupContactsBySharedPhones(contacts);
+
+    // for (var contact in contacts) {
+    //   if (contact.phones.isNotEmpty) {
+    //     for (var phone in contact.phones) {
+    //       final phoneNumber = phone.number;
+    //
+    //       if (phoneNumber.isNotEmpty) {
+    //         if (!groupedContacts.containsKey(phoneNumber)) {
+    //           groupedContacts[phoneNumber] = [];
+    //         }
+    //         groupedContacts[phoneNumber]!.add(contact);
+    //       }
+    //     }
+    //   }
+    // }
+    //
+    // Map<String, List<Contact>> tempContacts = {};
+    // tempContacts.addAll(groupedContacts);
+    // for (var key in groupedContacts.keys) {
+    //   if (groupedContacts[key]?.length == 1) {
+    //     tempContacts.remove(key);
+    //   }
+    // }
+
+    contactsByPhoneNumber.value = duplicateContact;
 
     contactCount = 0;
     for (var key in contactsByPhoneNumber.keys) {
@@ -78,12 +126,11 @@ class ContactDuplicateController extends BaseController {
     }
   }
 
-
   // 选择/取消选择联系人
-  void toggleSelectContact(String phoneNumber) {
+  void toggleSelectContact(Contact contact) {
     // final asset = ContactState.contactList.firstWhere((contact) => contact.id == selectContact.id);
 
-    final contacts = contactsByPhoneNumber[phoneNumber];
+    final contacts = contactsByPhoneNumber[contact];
     if (contacts != null) {
       for (var contact in contacts) {
         if (selectedContacts.contains(contact.id)) {
@@ -121,38 +168,116 @@ class ContactDuplicateController extends BaseController {
     isAllSelected.value = false;
   }
 
+  // 合并联系人
   Future<void> mergeBtnClick(List<Contact> contacts) async {
     if (!userRepository.isVip()) {
       StorePage.start();
       return;
     }
 
-    Contact? contactWithMostInfo;
-    int maxInfoCount = 0;
+    LoadingDialog.show(displayTime: 100);
+
+    Contact newContact = contacts.first;
 
     for (var contact in contacts) {
-      final infoCount = _countContactInfo(contact);
-      if (infoCount > maxInfoCount) {
-        maxInfoCount = infoCount;
-        contactWithMostInfo = contact;
+      if (contact != newContact) {
+        for (var phone in contact.phones) {
+          var canAdd = true;
+          for (var newPhone in newContact.phones) {
+            if (phone.number == newPhone.number) {
+              canAdd = false;
+            }
+          }
+          if (canAdd) {
+            newContact.phones.add(phone);
+          }
+        }
+        if (newContact.thumbnail == null) {
+          if (contact.thumbnail != null) {
+            newContact.thumbnail = contact.thumbnail;
+          }
+        }
+        if (newContact.photo == null) {
+          if (contact.photo != null) {
+            newContact.photo = contact.photo;
+          }
+        }
+        if (newContact.name.first.isEmpty) {
+          if (contact.name.first.isNotEmpty) {
+            newContact.name.first = contact.name.first;
+          }
+        }
+        if (newContact.name.last.isEmpty) {
+          if (contact.name.last.isNotEmpty) {
+            newContact.name.last = contact.name.last;
+          }
+        }
+        if (newContact.name.last.isEmpty) {
+          if (contact.name.last.isNotEmpty) {
+            newContact.name.last = contact.name.last;
+          }
+        }
+        if (contact.emails.isNotEmpty) {
+          for (var email in contact.emails) {
+            newContact.emails.add(email);
+          }
+        }
+        if (contact.addresses.isNotEmpty) {
+          for (var address in contact.addresses) {
+            newContact.addresses.add(address);
+          }
+        }
+        if (contact.organizations.isNotEmpty) {
+          for (var organization in contact.organizations) {
+            newContact.organizations.add(organization);
+          }
+        }
+        if (contact.websites.isNotEmpty) {
+          for (var website in contact.websites) {
+            newContact.websites.add(website);
+          }
+        }
+        if (contact.socialMedias.isNotEmpty) {
+          for (var socialMedia in contact.socialMedias) {
+            newContact.socialMedias.add(socialMedia);
+          }
+        }
+        if (contact.events.isNotEmpty) {
+          for (var event in contact.events) {
+            newContact.events.add(event);
+          }
+        }
+        if (contact.notes.isNotEmpty) {
+          for (var note in contact.notes) {
+            newContact.notes.add(note);
+          }
+        }
+        if (contact.groups.isNotEmpty) {
+          for (var group in contact.groups) {
+            newContact.groups.add(group);
+          }
+        }
       }
     }
 
     for (var contact in contacts) {
-      if (contact.id != contactWithMostInfo?.id) {
-        await contact.delete();
-      }
+      await contact.delete();
     }
 
-    ToastUtil.show("Successful");
+    newContact.id = "";
+    await FlutterContacts.insertContact(newContact);
+
     exitEditMode();
     await loadDuplicateContacts();
+
+    ToastUtil.show("Merge Successful");
   }
 
   void deleteBtnClick() {
     // 获取要删除的资产
-    final contactToDelete =
-    ContactState.contactList.where((contact) => selectedContacts.contains(contact.id)).toList();
+    final contactToDelete = ContactState.contactList
+        .where((contact) => selectedContacts.contains(contact.id))
+        .toList();
 
     for (var contact in contactToDelete) {
       contact.delete();
@@ -216,4 +341,28 @@ class ContactDuplicateController extends BaseController {
 
     return count;
   }
-}
+}
+
+// 并查集类
+class UnionFind {
+  final Map<Contact, Contact> _parent = {};
+
+  // 查找根节点
+  Contact find(Contact contact) {
+    if (!_parent.containsKey(contact)) {
+      _parent[contact] = contact;
+    } else if (_parent[contact] != contact) {
+      _parent[contact] = find(_parent[contact]!); // 路径压缩
+    }
+    return _parent[contact]!;
+  }
+
+  // 合并两个集合
+  void union(Contact a, Contact b) {
+    final rootA = find(a);
+    final rootB = find(b);
+    if (rootA != rootB) {
+      _parent[rootB] = rootA;
+    }
+  }
+}

+ 6 - 3
lib/module/contact/duplicate/view.dart

@@ -169,8 +169,11 @@ class ContactDuplicatePage extends BasePage<ContactDuplicateController> {
                       controller.contactsByPhoneNumber.keys.toList()[index];
                   final contact =
                       controller.contactsByPhoneNumber[phoneNumber]?.first;
-                  final contacts =
-                      controller.contactsByPhoneNumber[phoneNumber];
+                  List<Contact> contacts = [];
+                  if (controller.contactsByPhoneNumber[phoneNumber] != null) {
+                    contacts = controller.contactsByPhoneNumber[phoneNumber]!;
+                  }
+
                   return Column(
                     children: [
                       SizedBox(
@@ -248,7 +251,7 @@ class ContactDuplicatePage extends BasePage<ContactDuplicateController> {
                       SizedBox(
                         height: 12.h,
                       ),
-                      ...controller.contactsByPhoneNumber[phoneNumber]!
+                      ...contacts
                           .asMap()
                           .entries
                           .map((entry) {

+ 1 - 1
lib/module/home/home_view.dart

@@ -72,7 +72,7 @@ class HomePage extends BaseView<HomeController> {
           ),
           GestureDetector(
             onTap: () {
-              Get.toNamed(RoutePath.discount);
+              Get.toNamed(RoutePath.store);
             },
             child: Assets.images.iconHomeTitleVip
                 .image(width: 60.8.w, height: 20.h),

+ 1 - 1
pubspec.yaml

@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 # In Windows, build-name is used as the major, minor, and patch parts
 # of the product and file versions while build-number is used as the build suffix.
-version: 1.0.0+15
+version: 1.1.0+16
 
 environment:
   sdk: ^3.6.0