Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cat-voices): Campaign managment status UI #1314

Merged
merged 6 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions catalyst_voices/apps/voices/lib/app/view/app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class _AppState extends State<App> {
BlocProvider<ProposalsCubit>(
create: (_) => Dependencies.instance.get<ProposalsCubit>(),
),
BlocProvider<CampaignBuilderCubit>(
create: (_) => Dependencies.instance.get<CampaignBuilderCubit>(),
),
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,11 @@ final class Dependencies extends DependencyProvider {
return CampaignDetailsBloc(
get<CampaignRepository>(),
);
});
})
// TODO(ryszard-schossler): add repository for campaign management
..registerLazySingleton<CampaignBuilderCubit>(
CampaignBuilderCubit.new,
);
}

void _registerRepositories() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'package:catalyst_voices/widgets/buttons/voices_text_button.dart';
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
import 'package:catalyst_voices_models/catalyst_voices_models.dart';
import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart';
import 'package:flutter/material.dart';

/// The "events" tab of the [CampaignAdminToolsDialog].
Expand Down Expand Up @@ -31,13 +31,14 @@ class _CampaignStatusChooser extends StatelessWidget {
child: Column(
children: [
const SizedBox(height: 8),
for (final status in CampaignStatus.values)
// TODO(dtscalac): store active one somewhere
_EventItem(
status: status,
isActive: status == CampaignStatus.draft,
onTap: () {},
),
for (final status in CampaignStage.values)
if (status != CampaignStage.scheduled)
// TODO(dtscalac): store active one somewhere
_EventItem(
status: status,
isActive: status == CampaignStage.draft,
onTap: () {},
),
const SizedBox(height: 8),
],
),
Expand All @@ -46,7 +47,7 @@ class _CampaignStatusChooser extends StatelessWidget {
}

class _EventItem extends StatelessWidget {
final CampaignStatus status;
final CampaignStage status;
final bool isActive;
final VoidCallback onTap;

Expand Down Expand Up @@ -97,15 +98,15 @@ class _EventItem extends StatelessWidget {
}

SvgGenImage get _icon => switch (status) {
CampaignStatus.draft => VoicesAssets.icons.clock,
CampaignStatus.live => VoicesAssets.icons.flag,
CampaignStatus.completed => VoicesAssets.icons.calendar,
CampaignStage.draft => VoicesAssets.icons.clock,
CampaignStage.live => VoicesAssets.icons.flag,
_ => VoicesAssets.icons.calendar,
};

String _text(VoicesLocalizations l10n) => switch (status) {
CampaignStatus.draft => l10n.campaignPreviewEventBefore,
CampaignStatus.live => l10n.campaignPreviewEventDuring,
CampaignStatus.completed => l10n.campaignPreviewEventAfter,
CampaignStage.draft => l10n.campaignPreviewEventBefore,
CampaignStage.live => l10n.campaignPreviewEventDuring,
_ => l10n.campaignPreviewEventAfter,
};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import 'package:catalyst_voices/pages/campaign/details/widgets/campaign_management_dialog.dart';
import 'package:catalyst_voices/widgets/widgets.dart';
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart';
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
import 'package:catalyst_voices_models/catalyst_voices_models.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class CampaignManagement extends StatefulWidget {
const CampaignManagement({super.key});

@override
State<CampaignManagement> createState() => _CampaignManagementState();
}

class _CampaignManagementState extends State<CampaignManagement> {
@override
void initState() {
super.initState();
context.read<CampaignBuilderCubit>().getCampaignStatus();
}

@override
Widget build(BuildContext context) {
final currentStatus = context.watch<CampaignBuilderCubit>().campaignStatus;
return Row(
children: [
VoicesOutlinedButton(
child: Text(context.l10n.campaignManagement),
onTap: () async {
final result =
await CampaignManagementDialog.show(context, currentStatus);
_handleDialogResult(result);
},
),
_CampaignStatusIndicator(
campaignStatus: CampaignPublish.draft,
currentStatus: currentStatus,
),
_CampaignStatusIndicator(
campaignStatus: CampaignPublish.published,
currentStatus: currentStatus,
),
],
);
}

void _handleDialogResult(CampaignPublish? newStatus) {
if (newStatus == null) return;
context.read<CampaignBuilderCubit>().updateCampaignStatus(newStatus);
}
}

class _CampaignStatusIndicator extends StatelessWidget {
final CampaignPublish campaignStatus;
final CampaignPublish? currentStatus;

const _CampaignStatusIndicator({
required this.campaignStatus,
required this.currentStatus,
});

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);

return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: DecoratedBox(
decoration: BoxDecoration(
color: currentStatus == campaignStatus
? theme.colors.success
: theme.colors.onSurfaceNeutral012?.withOpacity(.12),
borderRadius: BorderRadius.circular(8),
),
child: Padding(
padding: const EdgeInsets.fromLTRB(8, 8, 16, 8),
child: Row(
children: [
VoicesAssets.icons.check.buildIcon(
color: currentStatus == campaignStatus
? theme.colors.successContainer
: theme.colors.onSurfaceNeutral012,
),
const SizedBox(width: 8),
switch (campaignStatus) {
CampaignPublish.draft => _Text(
context.l10n.draft,
isSelected: campaignStatus == currentStatus,
),
CampaignPublish.published => _Text(
context.l10n.published,
isSelected: campaignStatus == currentStatus,
),
},
],
),
),
),
);
}
}

