Skip to content

Commit fd33143

Browse files
committed
Merge branch 'main' into 2041-implement-account-tests
2 parents fac84a3 + 3ee1022 commit fd33143

File tree

19 files changed

+460
-107
lines changed

19 files changed

+460
-107
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
11
import 'package:flutter/material.dart';
2+
import 'package:flutter_test/flutter_test.dart';
3+
import 'package:patrol_finders/patrol_finders.dart';
4+
5+
import '../utils/translations_utils.dart';
26

37
class UnlockModalPage {
8+
UnlockModalPage(this.$);
9+
late PatrolTester $;
10+
411
static const unlockKeychainDialog = Key('UnlockKeychainDialog');
512
static const unlockKeychainInfoPanel = Key('UnlockKeychainInfoPanel');
613
static const unlockPasswordTextField = Key('UnlockPasswordTextField');
714
static const unlockConfirmPasswordButton = Key('UnlockConfirmPasswordButton');
815
static const unlockRecoverButton = Key('UnlockRecoverButton');
916
static const unlockContinueAsGuestButton = Key('UnlockContinueAsGuestButton');
17+
static const passwordTextField = Key('PasswordTextField');
18+
19+
Future<void> incorrectPasswordErrorShowsUp() async {
20+
expect(
21+
find.text((await t()).unlockDialogIncorrectPassword),
22+
findsOneWidget,
23+
);
24+
}
1025
}

catalyst_voices/apps/voices/integration_test/suites/account_test.dart

