Skip to content

Commit e28bfda

Browse files
authored
feat(cat-voices): campaign switching (#3847)
* chore: use consts * fix: date format * feat: set active campaign * feat: add ui in dev tools to change active campaign * chore: add more logs * fix: responsive * chore: code review changes
1 parent 0123286 commit e28bfda

File tree

16 files changed

+263
-101
lines changed

16 files changed

+263
-101
lines changed

catalyst_voices/apps/voices/integration_test/all_test.dart

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,11 @@ import 'suites/suites.dart';
1111

1212
void main() {
1313
final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
14+
final f14Campaign = Campaign.f14();
1415

1516
setUpAll(() async {
1617
binding.testTextInput.register();
1718

18-
// F14
19-
final f14Campaign = Campaign.f14();
20-
mockedActiveCampaign = f14Campaign;
21-
2219
final proposalSubmissionTimeline = f14Campaign.timeline.phases
2320
.firstWhere((element) => element.type == CampaignPhaseType.proposalSubmission)
2421
.timeline;
@@ -33,6 +30,7 @@ void main() {
3330

3431
await loggingService.updateSettings(printToConsole: const Optional(false));
3532

33+
await Dependencies.instance.get<CampaignService>().setActiveCampaign(f14Campaign);
3634
await Dependencies.instance.get<SyncManager>().waitForSync;
3735
});
3836

catalyst_voices/apps/voices/lib/dependency/dependencies.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ final class Dependencies extends DependencyProvider {
188188
isRegistered<LoggingService>() ? get<LoggingService>() : null,
189189
get<DownloaderService>(),
190190
get<DocumentsService>(),
191+
get<CampaignService>(),
191192
);
192193
})
193194
..registerFactory<DocumentLookupBloc>(() {

catalyst_voices/apps/voices/lib/pages/co_proposers/widgets/add_collaborator/add_collaborator_dialog.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,12 @@ class _AddCollaboratorDialogState extends State<AddCollaboratorDialog> {
4141
Widget build(BuildContext context) {
4242
return BlocProvider.value(
4343
value: _cubit,
44-
child: ScaffoldMessenger(
44+
child: const ScaffoldMessenger(
4545
child: Scaffold(
4646
backgroundColor: Colors.transparent,
4747
body: VoicesPanelDialog(
48-
constraints: const Responsive.single(BoxConstraints(maxWidth: 602, maxHeight: 396)),
49-
child: const AddCollaboratorView(),
48+
constraints: Responsive.single(BoxConstraints(maxWidth: 602, maxHeight: 396)),
49+
child: AddCollaboratorView(),
5050
),
5151
),
5252
),
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import 'package:catalyst_voices/pages/dev_tools/cards/info_card.dart';
2+
import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart';
3+
import 'package:catalyst_voices_models/catalyst_voices_models.dart';
4+
import 'package:flutter/material.dart';
5+
6+
class ActiveCampaignCard extends StatelessWidget {
7+
const ActiveCampaignCard({super.key});
8+
9+
@override
10+
Widget build(BuildContext context) {
11+
return BlocSelector<DevToolsBloc, DevToolsState, DevToolsCampaignState>(
12+
selector: (state) => state.campaign,
13+
builder: (context, state) {
14+
return _ActiveCampaignCard(
15+
activeCampaign: state.activeCampaign,
16+
allCampaigns: state.allCampaigns,
17+
);
18+
},
19+
);
20+
}
21+
}
22+
23+
class _ActiveCampaignCard extends StatelessWidget {
24+
final Campaign? activeCampaign;
25+
final List<Campaign> allCampaigns;
26+
27+
const _ActiveCampaignCard({
28+
required this.activeCampaign,
29+
required this.allCampaigns,
30+
});
31+
32+
@override
33+
Widget build(BuildContext context) {
34+
return InfoCard(
35+
title: const Text('Campaign'),
36+
children: [
37+
Row(
38+
mainAxisSize: MainAxisSize.min,
39+
spacing: 16,
40+
children: [
41+
const Text('Active Campaign'),
42+
DropdownButton<Campaign>(
43+
value: activeCampaign,
44+
hint: const Text('Select a campaign'),
45+
items: allCampaigns.map((Campaign value) {
46+
return DropdownMenuItem<Campaign>(
47+
value: value,
48+
child: Text(value.name),
49+
);
50+
}).toList(),
51+
onChanged: (Campaign? newValue) {
52+
if (newValue != null) {
53+
context.read<DevToolsBloc>().add(ChangeActiveCampaignEvent(newValue));
54+
}
55+
},
56+
),
57+
],
58+
),
59+
],
60+
);
61+
}
62+
}

catalyst_voices/apps/voices/lib/pages/dev_tools/dev_tools_page.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:catalyst_voices/pages/dev_tools/cards/active_campaign_card.dart';
12
import 'package:catalyst_voices/pages/dev_tools/cards/app_info_card.dart';
23
import 'package:catalyst_voices/pages/dev_tools/cards/config_card.dart';
34
import 'package:catalyst_voices/pages/dev_tools/cards/documents_card.dart';
@@ -50,6 +51,8 @@ class _DevToolsPageState extends State<DevToolsPage> {
5051
SizedBox(height: 12),
5152
ConfigCard(),
5253
SizedBox(height: 12),
54+
ActiveCampaignCard(),
55+
SizedBox(height: 12),
5356
DocumentsCard(),
5457
SizedBox(height: 12),
5558
FeatureFlagsCard(),
@@ -71,6 +74,7 @@ class _DevToolsPageState extends State<DevToolsPage> {
7174
void dispose() {
7275
_bloc?.add(const StopWatchingSystemInfoEvent());
7376
_bloc?.add(const StopWatchingDocumentsEvent());
77+
_bloc?.add(const StopWatchingActiveCampaignEvent());
7478
_bloc = null;
7579
super.dispose();
7680
}
@@ -80,8 +84,9 @@ class _DevToolsPageState extends State<DevToolsPage> {
8084
super.initState();
8185

8286
_bloc = context.read<DevToolsBloc>()
83-
..add(const UpdateAllEvent())
87+
..add(const InitDataEvent())
8488
..add(const WatchSystemInfoEvent())
85-
..add(const WatchDocumentsEvent());
89+
..add(const WatchDocumentsEvent())
90+
..add(const WatchActiveCampaignEvent());
8691
}
8792
}

catalyst_voices/apps/voices/lib/widgets/modals/proposals/leave_proposal_dialog.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ class _Icon extends StatelessWidget {
6666
class _LeaveProposalDialogState extends State<LeaveProposalDialog> {
6767
@override
6868
Widget build(BuildContext context) {
69-
return VoicesPanelDialog(
70-
constraints: const Responsive.single(
69+
return const VoicesPanelDialog(
70+
constraints: Responsive.single(
7171
BoxConstraints(maxWidth: 511, maxHeight: 285),
7272
),
73-
child: const Padding(
73+
child: Padding(
7474
padding: EdgeInsets.fromLTRB(16, 36, 16, 20),
7575
child: Column(
7676
children: [

catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/dev_tools/dev_tools_bloc.dart

Lines changed: 62 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -19,36 +19,39 @@ final class DevToolsBloc extends Bloc<DevToolsEvent, DevToolsState>
1919
final LoggingService? _loggingService;
2020
final DownloaderService _downloaderService;
2121
final DocumentsService _documentsService;
22+
final CampaignService _campaignService;
2223

2324
Timer? _resetCountTimer;
2425
StreamSubscription<SyncStats>? _syncStartsSub;
2526
StreamSubscription<int>? _documentsCountSub;
27+
StreamSubscription<Campaign?>? _activeCampaignSub;
2628

2729
DevToolsBloc(
2830
this._devToolsService,
2931
this._syncManager,
3032
this._loggingService,
3133
this._downloaderService,
3234
this._documentsService,
35+
this._campaignService,
3336
) : super(const DevToolsState()) {
3437
on<DevToolsEnablerTappedEvent>(_handleEnablerTap);
3538
on<DevToolsEnablerTapResetEvent>(_handleTapCountReset);
36-
on<RecoverDataEvent>(_handleRecoverData);
37-
on<UpdateSystemInfoEvent>(_handleUpdateSystemInfo);
39+
on<InitDataEvent>(_handleInitData);
3840
on<SyncDocumentsEvent>(_handleSyncDocuments);
39-
on<UpdateAllEvent>(_handleUpdateAll);
40-
on<WatchSystemInfoEvent>(_handleWatchSystemInfoEvent);
41-
on<StopWatchingSystemInfoEvent>(_handleStopWatchingSystemInfoEvent);
42-
on<WatchDocumentsEvent>(_handleWatchDocumentsEvent);
43-
on<StopWatchingDocumentsEvent>(_handleStopWatchingDocumentsEvent);
44-
on<DocumentsCountChangedEvent>(_updateDocumentsCount);
41+
on<WatchSystemInfoEvent>(_handleWatchSystemInfo);
42+
on<StopWatchingSystemInfoEvent>(_handleStopWatchingSystemInfo);
43+
on<WatchDocumentsEvent>(_handleWatchDocuments);
44+
on<StopWatchingDocumentsEvent>(_handleStopWatchingDocuments);
45+
on<WatchActiveCampaignEvent>(_handleWatchActiveCampaign);
46+
on<StopWatchingActiveCampaignEvent>(_handleStopWatchingActiveCampaign);
47+
on<DocumentsCountChangedEvent>(_updateDocumentsCountChanged);
4548
on<SyncStatsChangedEvent>(_handleSyncStatsChanged);
4649
on<ChangeLogLevelEvent>(_handleChangeLogLevel);
47-
on<ChangeCollectLogsEvent>(_handleChangeCollectLogs);
50+
on<ChangeCollectLogsEvent>(_handleChangeCollectLogsEvent);
51+
on<ChangeActiveCampaignEvent>(_handleChangeActiveCampaign);
4852
on<PrepareAndExportLogsEvent>(_handleExportLogs);
4953
on<ClearDocumentsEvent>(_handleClearDocuments);
50-
51-
add(const RecoverDataEvent());
54+
on<ActiveCampaignChangedEvent>(_handleActiveCampaignChanged);
5255
}
5356

5457
@override
@@ -62,10 +65,31 @@ final class DevToolsBloc extends Bloc<DevToolsEvent, DevToolsState>
6265
await _documentsCountSub?.cancel();
6366
_documentsCountSub = null;
6467

68+
await _activeCampaignSub?.cancel();
69+
_activeCampaignSub = null;
70+
6571
return super.close();
6672
}
6773

68-
Future<void> _handleChangeCollectLogs(
74+
void _handleActiveCampaignChanged(ActiveCampaignChangedEvent event, Emitter<DevToolsState> emit) {
75+
emit(state.copyWithActiveCampaign(event.campaign));
76+
}
77+
78+
Future<void> _handleChangeActiveCampaign(
79+
ChangeActiveCampaignEvent event,
80+
Emitter<DevToolsState> emit,
81+
) async {
82+
final previousCampaign = state.campaign.activeCampaign;
83+
try {
84+
emit(state.copyWithActiveCampaign(event.campaign));
85+
await _campaignService.setActiveCampaign(event.campaign);
86+
} catch (error, stackTrace) {
87+
_logger.severe('handleChangeActiveCampaign', error, stackTrace);
88+
emit(state.copyWithActiveCampaign(previousCampaign));
89+
}
90+
}
91+
92+
Future<void> _handleChangeCollectLogsEvent(
6993
ChangeCollectLogsEvent event,
7094
Emitter<DevToolsState> emit,
7195
) async {
@@ -142,19 +166,21 @@ final class DevToolsBloc extends Bloc<DevToolsEvent, DevToolsState>
142166
final content = await _loggingService!.prepareForExportCollectedLogs();
143167
final encodedContent = utf8.encode(content);
144168

145-
final filename = 'catalyst_app_${DateTimeExt.now().toIso8601String()}_logs.txt';
169+
final filename = 'catalyst_app_${DateTimeExt.now().microsecondsSinceEpoch}_logs.txt';
146170

147171
await _downloaderService.download(data: encodedContent, filename: filename);
148172
} catch (error, stack) {
149173
_logger.severe('Exporting logs failed', error, stack);
150174
}
151175
}
152176

153-
Future<void> _handleRecoverData(RecoverDataEvent event, Emitter<DevToolsState> emit) async {
177+
Future<void> _handleInitData(InitDataEvent event, Emitter<DevToolsState> emit) async {
154178
final isDeveloper = await _devToolsService.isDeveloper();
155179
final syncStats = await _devToolsService.getStats();
156180
final areLogsOptionsAvailable = _loggingService != null;
157181
final loggingSettings = await _loggingService?.getSettings();
182+
final activeCampaign = await _campaignService.getActiveCampaign();
183+
final allCampaigns = await _campaignService.getAllCampaigns();
158184

159185
if (!isClosed) {
160186
emit(
@@ -164,20 +190,32 @@ final class DevToolsBloc extends Bloc<DevToolsEvent, DevToolsState>
164190
areLogsOptionsAvailable: areLogsOptionsAvailable,
165191
logsLevel: Optional(loggingSettings?.effectiveLevel),
166192
collectLogs: loggingSettings?.effectiveCollectLogs ?? false,
193+
campaign: DevToolsCampaignState(
194+
activeCampaign: activeCampaign,
195+
allCampaigns: allCampaigns,
196+
),
167197
),
168198
);
169199
}
170200
}
171201

172-
Future<void> _handleStopWatchingDocumentsEvent(
202+
Future<void> _handleStopWatchingActiveCampaign(
203+
StopWatchingActiveCampaignEvent event,
204+
Emitter<DevToolsState> emit,
205+
) async {
206+
await _activeCampaignSub?.cancel();
207+
_activeCampaignSub = null;
208+
}
209+
210+
Future<void> _handleStopWatchingDocuments(
173211
StopWatchingDocumentsEvent event,
174212
Emitter<DevToolsState> emit,
175213
) async {
176214
await _documentsCountSub?.cancel();
177215
_documentsCountSub = null;
178216
}
179217

180-
Future<void> _handleStopWatchingSystemInfoEvent(
218+
Future<void> _handleStopWatchingSystemInfo(
181219
StopWatchingSystemInfoEvent event,
182220
Emitter<DevToolsState> emit,
183221
) async {
@@ -204,38 +242,16 @@ final class DevToolsBloc extends Bloc<DevToolsEvent, DevToolsState>
204242
emit(state.copyWith(enableTapCount: 0));
205243
}
206244

207-
Future<void> _handleUpdateAll(UpdateAllEvent event, Emitter<DevToolsState> emit) async {
208-
try {
209-
final systemInfo = await _devToolsService.getSystemInfo();
210-
final syncStats = await _devToolsService.getStats();
211-
212-
if (!isClosed) {
213-
emit(state.copyWith(systemInfo: Optional(systemInfo), syncStats: Optional(syncStats)));
214-
}
215-
} catch (error, stack) {
216-
_logger.warning('Updating all failed', error, stack);
217-
}
218-
}
219-
220-
Future<void> _handleUpdateSystemInfo(
221-
UpdateSystemInfoEvent event,
245+
Future<void> _handleWatchActiveCampaign(
246+
WatchActiveCampaignEvent event,
222247
Emitter<DevToolsState> emit,
223248
) async {
224-
SystemInfo? systemInfo;
225-
226-
try {
227-
systemInfo = await _devToolsService.getSystemInfo();
228-
} catch (error, stack) {
229-
_logger.warning('Updating system info failed', error, stack);
230-
systemInfo = null;
231-
} finally {
232-
if (!isClosed) {
233-
emit(state.copyWith(systemInfo: Optional(systemInfo)));
234-
}
235-
}
249+
_activeCampaignSub = _campaignService.watchActiveCampaign.listen((value) {
250+
add(ActiveCampaignChangedEvent(value));
251+
});
236252
}
237253

238-
Future<void> _handleWatchDocumentsEvent(
254+
Future<void> _handleWatchDocuments(
239255
WatchDocumentsEvent event,
240256
Emitter<DevToolsState> emit,
241257
) async {
@@ -244,7 +260,7 @@ final class DevToolsBloc extends Bloc<DevToolsEvent, DevToolsState>
244260
);
245261
}
246262

247-
Future<void> _handleWatchSystemInfoEvent(
263+
Future<void> _handleWatchSystemInfo(
248264
WatchSystemInfoEvent event,
249265
Emitter<DevToolsState> emit,
250266
) async {
@@ -253,7 +269,7 @@ final class DevToolsBloc extends Bloc<DevToolsEvent, DevToolsState>
253269
);
254270
}
255271

256-
void _updateDocumentsCount(DocumentsCountChangedEvent event, Emitter<DevToolsState> emit) {
272+
void _updateDocumentsCountChanged(DocumentsCountChangedEvent event, Emitter<DevToolsState> emit) {
257273
emit(state.copyWith(documentsCount: Optional(event.count)));
258274
}
259275
}

0 commit comments

Comments
 (0)