class _Text extends StatelessWidget {
final String text;
final bool isSelected;

const _Text(
this.text, {
required this.isSelected,
});

@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Text(
text,
style: Theme.of(context).textTheme.labelLarge?.copyWith(
color: isSelected
? theme.colors.successContainer
: theme.colors.onSurfaceNeutral012,
),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import 'package:catalyst_voices/widgets/modals/details/voices_align_title_header.dart';
import 'package:catalyst_voices/widgets/widgets.dart';
import 'package:catalyst_voices_blocs/catalyst_voices_blocs.dart';
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
import 'package:catalyst_voices_models/catalyst_voices_models.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class CampaignManagementDialog extends StatefulWidget {
final CampaignPublish? initialValue;
const CampaignManagementDialog._(this.initialValue);

static Future<CampaignPublish?> show(
BuildContext context,
CampaignPublish? initialValue,
) async {
final result = await VoicesDialog.show<CampaignPublish?>(
context: context,
builder: (context) => CampaignManagementDialog._(initialValue),
);

return result;
}

@override
State<StatefulWidget> createState() => _CampaignManagementDialogState();
}

class _CampaignManagementDialogState extends State<CampaignManagementDialog> {
late CampaignPublish _campaignSetup;

@override
void initState() {
super.initState();
_campaignSetup = widget.initialValue ?? CampaignPublish.draft;
}

@override
Widget build(BuildContext context) {
return VoicesDetailsDialog(
constraints: const BoxConstraints(maxWidth: 750, maxHeight: 270),
backgroundColor: Theme.of(context).colors.elevationsOnSurfaceNeutralLv0,
header: VoicesAlignTitleHeader(
title: context.l10n.campaignManagement,
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 24),
),
body: Padding(
padding: const EdgeInsets.fromLTRB(24, 12, 24, 24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
context.l10n.status,
style: Theme.of(context).textTheme.titleSmall,
),
const SizedBox(height: 8),
_CampaignPublishSegmentButton(
value: _campaignSetup,
onChanged: (value) => _campaignSetup = value,
),
const Spacer(),
Align(
alignment: Alignment.centerRight,
child: VoicesFilledButton(
child: Text(context.l10n.saveButtonText),
onTap: () {
Navigator.of(context).pop(_campaignSetup);
context
.read<CampaignBuilderCubit>()
.updateCampaignStatus(_campaignSetup);
},
),
),
],
),
),
);
}
}

class _CampaignPublishSegmentButton extends StatefulWidget {
final CampaignPublish value;
final ValueChanged<CampaignPublish> onChanged;

const _CampaignPublishSegmentButton({
required this.value,
required this.onChanged,
});

@override
State<_CampaignPublishSegmentButton> createState() => _SingleChoiceState();
}

class _SingleChoiceState extends State<_CampaignPublishSegmentButton> {
late CampaignPublish _segmentValue;

@override
void initState() {
super.initState();
_segmentValue = widget.value;
}

@override
Widget build(BuildContext context) {
return VoicesSegmentedButton<CampaignPublish>(
showSelectedIcon: false,
segments: <ButtonSegment<CampaignPublish>>[
ButtonSegment<CampaignPublish>(
value: CampaignPublish.draft,
label: Text(context.l10n.draft),
),
ButtonSegment<CampaignPublish>(
value: CampaignPublish.published,
label: Text(context.l10n.published),
),
],
selected: <CampaignPublish>{_segmentValue},
onChanged: (Set<CampaignPublish> newSelection) {
setState(() {
_segmentValue = newSelection.first;
});
widget.onChanged(_segmentValue);
},
);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:catalyst_voices/common/ext/ext.dart';
import 'package:catalyst_voices/pages/campaign/admin_tools/campaign_admin_tools_dialog.dart';
import 'package:catalyst_voices/pages/campaign/details/widgets/campaign_management.dart';
import 'package:catalyst_voices/pages/spaces/appbar/spaces_theme_mode_switch.dart';
import 'package:catalyst_voices/pages/spaces/drawer/spaces_drawer.dart';
import 'package:catalyst_voices/widgets/widgets.dart';
Expand Down Expand Up @@ -68,11 +69,7 @@ class _SpacesShellPageState extends State<SpacesShellPage> {
appBar: VoicesAppBar(
leading: isVisitor ? null : const DrawerToggleButton(),
automaticallyImplyLeading: false,
actions: const [
SpacesThemeModeSwitch(),
SessionActionHeader(),
SessionStateHeader(),
],
actions: _getActions(widget.space),
),
drawer: isVisitor
? null
Expand All @@ -96,6 +93,21 @@ class _SpacesShellPageState extends State<SpacesShellPage> {
);
}

List<Widget> _getActions(Space space) {
if (space == Space.treasury) {
return [
const CampaignManagement(),
const SpacesThemeModeSwitch(),
];
} else {
return [
const SpacesThemeModeSwitch(),
const SessionActionHeader(),
const SessionStateHeader(),
];
}
}

void _toggleCampaignAdminTools() {
setState(() {
_showAdminTools = !_showAdminTools;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import 'package:catalyst_voices/widgets/widgets.dart';
import 'package:catalyst_voices_assets/catalyst_voices_assets.dart';
import 'package:catalyst_voices_brands/catalyst_voices_brands.dart';
import 'package:catalyst_voices_localization/catalyst_voices_localization.dart';
import 'package:catalyst_voices_models/catalyst_voices_models.dart';
import 'package:catalyst_voices_view_models/catalyst_voices_view_models.dart';
import 'package:flutter/material.dart';

Expand Down Expand Up @@ -76,7 +75,7 @@ class CampaignPreviewCard extends StatelessWidget {
}

String _getButtonText(BuildContext context) {
if (campaign.stage == CampaignStatus.live) {
if (campaign.stage == CampaignStage.live) {
return context.l10n.viewProposals;
} else {
return context.l10n.viewVotingResults;
Expand Down
Loading
Loading