feat: add feature flags to enable/disable functionality

This commit is contained in:
Joey Boerwinkel 2024-07-29 15:15:11 +02:00
parent f3c8b5f473
commit ad0cb1c96e
4 changed files with 103 additions and 54 deletions

View file

@ -28,6 +28,11 @@ class AvailabilityOptions {
this.timePickerBuilder, this.timePickerBuilder,
this.loadingIndicatorBuilder = DefaultLoader.builder, this.loadingIndicatorBuilder = DefaultLoader.builder,
this.errorDisplayBuilder = DefaultErrorDisplayDialog.defaultErrorDisplay, this.errorDisplayBuilder = DefaultErrorDisplayDialog.defaultErrorDisplay,
this.featureSet = const {
AvailabilityFeature.breaks,
AvailabilityFeature.customAvailability,
AvailabilityFeature.templates,
},
AvailabilityDataInterface? dataInterface, AvailabilityDataInterface? dataInterface,
}) : dataInterface = dataInterface ?? LocalAvailabilityDataInterface(); }) : dataInterface = dataInterface ?? LocalAvailabilityDataInterface();
@ -95,6 +100,11 @@ class AvailabilityOptions {
/// base widget through [baseScreenBuilder]. /// base widget through [baseScreenBuilder].
final ErrorDisplayBuilder errorDisplayBuilder; final ErrorDisplayBuilder errorDisplayBuilder;
/// Enabled features when using these options
///
/// Look at [AvailabilityFeature] for more information.
final Set<AvailabilityFeature> featureSet;
final _borderRadius = BorderRadius.circular(5); final _borderRadius = BorderRadius.circular(5);
/// The border radius used on the individual elements /// The border radius used on the individual elements
@ -233,3 +243,21 @@ typedef ErrorDisplayBuilder = Future<void> Function(
BuildContext context, BuildContext context,
AvailabilityError error, AvailabilityError error,
); );
/// Lists all features that are toggleable
enum AvailabilityFeature {
/// Anything template related
templates,
/// Definition of breaks
breaks,
/// Definition of custom availabilities without templates
customAvailability;
}
/// Extension for validating whether a feature is enabled
extension FeatureRequirement on Set<AvailabilityFeature> {
/// Whether a required feature is enabled
bool require(AvailabilityFeature feature) => contains(feature);
}

View file

@ -241,6 +241,7 @@ class _AvailabilitiesModificationScreenLayout extends HookWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
var availabilityScope = AvailabilityScope.of(context); var availabilityScope = AvailabilityScope.of(context);
var options = availabilityScope.options; var options = availabilityScope.options;
var featureSet = options.featureSet;
return options.baseScreenBuilder( return options.baseScreenBuilder(
context, context,
@ -253,25 +254,31 @@ class _AvailabilitiesModificationScreenLayout extends HookWidget {
onChanged: onClearSection, onChanged: onClearSection,
), ),
if (!viewModel.clearAvailability) ...[ if (!viewModel.clearAvailability) ...[
const SizedBox(height: 24), if (featureSet.require(AvailabilityFeature.templates)) ...[
AvailabilityTemplateSelection( const SizedBox(height: 24),
selectedTemplates: selectedTemplates, AvailabilityTemplateSelection(
onTemplateAdd: onTemplateSelected, selectedTemplates: selectedTemplates,
onTemplatesRemoved: onTemplatesRemoved, onTemplateAdd: onTemplateSelected,
), onTemplatesRemoved: onTemplatesRemoved,
const SizedBox(height: 24), ),
AvailabilityTimeSelection( ],
viewModel: viewModel, if (featureSet.require(AvailabilityFeature.customAvailability)) ...[
key: ValueKey(viewModel), const SizedBox(height: 24),
onStartChanged: onStartChanged, AvailabilityTimeSelection(
onEndChanged: onEndChanged, viewModel: viewModel,
), key: ValueKey(viewModel),
const SizedBox(height: 24), onStartChanged: onStartChanged,
PauseSelection( onEndChanged: onEndChanged,
breaks: viewModel.breaks, ),
editingTemplate: false, if (featureSet.require(AvailabilityFeature.breaks)) ...[
onBreaksChanged: onBreaksChanged, const SizedBox(height: 24),
), PauseSelection(
breaks: viewModel.breaks,
editingTemplate: false,
onBreaksChanged: onBreaksChanged,
),
],
],
], ],
], ],
buttons: [saveButton], buttons: [saveButton],

View file

