Skip to content

Commit 5f6f82b

Browse files
authored
feat(cat-voices): Proposal builder localized errors (#3843)
* feat: handle more errors in proposal builder * feat: handle more errors * feat: add more exceptions * fix: restore error * chore: cleanup * chore: refine error message * chore: refine log message
1 parent cb4c292 commit 5f6f82b

File tree

13 files changed

+232
-116
lines changed

13 files changed

+232
-116
lines changed

catalyst_voices/apps/voices/lib/pages/proposal_builder/proposal_builder_page.dart

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@ import 'package:catalyst_voices/pages/workspace/submission_closing_warning_dialo
1515
import 'package:catalyst_voices/routes/routes.dart';
1616
import 'package:catalyst_voices/routes/routing/proposal_builder_route.dart';
1717
import 'package:catalyst_voices/widgets/modals/comment/submit_comment_error_dialog.dart';
18+
import 'package:catalyst_voices/widgets/modals/proposals/proposal_error_dialog.dart';
1819
import 'package:catalyst_voices/widgets/modals/proposals/proposal_limit_reached_dialog.dart';
19-
import 'package:catalyst_voices/widgets/modals/proposals/publish_proposal_error_dialog.dart';
2020
import 'package:catalyst_voices/widgets/modals/proposals/publish_proposal_iteration_dialog.dart';
21-
import 'package:catalyst_voices/widgets/modals/proposals/submit_proposal_error_dialog.dart';
2221
import 'package:catalyst_voices/widgets/modals/proposals/submit_proposal_for_review_dialog.dart';
2322
import 'package:catalyst_voices/widgets/modals/proposals/unlock_edit_proposal.dart';
2423
import 'package:catalyst_voices/widgets/snackbar/common_snackbars.dart';
@@ -142,8 +141,14 @@ class _ProposalBuilderBodyState extends State<_ProposalBuilderBody>
142141
unawaited(_showPublishException(error));
143142
case ProposalBuilderSubmitException():
144143
unawaited(_showSubmitException(error));
144+
case ProposalBuilderLimitReachedException():
145+
unawaited(_showLimitReachedException(error));
146+
case ProposalBuilderDocumentSignException():
147+
unawaited(_showDocumentSignException(error));
145148
case LocalizedUnknownPublishCommentException():
146149
unawaited(_showCommentException(error));
150+
case LocalizedException():
151+
unawaited(_showGenericException(error));
147152
default:
148153
super.handleError(error);
149154
}
@@ -277,6 +282,14 @@ class _ProposalBuilderBodyState extends State<_ProposalBuilderBody>
277282
);
278283
}
279284

285+
Future<void> _showDocumentSignException(ProposalBuilderDocumentSignException error) {
286+
return ProposalErrorDialog.show(
287+
context: context,
288+
title: error.title(context),
289+
message: error.message(context),
290+
);
291+
}
292+
280293
Future<void> _showEmailNotVerifiedDialog() async {
281294
final openAccount = await EmailNotVerifiedDialog.show(context);
282295

@@ -317,6 +330,22 @@ class _ProposalBuilderBodyState extends State<_ProposalBuilderBody>
317330
});
318331
}
319332

333+
Future<void> _showGenericException(LocalizedException error) {
334+
return ProposalErrorDialog.show(
335+
context: context,
336+
title: context.l10n.somethingWentWrong,
337+
message: error.message(context),
338+
);
339+
}
340+
341+
Future<void> _showLimitReachedException(ProposalBuilderLimitReachedException error) {
342+
return ProposalErrorDialog.show(
343+
context: context,
344+
title: error.title(context),
345+
message: error.message(context),
346+
);
347+
}
348+
320349
Future<void> _showProposalLimitReachedDialog(
321350
MaxProposalsLimitReachedSignal signal,
322351
) {
@@ -344,9 +373,10 @@ class _ProposalBuilderBodyState extends State<_ProposalBuilderBody>
344373
}
345374

346375
Future<void> _showPublishException(ProposalBuilderPublishException error) {
347-
return PublishProposalErrorDialog.show(
376+
return ProposalErrorDialog.show(
348377
context: context,
349-
exception: error,
378+
title: error.title(context),
379+
message: error.message(context),
350380
);
351381
}
352382

@@ -380,9 +410,10 @@ class _ProposalBuilderBodyState extends State<_ProposalBuilderBody>
380410
}
381411

