Реализовать бесконечный список прокрутки в магазине, который обновляет информацию об изменениях в коллекции - PullRequest
0 голосов
/ 05 февраля 2020

Что я пытаюсь добавить sh?

В настоящее время я сталкиваюсь с множеством проблем, связанных с реализацией обновленного бесконечного списка прокрутки в реальном времени с бэкэндом Firestore.

В моем приложении я хочу отображать комментарии (например, на YouTube или других сайтах социальных сетей) для пользователя. Поскольку количество комментариев в коллекции может быть довольно большим, я вижу возможность разбить коллекцию на страницы, получая обновления в режиме реального времени на основе снимков. Поэтому я изначально загружаю x комментариев с возможностью загружать до x дополнительных элементов всякий раз, когда пользователь нажимает кнопку. На изображении ниже x = 3.

enter image description here

Стандартное решение

На основе других вопросов SO I выяснил, что для реализации такого поведения предполагается использовать методы .limit() и .startAfter(). Таким образом, первая страница загружается как:

query = this
    .collection
    .orderBy('date', descending: true)
    .limit(pageSize);
query.snapshots().map((QuerySnapshot snap) {
  lastVisible = snap.documents.last;
  // convert the DocumentSnapshot into model object
});

Все дополнительные страницы загружаются с помощью следующего кода:

query = this.collection
        .orderBy('date', descending: true)
        .startAfterDocument(lastVisible)
        .limit(pageSize);

Кроме того, я хотел бы добавить, что этот код находится в класс репозитория, который используется с шаблоном BLo C, аналогичным коду, показанному в Учебнике Феликса Ангелова по флаттеру Todos . В то время как Феликс использует простой список трепетания, чтобы показать элементы, у меня есть список страниц с комментариями на основе данных, предоставленных их BLoC. Обратите внимание, что каждый BLo C получает доступ к общему хранилищу (части кода хранилища показаны ниже).

Проблема со стандартным решением

С указанным кодом выше я вижу несколько проблем:

  1. Если комментарий вставлен в середину упорядоченной коллекции (как это не важно), добавленный комментарий отображается из-за потока, предоставленного снимком. Однако другой уже существующий комментарий больше не отображается из-за оператора .limit() в запросе. Можно увеличить предел на единицу, но я не уверен, как редактировать запрос снимка. В случае, если редактирование запроса моментального снимка невозможно, можно создать новый (и больший) запрос, но это потребует дополнительных операций чтения.
  2. Аналогично 1. Если комментарий в середине удаляется, снимок вернет список, который больше не содержит удаленный комментарий, однако появляется другой комментарий (который уже покрыт другой страницей). Например, в сценарии, показанном на рисунке выше, загружаются 5 комментариев. Предполагая, что комментарий 3 удален, комментарий 2 будет показан дважды.

Улучшение стандартного решения

Основываясь на этих двух проблемах, рассмотренных выше, я решил, что этого решения недостаточно, и реализовал решение, которое сначала загружается x штук при получении двух «интервальных» документов. Затем создается запрос, который выбирает требуемые элементы в интервале с использованием .startAtDocument() и .endAtDocument(), что исключает оператор .limit().

DocumentSnapshot pageStartDocument;
DocumentSnapshot pageEndDocument;
Future<Stream<List<Comment>>> comments() async {
    // This fetches the first and next Document as initialization
    // (maybe should be implemented in constructor)
    if (pageStartDocument == null) {
      Query query = collection
          .orderBy('date', descending: true)
          .limit(pageSize);
      QuerySnapshot snap = await query.getDocuments();
      pageStartDocument = snap.documents.first;
      pageEndDocument = snap.documents.last;
    } else {
      Query query = collection
          .orderBy('date', descending: true)
          .startAfterDocument(pageEndDocument)
          .limit(pageSize);
      QuerySnapshot snap = await query.getDocuments();
      pageStartDocument = snap.documents.first;
      pageEndDocument = snap.documents.last;
    }

    // This fetches a subcollection of elements from the collection
    // with the tradeof of double the reads
    Query query = this
        .collection
        .orderBy('date', descending: true)
        .startAtDocument(pageStartDocument)
        .endAtDocument(pageEndDocument);

    return query.snapshots().asyncMap((QuerySnapshot snap) async {
      // convert the QuerySnapshot into model objects
    });

Как указано в коде, это решение имеет следующее недостаток:

  1. Поскольку для получения pageStartDocument и pageEndDocument требуется запрос, число операций чтения удваивается, поскольку при создании второго запроса все данные снова считываются. Влияние на производительность можно пренебречь, потому что я считаю, что данные кэшируются, однако иметь вдвое большую стоимость чтения базы данных.

Вопрос:

Поскольку я не только реализуя нумерацию страниц, но и обновления в реальном времени (с добавлением коллекций), оператор .limit() в моем случае не работает.

Как реализовать разбиение на страницы с обновлениями в реальном времени (без двойного чтения)?

Примечания:

Я наблюдал, как Тодд Керпельман пожирает огромного липкого медведя, объясняя нумерацию страниц , но в видео это выглядит не так тривиально (и было отмечено, что компромисс может потребоваться).

Если есть код с моей стороны обязательно скажите так в комментариях.

Для сценария комментариев не имеет смысла вставлять элемент в середину (отсортированной) коллекции. Однако я хотел бы понять, как это должно быть реализовано, если сценарий требует такой возможности.

...