@ -1,4 +1,5 @@
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_availability/src/config/availability_options.dart";
import "package:flutter_availability/src/util/scope.dart"; import "package:flutter_availability/src/util/scope.dart";
import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart"; import "package:flutter_availability_data_interface/flutter_availability_data_interface.dart";
@ -35,6 +36,7 @@ class _TemplateLegendState extends State<TemplateLegend> {
var options = availabilityScope.options; var options = availabilityScope.options;
var colors = options.colors; var colors = options.colors;
var translations = options.translations; var translations = options.translations;
var featureSet = options.featureSet;
var templatesLoading = var templatesLoading =
widget.availabilities.connectionState == ConnectionState.waiting; widget.availabilities.connectionState == ConnectionState.waiting;
@ -47,6 +49,11 @@ class _TemplateLegendState extends State<TemplateLegend> {
var templatesVisible = templatesAvailable && _templateDrawerOpen; var templatesVisible = templatesAvailable && _templateDrawerOpen;
if (!templatesAvailable &&
!featureSet.require(AvailabilityFeature.templates)) {
return const SizedBox.shrink();
}
void onDrawerHeaderClick() { void onDrawerHeaderClick() {
if (!templatesAvailable && !_templateDrawerOpen) { if (!templatesAvailable && !_templateDrawerOpen) {
return; return;
@ -115,25 +122,23 @@ class _TemplateLegendState extends State<TemplateLegend> {
), ),
), ),
], ],
for (var template in templates) ...[ if (featureSet.require(AvailabilityFeature.templates)) ...[
Padding( for (var template in templates) ...[
padding: const EdgeInsets.only( Padding(
top: 10, padding: const EdgeInsets.only(
left: 12, top: 10,
left: 12,
),
child: _TemplateLegendItem(
name: template.name,
backgroundColor: Color(template.color),
),
), ),
child: _TemplateLegendItem( ],
name: template.name, const SizedBox(height: 10),
backgroundColor: Color(template.color), createNewTemplateButton,
),
),
], ],
Padding( const SizedBox(height: 8),
padding: const EdgeInsets.only(
top: 10,
bottom: 8,
),
child: createNewTemplateButton,
),
], ],
), ),
), ),
@ -184,12 +189,14 @@ class _TemplateLegendState extends State<TemplateLegend> {
padding: const EdgeInsets.only(right: 2), padding: const EdgeInsets.only(right: 2),
child: body, child: body,
), ),
if (!templatesVisible) ...[ if (featureSet.require(AvailabilityFeature.templates)) ...[
const SizedBox(height: 12), if (!templatesVisible) ...[
if (templatesLoading) ...[ const SizedBox(height: 12),
options.loadingIndicatorBuilder(context), if (templatesLoading) ...[
] else ...[ options.loadingIndicatorBuilder(context),
createNewTemplateButton, ] else ...[
createNewTemplateButton,
],
], ],
], ],
], ],

View file

@ -1,7 +1,9 @@
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_availability/src/config/availability_options.dart";
import "package:flutter_availability/src/ui/view_models/template_daydata_view_model.dart"; import "package:flutter_availability/src/ui/view_models/template_daydata_view_model.dart";
import "package:flutter_availability/src/ui/widgets/pause_selection.dart"; import "package:flutter_availability/src/ui/widgets/pause_selection.dart";
import "package:flutter_availability/src/ui/widgets/template_time_selection.dart"; import "package:flutter_availability/src/ui/widgets/template_time_selection.dart";
import "package:flutter_availability/src/util/scope.dart";
/// Section for selecting the time and breaks for a single day /// Section for selecting the time and breaks for a single day
class TemplateTimeAndBreakSection extends StatelessWidget { class TemplateTimeAndBreakSection extends StatelessWidget {
@ -19,17 +21,20 @@ class TemplateTimeAndBreakSection extends StatelessWidget {
final void Function(DayTemplateDataViewModel data) onDayDataChanged; final void Function(DayTemplateDataViewModel data) onDayDataChanged;
@override @override
Widget build(BuildContext context) => Column( Widget build(BuildContext context) {
children: [ var featureSet = AvailabilityScope.of(context).options.featureSet;
TemplateTimeSelection( return Column(
key: ValueKey([dayData.startTime, dayData.endTime]), children: [
startTime: dayData.startTime, TemplateTimeSelection(
endTime: dayData.endTime, key: ValueKey([dayData.startTime, dayData.endTime]),
onStartChanged: (start) => startTime: dayData.startTime,
onDayDataChanged(dayData.copyWith(startTime: start)), endTime: dayData.endTime,
onEndChanged: (end) => onStartChanged: (start) =>
onDayDataChanged(dayData.copyWith(endTime: end)), onDayDataChanged(dayData.copyWith(startTime: start)),
), onEndChanged: (end) =>
onDayDataChanged(dayData.copyWith(endTime: end)),
),
if (featureSet.require(AvailabilityFeature.breaks)) ...[
const SizedBox(height: 24), const SizedBox(height: 24),
PauseSelection( PauseSelection(
editingTemplate: true, editingTemplate: true,
@ -38,5 +43,7 @@ class TemplateTimeAndBreakSection extends StatelessWidget {
onDayDataChanged(dayData.copyWith(breaks: pauses)), onDayDataChanged(dayData.copyWith(breaks: pauses)),
), ),
], ],
); ],
);
}
} }