diff --git a/catalyst_voices/apps/voices/integration_test/all_test.dart b/catalyst_voices/apps/voices/integration_test/all_test.dart index 0e44bdf69a6d..9ed49f2e7d26 100644 --- a/catalyst_voices/apps/voices/integration_test/all_test.dart +++ b/catalyst_voices/apps/voices/integration_test/all_test.dart @@ -11,14 +11,11 @@ import 'suites/suites.dart'; void main() { final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + final f14Campaign = Campaign.f14(); setUpAll(() async { binding.testTextInput.register(); - // F14 - final f14Campaign = Campaign.f14(); - mockedActiveCampaign = f14Campaign; - final proposalSubmissionTimeline = f14Campaign.timeline.phases .firstWhere((element) => element.type == CampaignPhaseType.proposalSubmission) .timeline; @@ -33,6 +30,7 @@ void main() { await loggingService.updateSettings(printToConsole: const Optional(false)); + await Dependencies.instance.get().setActiveCampaign(f14Campaign); await Dependencies.instance.get().waitForSync; }); diff --git a/catalyst_voices/apps/voices/lib/dependency/dependencies.dart b/catalyst_voices/apps/voices/lib/dependency/dependencies.dart index 7ae9e0a027a0..c4fc1890d481 100644 --- a/catalyst_voices/apps/voices/lib/dependency/dependencies.dart +++ b/catalyst_voices/apps/voices/lib/dependency/dependencies.dart @@ -188,6 +188,7 @@ final class Dependencies extends DependencyProvider { isRegistered() ? get() : null, get(), get(), + get(), ); }) ..registerFactory(() { diff --git a/catalyst_voices/apps/voices/lib/pages/co_proposers/widgets/add_collaborator/add_collaborator_dialog.dart b/catalyst_voices/apps/voices/lib/pages/co_proposers/widgets/add_collaborator/add_collaborator_dialog.dart index ad67a6521e9e..b40b6a1f87ba 100644 --- a/catalyst_voices/apps/voices/lib/pages/co_proposers/widgets/add_collaborator/add_collaborator_dialog.dart +++ b/catalyst_voices/apps/voices/lib/pages/co_proposers/widgets/add_collaborator/add_collaborator_dialog.dart @@ -41,12 +41,12 @@ class _AddCollaboratorDialogState extends State { Widget build(BuildContext context) { return BlocProvider.value( value: _cubit, - child: ScaffoldMessenger( + child: const ScaffoldMessenger( child: Scaffold( backgroundColor: Colors.transparent, body: VoicesPanelDialog( - constraints: const Responsive.single(BoxConstraints(maxWidth: 602, maxHeight: 396)), - child: const AddCollaboratorView(), + constraints: Responsive.single(BoxConstraints(maxWidth: 602, maxHeight: 396)), + child: AddCollaboratorView(), ), ), ), diff --git a/catalyst_voices/apps/voices/lib/pages/dev_tools/cards/active_campaign_card.dart b/catalyst_voices/apps/voices/lib/pages/dev_tools/cards/active_campaign_card.dart new file mode 100644 index 000000000000..567e5c1c3f1c --- /dev/null +++ b/catalyst_voices/apps/voices/lib/pages/dev_tools/cards/active_campaign_card.dart @@ -0,0 +1,62 @@ +import 'package:catalyst_voices/pages/dev_tools/cards/info_card.dart'; +import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart'; +import 'package:catalyst_voices_models/catalyst_voices_models.dart'; +import 'package:flutter/material.dart'; + +class ActiveCampaignCard extends StatelessWidget { + const ActiveCampaignCard({super.key}); + + @override + Widget build(BuildContext context) { + return BlocSelector( + selector: (state) => state.campaign, + builder: (context, state) { + return _ActiveCampaignCard( + activeCampaign: state.activeCampaign, + allCampaigns: state.allCampaigns, + ); + }, + ); + } +} + +class _ActiveCampaignCard extends StatelessWidget { + final Campaign? activeCampaign; + final List allCampaigns; + + const _ActiveCampaignCard({ + required this.activeCampaign, + required this.allCampaigns, + }); + + @override + Widget build(BuildContext context) { + return InfoCard( + title: const Text('Campaign'), + children: [ + Row( + mainAxisSize: MainAxisSize.min, + spacing: 16, + children: [ + const Text('Active Campaign'), + DropdownButton( + value: activeCampaign, + hint: const Text('Select a campaign'), + items: allCampaigns.map((Campaign value) { + return DropdownMenuItem( + value: value, + child: Text(value.name), + ); + }).toList(), + onChanged: (Campaign? newValue) { + if (newValue != null) { + context.read().add(ChangeActiveCampaignEvent(newValue)); + } + }, + ), + ], + ), + ], + ); + } +} diff --git a/catalyst_voices/apps/voices/lib/pages/dev_tools/dev_tools_page.dart b/catalyst_voices/apps/voices/lib/pages/dev_tools/dev_tools_page.dart index b8fddf6acd3f..a40a108271a1 100644 --- a/catalyst_voices/apps/voices/lib/pages/dev_tools/dev_tools_page.dart +++ b/catalyst_voices/apps/voices/lib/pages/dev_tools/dev_tools_page.dart @@ -1,3 +1,4 @@ +import 'package:catalyst_voices/pages/dev_tools/cards/active_campaign_card.dart'; import 'package:catalyst_voices/pages/dev_tools/cards/app_info_card.dart'; import 'package:catalyst_voices/pages/dev_tools/cards/config_card.dart'; import 'package:catalyst_voices/pages/dev_tools/cards/documents_card.dart'; @@ -50,6 +51,8 @@ class _DevToolsPageState extends State { SizedBox(height: 12), ConfigCard(), SizedBox(height: 12), + ActiveCampaignCard(), + SizedBox(height: 12), DocumentsCard(), SizedBox(height: 12), FeatureFlagsCard(), @@ -71,6 +74,7 @@ class _DevToolsPageState extends State { void dispose() { _bloc?.add(const StopWatchingSystemInfoEvent()); _bloc?.add(const StopWatchingDocumentsEvent()); + _bloc?.add(const StopWatchingActiveCampaignEvent()); _bloc = null; super.dispose(); } @@ -80,8 +84,9 @@ class _DevToolsPageState extends State { super.initState(); _bloc = context.read() - ..add(const UpdateAllEvent()) + ..add(const InitDataEvent()) ..add(const WatchSystemInfoEvent()) - ..add(const WatchDocumentsEvent()); + ..add(const WatchDocumentsEvent()) + ..add(const WatchActiveCampaignEvent()); } } diff --git a/catalyst_voices/apps/voices/lib/widgets/modals/proposals/leave_proposal_dialog.dart b/catalyst_voices/apps/voices/lib/widgets/modals/proposals/leave_proposal_dialog.dart index 588ddcb9e1b5..b7f014fa6082 100644 --- a/catalyst_voices/apps/voices/lib/widgets/modals/proposals/leave_proposal_dialog.dart +++ b/catalyst_voices/apps/voices/lib/widgets/modals/proposals/leave_proposal_dialog.dart @@ -66,11 +66,11 @@ class _Icon extends StatelessWidget { class _LeaveProposalDialogState extends State { @override Widget build(BuildContext context) { - return VoicesPanelDialog( - constraints: const Responsive.single( + return const VoicesPanelDialog( + constraints: Responsive.single( BoxConstraints(maxWidth: 511, maxHeight: 285), ), - child: const Padding( + child: Padding( padding: EdgeInsets.fromLTRB(16, 36, 16, 20), child: Column( children: [ diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/dev_tools/dev_tools_bloc.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/dev_tools/dev_tools_bloc.dart index 9f118283a71e..43a68ec5885c 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/dev_tools/dev_tools_bloc.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/dev_tools/dev_tools_bloc.dart @@ -19,10 +19,12 @@ final class DevToolsBloc extends Bloc final LoggingService? _loggingService; final DownloaderService _downloaderService; final DocumentsService _documentsService; + final CampaignService _campaignService; Timer? _resetCountTimer; StreamSubscription? _syncStartsSub; StreamSubscription? _documentsCountSub; + StreamSubscription? _activeCampaignSub; DevToolsBloc( this._devToolsService, @@ -30,25 +32,26 @@ final class DevToolsBloc extends Bloc this._loggingService, this._downloaderService, this._documentsService, + this._campaignService, ) : super(const DevToolsState()) { on(_handleEnablerTap); on(_handleTapCountReset); - on(_handleRecoverData); - on(_handleUpdateSystemInfo); + on(_handleInitData); on(_handleSyncDocuments); - on(_handleUpdateAll); - on(_handleWatchSystemInfoEvent); - on(_handleStopWatchingSystemInfoEvent); - on(_handleWatchDocumentsEvent); - on(_handleStopWatchingDocumentsEvent); - on(_updateDocumentsCount); + on(_handleWatchSystemInfo); + on(_handleStopWatchingSystemInfo); + on(_handleWatchDocuments); + on(_handleStopWatchingDocuments); + on(_handleWatchActiveCampaign); + on(_handleStopWatchingActiveCampaign); + on(_updateDocumentsCountChanged); on(_handleSyncStatsChanged); on(_handleChangeLogLevel); - on(_handleChangeCollectLogs); + on(_handleChangeCollectLogsEvent); + on(_handleChangeActiveCampaign); on(_handleExportLogs); on(_handleClearDocuments); - - add(const RecoverDataEvent()); + on(_handleActiveCampaignChanged); } @override @@ -62,10 +65,31 @@ final class DevToolsBloc extends Bloc await _documentsCountSub?.cancel(); _documentsCountSub = null; + await _activeCampaignSub?.cancel(); + _activeCampaignSub = null; + return super.close(); } - Future _handleChangeCollectLogs( + void _handleActiveCampaignChanged(ActiveCampaignChangedEvent event, Emitter emit) { + emit(state.copyWithActiveCampaign(event.campaign)); + } + + Future _handleChangeActiveCampaign( + ChangeActiveCampaignEvent event, + Emitter emit, + ) async { + final previousCampaign = state.campaign.activeCampaign; + try { + emit(state.copyWithActiveCampaign(event.campaign)); + await _campaignService.setActiveCampaign(event.campaign); + } catch (error, stackTrace) { + _logger.severe('handleChangeActiveCampaign', error, stackTrace); + emit(state.copyWithActiveCampaign(previousCampaign)); + } + } + + Future _handleChangeCollectLogsEvent( ChangeCollectLogsEvent event, Emitter emit, ) async { @@ -142,7 +166,7 @@ final class DevToolsBloc extends Bloc final content = await _loggingService!.prepareForExportCollectedLogs(); final encodedContent = utf8.encode(content); - final filename = 'catalyst_app_${DateTimeExt.now().toIso8601String()}_logs.txt'; + final filename = 'catalyst_app_${DateTimeExt.now().microsecondsSinceEpoch}_logs.txt'; await _downloaderService.download(data: encodedContent, filename: filename); } catch (error, stack) { @@ -150,11 +174,13 @@ final class DevToolsBloc extends Bloc } } - Future _handleRecoverData(RecoverDataEvent event, Emitter emit) async { + Future _handleInitData(InitDataEvent event, Emitter emit) async { final isDeveloper = await _devToolsService.isDeveloper(); final syncStats = await _devToolsService.getStats(); final areLogsOptionsAvailable = _loggingService != null; final loggingSettings = await _loggingService?.getSettings(); + final activeCampaign = await _campaignService.getActiveCampaign(); + final allCampaigns = await _campaignService.getAllCampaigns(); if (!isClosed) { emit( @@ -164,12 +190,24 @@ final class DevToolsBloc extends Bloc areLogsOptionsAvailable: areLogsOptionsAvailable, logsLevel: Optional(loggingSettings?.effectiveLevel), collectLogs: loggingSettings?.effectiveCollectLogs ?? false, + campaign: DevToolsCampaignState( + activeCampaign: activeCampaign, + allCampaigns: allCampaigns, + ), ), ); } } - Future _handleStopWatchingDocumentsEvent( + Future _handleStopWatchingActiveCampaign( + StopWatchingActiveCampaignEvent event, + Emitter emit, + ) async { + await _activeCampaignSub?.cancel(); + _activeCampaignSub = null; + } + + Future _handleStopWatchingDocuments( StopWatchingDocumentsEvent event, Emitter emit, ) async { @@ -177,7 +215,7 @@ final class DevToolsBloc extends Bloc _documentsCountSub = null; } - Future _handleStopWatchingSystemInfoEvent( + Future _handleStopWatchingSystemInfo( StopWatchingSystemInfoEvent event, Emitter emit, ) async { @@ -204,38 +242,16 @@ final class DevToolsBloc extends Bloc emit(state.copyWith(enableTapCount: 0)); } - Future _handleUpdateAll(UpdateAllEvent event, Emitter emit) async { - try { - final systemInfo = await _devToolsService.getSystemInfo(); - final syncStats = await _devToolsService.getStats(); - - if (!isClosed) { - emit(state.copyWith(systemInfo: Optional(systemInfo), syncStats: Optional(syncStats))); - } - } catch (error, stack) { - _logger.warning('Updating all failed', error, stack); - } - } - - Future _handleUpdateSystemInfo( - UpdateSystemInfoEvent event, + Future _handleWatchActiveCampaign( + WatchActiveCampaignEvent event, Emitter emit, ) async { - SystemInfo? systemInfo; - - try { - systemInfo = await _devToolsService.getSystemInfo(); - } catch (error, stack) { - _logger.warning('Updating system info failed', error, stack); - systemInfo = null; - } finally { - if (!isClosed) { - emit(state.copyWith(systemInfo: Optional(systemInfo))); - } - } + _activeCampaignSub = _campaignService.watchActiveCampaign.listen((value) { + add(ActiveCampaignChangedEvent(value)); + }); } - Future _handleWatchDocumentsEvent( + Future _handleWatchDocuments( WatchDocumentsEvent event, Emitter emit, ) async { @@ -244,7 +260,7 @@ final class DevToolsBloc extends Bloc ); } - Future _handleWatchSystemInfoEvent( + Future _handleWatchSystemInfo( WatchSystemInfoEvent event, Emitter emit, ) async { @@ -253,7 +269,7 @@ final class DevToolsBloc extends Bloc ); } - void _updateDocumentsCount(DocumentsCountChangedEvent event, Emitter emit) { + void _updateDocumentsCountChanged(DocumentsCountChangedEvent event, Emitter emit) { emit(state.copyWith(documentsCount: Optional(event.count))); } } diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/dev_tools/dev_tools_event.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/dev_tools/dev_tools_event.dart index 9cf7e4e5255a..8f776a4dddbe 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/dev_tools/dev_tools_event.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/dev_tools/dev_tools_event.dart @@ -2,6 +2,24 @@ import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:catalyst_voices_shared/catalyst_voices_shared.dart' show Level; import 'package:equatable/equatable.dart'; +final class ActiveCampaignChangedEvent extends DevToolsEvent { + final Campaign? campaign; + + const ActiveCampaignChangedEvent(this.campaign); + + @override + List get props => [campaign]; +} + +final class ChangeActiveCampaignEvent extends DevToolsEvent { + final Campaign campaign; + + const ChangeActiveCampaignEvent(this.campaign); + + @override + List get props => [campaign]; +} + final class ChangeCollectLogsEvent extends DevToolsEvent { final bool isEnabled; @@ -48,6 +66,10 @@ final class DocumentsCountChangedEvent extends DevToolsEvent { List get props => [count]; } +final class InitDataEvent extends DevToolsEvent { + const InitDataEvent(); +} + final class PrepareAndExportLogsEvent extends DevToolsEvent { const PrepareAndExportLogsEvent(); } @@ -56,8 +78,8 @@ final class RecoverConfigEvent extends DevToolsEvent { const RecoverConfigEvent(); } -final class RecoverDataEvent extends DevToolsEvent { - const RecoverDataEvent(); +final class StopWatchingActiveCampaignEvent extends DevToolsEvent { + const StopWatchingActiveCampaignEvent(); } final class StopWatchingDocumentsEvent extends DevToolsEvent { @@ -81,12 +103,8 @@ final class SyncStatsChangedEvent extends DevToolsEvent { List get props => [stats]; } -final class UpdateAllEvent extends DevToolsEvent { - const UpdateAllEvent(); -} - -final class UpdateSystemInfoEvent extends DevToolsEvent { - const UpdateSystemInfoEvent(); +final class WatchActiveCampaignEvent extends DevToolsEvent { + const WatchActiveCampaignEvent(); } final class WatchDocumentsEvent extends DevToolsEvent { diff --git a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/dev_tools/dev_tools_state.dart b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/dev_tools/dev_tools_state.dart index 3c235ae38b66..b3801541a710 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/dev_tools/dev_tools_state.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/dev_tools/dev_tools_state.dart @@ -2,12 +2,36 @@ import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; import 'package:equatable/equatable.dart'; +final class DevToolsCampaignState extends Equatable { + final Campaign? activeCampaign; + final List allCampaigns; + + const DevToolsCampaignState({ + this.activeCampaign, + this.allCampaigns = const [], + }); + + @override + List get props => [activeCampaign, allCampaigns]; + + DevToolsCampaignState copyWith({ + Optional? activeCampaign, + List? allCampaigns, + }) { + return DevToolsCampaignState( + activeCampaign: activeCampaign.dataOr(this.activeCampaign), + allCampaigns: allCampaigns ?? this.allCampaigns, + ); + } +} + final class DevToolsState extends Equatable { final int enableTapCount; final bool isDeveloper; final SystemInfo? systemInfo; final SyncStats? syncStats; final int? documentsCount; + final DevToolsCampaignState campaign; final bool areLogsOptionsAvailable; final Level? logsLevel; final bool collectLogs; @@ -18,6 +42,7 @@ final class DevToolsState extends Equatable { this.systemInfo, this.syncStats, this.documentsCount, + this.campaign = const DevToolsCampaignState(), this.areLogsOptionsAvailable = false, this.logsLevel, this.collectLogs = false, @@ -30,6 +55,7 @@ final class DevToolsState extends Equatable { systemInfo, syncStats, documentsCount, + campaign, areLogsOptionsAvailable, logsLevel, collectLogs, @@ -41,6 +67,7 @@ final class DevToolsState extends Equatable { Optional? systemInfo, Optional? syncStats, Optional? documentsCount, + DevToolsCampaignState? campaign, bool? areLogsOptionsAvailable, Optional? logsLevel, bool? collectLogs, @@ -52,8 +79,15 @@ final class DevToolsState extends Equatable { syncStats: syncStats.dataOr(this.syncStats), documentsCount: documentsCount.dataOr(this.documentsCount), areLogsOptionsAvailable: areLogsOptionsAvailable ?? this.areLogsOptionsAvailable, + campaign: campaign ?? this.campaign, logsLevel: logsLevel.dataOr(this.logsLevel), collectLogs: collectLogs ?? this.collectLogs, ); } + + DevToolsState copyWithActiveCampaign(Campaign? activeCampaign) { + return copyWith( + campaign: campaign.copyWith(activeCampaign: Optional(activeCampaign)), + ); + } } diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/campaign/campaign.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/campaign/campaign.dart index 51fcfee36d47..857555714af6 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/campaign/campaign.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/campaign/campaign.dart @@ -179,17 +179,20 @@ Project Catalyst turns economic power into innovation power by using the Cardano return categories.any((element) => element.id.id == id); } - /// Returns the state of the campaign for a specific phase. + /// Returns the state of the campaign for a specific phase at given [date], which defaults to + /// [DateTimeExt.now]. /// It's a shortcut for [state] when you are only interested in a specific phase. And want to know /// the status of that phase. - CampaignPhaseState phaseStateTo(CampaignPhaseType type) { + CampaignPhaseState phaseStateTo(CampaignPhaseType type, [DateTime? date]) { + date ??= DateTimeExt.now(); + final phase = timeline.phases.firstWhere( (phase) => phase.type == type, orElse: () => throw StateError('Type $type not found'), ); return CampaignPhaseState( phase: phase, - status: CampaignPhaseStatus.fromRange(phase.timeline, DateTimeExt.now()), + status: CampaignPhaseStatus.fromRange(phase.timeline, date), ); } } diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/document/constant/active_const_documents_refs.dart b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/document/constant/active_const_documents_refs.dart index be641cfb2969..34a65eab410d 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/document/constant/active_const_documents_refs.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_models/lib/src/document/constant/active_const_documents_refs.dart @@ -1,4 +1,4 @@ import 'package:catalyst_voices_models/catalyst_voices_models.dart'; -const activeCampaignRef = Campaign.f15Ref; const List activeConstantDocumentRefs = f15ConstDocumentsRefs; +const SignedDocumentRef initialActiveCampaignRef = Campaign.f15Ref; diff --git a/catalyst_voices/packages/internal/catalyst_voices_models/test/campaign/campaign_test.dart b/catalyst_voices/packages/internal/catalyst_voices_models/test/campaign/campaign_test.dart index 572f0569a19d..7e4c8db692af 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_models/test/campaign/campaign_test.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_models/test/campaign/campaign_test.dart @@ -379,9 +379,18 @@ void main() { ); // Act - final resultProposalSubmission = campaign.phaseStateTo(CampaignPhaseType.proposalSubmission); - final resultVotingRegistration = campaign.phaseStateTo(CampaignPhaseType.votingRegistration); - final resultReviewRegistration = campaign.phaseStateTo(CampaignPhaseType.reviewRegistration); + final resultProposalSubmission = campaign.phaseStateTo( + CampaignPhaseType.proposalSubmission, + now, + ); + final resultVotingRegistration = campaign.phaseStateTo( + CampaignPhaseType.votingRegistration, + now, + ); + final resultReviewRegistration = campaign.phaseStateTo( + CampaignPhaseType.reviewRegistration, + now, + ); // Assert expect( diff --git a/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/campaign/campaign_repository.dart b/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/campaign/campaign_repository.dart index 698abc8b0abc..a4a22905aa0f 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/campaign/campaign_repository.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/campaign/campaign_repository.dart @@ -6,6 +6,8 @@ import 'package:collection/collection.dart'; abstract interface class CampaignRepository { const factory CampaignRepository(ProposalDocumentDataLocalSource source) = CampaignRepositoryImpl; + Future> getAllCampaigns(); + Future getCampaign({ required String id, }); @@ -28,6 +30,11 @@ final class CampaignRepositoryImpl implements CampaignRepository { const CampaignRepositoryImpl(this._source); + @override + Future> getAllCampaigns() async { + return Campaign.all; + } + @override Future getCampaign({ required String id, diff --git a/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/campaign/campaign_service.dart b/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/campaign/campaign_service.dart index fade1be9f402..1f6396278878 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/campaign/campaign_service.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/campaign/campaign_service.dart @@ -2,21 +2,10 @@ import 'package:catalyst_voices_models/catalyst_voices_models.dart'; import 'package:catalyst_voices_repositories/catalyst_voices_repositories.dart'; import 'package:catalyst_voices_services/src/campaign/active_campaign_observer.dart'; import 'package:catalyst_voices_shared/catalyst_voices_shared.dart'; -import 'package:flutter/foundation.dart'; import 'package:rxdart/rxdart.dart'; final _logger = Logger('CampaignService'); -Campaign? _mockedActiveCampaign; - -/// Overrides the current campaign returned by [CampaignService.getActiveCampaign]. -/// Only for unit testing. -@visibleForTesting -//ignore: avoid_setters_without_getters -set mockedActiveCampaign(Campaign? campaign) { - _mockedActiveCampaign = campaign; -} - typedef _ProposalTemplateCategoryAndMoneyFormat = ({ SignedDocumentRef? category, MoneyFormat? moneyFormat, @@ -38,6 +27,8 @@ abstract interface class CampaignService { Future getActiveCampaign(); + Future> getAllCampaigns(); + Future getCampaign({ required String id, }); @@ -48,6 +39,8 @@ abstract interface class CampaignService { Future getCategoryTotalAsk({required SignedDocumentRef ref}); + Future setActiveCampaign(Campaign campaign); + Stream watchCampaignTotalAsk({required ProposalsTotalAskFilters filters}); Stream watchCategoryTotalAsk({required SignedDocumentRef ref}); @@ -73,29 +66,23 @@ final class CampaignServiceImpl implements CampaignService { return _activeCampaignObserver.campaign; } // TODO(LynxLynxx): Call backend to get latest active campaign - final campaign = _mockedActiveCampaign ?? await getCampaign(id: activeCampaignRef.id); + final campaign = await getCampaign(id: initialActiveCampaignRef.id); _activeCampaignObserver.campaign = campaign; return campaign; } + @override + Future> getAllCampaigns() async { + final campaigns = await _campaignRepository.getAllCampaigns(); + return campaigns.map(_updateSubmissionCloseDate).wait; + } + @override Future getCampaign({ required String id, }) async { final campaign = await _campaignRepository.getCampaign(id: id); - final proposalSubmissionTime = campaign - .phaseStateTo(CampaignPhaseType.proposalSubmission) - .phase - .timeline - .to; - - final updatedCategories = campaign.categories - .map((e) => e.copyWith(submissionCloseDate: proposalSubmissionTime)) - .toList(); - - return campaign.copyWith( - categories: updatedCategories, - ); + return _updateSubmissionCloseDate(campaign); } @override @@ -135,6 +122,13 @@ final class CampaignServiceImpl implements CampaignService { return watchCategoryTotalAsk(ref: ref).first; } + @override + Future setActiveCampaign(Campaign campaign) async { + if (_activeCampaignObserver.campaign?.id != campaign.id) { + _activeCampaignObserver.campaign = campaign; + } + } + @override Stream watchCampaignTotalAsk({required ProposalsTotalAskFilters filters}) { return _proposalRepository @@ -202,6 +196,22 @@ final class CampaignServiceImpl implements CampaignService { return CampaignTotalAsk(categoriesAsks: Map.unmodifiable(categoriesAsks)); } + + Future _updateSubmissionCloseDate(Campaign campaign) async { + final proposalSubmissionTime = campaign + .phaseStateTo(CampaignPhaseType.proposalSubmission) + .phase + .timeline + .to; + + final updatedCategories = campaign.categories + .map((e) => e.copyWith(submissionCloseDate: proposalSubmissionTime)) + .toList(); + + return campaign.copyWith( + categories: updatedCategories, + ); + } } extension on ProposalTemplate { diff --git a/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/catalyst_voices_services.dart b/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/catalyst_voices_services.dart index 403b0aac9024..6cd412179d2c 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/catalyst_voices_services.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_services/lib/src/catalyst_voices_services.dart @@ -4,7 +4,6 @@ export 'auth/auth_token_generator.dart' show AuthTokenGenerator; export 'blockchain/blockchain_service.dart' show BlockchainService; export 'campaign/active_campaign_observer.dart'; export 'campaign/campaign_service.dart' show CampaignService; -export 'campaign/campaign_service.dart' show mockedActiveCampaign; export 'comment/comment_service.dart' show CommentService; export 'config/config_service.dart' show ConfigService; export 'crypto/bip32_ed25519_catalyst_private_key.dart' show Bip32Ed25519XCatalystPrivateKeyFactory; diff --git a/catalyst_voices/packages/internal/catalyst_voices_shared/lib/src/responsive/responsive.dart b/catalyst_voices/packages/internal/catalyst_voices_shared/lib/src/responsive/responsive.dart index c47d4d71621e..9d55c37cdf2b 100644 --- a/catalyst_voices/packages/internal/catalyst_voices_shared/lib/src/responsive/responsive.dart +++ b/catalyst_voices/packages/internal/catalyst_voices_shared/lib/src/responsive/responsive.dart @@ -20,11 +20,11 @@ abstract interface class Responsive { T? sm, T? md, T? lg, - }) = _ResponsiveBreakpoints; + }) = _ResponsiveBreakpoints; /// A singleton implementation of responsive, just in case a [Responsive] instance /// is needed even though the widget is not going to be responsive. - const factory Responsive.single(T value) = _SingleResponsive; + const factory Responsive.single(T value) = _SingleResponsive; /// Provides responsive data based on the given [screenSize]. ///