382412
Future<void> _showSubmitException(ProposalBuilderSubmitException error) {
383-
return SubmitProposalErrorDialog.show(
413+
return ProposalErrorDialog.show(
384414
context: context,
385-
exception: error,
415+
title: error.title(context),
416+
message: error.message(context),
386417
);
387418
}
388419

catalyst_voices/apps/voices/lib/widgets/modals/proposals/submit_proposal_error_dialog.dart renamed to catalyst_voices/apps/voices/lib/widgets/modals/proposals/proposal_error_dialog.dart

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,27 @@ import 'package:catalyst_voices/widgets/modals/voices_info_dialog.dart';
44
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
55
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
66
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
7-
import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart';
87
import 'package:flutter/material.dart';
98

10-
/// Error dialog when submitting proposal for review fails.
11-
class SubmitProposalErrorDialog {
9+
/// Generic error dialog related to proposal errors.
10+
class ProposalErrorDialog {
1211
static Future<void> show({
1312
required BuildContext context,
14-
required ProposalBuilderSubmitException exception,
13+
required String title,
14+
required String message,
1515
}) {
1616
return VoicesDialog.show(
1717
context: context,
1818
routeSettings: const RouteSettings(
19-
name: '/proposal-builder/submit-error',
19+
name: '/proposal-builder/error',
2020
),
2121
builder: (context) {
2222
return VoicesInfoDialog(
2323
icon: VoicesAssets.icons.exclamation.buildIcon(
2424
color: Theme.of(context).colors.iconsWarning,
2525
),
26-
title: Text(exception.title(context)),
27-
message: Text(exception.message(context)),
26+
title: Text(title),
27+
message: Text(message),
2828
action: VoicesFilledButton(
2929
onTap: () => Navigator.of(context).pop(),
3030
child: Text(context.l10n.okay),

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

Lines changed: 0 additions & 36 deletions
This file was deleted.

catalyst_voices/packages/internal/catalyst_voices_blocs/lib/src/proposal_builder/proposal_builder_bloc.dart

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -790,9 +790,21 @@ final class ProposalBuilderBloc extends Bloc<ProposalBuilderEvent, ProposalBuild
790790
versions: updatedVersions,
791791
);
792792
emitSignal(const PublishedProposalBuilderSignal());
793+
} on ProposalLimitReachedException {
794+
_logger.info('publishProposal: limit reached');
795+
emitError(const ProposalBuilderLimitReachedException());
796+
} on DocumentSignException catch (error, stackTrace) {
797+
_logger.severe('publishProposal: failed to sign the document', error, stackTrace);
798+
emitError(const ProposalBuilderDocumentSignException());
793799
} catch (error, stackTrace) {
794-
_logger.severe('PublishProposal', error, stackTrace);
795-
emitError(const ProposalBuilderPublishException());
800+
_logger.severe('publishProposal', error, stackTrace);
801+
802+
emitError(
803+
LocalizedException.create(
804+
error,
805+
fallback: () => const ProposalBuilderPublishException(),
806+
),
807+
);
796808
} finally {
797809
emit(state.copyWith(isChanging: false));
798810
}
@@ -1092,9 +1104,21 @@ final class ProposalBuilderBloc extends Bloc<ProposalBuilderEvent, ProposalBuild
10921104
// already submitted, do nothing
10931105
break;
10941106
}
1107+
} on ProposalLimitReachedException {
1108+
_logger.info('SubmitProposalForReview: limit reached');
1109+
emitError(const ProposalBuilderLimitReachedException());
1110+
} on DocumentSignException catch (error, stackTrace) {
1111+
_logger.severe('publishProposal: failed to sign the document', error, stackTrace);
1112+
emitError(const ProposalBuilderDocumentSignException());
10951113
} catch (error, stackTrace) {
10961114
_logger.severe('SubmitProposalForReview', error, stackTrace);
1097-
emitError(const ProposalBuilderSubmitException());
1115+
1116+
emitError(
1117+
LocalizedException.create(
1118+
error,
1119+
fallback: () => const ProposalBuilderSubmitException(),
1120+
),
1121+
);
10981122
} finally {
10991123
emit(state.copyWith(isChanging: false));
11001124
}

catalyst_voices/packages/internal/catalyst_voices_localization/lib/l10n/intl_en.arb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2194,6 +2194,22 @@
21942194
"@proposalEditorDeleteDialogTitle": {
21952195
"description": "A title for the dialog to confirm proposal deletion."
21962196
},
2197+
"proposalEditorDocumentSignErrorMessage": "We couldn't sign the proposal, please try again later.",
2198+
"@proposalEditorDocumentSignErrorMessage": {
2199+
"description": "Dialog message in proposal editor when user couldn't sign the proposal."
2200+
},
2201+
"proposalEditorDocumentSignErrorTitle": "Unable to Sign the Proposal",
2202+
"@proposalEditorDocumentSignErrorTitle": {
2203+
"description": "Dialog title in proposal editor when user couldn't sign the proposal."
2204+
},
2205+
"proposalEditorLimitReachedErrorMessage": "You have reached the maximum number of proposals allowed.",
2206+
"@proposalEditorLimitReachedErrorMessage": {
2207+
"description": "Dialog message in proposal editor when user has reached the max proposals limit."
2208+
},
2209+
"proposalEditorLimitReachedErrorTitle": "Unable to Publish Proposal",
2210+
"@proposalEditorLimitReachedErrorTitle": {
2211+
"description": "Dialog title in proposal editor when user has reached the max proposals limit."
2212+
},
21972213
"proposalEditorNotAnswered": "Not Answered",
21982214
"@proposalEditorNotAnswered": {
21992215
"description": "Placeholder text when a property has been not filled."

catalyst_voices/packages/internal/catalyst_voices_models/lib/src/catalyst_voices_models.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export 'document/enums/document_content_media_type.dart';
5151
export 'document/enums/document_property_format.dart';
5252
export 'document/enums/document_property_type.dart';
5353
export 'document/exception/document_import_invalid_data_exception.dart';
54+
export 'document/exception/document_sign_exception.dart';
5455
export 'document/schema/document_schema.dart';
5556
export 'document/schema/property/document_property_schema.dart';
5657
export 'document/specialized/comment_document.dart';
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import 'package:equatable/equatable.dart';
2+
3+
/// An exception denoting a problem with signed document creation.
4+
class DocumentSignException extends Equatable implements Exception {
5+
final String message;
6+
7+
const DocumentSignException(this.message);
8+
9+
@override
10+
List<Object?> get props => [message];
11+
}

catalyst_voices/packages/internal/catalyst_voices_repositories/lib/src/signed_document/signed_document_manager_impl.dart

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,25 @@ final class SignedDocumentManagerImpl implements SignedDocumentManager {
4848
required CatalystId catalystId,
4949
required CatalystPrivateKey privateKey,
5050
}) async {
51-
final compressedPayload = await _brotliCompressPayload(document.toBytes());
52-
53-
final coseSign = await CoseSign.sign(
54-
protectedHeaders: metadata.asCoseProtectedHeaders,
55-
unprotectedHeaders: metadata.asCoseUnprotectedHeaders,
56-
payload: compressedPayload,
57-
signers: [_CatalystSigner(catalystId, privateKey)],
58-
);
59-
60-
return _CoseSignedDocument(
61-
coseSign: coseSign,
62-
payload: document,
63-
metadata: metadata,
64-
signers: [catalystId],
65-
);
51+
try {
52+
final compressedPayload = await _brotliCompressPayload(document.toBytes());
53+
54+
final coseSign = await CoseSign.sign(
55+
protectedHeaders: metadata.asCoseProtectedHeaders,
56+
unprotectedHeaders: metadata.asCoseUnprotectedHeaders,
57+
payload: compressedPayload,
58+
signers: [_CatalystSigner(catalystId, privateKey)],
59+
);
60+
61+
return _CoseSignedDocument(
62+
coseSign: coseSign,
63+
payload: document,
64+
metadata: metadata,
65+
signers: [catalystId],
66+
);
67+
} on CoseSignException catch (error) {
68+
throw DocumentSignException('Failed to create a signed document!\nSource: ${error.source}');
69+
}
6670
}
6771

6872
Future<Uint8List> _brotliCompressPayload(Uint8List payload) async {

catalyst_voices/packages/internal/catalyst_voices_view_models/lib/src/proposal_builder/exception/proposal_builder_exception.dart

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,35 @@ import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
22
import 'package:catalyst_voices_view_models/src/exception/localized_exception.dart';
33
import 'package:flutter/material.dart';
44

5+
/// An exception thrown when the app cannot sign the document on the user's behalf.
6+
final class ProposalBuilderDocumentSignException extends LocalizedException {
7+
const ProposalBuilderDocumentSignException();
8+
9+
@override
10+
String message(BuildContext context) {
11+
return context.l10n.proposalEditorDocumentSignErrorMessage;
12+
}
13+
14+
String title(BuildContext context) {
15+
return context.l10n.proposalEditorDocumentSignErrorTitle;
16+
}
17+
}
18+
19+
/// An exception thrown attempting to create / import a new
20+
/// proposal when the user has reached the limit.
21+
final class ProposalBuilderLimitReachedException extends LocalizedException {
22+
const ProposalBuilderLimitReachedException();
23+
24+
@override
25+
String message(BuildContext context) {
26+
return context.l10n.proposalEditorLimitReachedErrorMessage;
27+
}
28+
29+
String title(BuildContext context) {
30+
return context.l10n.proposalEditorLimitReachedErrorTitle;
31+
}
32+
}
33+
534
/// Localized exception thrown when a proposal builder fails to publish a proposal.
635
final class ProposalBuilderPublishException extends LocalizedException {
736
const ProposalBuilderPublishException();
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export 'src/cose_constants.dart';
22
export 'src/cose_sign.dart';
33
export 'src/cose_sign1.dart';
4+
export 'src/exception/cose_exception.dart';
45
export 'src/types/cose_headers.dart';
56
export 'src/types/string_or_int.dart';
67
export 'src/types/uuid.dart';

0 commit comments

Comments
 (0)