Skip to content

Commit 98b19df

Browse files
Merge branch 'feat/database_optimization_3528' into feat/campaign_total_ask
2 parents 9fb6875 + 6e3c3ff commit 98b19df

File tree

19 files changed

+1361
-190
lines changed

19 files changed

+1361
-190
lines changed

catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/proposal/proposal_cubit.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ final class ProposalCubit extends Cubit<ProposalState>
4747
_cache = _cache.copyWith(
4848
activeAccountId: Optional(_userService.user.activeAccount?.catalystId),
4949
);
50-
_activeAccountIdSub = _userService.watchUser
51-
.map((event) => event.activeAccount?.catalystId)
50+
_activeAccountIdSub = _userService.watchUnlockedActiveAccount
51+
.map((activeAccount) => activeAccount?.catalystId)
5252
.distinct()
5353
.listen(_handleActiveAccountIdChanged);
5454
}

catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/proposal_builder/proposal_builder_bloc.dart

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,9 @@ final class ProposalBuilderBloc extends Bloc<ProposalBuilderEvent, ProposalBuild
8282
accountPublicStatus: Optional(activeAccount?.publicStatus),
8383
);
8484

85-
_activeAccountSub = _userService.watchUser
86-
.map((event) => event.activeAccount)
87-
.distinct()
88-
.listen(
89-
(value) => add(RebuildActiveAccountProposalEvent(account: value)),
90-
);
85+
_activeAccountSub = _userService.watchUnlockedActiveAccount.listen(
86+
(value) => add(RebuildActiveAccountProposalEvent(account: value)),
87+
);
9188

9289
_isMaxProposalsLimitReachedSub = _proposalService.watchMaxProposalsLimitReached().listen((
9390
event,

catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/session/session_cubit.dart

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import 'package:catalyst_voices_shared/catalyst_voices_shared.dart';
77
import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart';
88
import 'package:collection/collection.dart';
99
import 'package:flutter/foundation.dart';
10-
import 'package:rxdart/rxdart.dart';
1110

1211
bool _alwaysAllowRegistration = kDebugMode;
1312

@@ -51,11 +50,8 @@ final class SessionCubit extends Cubit<SessionState>
5150
.distinct()
5251
.listen(_handleUserSettings);
5352

54-
_keychainUnlockedSub = _userService.watchUser
55-
.map((user) => user.activeAccount)
56-
.switchMap((account) {
57-
return account?.keychain.watchIsUnlocked ?? Stream.value(false);
58-
})
53+
_keychainUnlockedSub = _userService.watchUnlockedActiveAccount
54+
.map((account) => account != null)
5955
.distinct()
6056
.listen(_onActiveKeychainUnlockChanged);
6157

@@ -229,8 +225,6 @@ final class SessionCubit extends Cubit<SessionState>
229225
);
230226
}
231227

