Skip to content

Commit 6e3c3ff

Browse files
Merge branch 'feat/face-performance-optimization-3352' into feat/database_optimization_3528
2 parents 56e62fc + bb5429d commit 6e3c3ff

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
@@ -586,82 +586,94 @@ final class ProposalServiceImpl implements ProposalService {
586586

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

613-
final groupedProposals = groupBy(
614-
validProposalsData,
615-
(data) => data.document.metadata.selfRef.id,
601+
return _proposalRepository
602+
.watchUserProposals(authorId: authorId)
603+
.distinct()
604+
.switchMap<List<DetailProposal>>((documents) async* {
605+
if (documents.isEmpty) {
606+
yield [];
607+
return;
608+
}
609+
final proposalsDataStreams = await Future.wait(
610+
documents.map(_createProposalDataStream).toList(),
616611
);
617612

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

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

663-
return watchProposalsCount(filters: filters);
664-
});
666+
final activeCampaign = _activeCampaignObserver.campaign;
667+
final categoriesIds = activeCampaign?.categories.map((e) => e.selfRef.id).toList();
668+
669+
final filters = ProposalsCountFilters(
670+
author: authorId,
671+
onlyAuthor: true,
672+
campaign: categoriesIds != null ? CampaignFilters(categoriesIds: categoriesIds) : null,
673+
);
674+
675+
return watchProposalsCount(filters: filters);
676+
});
665677
}
666678

667679
// TODO(damian-molinski): Remove this when voteBy is implemented.
@@ -792,4 +804,13 @@ final class ProposalServiceImpl implements ProposalService {
792804

793805
return page.copyWithItems(proposals);
794806
}
807+
808+
Stream<User?> _userWhenUnlockedStream(User user) {
809+
final activeAccount = user.activeAccount;
810+
811+
if (activeAccount == null) return Stream.value(null);
812+
813+
final isUnlockedStream = activeAccount.keychain.watchIsUnlocked;
814+
return isUnlockedStream.map((isUnlocked) => isUnlocked ? user : null);
815+
}
795816
}

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)