Как я могу работать с 2 потоками, когда поток два зависит от потока один? - PullRequest
0 голосов
/ 06 мая 2020

У меня 2 потока

  Stream<List<GroupModel>> groupStream() {
    final CollectionReference groupCollection = fireStore.collection('groups');

    return groupCollection
        .where('members.${user.id}', isEqualTo: true)
        .snapshots()
        .map((snapshot) => snapshot.documents.map((document) => GroupModel.fromFirestore(document)).toList());
  }

  Stream<List<GroupSubscriptionModel>> groupSubscriptionsStream(List<GroupModel> groups) {
    final chunks = chunkSizeCollection(groups, 10);
    List<Stream<List<GroupSubscriptionModel>>> streams = List<Stream<List<GroupSubscriptionModel>>>();
    chunks.forEach((chunck) => streams.add(fireStore
        .collection('users/${userID}/userGroupSubscriptions')
        .where(FieldPath.documentId, whereIn: chunck)
        .snapshots()
        .map((snapshot) =>
            snapshot.documents.map((document) => GroupSubscriptionModel.fromFirestore(document)).toList())));
    return ZipStream(streams, (value) => value.last);
  }

Я хочу получить все группы, а затем от пользователя получить подписки, которые в основном говорят, подписаны ли пользователи на группу или нет. Затем в моем пользовательском интерфейсе я переключаю значок в зависимости от того, подписан пользователь или нет.

Моя проблема в том, что мой groupSubscriptionsStream зависит от идентификаторов из групп, чтобы получить соответствующий документ. Потому что я не могу просто транслировать все документы в коллекции. Если бы я мог, у меня не было бы никаких проблем.

Я использую blo c с поставщиком, чтобы предоставить мои контроллеры потока моему виджету. Но моему StreamSubscriptionsBloc нужен List<GroupModel>, чтобы добавить поток к его контроллеру

class GroupSubscriptionsBloc{

  final StreamController<List<GroupSubscriptionModel>> _controller = StreamController<List<GroupSubscriptionModel>>();
  Stream<List<GroupSubscriptionModel>> get stream => _controller.stream;

  GroupSubscriptionsBloc(DatabaseService database, List<GroupModel> groups)
  {
    _controller.addStream(database.groupSubscriptionsStream(groups));
  }

  void dispose(){
    _controller.close();
  }
}

Итак, в виджете у меня есть метод stati c, который предоставляет два blo c к виджету

  static Widget create(BuildContext context) {
    final database = Provider.of<DatabaseService>(context);
    return MultiProvider(
      providers: [
        Provider(
          create: (_) => GroupBloc(database),
          dispose: (BuildContext context, GroupBloc bloc) => bloc.dispose(),
        ),
        Provider(
          create: (_) => GroupSubscriptionsBloc(database, null),
          dispose: (BuildContext context, GroupSubscriptionsBloc bloc) => bloc.dispose(),
        ),
      ],
      child: TheGroupOverviewPage(),
    );
  }

Но, как вы можете видеть, в настоящее время я передаю null подпискам blo c, потому что у меня нет групп. Поэтому я не могу добавить поток к контроллеру.

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

Итак, вопрос в том, как мне получить это List<GroupModel>?

Дело в том, что мне даже не понадобились бы два потока, если бы я мог каким-то образом объединить два потока и сопоставить их с моим GroupModel, чтобы я переключил isSubscribed на true.

class GroupModel
{
  final String _id;
  final String _displayName;
  final Map<String, bool> _members;
  final Map<String, bool> _admins;
  bool isSubscribed = false;

  String get id => _id;
  String get displayName => _displayName;
  Map<String, bool> get members => _members;
  Map<String, bool> get admins => _admins;

  GroupModel._internal(this._id, this._displayName, this._members, this._admins);

  factory GroupModel.fromFirestore(DocumentSnapshot document)
  {
    return GroupModel._internal(
      document.documentID ?? '',
      document['displayName'] ?? '',
      document['members'] != null ? Map.from(document['members']) :  Map<String,bool>(),
      document['admins'] != null ? Map.from(document['admins']) : Map<String,bool>(),
    );
  }
}

Я знаю, что вы можете комбинировать потоки, но в этом случае второй поток зависит от первого потока. Так это вообще вариант? Это имело бы наибольший смысл, потому что он выводил бы List<GroupModel>, где isSubscribed устанавливается вторым потоком. И я могу сохранить все групповые группы, связанные в одном потоке.

Вот как мне сейчас нужно создавать потоки в TheGroupOverview виджете

    body: StreamBuilder(
      initialData: List<GroupModel>(),
      stream: groupBloc.groupStream,
      builder: (BuildContext context, AsyncSnapshot<List<GroupModel>> groupsSnapshot) {
        return ConditionalWidget(
          condition: groupsSnapshot.connectionState == ConnectionState.active,
          widgetTrue: StreamBuilder(
            initialData: List<GroupSubscriptionModel>(),
            stream: groupSubscriptionsBloc.stream,
            builder: (BuildContext context, AsyncSnapshot<List<GroupSubscriptionModel>> subscriptionSnapshot) {

              groupsSnapshot.data?.forEach((group) => group.isSubscribed =
                  subscriptionSnapshot.data?.any((subscription) => subscription.belongsTo(group)));

              return ConditionalWidget(
                condition: subscriptionSnapshot.connectionState == ConnectionState.active,
                widgetTrue: Builder(
                  builder: (BuildContext context) {
                    return GroupList(
                      groupsSnapshot.data,
                      padding: const EdgeInsets.only(top: 25.0, left: 20.0, right: 20.0),
                      onNotificationIconTap: _onGroupNotificationIconTap,
                    );
                  },
                ),
                widgetFalse: PlatformCircularProgressIndicator(),
              );
            },
          ),
          widgetFalse: PlatformCircularProgressIndicator(),
        );
      },
    ),

Edit - WIP

    return groupCollection
        .where('members.${user.id}', isEqualTo: true)
        .snapshots()
        .flatMap((groups) => _groupSubscriptionsStream(groups) , //here should merging happen?));
  }

  Stream<QuerySnapshot> _groupSubscriptionsStream(QuerySnapshot groups) {
    final chunks = chunkSizeCollection(groups.documents.map((group) => group.documentID).toList(), 10);
    List<Stream<QuerySnapshot>> streams = List<Stream<QuerySnapshot>>();
    chunks.forEach((chunck) => streams.add(fireStore
        .collection(APIRoutes.userSubscriptions(user.id))
        .where(FieldPath.documentId, whereIn: chunck)
        .snapshots()));
    return ZipStream(streams, (value) => value.last);

1 Ответ

0 голосов
/ 06 мая 2020

Я закончил тем, что изменил свой документ firestore, чтобы иметь такой запрос

    return _groupSubscriptionsStream().switchMap((subscriptions) => groupCollection
        .where('members.${user.id}', isEqualTo: true)
        .snapshots()
        .map((snapshot) => snapshot.documents
            .map((document) => GroupModel.fromFirestore(document).checkSubscriptions(subscriptions))));
  }

  Stream<List<GroupSubscriptionModel>> _groupSubscriptionsStream() {
    return fireStore
        .collection(APIRoutes.userSubscriptions(user.id))
        .where('isSubscribed', isEqualTo: true)
        .snapshots()
        .map((snapshot) =>
            snapshot.documents.map((document) => GroupSubscriptionModel.fromFirestore(document)).toList());
  }

Итак, теперь у меня есть поле isSubscribed, которое всегда верно для запроса. Это действительно отлично работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...