+15-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:catalyst_voices/configs/bootstrap.dart';
33
import 'package:catalyst_voices/routes/routes.dart';
44
// import 'package:catalyst_voices/widgets/text_field/voices_text_field.dart';
55
import 'package:catalyst_voices_models/catalyst_voices_models.dart';
6+
import 'package:flutter/material.dart';
67
// import 'package:flutter/material.dart';
78
import 'package:flutter_test/flutter_test.dart';
89
import 'package:go_router/go_router.dart';
@@ -81,7 +82,20 @@ void main() async {
8182
await $(UnlockModalPage.unlockConfirmPasswordButton).tap();
8283
await AppBarPage($).unlockBtnIsVisible();
8384
});
84-
patrolWidgetTest('user changing email works', (PatrolTester $) async {
85+
patrolWidgetTest('user - unlocking - wrong password error appears',
86+
(PatrolTester $) async {
87+
await $.pumpWidgetAndSettle(App(routerConfig: router));
88+
await UnlockPasswordSuccessPanel($).goto();
89+
await UnlockPasswordSuccessPanel($).clickGoToDashboard();
90+
await AppBarPage($).lockBtnClick();
91+
await AppBarPage($).unlockBtnClick();
92+
await $(UnlockModalPage.unlockPasswordTextField).enterText('Test12345');
93+
await $(UnlockModalPage.unlockConfirmPasswordButton).tap();
94+
await UnlockModalPage($).incorrectPasswordErrorShowsUp();
95+
});
96+
97+
patrolWidgetTest(skip: true, 'user changing email works',
98+
(PatrolTester $) async {
8599
await $.pumpWidgetAndSettle(App(routerConfig: router));
86100
await UnlockPasswordSuccessPanel($).goto();
87101
await UnlockPasswordSuccessPanel($).clickGoToDashboard();

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

+7
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@ final class Dependencies extends DependencyProvider {
169169
get<CatalystDatabase>(),
170170
);
171171
})
172+
..registerLazySingleton<DocumentFavoriteSource>(() {
173+
return DatabaseDocumentFavoriteSource(
174+
get<CatalystDatabase>(),
175+
);
176+
})
172177
..registerLazySingleton<CatGatewayDocumentDataSource>(() {
173178
return CatGatewayDocumentDataSource(
174179
get<ApiServices>(),
@@ -182,6 +187,7 @@ final class Dependencies extends DependencyProvider {
182187
get<DatabaseDraftsDataSource>(),
183188
get<SignedDocumentDataSource>(),
184189
get<CatGatewayDocumentDataSource>(),
190+
get<DocumentFavoriteSource>(),
185191
);
186192
})
187193
..registerLazySingleton<DocumentMapper>(() => const DocumentMapperImpl())
@@ -244,6 +250,7 @@ final class Dependencies extends DependencyProvider {
244250
registerLazySingleton<ProposalService>(() {
245251
return ProposalService(
246252
get<ProposalRepository>(),
253+
get<DocumentRepository>(),
247254
get<UserService>(),
248255
get<KeyDerivationService>(),
249256
get<CampaignRepository>(),

catalyst_voices/apps/voices/lib/pages/proposals/proposals_pagination.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class _ProposalsPaginationState extends State<ProposalsPagination> {
9292
},
9393
onFavoriteChanged: (isFavorite) async {
9494
await context.read<ProposalsCubit>().onChangeFavoriteProposal(
95-
item.ref.id,
95+
item.ref,
9696
isFavorite: isFavorite,
9797
);
9898
},

catalyst_voices/apps/voices/lib/widgets/text_field/voices_password_text_field.dart

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ final class VoicesPasswordTextField extends StatelessWidget {
3535
@override
3636
Widget build(BuildContext context) {
3737
return VoicesTextField(
38+
key: const Key('PasswordTextField'),
3839
controller: controller,
3940
keyboardType: TextInputType.visiblePassword,
4041
autofocus: autofocus,

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

+100-55
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,10 @@ final class ProposalCubit extends Cubit<ProposalState>
2121

2222
// Cache
2323
DocumentRef? _ref;
24-
25-
// ignore: unused_field
2624
ProposalData? _proposal;
27-
28-
// ignore: unused_field
25+
CommentTemplate? _commentTemplate;
2926
List<CommentWithReplies>? _comments;
27+
bool? _isFavorite;
3028

3129
// Note for integration.
3230
// 1. Fetch proposal document
@@ -46,45 +44,42 @@ final class ProposalCubit extends Cubit<ProposalState>
4644

4745
final proposal = await _proposalService.getProposal(ref: ref);
4846
final comments = _buildComments();
49-
50-
if (isClosed) {
51-
return;
52-
}
53-
54-
final commentsSort = state.data.commentsSort;
55-
56-
final proposalViewData = _buildProposalViewData(
57-
hasActiveAccount: _userService.user.activeAccount != null,
58-
proposal: proposal,
59-
comments: comments,
60-
commentSchema: _buildCommentTemplate().document.schema,
61-
commentsSort: commentsSort,
62-
isFavorite: false,
63-
);
47+
final isFavorite =
48+
await _proposalService.watchIsFavoritesProposal(ref: ref).first;
6449

6550
_ref = ref;
6651
_proposal = proposal;
52+
_commentTemplate = _buildCommentTemplate();
6753
_comments = comments;
54+
_isFavorite = isFavorite;
6855

69-
emit(ProposalState(data: proposalViewData));
56+
if (!isClosed) {
57+
final proposalState = _rebuildProposalState();
7058

71-
if (proposalViewData.isCurrentVersionLatest == false) {
72-
emitSignal(const ViewingOlderVersionSignal());
59+
emit(ProposalState(data: proposalState));
60+
61+
if (proposalState.isCurrentVersionLatest == false) {
62+
emitSignal(const ViewingOlderVersionSignal());
63+
}
7364
}
7465
} on LocalizedException catch (error, stack) {
7566
_logger.severe('Loading $ref failed', error, stack);
7667

7768
_ref = null;
7869
_proposal = null;
70+
_commentTemplate = null;
7971
_comments = null;
72+
_isFavorite = null;
8073

8174
emit(ProposalState(error: error));
8275
} catch (error, stack) {
8376
_logger.severe('Loading $ref failed', error, stack);
8477

8578
_ref = null;
8679
_proposal = null;
80+
_commentTemplate = null;
8781
_comments = null;
82+
_isFavorite = null;
8883

8984
emit(const ProposalState(error: LocalizedUnknownException()));
9085
} finally {
@@ -149,39 +144,80 @@ final class ProposalCubit extends Cubit<ProposalState>
149144
emit(state.copyWith(data: updatedData));
150145
}
151146

152-
// TODO(damian-molinski): not implemented
153147
Future<void> updateIsFavorite({required bool value}) async {
154-
final proposalId = _ref?.id;
155-
assert(proposalId != null, 'Proposal ref not found. Load doc first');
148+
final ref = _ref;
149+
assert(ref != null, 'Proposal ref not found. Load doc first');
156150

157151
emit(state.copyWithFavorite(isFavorite: value));
152+
153+
if (value) {
154+
await _proposalService.addFavoriteProposal(ref: ref!);
155+
} else {
156+
await _proposalService.removeFavoriteProposal(ref: ref!);
157+
}
158158
}
159159

160160
ProposalViewData _buildProposalViewData({
161161
required bool hasActiveAccount,
162-
required ProposalData proposal,
162+
required ProposalData? proposal,
163163
required List<CommentWithReplies> comments,
164-
required DocumentSchema commentSchema,
164+
required DocumentSchema? commentSchema,
165165
required ProposalCommentsSort commentsSort,
166166
required bool isFavorite,
167167
}) {
168-
final proposalDocument = proposal.document;
169-
final proposalDocumentRef = proposalDocument.metadata.selfRef;
170-
final documentSegments = mapDocumentToSegments(proposalDocument.document);
168+
final proposalDocument = proposal?.document;
169+
final proposalDocumentRef = proposalDocument?.metadata.selfRef;
171170

172171
/* cSpell:disable */
173-
final versions = proposal.versions.mapIndexed((index, version) {
172+
final proposalVersions = proposal?.versions ?? const [];
173+
final versions = proposalVersions.mapIndexed((index, version) {
174+
final ver = version.document.metadata.selfRef.version;
175+
174176
return DocumentVersion(
175-
id: version.document.metadata.selfRef.version ?? '',
177+
id: ver ?? '',
176178
number: index + 1,
177-
isCurrent: version.document.metadata.selfRef.version ==
178-
proposalDocumentRef.version,
179-
isLatest: index == proposal.versions.length - 1,
179+
isCurrent: ver == proposalDocumentRef?.version,
180+
isLatest: index == proposalVersions.length - 1,
180181
);
181182
}).toList();
182-
183183
final currentVersion = versions.singleWhereOrNull((e) => e.isCurrent);
184184

185+
final segments = proposal != null
186+
? _buildSegments(
187+
proposal: proposal,
188+
version: currentVersion,
189+
comments: comments,
190+
commentSchema: commentSchema,
191+
commentsSort: commentsSort,
192+
hasActiveAccount: hasActiveAccount,
193+
)
194+
: const <Segment>[];
195+
196+
return ProposalViewData(
197+
isCurrentVersionLatest: currentVersion?.isLatest,
198+
header: ProposalViewHeader(
199+
title: 'Project Mayhem: Freedom by Chaos',
200+
authorDisplayName: 'Tyler Durden',
201+
createdAt: DateTime.timestamp(),
202+
commentsCount: comments.length,
203+
versions: versions,
204+
isFavorite: isFavorite,
205+
),
206+
segments: segments,
207+
commentsSort: commentsSort,
208+
);
209+
/* cSpell:enable */
210+
}
211+
212+
/* cSpell:disable */
213+
List<Segment> _buildSegments({
214+
required ProposalData proposal,
215+
required DocumentVersion? version,
216+
required List<CommentWithReplies> comments,
217+
required DocumentSchema? commentSchema,
218+
required ProposalCommentsSort commentsSort,
219+
required bool hasActiveAccount,
220+
}) {
185221
final overviewSegment = ProposalOverviewSegment.build(
186222
categoryName: 'Cardano Partners: Growth & Acceleration',
187223
proposalTitle: 'Project Mayhem: Freedom by Chaos',
@@ -195,8 +231,8 @@ final class ProposalCubit extends Cubit<ProposalState>
195231
'inspiring global action for liberation and a return to human '
196232
'authenticity.',
197233
status: ProposalStatus.draft,
198-
createdAt: currentVersion?.id.tryDateTime ?? DateTime.now(),
199-
warningCreatedAt: currentVersion?.isLatest == false,
234+
createdAt: version?.id.tryDateTime ?? DateTime.now(),
235+
warningCreatedAt: version?.isLatest == false,
200236
tag: 'Community Outreach',
201237
commentsCount: comments.length,
202238
fundsRequested: 200000,
@@ -205,6 +241,8 @@ final class ProposalCubit extends Cubit<ProposalState>
205241
),
206242
);
207243

244+
final proposalSegments = mapDocumentToSegments(proposal.document.document);
245+
208246
final commentsSegment = ProposalCommentsSegment(
209247
id: const NodeId('comments'),
210248
sort: commentsSort,
@@ -213,31 +251,38 @@ final class ProposalCubit extends Cubit<ProposalState>
213251
id: const NodeId('comments.view'),
214252
comments: commentsSort.applyTo(comments),
215253
),
216-
if (hasActiveAccount)
254+
if (hasActiveAccount && commentSchema != null)
217255
AddCommentSection(
218256
id: const NodeId('comments.add'),
219257
schema: commentSchema,
220258
),
221259
],
222260
);
223261

224-
return ProposalViewData(
225-
isCurrentVersionLatest: currentVersion?.isLatest,
226-
header: ProposalViewHeader(
227-
title: 'Project Mayhem: Freedom by Chaos',
228-
authorDisplayName: 'Tyler Durden',
229-
createdAt: DateTime.timestamp(),
230-
commentsCount: comments.length,
231-
versions: versions,
232-
isFavorite: isFavorite,
233-
),
234-
segments: [
235-
overviewSegment,
236-
...documentSegments,
237-
commentsSegment,
238-
],
262+
return [
263+
overviewSegment,
264+
...proposalSegments,
265+
commentsSegment,
266+
];
267+
}
268+
269+
/* cSpell:enable */
270+
271+
ProposalViewData _rebuildProposalState() {
272+
final proposal = _proposal;
273+
final commentTemplate = _commentTemplate;
274+
final comments = _comments ?? const [];
275+
final commentsSort = state.data.commentsSort;
276+
final isFavorite = _isFavorite ?? false;
277+
final activeAccount = _userService.user.activeAccount;
278+
279+
return _buildProposalViewData(
280+
hasActiveAccount: activeAccount != null,
281+
proposal: proposal,
282+
comments: comments,
283+
commentSchema: commentTemplate?.document.schema,
239284
commentsSort: commentsSort,
285+
isFavorite: isFavorite,
240286
);
241-
/* cSpell:enable */
242287
}
243288
}

catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/proposals/proposals_cubit.dart

+6-8
Original file line numberDiff line numberDiff line change
@@ -111,39 +111,37 @@ final class ProposalsCubit extends Cubit<ProposalsState> {
111111
emit(state.copyWith(favoritesIds: favoritesList));
112112
}
113113

114-
/// Changes the favorite status of the proposal with [proposalId].
114+
/// Changes the favorite status of the proposal with [ref].
115115
Future<void> onChangeFavoriteProposal(
116-
String proposalId, {
116+
DocumentRef ref, {
117117
required bool isFavorite,
118118
}) async {
119119
if (isFavorite) {
120120
// ignore: unused_local_variable
121-
final favIds = await _proposalService.addFavoriteProposal(proposalId);
121+
final favIds = await _proposalService.addFavoriteProposal(ref: ref);
122122
// TODO(LynxLynxx): to mock data. remove after implementing db
123-
final favoritesIds = [...state.favoritesIds, proposalId];
123+
final favoritesIds = [...state.favoritesIds, ref.id];
124124
emit(state.copyWith(favoritesIds: favoritesIds));
125125
// TODO(LynxLynxx): to mock data. should read proposal from db and change
126126
// isFavorite
127127
// await _proposalService.getProposal(id: proposalId);
128128

129129
// TODO(LynxLynxx): to mock data. remove after implementing db
130-
final ref = SignedDocumentRef(id: proposalId);
131130
final proposal = state.allProposals.items.first.copyWith(
132131
ref: ref,
133132
isFavorite: isFavorite,
134133
);
135134
await _favorite(isFavorite, proposal);
136135
} else {
137136
// TODO(LynxLynxx): to mock data. remove after implementing db
138-
final ref = SignedDocumentRef(id: proposalId);
139137
final proposal = state.allProposals.items.first.copyWith(
140138
ref: ref,
141139
isFavorite: isFavorite,
142140
);
143141
await _favorite(isFavorite, proposal);
144-
await _proposalService.removeFavoriteProposal(proposalId);
142+
await _proposalService.removeFavoriteProposal(ref: ref);
145143
// TODO(LynxLynxx): to mock data. remove after implementing db
146-
final favoritesIds = [...state.favoritesIds]..remove(proposalId);
144+
final favoritesIds = [...state.favoritesIds]..remove(ref.id);
147145

148146
emit(state.copyWith(favoritesIds: favoritesIds));
149147
}

0 commit comments

Comments
 (0)