发生 Firestore 事件时更新 ObservableCollection

时间:2021-02-04 20:31:23

标签: c# listview xamarin.forms mvvm google-cloud-firestore

我正在尝试使用 Xamarin Forms 创建跨平台应用程序。我决定使用 Firestore 作为我应用的数据库。

我正在尝试将聊天功能添加到我的应用程序中,但我正在努力实现实时收听功能。我已经创建了一个 ViewModel 类,其中包含一个由 UI 中的 ListView 使用的 ObservableCollection 聊天。

public class ChatsVM
{
    public ObservableCollection<Chat> Chats { get; set; }

    public ChatsVM()
    {
        Chats = new ObservableCollection<Chat>();
        ReadMessages();
    }

    public async void ReadMessages()
    {
        User currentUser = await DependencyService.Get<IFirebaseAuthenticationService>().GetCurrentUserProfileAsync();
        IList<Chat> chatList = await DependencyService.Get<IChatService>().GetChatsForUserAndListenAsync(currentUser.IsLandlord, currentUser.Id);

        foreach (var chat in chatList)
        {
            Chats.Add(chat);
        }
    }
}

我还创建了从 Firestore 获取数据的服务。在服务方面(示例显示 Android 服务)我使用标准列表来保存聊天对象

List<Chat> Chats;
bool hasReadChats;

GetChatsForUserAndListenAsync 方法向我的查询添加了一个快照侦听器,并将事件传递给 OnEvent 方法。

public async Task<IList<Chat>> GetChatsForUserAndListenAsync(bool isLandlord, string userId)
    {
        string fieldToSearch;

        if (isLandlord)
        {
            fieldToSearch = "landlordId";
        }
        else
        {
            fieldToSearch = "tenantId";
        }

        try
        {
            // Reset the hasReadChats value.
            hasReadChats = false;
            CollectionReference collectionReference = FirebaseFirestore.Instance.Collection(Constants.Chats);
            // Get all documents in the collection and attach a OnCompleteListener to
            // provide a callback function.
            collectionReference.WhereEqualTo(fieldToSearch, userId).AddSnapshotListener(this);

            // Wait until the callback has finished reading and formatting the returned
            // documents.
            for (int i = 0; i < 10; i++)
            {
                await System.Threading.Tasks.Task.Delay(100);
                // If the callback has finished, continue rest of the execution.
                if (hasReadChats)
                {
                    break;
                }
            }

            return Chats;
        }
        catch (FirebaseFirestoreException ex)
        {
            throw new Exception(ex.Message);
        }
        catch (Exception)
        {
            throw new Exception("An unknown error occurred. Please try again.");
        }
    }

public void OnEvent(Java.Lang.Object value, FirebaseFirestoreException error)
    {
        var snapshot = (QuerySnapshot) value;

        if (!snapshot.IsEmpty)
        {
            var documents = snapshot.Documents;
            Chats.Clear();

            foreach (var document in documents)
            {
                Chat chat = new Chat
                {
                    Id = document.Id,
                    LandlordId = document.Get("landlordId") != null ? document.Get("landlordId").ToString() : "",
                    TenantId = document.Get("tenantId") != null ? document.Get("tenantId").ToString() : ""
                };
                //JavaList messageList = (JavaList) document.Get("messages");
                //List<Message> messages = new List<Message>();

                // chat.Messages = messages;

                Chats.Add(chat);
            }
            hasReadChats = true;
        }
    }

我如何将事件处理程序对此列表所做的任何更改传播到我的 VM 类中的 ObservableCollection?

1 个答案:

答案 0 :(得分:1)

感谢 Jason 提出这个答案。

所以我修改了事件处理程序以在 Firestore 中查找文档更改。根据这些变化,服务将使用 MessagingCenter 发送不同的消息。

public async void OnEvent(Java.Lang.Object value, FirebaseFirestoreException error)
    {
        var snapshot = (QuerySnapshot) value;

        if (!snapshot.IsEmpty)
        {
            var documentChanges = snapshot.DocumentChanges;
            IFirebaseAuthenticationService firebaseAuthenticationService = DependencyService.Get<IFirebaseAuthenticationService>();
            Chats.Clear();
            
            foreach (var documentChange in documentChanges)
            {
                var document = documentChange.Document;
        
                string memberOne = document.Get(Constants.MemberOne) != null ? document.Get(Constants.MemberOne).ToString() : "";
                string memberTwo = document.Get(Constants.MemberTwo) != null ? document.Get(Constants.MemberTwo).ToString() : "";
                
                Chat chat = new Chat
                {
                    Id = document.Id,
                    MemberOne = memberOne,
                    MemberTwo = memberTwo,
                };

                var documentType = documentChange.GetType().ToString();

                switch (documentType)
                {
                    case Constants.Added:
                        MessagingCenter.Send<IChatService, Chat>(this, Constants.Added, chat);
                        break;
                    case Constants.Modified:
                        MessagingCenter.Send<IChatService, Chat>(this, Constants.Modified, chat);
                        break;
                    case Constants.Removed:
                        MessagingCenter.Send<IChatService, Chat>(this, Constants.Removed, chat);
                        break;
                    default:
                        break;
                }
               
                Chats.Add(chat);
            }
            hasReadChats = true;
        }
    }

在 VM 的构造函数中,我订阅了这些消息的侦听器,并相应地更新了 ObservableCollection。

public class ChatsVM
{
    public ObservableCollection<Chat> Chats { get; set; }

    public ChatsVM()
    {
        Chats = new ObservableCollection<Chat>();
        ReadChats();
    }

    public async void ReadChats()
    {
        IFirebaseAuthenticationService firebaseAuthenticationService = DependencyService.Get<IFirebaseAuthenticationService>();

        MessagingCenter.Subscribe<IChatService, Chat>(this, Constants.Added, (sender, args) =>
        {
            Chat chat = args;
            Chats.Add(chat);
        });

        MessagingCenter.Subscribe<IChatService, Chat>(this, Constants.Modified, (sender, args) =>
        {
            Chat updatedChat = args;
            Chats.Any(chat =>
            {
                if (chat.Id.Equals(updatedChat.Id))
                {
                    int oldIndex = Chats.IndexOf(chat);
                    Chats.Move(oldIndex, 0);
                    return true;
                }
                return false;
            });
        });

        MessagingCenter.Subscribe<IChatService, Chat>(this, Constants.Removed, (sender, args) =>
        {
            Chat removedChat = args;
            Chats.Any(chat =>
            {
                if (chat.Id.Equals(removedChat.Id))
                {
                    Chats.Remove(chat);
                    return true;
                }
                return false;
            });
        });
        
        User currentUser = await firebaseAuthenticationService.GetCurrentUserProfileAsync();
        await DependencyService.Get<IChatService>().GetChatsForUserAndListenAsync(currentUser.Id);
    }
}