232-
// TODO(damian-molinski): Refactor active account stream so it emits null when account
233-
// keychain is locked.
234228
void _emitAccountBasedSignal() {
235229
final account = _account;
236230

catalyst_voices/packages/internal/catalyst_voices_services/lib/src/proposal/proposal_service.dart

Lines changed: 89 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -582,82 +582,94 @@ final class ProposalServiceImpl implements ProposalService {
582582

583583
@override
584584
Stream<List<DetailProposal>> watchUserProposals() async* {
585-
yield* _userService.watchUser.distinct().switchMap((user) {
586-
final authorId = user.activeAccount?.catalystId;
587-
if (!_isProposer(user) || authorId == null) {
588-
return const Stream.empty();
589-
}
590-
591-
return _proposalRepository
592-
.watchUserProposals(authorId: authorId)
593-
.distinct()
594-
.switchMap<List<DetailProposal>>((documents) async* {
595-
if (documents.isEmpty) {
596-
yield [];
597-
return;
598-
}
599-
final proposalsDataStreams = await Future.wait(
600-
documents.map(_createProposalDataStream).toList(),
601-
);
602-
603-
yield* Rx.combineLatest(
604-
proposalsDataStreams,
605-
(List<ProposalData?> proposalsData) async {
606-
// Note. one is null and two versions of same id.
607-
final validProposalsData = proposalsData.whereType<ProposalData>().toList();
585+
yield* _userService //
586+
.watchUser
587+
.distinct()
588+
.switchMap(_userWhenUnlockedStream)
589+
.switchMap((user) {
590+
if (user == null) return const Stream.empty();
591+
592+
final authorId = user.activeAccount?.catalystId;
593+
if (!_isProposer(user) || authorId == null) {
594+
return const Stream.empty();
595+
}
608596

609-
final groupedProposals = groupBy(
610-
validProposalsData,
611-
(data) => data.document.metadata.selfRef.id,
597+
return _proposalRepository
598+
.watchUserProposals(authorId: authorId)
599+
.distinct()
600+
.switchMap<List<DetailProposal>>((documents) async* {
601+
if (documents.isEmpty) {
602+
yield [];
603+
return;
604+
}
605+
final proposalsDataStreams = await Future.wait(
606+
documents.map(_createProposalDataStream).toList(),
612607
);
613608

614-
final filteredProposalsData = groupedProposals.values
615-
.map((group) {
616-
if (group.any(
617-
(p) => p.publish != ProposalPublish.localDraft,
618-
)) {
619-
return group.where(
620-
(p) => p.publish != ProposalPublish.localDraft,
621-
);
622-
}
623-
return group;
624-
})
625-
.expand((group) => group)
626-
.toList();
627-
628-
final proposalsWithVersions = await Future.wait(
629-
filteredProposalsData.map((proposalData) async {
630-
final versions = await _getDetailVersionsOfProposal(proposalData);
631-
return DetailProposal.fromData(proposalData, versions);
632-
}),
633-
);
634-
return proposalsWithVersions;
635-
},
636-
).switchMap(Stream.fromFuture);
637-
});
638-
});
609+
yield* Rx.combineLatest(
610+
proposalsDataStreams,
611+
(List<ProposalData?> proposalsData) async {
612+
// Note. one is null and two versions of same id.
613+
final validProposalsData = proposalsData.whereType<ProposalData>().toList();
614+
615+
final groupedProposals = groupBy(
616+
validProposalsData,
617+
(data) => data.document.metadata.selfRef.id,
618+
);
619+
620+
final filteredProposalsData = groupedProposals.values
621+
.map((group) {
622+
if (group.any(
623+
(p) => p.publish != ProposalPublish.localDraft,
624+
)) {
625+
return group.where(
626+
(p) => p.publish != ProposalPublish.localDraft,
627+
);
628+
}
629+
return group;
630+
})
631+
.expand((group) => group)
632+
.toList();
633+
634+
final proposalsWithVersions = await Future.wait(
635+
filteredProposalsData.map((proposalData) async {
636+
final versions = await _getDetailVersionsOfProposal(proposalData);
637+
return DetailProposal.fromData(proposalData, versions);
638+
}),
639+
);
640+
return proposalsWithVersions;
641+
},
642+
).switchMap(Stream.fromFuture);
643+
});
644+
});
639645
}
640646

641647
@override
642648
Stream<ProposalsCount> watchUserProposalsCount() {
643-
return _userService.watchUser.distinct().switchMap((user) {
644-
final authorId = user.activeAccount?.catalystId;
645-
if (!_isProposer(user) || authorId == null) {
646-
// user is not eligible for creating proposals
647-
return const Stream.empty();
648-
}
649-
650-
final activeCampaign = _activeCampaignObserver.campaign;
651-
final categoriesIds = activeCampaign?.categories.map((e) => e.selfRef.id).toList();
652-
653-
final filters = ProposalsCountFilters(
654-
author: authorId,
655-
onlyAuthor: true,
656-
campaign: categoriesIds != null ? CampaignFilters(categoriesIds: categoriesIds) : null,
657-
);
649+
return _userService //
650+
.watchUser
651+
.distinct()
652+
.switchMap(_userWhenUnlockedStream)
653+
.switchMap((user) {
654+
if (user == null) return const Stream.empty();
655+
656+
final authorId = user.activeAccount?.catalystId;
657+
if (!_isProposer(user) || authorId == null) {
658+
// user is not eligible for creating proposals
659+
return const Stream.empty();
660+
}
658661

659-
return watchProposalsCount(filters: filters);
660-
});
662+
final activeCampaign = _activeCampaignObserver.campaign;
663+
final categoriesIds = activeCampaign?.categories.map((e) => e.selfRef.id).toList();
664+
665+
final filters = ProposalsCountFilters(
666+
author: authorId,
667+
onlyAuthor: true,
668+
campaign: categoriesIds != null ? CampaignFilters(categoriesIds: categoriesIds) : null,
669+
);
670+
671+
return watchProposalsCount(filters: filters);
672+
});
661673
}
662674

663675
// TODO(damian-molinski): Remove this when voteBy is implemented.
@@ -788,4 +800,13 @@ final class ProposalServiceImpl implements ProposalService {
788800

789801
return page.copyWithItems(proposals);
790802
}
803+
804+
Stream<User?> _userWhenUnlockedStream(User user) {
805+
final activeAccount = user.activeAccount;
806+
807+
if (activeAccount == null) return Stream.value(null);
808+
809+
final isUnlockedStream = activeAccount.keychain.watchIsUnlocked;
810+
return isUnlockedStream.map((isUnlocked) => isUnlocked ? user : null);
811+
}
791812
}

catalyst_voices/packages/internal/catalyst_voices_services/lib/src/user/user_service.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:catalyst_voices_services/catalyst_voices_services.dart';
77
import 'package:catalyst_voices_shared/catalyst_voices_shared.dart';
88
import 'package:collection/collection.dart';
99
import 'package:flutter/foundation.dart';
10+
import 'package:rxdart/rxdart.dart';
1011

1112
/// [UserService] allows to manage user accounts.
1213
/// [watchUser] returns a stream of user changes which allows to react to user changes.
@@ -19,6 +20,9 @@ abstract interface class UserService implements ActiveAware {
1920

2021
User get user;
2122

23+
/// Returns [Account] when keychain is unlocked, otherwise returns `null`.
24+
Stream<Account?> get watchUnlockedActiveAccount;
25+
2226
Stream<User> get watchUser;
2327

2428
Future<void> dispose();
@@ -115,6 +119,15 @@ final class UserServiceImpl implements UserService {
115119
@override
116120
User get user => _userObserver.user;
117121

122+
@override
123+
Stream<Account?> get watchUnlockedActiveAccount =>
124+
watchUser.map((e) => e.activeAccount).switchMap((account) {
125+
if (account == null) return Stream.value(null);
126+
127+
final isUnlockedStream = account.keychain.watchIsUnlocked;
128+
return isUnlockedStream.map((isUnlocked) => isUnlocked ? account : null);
129+
}).distinct();
130+
118131
@override
119132
Stream<User> get watchUser => _userObserver.watchUser;
120133

0 commit comments

Comments
 (0)