Mobile Client: Form: inherit reutrn from preceding actions

This commit is contained in:
Arthi-chaud
2022-03-04 00:40:07 +01:00
parent fb658e3f8d
commit 99c4b11ac7
6 changed files with 152 additions and 31 deletions
+6
View File
@@ -1,5 +1,7 @@
PODS:
- Flutter (1.0.0)
- flutter_keyboard_visibility (0.0.1):
- Flutter
- FMDB (2.7.5):
- FMDB/standard (= 2.7.5)
- FMDB/standard (2.7.5)
@@ -15,6 +17,7 @@ PODS:
DEPENDENCIES:
- Flutter (from `Flutter`)
- flutter_keyboard_visibility (from `.symlinks/plugins/flutter_keyboard_visibility/ios`)
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
- shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`)
@@ -27,6 +30,8 @@ SPEC REPOS:
EXTERNAL SOURCES:
Flutter:
:path: Flutter
flutter_keyboard_visibility:
:path: ".symlinks/plugins/flutter_keyboard_visibility/ios"
path_provider_ios:
:path: ".symlinks/plugins/path_provider_ios/ios"
shared_preferences_ios:
@@ -38,6 +43,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
flutter_keyboard_visibility: 0339d06371254c3eb25eeb90ba8d17dca8f9c069
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
path_provider_ios: 7d7ce634493af4477d156294792024ec3485acd5
shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad
+12 -3
View File
@@ -79,8 +79,10 @@ class _CreatePipelinePageState extends State<CreatePipelinePage> {
onTap: () {
showAerisCardPage(
context,
(_) =>
SetupActionPage(action: trigger))
(_) => SetupActionPage(
action: trigger,
parentReactions: reactions
))
.then((_) => setState(() {}));
})
: ActionCard(
@@ -88,6 +90,8 @@ class _CreatePipelinePageState extends State<CreatePipelinePage> {
title: trigger.displayName(),
trailing: ActionCardPopupMenu(
deletable: false,
parentReactions: reactions,
parentTrigger: trigger,
action: trigger,
then: () => setState(() {})),
),
@@ -106,6 +110,8 @@ class _CreatePipelinePageState extends State<CreatePipelinePage> {
leading: reaction.service.getLogo(logoSize: 50),
title: reaction.displayName(),
trailing: ActionCardPopupMenu(
parentTrigger: trigger == Trigger.template() ? null : trigger,
parentReactions: reactions,
deletable: reactions.length > 1,
action: reaction,
then: () => setState(() {}),
@@ -128,7 +134,10 @@ class _CreatePipelinePageState extends State<CreatePipelinePage> {
showAerisCardPage(
context,
(_) => SetupActionPage(
action: newreact))
action: newreact,
parentReactions: reactions,
parentTrigger: trigger == Trigger.template() ? null : trigger,
))
.then((_) => setState(() {
if (newreact != Reaction.template()) {
reactions.add(newreact);
+10 -1
View File
@@ -95,7 +95,12 @@ class _PipelineDetailPageState extends State<PipelineDetailPage> {
onTap: () {
Reaction newreaction = Reaction.template();
showAerisCardPage(
context, (_) => SetupActionPage(action: newreaction))
context, (_) => SetupActionPage(
action: newreaction,
parentTrigger: pipeline.trigger,
parentReactions: pipeline.reactions,
)
)
.then((r) {
if (newreaction != Reaction.template()) {
setState(() {
@@ -138,6 +143,8 @@ class _PipelineDetailPageState extends State<PipelineDetailPage> {
title: pipeline.trigger.displayName(),
trailing: ActionCardPopupMenu(
deletable: false,
parentTrigger: pipeline.trigger,
parentReactions: pipeline.reactions,
action: pipeline.trigger,
then: () {
setState(() {});
@@ -154,6 +161,8 @@ class _PipelineDetailPageState extends State<PipelineDetailPage> {
leading: reaction.service.getLogo(logoSize: 50),
title: reaction.displayName(),
trailing: ActionCardPopupMenu(
parentTrigger: pipeline.trigger,
parentReactions: pipeline.reactions,
deletable: pipeline.reactions.length > 1,
action: reaction,
then: () {
+10 -1
View File
@@ -1,6 +1,7 @@
import 'package:aeris/src/models/action_parameter.dart';
import 'package:aeris/src/models/action_template.dart';
import 'package:aeris/src/aeris_api.dart';
import 'package:aeris/src/models/reaction.dart';
import 'package:aeris/src/models/trigger.dart';
import 'package:flutter/material.dart';
import 'package:aeris/src/models/action.dart' as aeris;
@@ -14,10 +15,15 @@ import 'package:skeleton_loader/skeleton_loader.dart';
///Page to setup an action
class SetupActionPage extends StatefulWidget {
const SetupActionPage({Key? key, required this.action}) : super(key: key);
const SetupActionPage({Key? key, required this.action, required this.parentReactions, this.parentTrigger}) : super(key: key);
/// Action to setup
final aeris.Action action;
/// Trigger of Parent of the action to setup
final Trigger? parentTrigger;
/// reactions of Parent of the action to setup
final List<Reaction> parentReactions;
@override
State<SetupActionPage> createState() => _SetupActionPageState();
@@ -120,6 +126,9 @@ class _SetupActionPageState extends State<SetupActionPage> {
expanded: Padding(
padding: const EdgeInsets.all(20),
child: ActionForm(
reactionsCandidates: widget.parentReactions,
triggerCandidate: widget.parentTrigger,
candidate: widget.action,
key: Key("${availableAction.name}${availableAction.description}${availableAction.service}"),
description: availableAction.description!,
name: availableAction.name,
@@ -1,3 +1,5 @@
import 'package:aeris/src/models/reaction.dart';
import 'package:aeris/src/models/trigger.dart';
import 'package:aeris/src/widgets/aeris_card_page.dart';
import 'package:flutter/material.dart';
import 'package:aeris/src/views/setup_action_page.dart';
@@ -11,6 +13,8 @@ class ActionCardPopupMenu extends StatelessWidget {
ActionCardPopupMenu({
Key? key,
required this.action,
this.parentTrigger,
required this.parentReactions,
required this.then,
required this.deletable,
this.onDelete,
@@ -22,6 +26,10 @@ class ActionCardPopupMenu extends StatelessWidget {
/// Selected Action
final aeris.Action action;
/// Trigger of the Parent of the action
final Trigger? parentTrigger;
/// Trigger of the Parent of the action
final List<Reaction> parentReactions;
/// Function to trigger once the Edit menu is closed
final void Function() then;
@@ -46,15 +54,18 @@ class ActionCardPopupMenu extends StatelessWidget {
icon: Icons.settings,
title: AppLocalizations.of(context).modify,
value: () => showAerisCardPage(
context, (_) => SetupActionPage(action: action)).then((_) => then())
),
context, (_) => SetupActionPage(
action: action,
parentTrigger: parentTrigger,
parentReactions: parentReactions,
))
.then((_) => then())),
AerisPopupMenuItem(
context: context,
icon: Icons.delete,
title: AppLocalizations.of(context).delete,
value: onDelete,
enabled: deletable
),
context: context,
icon: Icons.delete,
title: AppLocalizations.of(context).delete,
value: onDelete,
enabled: deletable),
]);
}
}
+95 -18
View File
@@ -1,9 +1,16 @@
import 'package:aeris/src/models/action_parameter.dart';
import 'package:aeris/src/models/action_template.dart';
import 'package:aeris/src/models/reaction.dart';
import 'package:aeris/src/models/action.dart' as aeris;
import 'package:aeris/src/models/trigger.dart';
import 'package:aeris/src/providers/action_catalogue_provider.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:flutter_typeahead/flutter_typeahead.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import 'package:recase/recase.dart';
import 'package:tuple/tuple.dart';
/// Form for an action
class ActionForm extends StatefulWidget {
@@ -14,6 +21,13 @@ class ActionForm extends StatefulWidget {
/// What the action does
final String description;
/// The Action that will be eventually filled by the form
final aeris.Action candidate;
/// The trigger candidate in the parent form page
final Trigger? triggerCandidate;
/// The trigger candidate in the parent form page
final List<Reaction> reactionsCandidates;
/// On validate callback
final void Function(Map<String, String>) onValidate;
@@ -22,7 +36,11 @@ class ActionForm extends StatefulWidget {
required this.name,
required this.description,
required this.parameters,
required this.onValidate})
required this.onValidate,
required this.candidate,
this.triggerCandidate,
required this.reactionsCandidates,
})
: super(key: key);
@override
@@ -30,34 +48,93 @@ class ActionForm extends StatefulWidget {
}
class _ActionFormState extends State<ActionForm> {
final _formKey = GlobalKey<FormBuilderState>();
final _formKey = GlobalKey<FormState>();
Map<String, String> formValues = {};
List getSuggestions(String pattern, ActionCatalogueProvider catalogue) {
List<Tuple3<int, ActionParameter, ActionTemplate>> suggestions = [];
if (pattern.endsWith("#") == false) return suggestions;
print(widget.candidate.runtimeType);
if (widget.candidate is Trigger) return suggestions;
if (widget.triggerCandidate != null) {
Trigger trigger = widget.triggerCandidate!;
///TODO Dumb ass; look for returns instead of param
var triggerTemplate = catalogue.triggerTemplates[trigger.service]!.firstWhere(
(element) => element.name == trigger.name
);
for (var parameter in triggerTemplate.returnedValues) {
suggestions.add(Tuple3(0, parameter, triggerTemplate));
}
}
int index = 1;
for (var reactionCandidate in widget.reactionsCandidates) {
var reactionTemplate = catalogue.triggerTemplates[reactionCandidate.service]!.firstWhere(
(element) => element.name == reactionCandidate.name
);
for (var parameter in reactionTemplate.returnedValues) {
suggestions.add(Tuple3(index, parameter, reactionTemplate));
}
index++;
}
print(index);
suggestions.forEach((element) => print(element.item2.name));
return suggestions;
}
@override
Widget build(BuildContext context) {
return FormBuilder(
return Consumer<ActionCatalogueProvider>(
builder: (__, catalogue, _) => Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(widget.description, textAlign: TextAlign.left, style: TextStyle(color: Theme.of(context).colorScheme.onSurface)),
...widget.parameters.map((param) => FormBuilderTextField(
initialValue: param.value?.toString(),
name: param.name,
decoration: InputDecoration(
labelText: ReCase(param.name).titleCase,
helperText: param.description
),
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(context),
]),
)),
...widget.parameters.map((param) {
return TypeAheadFormField(
initialValue: formValues[param.name] ?? param.value?.toString(),
validator: FormBuilderValidators.compose([
FormBuilderValidators.required(context),
]),
hideOnEmpty: true,
textFieldConfiguration: TextFieldConfiguration(
decoration: InputDecoration(
labelText: ReCase(param.name).titleCase,
helperText: param.description
),
),
suggestionsCallback: (suggestion) => getSuggestions(suggestion, catalogue),
noItemsFoundBuilder: (_) => Container(),
onSuggestionSelected: (suggestion) {
var parameterSuggestion = suggestion as Tuple3<int, ActionParameter, ActionTemplate>;
String content = formValues[param.name]!;
content += "{${parameterSuggestion.item2}@${parameterSuggestion.item1}}";
print(content);
setState(() {
formValues[param.name] = content;
});
},
itemBuilder: (context, input) {
var suggestion = input as Tuple3<int, ActionParameter, ActionTemplate>;
return ListTile(
isThreeLine: true,
leading: suggestion.item3.service.getLogo(logoSize: 30),
title: Text(suggestion.item2.name),
subtitle: Text("${suggestion.item2.description}, from '${suggestion.item3.displayName()}'")
);
},
onSaved: (value) => setState(() {
formValues[param.name] = value!;
}),
);
}),
...[
ElevatedButton(
child: Text(AppLocalizations.of(context).save),
onPressed: () {
_formKey.currentState!.save();
if (_formKey.currentState!.validate()) {
widget.onValidate(_formKey.currentState!.value.map((key, value) => MapEntry(key, value)));
_formKey.currentState!.save();
widget.onValidate(formValues.map((key, value) => MapEntry(key, value)));
}
},
),
@@ -65,6 +142,6 @@ class _ActionFormState extends State<ActionForm> {
]
]
)
);
));
}
}