Skip to content

Commit af39a1d

Browse files
LynxLynxxdt-iohk
andauthored
feat(cat-voices): implement favorite proposal on discovery page (#2167)
* feat(cat-voices): implement favorite proposal functionality and enhance state management for recent proposals * refactor(cat-voices): streamline error handling and enhance favorites emission in DiscoveryCubit class * fix(cat-voices): update background color to use theme colors in TimezoneDateTimeText widget * fix: tests * fix:review * fix: review --------- Co-authored-by: Dominik Toton <[email protected]>
1 parent bbbaf30 commit af39a1d

File tree

8 files changed

+132
-47
lines changed

8 files changed

+132
-47
lines changed

catalyst_voices/apps/voices/lib/pages/discovery/discovery_page.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'dart:async';
22

3+
import 'package:catalyst_voices/common/error_handler.dart';
34
import 'package:catalyst_voices/pages/discovery/sections/campaign_hero.dart';
45
import 'package:catalyst_voices/pages/discovery/sections/how_it_works.dart';
56
import 'package:catalyst_voices/pages/discovery/sections/stay_involved.dart';
@@ -41,7 +42,8 @@ class _Body extends StatelessWidget {
4142
}
4243
}
4344

44-
class _DiscoveryPageState extends State<DiscoveryPage> {
45+
class _DiscoveryPageState extends State<DiscoveryPage>
46+
with ErrorHandlerStateMixin<DiscoveryCubit, DiscoveryPage> {
4547
@override
4648
Widget build(BuildContext context) {
4749
return const SelectionArea(

catalyst_voices/apps/voices/lib/pages/discovery/sections/most_recent_proposals.dart

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import 'package:catalyst_voices/widgets/buttons/voices_filled_button.dart';
66
import 'package:catalyst_voices/widgets/cards/pending_proposal_card.dart';
77
import 'package:catalyst_voices/widgets/scrollbar/voices_slider.dart';
88
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
9+
import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart';
910
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
1011
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
1112
import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart';
1213
import 'package:flutter/material.dart';
14+
import 'package:flutter_bloc/flutter_bloc.dart';
1315
import 'package:skeletonizer/skeletonizer.dart';
1416

1517
class MostRecentProposals extends StatefulWidget {
@@ -85,9 +87,15 @@ class _LatestProposalsState extends State<MostRecentProposals> {
8587
).push(context),
8688
);
8789
},
88-
onFavoriteChanged: (value) {
89-
// TODO(LynxLynxx): add on change logic
90+
onFavoriteChanged: (value) async {
91+
final bloc = context.read<DiscoveryCubit>();
92+
if (value) {
93+
await bloc.addFavorite(ref);
94+
} else {
95+
await bloc.removeFavorite(ref);
96+
}
9097
},
98+
isFavorite: proposal.isFavorite,
9199
),
92100
);
93101
},

catalyst_voices/apps/voices/lib/widgets/text/timezone_date_time_text.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,8 @@ class TimezoneDateTimeText extends StatelessWidget {
9292
final timezoneTextStyle = timezoneTheme?.timezoneTextStyle ??
9393
WidgetStatePropertyAll(textTheme.labelSmall);
9494

95-
// TODO(damian-molinski): update this color from schema.
9695
final backgroundColor = timezoneTheme?.backgroundColor ??
97-
const WidgetStatePropertyAll(Color(0xFFE8ECFD));
96+
WidgetStatePropertyAll(theme.colors.primary98);
9897
final foregroundColor = timezoneTheme?.foregroundColor ??
9998
WidgetStatePropertyAll(theme.colors.textOnPrimaryLevel1);
10099

catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/discovery/discovery_cubit.dart

Lines changed: 99 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'dart:async';
22

3+
import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart';
34
import 'package:catalyst_voices_models/catalyst_voices_models.dart';
45
import 'package:catalyst_voices_services/catalyst_voices_services.dart';
56
import 'package:catalyst_voices_shared/catalyst_voices_shared.dart';
@@ -11,21 +12,33 @@ part 'discovery_state.dart';
1112

1213
final _logger = Logger('DiscoveryCubit');
1314

14-
class DiscoveryCubit extends Cubit<DiscoveryState> {
15+
class DiscoveryCubit extends Cubit<DiscoveryState> with BlocErrorEmitterMixin {
1516
// ignore: unused_field
1617
final CampaignService _campaignService;
1718
final ProposalService _proposalService;
1819
StreamSubscription<List<Proposal>>? _proposalsSubscription;
20+
StreamSubscription<List<String>>? _favoritesProposalsIdsSubscription;
1921

2022
DiscoveryCubit(
2123
this._campaignService,
2224
this._proposalService,
2325
) : super(const DiscoveryState());
2426

27+
Future<void> addFavorite(DocumentRef ref) async {
28+
try {
29+
await _proposalService.addFavoriteProposal(ref: ref);
30+
} catch (e, st) {
31+
_logger.severe('Error adding favorite', e, st);
32+
emitError(LocalizedException.create(e));
33+
}
34+
}
35+
2536
@override
2637
Future<void> close() {
2738
_proposalsSubscription?.cancel();
2839
_proposalsSubscription = null;
40+
_favoritesProposalsIdsSubscription?.cancel();
41+
_favoritesProposalsIdsSubscription = null;
2942
return super.close();
3043
}
3144

@@ -90,51 +103,101 @@ class DiscoveryCubit extends Cubit<DiscoveryState> {
90103
await _proposalsSubscription?.cancel();
91104
_proposalsSubscription = null;
92105
_setupProposalsSubscription();
106+
107+
await _favoritesProposalsIdsSubscription?.cancel();
108+
_favoritesProposalsIdsSubscription = null;
109+
_setupFavoritesProposalsIdsSubscription();
110+
111+
final mostRecentState = state.mostRecentProposals;
112+
emit(
113+
state.copyWith(
114+
mostRecentProposals: mostRecentState.copyWith(isLoading: false),
115+
),
116+
);
117+
}
118+
119+
Future<void> removeFavorite(DocumentRef ref) async {
120+
try {
121+
await _proposalService.removeFavoriteProposal(ref: ref);
122+
} catch (e, st) {
123+
_logger.severe('Error adding favorite', e, st);
124+
emitError(LocalizedException.create(e));
125+
}
126+
}
127+
128+
void _emitFavoritesIds(List<String> ids) {
129+
final proposals = state.mostRecentProposals.proposals;
130+
131+
final newProposals = [...proposals]
132+
.map((e) => e.copyWith(isFavorite: ids.contains(e.ref.id)))
133+
.toList();
134+
135+
emit(
136+
state.copyWith(
137+
mostRecentProposals: state.mostRecentProposals.copyWith(
138+
proposals: newProposals,
139+
),
140+
),
141+
);
142+
}
143+
144+
void _emitMostRecentError(Object error) {
145+
emit(
146+
state.copyWith(
147+
mostRecentProposals: state.mostRecentProposals.copyWith(
148+
isLoading: false,
149+
error: LocalizedException.create(error),
150+
proposals: const [],
151+
),
152+
),
153+
);
154+
}
155+
156+
void _emitMostRecentProposals(List<Proposal> proposals) {
157+
final proposalList = proposals
158+
.map(
159+
(e) => PendingProposal.fromProposal(
160+
e,
161+
campaignName: 'f14',
162+
),
163+
)
164+
.toList();
165+
emit(
166+
state.copyWith(
167+
mostRecentProposals: state.mostRecentProposals.copyWith(
168+
isLoading: false,
169+
error: null,
170+
proposals: proposalList,
171+
),
172+
),
173+
);
174+
}
175+
176+
void _setupFavoritesProposalsIdsSubscription() {
177+
_logger.info('Setting up favorites proposals ids subscription');
178+
_favoritesProposalsIdsSubscription =
179+
_proposalService.watchFavoritesProposalsIds().listen(
180+
_emitFavoritesIds,
181+
onError: (Object error) {
182+
_emitMostRecentError(error);
183+
},
184+
);
93185
}
94186

95187
void _setupProposalsSubscription() {
96188
_logger.info('Setting up proposals subscription');
97189
_proposalsSubscription =
98190
_proposalService.watchLatestProposals(limit: 7).listen(
99-
(proposals) {
100-
if (isClosed) return;
191+
(proposals) async {
101192
_logger.info('Got proposals: ${proposals.length}');
102-
final proposalList = proposals
103-
.map(
104-
(e) => PendingProposal.fromProposal(
105-
e,
106-
campaignName: 'f14',
107-
),
108-
)
109-
.toList();
110-
emit(
111-
state.copyWith(
112-
mostRecentProposals: DiscoveryMostRecentProposalsState(
113-
isLoading: false,
114-
error: null,
115-
proposals: proposalList,
116-
),
117-
),
118-
);
193+
_emitMostRecentProposals(proposals);
194+
final currentFavorites =
195+
await _proposalService.watchFavoritesProposalsIds().first;
196+
_emitFavoritesIds(currentFavorites);
119197
},
120198
onError: (Object error) {
121-
if (isClosed) return;
122-
emit(
123-
state.copyWith(
124-
mostRecentProposals: DiscoveryMostRecentProposalsState(
125-
isLoading: false,
126-
error: LocalizedException.create(error),
127-
proposals: const [],
128-
),
129-
),
130-
);
199+
_emitMostRecentError(error);
131200
},
132201
);
133-
emit(
134-
state.copyWith(
135-
mostRecentProposals:
136-
const DiscoveryMostRecentProposalsState(isLoading: false),
137-
),
138-
);
139202
}
140203
}

catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/discovery/discovery_state.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@ final class DiscoveryMostRecentProposalsState extends Equatable {
8080
bool get showError => !isLoading && error != null;
8181

8282
bool get showProposals => !isLoading && proposals.isNotEmpty && error == null;
83+
84+
DiscoveryMostRecentProposalsState copyWith({
85+
bool? isLoading,
86+
LocalizedException? error,
87+
List<PendingProposal>? proposals,
88+
}) {
89+
return DiscoveryMostRecentProposalsState(
90+
isLoading: isLoading ?? this.isLoading,
91+
error: error ?? this.error,
92+
proposals: proposals ?? this.proposals,
93+
);
94+
}
8395
}
8496

8597
final class DiscoveryState extends Equatable {

catalyst_voices/packages/internal/catalyst_voices_brands/lib/src/themes/widgets/slider_theme.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ class VoicesSliderThemeData extends SliderThemeData {
1616
),
1717
overlayColor: Colors.transparent,
1818
inactiveTickMarkColor: Colors.transparent,
19+
activeTickMarkColor: Colors.transparent,
1920
);
2021
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ final class ProposalServiceImpl implements ProposalService {
477477

478478
final commentsCountStream = _proposalRepository.watchCount(
479479
ref: selfRef,
480-
type: DocumentType.commentTemplate,
480+
type: DocumentType.commentDocument,
481481
);
482482

483483
return Rx.combineLatest2(

catalyst_voices/packages/internal/catalyst_voices_services/test/src/proposal/proposal_service_test.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ void main() {
3737
when(
3838
() => mockDocumentRepository.watchCount(
3939
ref: any(named: 'ref'),
40-
type: DocumentType.commentTemplate,
40+
type: DocumentType.commentDocument,
4141
),
4242
).thenAnswer((_) => Stream.fromIterable([5]));
4343
});
@@ -111,7 +111,7 @@ void main() {
111111
when(
112112
() => mockProposalRepository.watchCount(
113113
ref: any(named: 'ref'),
114-
type: DocumentType.commentTemplate,
114+
type: DocumentType.commentDocument,
115115
),
116116
).thenAnswer((_) => Stream.fromIterable([5]));
117117

@@ -151,7 +151,7 @@ void main() {
151151
verify(
152152
() => mockProposalRepository.watchCount(
153153
ref: any(named: 'ref'),
154-
type: DocumentType.commentTemplate,
154+
type: DocumentType.commentDocument,
155155
),
156156
).called(2);
157157

@@ -251,14 +251,14 @@ void main() {
251251
when(
252252
() => mockProposalRepository.watchCount(
253253
ref: proposalRef1,
254-
type: DocumentType.commentTemplate,
254+
type: DocumentType.commentDocument,
255255
),
256256
).thenAnswer((_) => comments1.stream);
257257

258258
when(
259259
() => mockProposalRepository.watchCount(
260260
ref: proposalRef2,
261-
type: DocumentType.commentTemplate,
261+
type: DocumentType.commentDocument,
262262
),
263263
).thenAnswer((_) => comments2.stream);
264264

0 commit comments

Comments
 (0)