Skip to content

Commit 4e5e8f2

Browse files
LynxLynxxdt-iohk
andauthored
feat(cat-voices): menu action (#2083)
* fix: adding all action that can be done on proposal in proposal builder and workspace * feat(cat-voices): update visibility logic for ProposalIterationHistory in WorkspaceProposalCard based on version length * feat: adding localization strings * feat: delete proposal * feat: implement proposal deletion and export actions in proposal_iteration_history_card.dart * fix: ensure context is mounted before deleting draft proposals in proposal_iteration_history_card.dart * fix: remove unused log statement from document_repository.dart to clean up code * feat: add dialog to unlock and edit proposals, with handling for local drafts in proposal_menu_action_button.dart and related files * feat: implement forget proposal functionality with confirmation dialog and new state handling in workspace_bloc and related files * fix: format * fix: self-review * fix: review changes --------- Co-authored-by: Dominik Toton <[email protected]>
1 parent c107281 commit 4e5e8f2

File tree

25 files changed

+1448
-270
lines changed

25 files changed

+1448
-270
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ final class Dependencies extends DependencyProvider {
9999
return WorkspaceBloc(
100100
get<CampaignService>(),
101101
get<ProposalService>(),
102+
get<DocumentMapper>(),
103+
get<DownloaderService>(),
102104
);
103105
})
104106
..registerFactory<ProposalBuilderBloc>(() {

catalyst_voices/apps/voices/lib/pages/proposal_builder/appbar/proposal_builder_status_action.dart

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

33
import 'package:catalyst_voices/common/ext/build_context_ext.dart';
4+
import 'package:catalyst_voices/pages/proposal_builder/appbar/proposal_menu_item_action_enum.dart';
45
import 'package:catalyst_voices/widgets/modals/proposals/proposal_builder_delete_confirmation_dialog.dart';
56
import 'package:catalyst_voices/widgets/modals/proposals/publish_proposal_iteration_dialog.dart';
67
import 'package:catalyst_voices/widgets/modals/proposals/submit_proposal_for_review_dialog.dart';
@@ -20,11 +21,13 @@ class ProposalBuilderStatusAction extends StatelessWidget {
2021
@override
2122
Widget build(BuildContext context) {
2223
return BlocSelector<ProposalBuilderBloc, ProposalBuilderState,
23-
({bool offstage, List<_MenuItemEnum> items})>(
24+
({bool offstage, List<ProposalMenuItemAction> items})>(
2425
selector: (state) {
2526
return (
2627
offstage: state.isLoading || state.error != null,
27-
items: _MenuItemEnum.availableOptions(state.metadata.publish)
28+
items: ProposalMenuItemAction.proposalBuilderAvailableOptions(
29+
state.metadata.publish,
30+
)
2831
);
2932
},
3033
builder: (context, state) {
@@ -66,7 +69,7 @@ class _Button extends StatelessWidget {
6669
}
6770

6871
class _MenuItem extends StatelessWidget {
69-
final _MenuItemEnum item;
72+
final ProposalMenuItemAction item;
7073
final String? proposalTitle;
7174
final ProposalBuilderMetadata metadata;
7275

@@ -87,9 +90,9 @@ class _MenuItem extends StatelessWidget {
8790
final description = item.description(context, metadata);
8891

8992
return ListTile(
90-
title: Text(
91-
title,
92-
style: Theme.of(context).textTheme.bodyLarge,
93+
title: MarkdownText(
94+
selectable: false,
95+
MarkdownData(title),
9396
),
9497
subtitle: description == null
9598
? null
@@ -99,97 +102,14 @@ class _MenuItem extends StatelessWidget {
99102
color: Theme.of(context).colors.textOnPrimaryLevel1,
100103
),
101104
),
102-
leading: item.icon.buildIcon(),
105+
leading: item.icon().buildIcon(),
103106
mouseCursor: item.clickable ? SystemMouseCursors.click : null,
104107
);
105108
}
106109
}
107110

108-
enum _MenuItemEnum {
109-
view(clickable: false),
110-
publish,
111-
submit,
112-
export,
113-
delete;
114-
115-
final bool clickable;
116-
117-
const _MenuItemEnum({this.clickable = true});
118-
119-
SvgGenImage get icon {
120-
switch (this) {
121-
case _MenuItemEnum.view:
122-
return VoicesAssets.icons.documentText;
123-
case _MenuItemEnum.publish:
124-
return VoicesAssets.icons.chatAlt2;
125-
case _MenuItemEnum.submit:
126-
return VoicesAssets.icons.badgeCheck;
127-
case _MenuItemEnum.export:
128-
return VoicesAssets.icons.folderOpen;
129-
case _MenuItemEnum.delete:
130-
return VoicesAssets.icons.trash;
131-
}
132-
}
133-
134-
String? description(BuildContext context, ProposalBuilderMetadata metadata) {
135-
return switch (this) {
136-
_MenuItemEnum.view => _formatProposalDescription(context, metadata),
137-
_MenuItemEnum.publish =>
138-
context.l10n.proposalEditorStatusDropdownPublishDescription,
139-
_MenuItemEnum.submit =>
140-
context.l10n.proposalEditorStatusDropdownSubmitDescription,
141-
_MenuItemEnum.export || _MenuItemEnum.delete => null,
142-
};
143-
}
144-
145-
String title(
146-
BuildContext context,
147-
String? proposalTitle,
148-
int currentIteration,
149-
) {
150-
final nextIteration = currentIteration + 1;
151-
return switch (this) {
152-
_MenuItemEnum.view => (proposalTitle != null && proposalTitle.isNotBlank)
153-
? proposalTitle
154-
: context.l10n.proposalEditorStatusDropdownViewTitle,
155-
_MenuItemEnum.publish =>
156-
context.l10n.proposalEditorStatusDropdownPublishTitle(nextIteration),
157-
_MenuItemEnum.submit =>
158-
context.l10n.proposalEditorStatusDropdownSubmitTitle(nextIteration),
159-
_MenuItemEnum.export =>
160-
context.l10n.proposalEditorStatusDropdownExportTitle,
161-
_MenuItemEnum.delete =>
162-
context.l10n.proposalEditorStatusDropdownDeleteTitle,
163-
};
164-
}
165-
166-
String _formatProposalDescription(
167-
BuildContext context,
168-
ProposalBuilderMetadata metadata,
169-
) {
170-
final currentIteration = metadata.latestVersion?.number ?? 0;
171-
final nextIteration = currentIteration + 1;
172-
return context.l10n.proposalEditorStatusDropdownViewDescription(
173-
nextIteration,
174-
);
175-
}
176-
177-
static List<_MenuItemEnum> availableOptions(ProposalPublish proposalPublish) {
178-
switch (proposalPublish) {
179-
case ProposalPublish.localDraft:
180-
return _MenuItemEnum.values;
181-
case ProposalPublish.publishedDraft:
182-
// TODO(dtscalac): delete? revert?
183-
return [view, submit, export];
184-
case ProposalPublish.submittedProposal:
185-
// TODO(dtscalac): delete? revert?
186-
return [view, export];
187-
}
188-
}
189-
}
190-
191111
class _MenuItemSelector extends StatelessWidget {
192-
final _MenuItemEnum item;
112+
final ProposalMenuItemAction item;
193113

194114
const _MenuItemSelector({required this.item});
195115

@@ -218,7 +138,7 @@ class _MenuItemSelector extends StatelessWidget {
218138
}
219139

220140
class _PopupMenuButton extends StatefulWidget {
221-
final List<_MenuItemEnum> items;
141+
final List<ProposalMenuItemAction> items;
222142

223143
const _PopupMenuButton({required this.items});
224144

@@ -252,7 +172,8 @@ class _PopupMenuButtonState extends State<_PopupMenuButton> {
252172
].separatedBy(const PopupMenuDivider(height: 0)).toList();
253173
},
254174
onSelected: (value) {
255-
final item = _MenuItemEnum.values[value];
175+
final item = ProposalMenuItemAction.values[value];
176+
256177
_onSelected(item);
257178
},
258179
);
@@ -278,19 +199,18 @@ class _PopupMenuButtonState extends State<_PopupMenuButton> {
278199
.add(ExportProposalEvent(filePrefix: prefix));
279200
}
280201

281-
void _onSelected(_MenuItemEnum item) {
202+
void _onSelected(ProposalMenuItemAction item) {
282203
switch (item) {
283-
case _MenuItemEnum.view:
284-
// do nothing
285-
break;
286-
case _MenuItemEnum.publish:
204+
case ProposalMenuItemAction.publish:
287205
unawaited(_publishIteration());
288-
case _MenuItemEnum.submit:
206+
case ProposalMenuItemAction.submit:
289207
unawaited(_submitForReview());
290-
case _MenuItemEnum.export:
208+
case ProposalMenuItemAction.export:
291209
_exportProposal();
292-
case _MenuItemEnum.delete:
210+
case ProposalMenuItemAction.delete:
293211
unawaited(_deleteProposal());
212+
case _:
213+
break;
294214
}
295215
}
296216

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import 'package:catalyst_voices_assets/generated/assets.gen.dart';
2+
import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart';
3+
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
4+
import 'package:catalyst_voices_models/catalyst_voices_models.dart';
5+
import 'package:catalyst_voices_shared/catalyst_voices_shared.dart';
6+
import 'package:flutter/widgets.dart';
7+
8+
enum ProposalMenuItemAction {
9+
view(clickable: false),
10+
back,
11+
edit,
12+
publish,
13+
submit,
14+
forget,
15+
export,
16+
share,
17+
delete;
18+
19+
final bool clickable;
20+
21+
const ProposalMenuItemAction({this.clickable = true});
22+
23+
String? description(BuildContext context, ProposalBuilderMetadata metadata) {
24+
return switch (this) {
25+
ProposalMenuItemAction.view =>
26+
_formatProposalDescription(context, metadata),
27+
ProposalMenuItemAction.publish =>
28+
context.l10n.proposalEditorStatusDropdownPublishDescription,
29+
ProposalMenuItemAction.submit =>
30+
context.l10n.proposalEditorStatusDropdownSubmitDescription,
31+
_ => null,
32+
};
33+
}
34+
35+
SvgGenImage icon({bool workspace = false}) {
36+
switch (this) {
37+
case ProposalMenuItemAction.view:
38+
return workspace
39+
? VoicesAssets.icons.eye
40+
: VoicesAssets.icons.documentText;
41+
case ProposalMenuItemAction.back:
42+
return VoicesAssets.icons.logout1;
43+
case ProposalMenuItemAction.publish:
44+
return VoicesAssets.icons.chatAlt2;
45+
case ProposalMenuItemAction.submit:
46+
return VoicesAssets.icons.badgeCheck;
47+
case ProposalMenuItemAction.export:
48+
return workspace
49+
? VoicesAssets.icons.duplicate
50+
: VoicesAssets.icons.folderOpen;
51+
case ProposalMenuItemAction.delete:
52+
return VoicesAssets.icons.trash;
53+
case ProposalMenuItemAction.edit:
54+
return VoicesAssets.icons.pencilAlt;
55+
case ProposalMenuItemAction.forget:
56+
return VoicesAssets.icons.unlink;
57+
case ProposalMenuItemAction.share:
58+
return VoicesAssets.icons.upload;
59+
}
60+
}
61+
62+
String title(
63+
BuildContext context,
64+
String? proposalTitle,
65+
int currentIteration,
66+
) {
67+
final nextIteration = currentIteration + 1;
68+
return switch (this) {
69+
ProposalMenuItemAction.view =>
70+
(proposalTitle != null && proposalTitle.isNotBlank)
71+
? proposalTitle
72+
: context.l10n.proposalEditorStatusDropdownViewTitle,
73+
ProposalMenuItemAction.back => context.l10n.proposalEditorBackToProposals,
74+
ProposalMenuItemAction.publish =>
75+
context.l10n.proposalEditorStatusDropdownPublishTitle(nextIteration),
76+
ProposalMenuItemAction.submit =>
77+
context.l10n.proposalEditorStatusDropdownSubmitTitle(nextIteration),
78+
ProposalMenuItemAction.forget => context.l10n.forgetProposal,
79+
ProposalMenuItemAction.export =>
80+
context.l10n.proposalEditorStatusDropdownExportTitle,
81+
ProposalMenuItemAction.delete =>
82+
context.l10n.proposalEditorStatusDropdownDeleteTitle,
83+
_ => '',
84+
};
85+
}
86+
87+
String workspaceTitle(BuildContext context, ProposalPublish proposalPublish) {
88+
final isFinal = proposalPublish.isPublished;
89+
return switch (this) {
90+
ProposalMenuItemAction.edit =>
91+
isFinal ? context.l10n.unlockAndEdit : context.l10n.editButtonText,
92+
ProposalMenuItemAction.view => context.l10n.view,
93+
ProposalMenuItemAction.share => context.l10n.share,
94+
ProposalMenuItemAction.forget => context.l10n.forgetProposal,
95+
ProposalMenuItemAction.export => context.l10n.export,
96+
ProposalMenuItemAction.delete => context.l10n.delete,
97+
_ => '',
98+
};
99+
}
100+
101+
String _formatProposalDescription(
102+
BuildContext context,
103+
ProposalBuilderMetadata metadata,
104+
) {
105+
final currentIteration = metadata.latestVersion?.number ?? 0;
106+
final nextIteration = currentIteration + 1;
107+
return context.l10n.proposalEditorStatusDropdownViewDescription(
108+
nextIteration,
109+
);
110+
}
111+
112+
static List<ProposalMenuItemAction> proposalBuilderAvailableOptions(
113+
ProposalPublish proposalPublish,
114+
) {
115+
switch (proposalPublish) {
116+
case ProposalPublish.localDraft:
117+
return [view, back, publish, submit, export, delete];
118+
case ProposalPublish.publishedDraft:
119+
return [view, back, submit, forget, export];
120+
case ProposalPublish.submittedProposal:
121+
// Submitted can't be open in editor
122+
return [];
123+
}
124+
}
125+
126+
static List<ProposalMenuItemAction> workspaceAvailableOptions(
127+
ProposalPublish proposalPublish,
128+
) {
129+
return switch (proposalPublish) {
130+
ProposalPublish.localDraft => [edit, export, delete],
131+
_ => [
132+
edit,
133+
view,
134+
share,
135+
forget,
136+
export,
137+
],
138+
};
139+
}
140+
}

0 commit comments

Comments
 (0)