mirror of
https://github.com/zoriya/Aeris.git
synced 2026-06-08 04:40:46 +00:00
Merge branch 'multilng-about-json' of github.com:AnonymusRaccoon/Aeris into multilng-about-json
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
import 'package:aeris/src/models/action_parameter.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:aeris/src/models/service.dart';
|
||||
import 'package:recase/recase.dart';
|
||||
|
||||
///Base class for reactions and trigger
|
||||
abstract class Action {
|
||||
///Action's service
|
||||
Service service;
|
||||
|
||||
///Name fo the action
|
||||
///Identifier of the action type
|
||||
String name;
|
||||
///Name odf the action
|
||||
String displayName;
|
||||
|
||||
///Action's parameters
|
||||
List<ActionParameter> parameters;
|
||||
@@ -21,6 +22,7 @@ abstract class Action {
|
||||
{Key? key,
|
||||
required this.service,
|
||||
required this.name,
|
||||
required this.displayName,
|
||||
this.description,
|
||||
this.parameters = const []});
|
||||
|
||||
@@ -29,10 +31,4 @@ abstract class Action {
|
||||
var service = snake.removeAt(0);
|
||||
return Service.factory(service);
|
||||
}
|
||||
|
||||
String displayName() {
|
||||
var words = name.split('_');
|
||||
words.removeAt(0);
|
||||
return ReCase(words.join()).titleCase;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,9 @@ class ActionTemplate extends Action {
|
||||
{Key? key,
|
||||
required Service service,
|
||||
required String name,
|
||||
required String displayName,
|
||||
required String description,
|
||||
this.returnedValues = const [],
|
||||
List<ActionParameter> parameters = const []})
|
||||
: super(service: service, name: name, parameters: parameters, description: description);
|
||||
: super(service: service, name: name, parameters: parameters, description: description, displayName: displayName);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
// ignore_for_file: hash_and_equals
|
||||
|
||||
import 'package:aeris/main.dart';
|
||||
import 'package:aeris/src/models/action.dart' as aeris_action;
|
||||
import 'package:aeris/src/models/action_parameter.dart';
|
||||
import 'package:aeris/src/providers/action_catalogue_provider.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:aeris/src/models/service.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
///Object representation of a reaction
|
||||
class Reaction extends aeris_action.Action {
|
||||
@@ -11,19 +14,26 @@ class Reaction extends aeris_action.Action {
|
||||
{Key? key,
|
||||
required Service service,
|
||||
required String name,
|
||||
required String displayName,
|
||||
List<ActionParameter> parameters = const []})
|
||||
: super(service: service, name: name, parameters: parameters);
|
||||
: super(service: service, name: name, parameters: parameters, displayName: displayName);
|
||||
|
||||
/// Template trigger, used as an 'empty' trigger
|
||||
Reaction.template()
|
||||
: super(service: Service.all()[0], name: '', parameters: []);
|
||||
: super(service: Service.all()[0], name: '', parameters: [],displayName: '');
|
||||
|
||||
static Reaction fromJSON(Object reaction) {
|
||||
var reactionJSON = reaction as Map<String, dynamic>;
|
||||
var service = aeris_action.Action.parseServiceInName(reactionJSON['rType'] as String);
|
||||
String rType = reactionJSON['rType'] as String;
|
||||
var service = aeris_action.Action.parseServiceInName(rType);
|
||||
return Reaction(
|
||||
displayName: reactionJSON['label']?['en']
|
||||
?? Provider.of<ActionCatalogueProvider>(Aeris.materialKey.currentContext!, listen: false)
|
||||
.reactionTemplates[service]!.firstWhere((template) {
|
||||
return template.name == rType;
|
||||
}).displayName, ///TODO use locale
|
||||
service: service,
|
||||
name: reactionJSON['rType'] as String,
|
||||
name: rType,
|
||||
parameters: ActionParameter.fromJSON((reactionJSON['rParams'] as Map<String, dynamic>)));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// ignore_for_file: hash_and_equals
|
||||
|
||||
import 'package:aeris/src/models/action_parameter.dart';
|
||||
import 'package:aeris/src/providers/action_catalogue_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:aeris/main.dart';
|
||||
import 'package:aeris/src/models/service.dart';
|
||||
import 'package:aeris/src/models/action.dart' as aeris_action;
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
///Object representation of a pipeline trigger
|
||||
class Trigger extends aeris_action.Action {
|
||||
@@ -15,9 +17,10 @@ class Trigger extends aeris_action.Action {
|
||||
{Key? key,
|
||||
required Service service,
|
||||
required String name,
|
||||
required String displayName,
|
||||
List<ActionParameter> parameters = const [],
|
||||
this.last})
|
||||
: super(service: service, name: name, parameters: parameters);
|
||||
: super(service: service, name: name, parameters: parameters, displayName: displayName);
|
||||
|
||||
/// Unserialize
|
||||
static Trigger fromJSON(Object action) {
|
||||
@@ -28,10 +31,16 @@ class Trigger extends aeris_action.Action {
|
||||
DateTime? last = lastTriggerField == null
|
||||
? null
|
||||
: DateTime.parse(lastTriggerField as String);
|
||||
String pType = triggerJSON['pType'] as String;
|
||||
|
||||
return Trigger(
|
||||
displayName: triggerJSON['label']?['en']
|
||||
?? Provider.of<ActionCatalogueProvider>(Aeris.materialKey.currentContext!, listen: false)
|
||||
.triggerTemplates[service]!.firstWhere((template) {
|
||||
return template.name == pType;
|
||||
}).displayName, ///TODO use locale
|
||||
service: service,
|
||||
name: triggerJSON['pType'] as String,
|
||||
name: pType,
|
||||
last: last,
|
||||
parameters: ActionParameter.fromJSON((triggerJSON['pParams'] as Map<String, dynamic>))
|
||||
);
|
||||
@@ -49,7 +58,7 @@ class Trigger extends aeris_action.Action {
|
||||
|
||||
/// Template trigger, used as an 'empty' trigger
|
||||
Trigger.template({Key? key, this.last})
|
||||
: super(service: Service.all()[0], name: '', parameters: []);
|
||||
: super(service: Service.all()[0], name: '', parameters: [], displayName: '');
|
||||
|
||||
@override
|
||||
// ignore: avoid_renaming_method_parameters
|
||||
|
||||
@@ -36,13 +36,14 @@ class ActionCatalogueProvider extends ChangeNotifier {
|
||||
_triggerTemplates[service]!.add(
|
||||
ActionTemplate(
|
||||
name: action['name'],
|
||||
displayName: action['label']['en'], ///TODO use locale
|
||||
service: service,
|
||||
description: action['description'],
|
||||
description: action['description']['en'], ///TODO use locale
|
||||
parameters: (action['params'] as List).map(
|
||||
(e) => ActionParameter(name: e['name'], description: e['description'])
|
||||
(e) => ActionParameter(name: e['name'], description: e['description']['en'])///TODO use locale
|
||||
).toList(),
|
||||
returnedValues: (action['returns'] as List).map(
|
||||
(e) => ActionParameter(name: e['name'], description: e['description'])
|
||||
(e) => ActionParameter(name: e['name'], description: e['description']['en']) ///TODO use locale
|
||||
).toList(),
|
||||
)
|
||||
);
|
||||
@@ -50,14 +51,15 @@ class ActionCatalogueProvider extends ChangeNotifier {
|
||||
for (var reaction in serviceContent['reactions']) {
|
||||
_reactionTemplates[service]!.add(
|
||||
ActionTemplate(
|
||||
displayName: reaction['label']['en'], ///TODO use locale
|
||||
name: reaction['name'],
|
||||
service: service,
|
||||
description: reaction['description'],
|
||||
description: reaction['description']['en'], ///TODO use locale
|
||||
parameters: (reaction['params'] as List).map(
|
||||
(e) => ActionParameter(name: e['name'], description: e['description'])
|
||||
(e) => ActionParameter(name: e['name'], description: e['description']['en']) ///TODO use locale
|
||||
).toList(),
|
||||
returnedValues: (reaction['returns'] as List).map(
|
||||
(e) => ActionParameter(name: e['name'], description: e['description'])
|
||||
(e) => ActionParameter(name: e['name'], description: e['description']['en']) ///TODO use locale
|
||||
).toList(),
|
||||
)
|
||||
);
|
||||
|
||||
@@ -38,149 +38,170 @@ class _CreatePipelinePageState extends State<CreatePipelinePage> {
|
||||
final _formKey = GlobalKey<FormBuilderState>();
|
||||
return Consumer<PipelineProvider>(
|
||||
builder: (context, provider, _) => AerisCardPage(
|
||||
body: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
Text(AppLocalizations.of(context).createNewPipeline,
|
||||
style: const TextStyle(
|
||||
fontSize: 25,
|
||||
)),
|
||||
FormBuilder(
|
||||
key: _formKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
|
||||
FormBuilderTextField(
|
||||
name: 'name',
|
||||
initialValue: name,
|
||||
decoration: InputDecoration(
|
||||
labelText:
|
||||
AppLocalizations.of(context).nameOfThePipeline,
|
||||
),
|
||||
validator: FormBuilderValidators.compose([
|
||||
FormBuilderValidators.required(context),
|
||||
FormBuilderValidators.minLength(context, 5),
|
||||
]),
|
||||
onChanged: (value) {
|
||||
name = value;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
trigger != Trigger.template()
|
||||
? Text(AppLocalizations.of(context).action,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500))
|
||||
: Container(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: trigger == Trigger.template()
|
||||
? ColoredClickableCard(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondaryContainer,
|
||||
text: AppLocalizations.of(context).addTrigger,
|
||||
onTap: () {
|
||||
showAerisCardPage(
|
||||
body: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(AppLocalizations.of(context).createNewPipeline,
|
||||
style: const TextStyle(
|
||||
fontSize: 25,
|
||||
)),
|
||||
FormBuilder(
|
||||
key: _formKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
FormBuilderTextField(
|
||||
name: 'name',
|
||||
initialValue: name,
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalizations.of(context)
|
||||
.nameOfThePipeline,
|
||||
),
|
||||
validator: FormBuilderValidators.compose([
|
||||
FormBuilderValidators.required(context),
|
||||
FormBuilderValidators.minLength(context, 5),
|
||||
]),
|
||||
onChanged: (value) {
|
||||
name = value;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
trigger != Trigger.template()
|
||||
? Text(AppLocalizations.of(context).action,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w500))
|
||||
: Container(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: trigger == Trigger.template()
|
||||
? ColoredClickableCard(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondaryContainer,
|
||||
text: AppLocalizations.of(context)
|
||||
.addTrigger,
|
||||
onTap: () {
|
||||
showAerisCardPage(
|
||||
context,
|
||||
(_) => SetupActionPage(
|
||||
action: trigger,
|
||||
parentReactions:
|
||||
reactions))
|
||||
.then((_) => setState(() {}));
|
||||
})
|
||||
: ActionCard(
|
||||
leading: trigger.service
|
||||
.getLogo(logoSize: 50),
|
||||
title: trigger.displayName,
|
||||
trailing: ActionCardPopupMenu(
|
||||
deletable: false,
|
||||
parentReactions: reactions,
|
||||
parentTrigger: trigger,
|
||||
action: trigger,
|
||||
then: () => setState(() {})),
|
||||
),
|
||||
),
|
||||
reactions.isNotEmpty
|
||||
? Text(AppLocalizations.of(context).reactions,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w500))
|
||||
: Container(),
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(left: 8, right: 8),
|
||||
child: ReorderableReactionCardsList(
|
||||
reactionList: reactions,
|
||||
onReorder: () {},
|
||||
itemBuilder: (reaction) => ActionCard(
|
||||
key: ValueKey(
|
||||
reactions.indexOf(reaction)),
|
||||
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(() {}),
|
||||
onDelete: () {
|
||||
setState(() {
|
||||
reactions.remove(reaction);
|
||||
});
|
||||
})),
|
||||
)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ColoredClickableCard(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondaryContainer,
|
||||
text: AppLocalizations.of(context)
|
||||
.addReaction,
|
||||
onTap: () async {
|
||||
var newreact = Reaction.template();
|
||||
showAerisCardPage(
|
||||
context,
|
||||
(_) => SetupActionPage(
|
||||
action: trigger,
|
||||
parentReactions: reactions
|
||||
))
|
||||
.then((_) => setState(() {}));
|
||||
})
|
||||
: ActionCard(
|
||||
leading: trigger.service.getLogo(logoSize: 50),
|
||||
title: trigger.displayName(),
|
||||
trailing: ActionCardPopupMenu(
|
||||
deletable: false,
|
||||
parentReactions: reactions,
|
||||
parentTrigger: trigger,
|
||||
action: trigger,
|
||||
then: () => setState(() {})),
|
||||
action: newreact,
|
||||
parentReactions: reactions,
|
||||
parentTrigger: trigger ==
|
||||
Trigger.template()
|
||||
? null
|
||||
: trigger,
|
||||
)).then((_) => setState(() {
|
||||
if (newreact !=
|
||||
Reaction.template()) {
|
||||
reactions.add(newreact);
|
||||
}
|
||||
}));
|
||||
}),
|
||||
),
|
||||
),
|
||||
reactions.isNotEmpty
|
||||
? Text(AppLocalizations.of(context).reactions,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500))
|
||||
: Container(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8, right: 8),
|
||||
child: ReorderableReactionCardsList(
|
||||
reactionList: reactions,
|
||||
onReorder: () { },
|
||||
itemBuilder: (reaction) => ActionCard(
|
||||
key: ValueKey(reactions.indexOf(reaction)),
|
||||
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(() {}),
|
||||
onDelete: () {
|
||||
setState(() {
|
||||
reactions.remove(reaction);
|
||||
});
|
||||
})),
|
||||
)
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: ColoredClickableCard(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondaryContainer,
|
||||
text: AppLocalizations.of(context).addReaction,
|
||||
onTap: () async {
|
||||
var newreact = Reaction.template();
|
||||
showAerisCardPage(
|
||||
context,
|
||||
(_) => SetupActionPage(
|
||||
action: newreact,
|
||||
parentReactions: reactions,
|
||||
parentTrigger: trigger == Trigger.template() ? null : trigger,
|
||||
))
|
||||
.then((_) => setState(() {
|
||||
if (newreact != Reaction.template()) {
|
||||
reactions.add(newreact);
|
||||
Center(
|
||||
child: ElevatedButton(
|
||||
child: Text(AppLocalizations.of(context).save),
|
||||
onPressed: () {
|
||||
_formKey.currentState!.save();
|
||||
if (_formKey.currentState!.validate()) {
|
||||
if (trigger == Trigger.template() ||
|
||||
reactions.isEmpty ||
|
||||
reactions
|
||||
.where((element) =>
|
||||
element == Reaction.template())
|
||||
.isNotEmpty) {
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(SnackBar(
|
||||
backgroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondary,
|
||||
content: const Text(
|
||||
"You must select at least a trigger and a reaction")));
|
||||
} else {
|
||||
Pipeline newPipeline = Pipeline(
|
||||
id: 0,
|
||||
name: _formKey
|
||||
.currentState!.value['name'],
|
||||
triggerCount: 0,
|
||||
enabled: true,
|
||||
trigger: trigger,
|
||||
reactions: reactions);
|
||||
provider.addPipeline(newPipeline);
|
||||
Navigator.of(context).pop();
|
||||
showAerisCardPage(
|
||||
context,
|
||||
(_) => PipelineDetailPage(
|
||||
pipeline: newPipeline));
|
||||
}
|
||||
}));
|
||||
}),
|
||||
),
|
||||
Center(child: ElevatedButton(
|
||||
child: Text(AppLocalizations.of(context).save),
|
||||
onPressed: () {
|
||||
_formKey.currentState!.save();
|
||||
if (_formKey.currentState!.validate()) {
|
||||
if (trigger == Trigger.template() ||
|
||||
reactions.isEmpty ||
|
||||
reactions
|
||||
.where((element) =>
|
||||
element == Reaction.template())
|
||||
.isNotEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.secondary,
|
||||
content: const Text(
|
||||
"You must select at least a trigger and a reaction")));
|
||||
} else {
|
||||
Pipeline newPipeline = Pipeline(
|
||||
id: 0,
|
||||
name: _formKey.currentState!.value['name'],
|
||||
triggerCount: 0,
|
||||
enabled: true,
|
||||
trigger: trigger,
|
||||
reactions: reactions);
|
||||
provider.addPipeline(newPipeline);
|
||||
Navigator.of(context).pop();
|
||||
showAerisCardPage(
|
||||
context,
|
||||
(_) => PipelineDetailPage(pipeline: newPipeline)
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
)),
|
||||
]),
|
||||
)),
|
||||
]),
|
||||
)),
|
||||
])));
|
||||
])));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,13 +95,12 @@ class _PipelineDetailPageState extends State<PipelineDetailPage> {
|
||||
onTap: () {
|
||||
Reaction newreaction = Reaction.template();
|
||||
showAerisCardPage(
|
||||
context, (_) => SetupActionPage(
|
||||
context,
|
||||
(_) => SetupActionPage(
|
||||
action: newreaction,
|
||||
parentTrigger: pipeline.trigger,
|
||||
parentReactions: pipeline.reactions,
|
||||
)
|
||||
)
|
||||
.then((r) {
|
||||
)).then((r) {
|
||||
if (newreaction != Reaction.template()) {
|
||||
setState(() {
|
||||
pipeline.reactions.add(newreaction);
|
||||
@@ -140,7 +139,7 @@ class _PipelineDetailPageState extends State<PipelineDetailPage> {
|
||||
style: const TextStyle(fontWeight: FontWeight.w500)),
|
||||
ActionCard(
|
||||
leading: pipeline.trigger.service.getLogo(logoSize: 50),
|
||||
title: pipeline.trigger.displayName(),
|
||||
title: pipeline.trigger.displayName,
|
||||
trailing: ActionCardPopupMenu(
|
||||
deletable: false,
|
||||
parentTrigger: pipeline.trigger,
|
||||
@@ -154,12 +153,12 @@ class _PipelineDetailPageState extends State<PipelineDetailPage> {
|
||||
Text(AppLocalizations.of(context).reactions,
|
||||
style: const TextStyle(fontWeight: FontWeight.w500)),
|
||||
ReorderableReactionCardsList(
|
||||
onReorder: () => GetIt.I<AerisAPI>().editPipeline(pipeline),
|
||||
reactionList: pipeline.reactions,
|
||||
itemBuilder: (reaction) => ActionCard(
|
||||
onReorder: () => GetIt.I<AerisAPI>().editPipeline(pipeline),
|
||||
reactionList: pipeline.reactions,
|
||||
itemBuilder: (reaction) => ActionCard(
|
||||
key: ValueKey(pipeline.reactions.indexOf(reaction)),
|
||||
leading: reaction.service.getLogo(logoSize: 50),
|
||||
title: reaction.displayName(),
|
||||
title: reaction.displayName,
|
||||
trailing: ActionCardPopupMenu(
|
||||
parentTrigger: pipeline.trigger,
|
||||
parentReactions: pipeline.reactions,
|
||||
@@ -174,8 +173,7 @@ class _PipelineDetailPageState extends State<PipelineDetailPage> {
|
||||
setState(() {});
|
||||
GetIt.I<AerisAPI>().editPipeline(pipeline);
|
||||
}),
|
||||
)
|
||||
),
|
||||
)),
|
||||
addReactionbutton,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 30, bottom: 5),
|
||||
|
||||
@@ -15,10 +15,16 @@ import 'package:skeleton_loader/skeleton_loader.dart';
|
||||
|
||||
///Page to setup an action
|
||||
class SetupActionPage extends StatefulWidget {
|
||||
const SetupActionPage({Key? key, required this.action, required this.parentReactions, this.parentTrigger}) : 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;
|
||||
|
||||
@@ -37,12 +43,12 @@ class _SetupActionPageState extends State<SetupActionPage> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
serviceState = widget.action.service;
|
||||
availableActions = GetIt.I<AerisAPI>().getActionsFor(serviceState!, widget.action);
|
||||
availableActions =
|
||||
GetIt.I<AerisAPI>().getActionsFor(serviceState!, widget.action);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
final Widget serviceDropdown = DropdownButton<Service>(
|
||||
value: serviceState,
|
||||
elevation: 8,
|
||||
@@ -50,7 +56,8 @@ class _SetupActionPageState extends State<SetupActionPage> {
|
||||
onChanged: (service) {
|
||||
setState(() {
|
||||
serviceState = service;
|
||||
availableActions = GetIt.I<AerisAPI>().getActionsFor(service!, widget.action);
|
||||
availableActions =
|
||||
GetIt.I<AerisAPI>().getActionsFor(service!, widget.action);
|
||||
});
|
||||
},
|
||||
items: Service.all().map<DropdownMenuItem<Service>>((Service service) {
|
||||
@@ -77,9 +84,10 @@ class _SetupActionPageState extends State<SetupActionPage> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(widget.action is Trigger
|
||||
? AppLocalizations.of(context).setupTrigger
|
||||
: AppLocalizations.of(context).setupReaction,
|
||||
Text(
|
||||
widget.action is Trigger
|
||||
? AppLocalizations.of(context).setupTrigger
|
||||
: AppLocalizations.of(context).setupReaction,
|
||||
style: const TextStyle(
|
||||
fontSize: 25,
|
||||
)),
|
||||
@@ -108,49 +116,61 @@ class _SetupActionPageState extends State<SetupActionPage> {
|
||||
const SizedBox(height: 20),
|
||||
if (availableActions == null)
|
||||
SkeletonLoader(
|
||||
builder: Card(shape: cardShape, child: const SizedBox(height: 40), elevation: 5),
|
||||
builder: Card(
|
||||
shape: cardShape,
|
||||
child: const SizedBox(height: 40),
|
||||
elevation: 5),
|
||||
items: 15,
|
||||
highlightColor: Theme.of(context).colorScheme.secondary
|
||||
)
|
||||
else
|
||||
...[for (ActionTemplate availableAction in availableActions!)
|
||||
Card(
|
||||
elevation: 5,
|
||||
shape: cardShape,
|
||||
child: ExpandableNotifier(
|
||||
child: ScrollOnExpand(child: ExpandablePanel(
|
||||
header: Padding(
|
||||
padding:
|
||||
const EdgeInsets.only(left: 30, top: 20, bottom: 20),
|
||||
child: Text(availableAction.displayName(),
|
||||
style: const TextStyle(fontSize: 15))),
|
||||
collapsed: Container(),
|
||||
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,
|
||||
parameters: availableAction.parameters.map((param) {
|
||||
if (widget.action.service.name == serviceState!.name && widget.action.name == availableAction.name) {
|
||||
var previousParams = widget.action.parameters.where((element) => element.name == param.name);
|
||||
if (previousParams.isNotEmpty) {
|
||||
param.value = previousParams.first.value;
|
||||
}
|
||||
}
|
||||
return param;
|
||||
}).toList(),
|
||||
onValidate: (parameters) {
|
||||
widget.action.service = serviceState!;
|
||||
widget.action.parameters = ActionParameter.fromJSON(parameters);
|
||||
widget.action.name = availableAction.name;
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
)),
|
||||
))),
|
||||
highlightColor: Theme.of(context).colorScheme.secondary)
|
||||
else ...[
|
||||
for (ActionTemplate availableAction in availableActions!)
|
||||
Card(
|
||||
elevation: 5,
|
||||
shape: cardShape,
|
||||
child: ExpandableNotifier(
|
||||
child: ScrollOnExpand(
|
||||
child: ExpandablePanel(
|
||||
header: Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 30, top: 20, bottom: 20),
|
||||
child: Text(availableAction.displayName,
|
||||
style: const TextStyle(fontSize: 15))),
|
||||
collapsed: Container(),
|
||||
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,
|
||||
parameters:
|
||||
availableAction.parameters.map((param) {
|
||||
if (widget.action.service.name ==
|
||||
serviceState!.name &&
|
||||
widget.action.name ==
|
||||
availableAction.name) {
|
||||
var previousParams = widget.action.parameters
|
||||
.where((element) =>
|
||||
element.name == param.name);
|
||||
if (previousParams.isNotEmpty) {
|
||||
param.value = previousParams.first.value;
|
||||
}
|
||||
}
|
||||
return param;
|
||||
}).toList(),
|
||||
onValidate: (parameters) {
|
||||
widget.action.service = serviceState!;
|
||||
widget.action.parameters =
|
||||
ActionParameter.fromJSON(parameters);
|
||||
widget.action.name = availableAction.name;
|
||||
widget.action.displayName = availableAction.displayName;
|
||||
Navigator.of(context).pop();
|
||||
}),
|
||||
)),
|
||||
))),
|
||||
const SizedBox(height: 10)
|
||||
]
|
||||
],
|
||||
|
||||
@@ -13,10 +13,11 @@ import 'package:tuple/tuple.dart';
|
||||
import 'package:flutter_typeahead/flutter_typeahead.dart';
|
||||
|
||||
class Suggestion extends Tuple3<int, ActionParameter, ActionTemplate> {
|
||||
Suggestion(int item1, ActionParameter item2, ActionTemplate item3) : super(item1, item2, item3);
|
||||
Suggestion(int item1, ActionParameter item2, ActionTemplate item3)
|
||||
: super(item1, item2, item3);
|
||||
|
||||
// Overriding show method
|
||||
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "${item2.name}@$item1";
|
||||
@@ -27,32 +28,35 @@ class Suggestion extends Tuple3<int, ActionParameter, ActionTemplate> {
|
||||
class ActionForm extends StatefulWidget {
|
||||
/// Name of the action
|
||||
final String name;
|
||||
|
||||
/// List of parameters, 'values' are used as default values
|
||||
final List<ActionParameter> parameters;
|
||||
|
||||
/// 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;
|
||||
|
||||
const ActionForm(
|
||||
{Key? key,
|
||||
required this.name,
|
||||
required this.description,
|
||||
required this.parameters,
|
||||
required this.onValidate,
|
||||
required this.candidate,
|
||||
this.triggerCandidate,
|
||||
required this.reactionsCandidates,
|
||||
})
|
||||
: super(key: key);
|
||||
const ActionForm({
|
||||
Key? key,
|
||||
required this.name,
|
||||
required this.description,
|
||||
required this.parameters,
|
||||
required this.onValidate,
|
||||
required this.candidate,
|
||||
this.triggerCandidate,
|
||||
required this.reactionsCandidates,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<ActionForm> createState() => _ActionFormState();
|
||||
@@ -61,26 +65,27 @@ class ActionForm extends StatefulWidget {
|
||||
class _ActionFormState extends State<ActionForm> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
List<Suggestion> getSuggestions(String pattern, ActionCatalogueProvider catalogue) {
|
||||
List<Suggestion> getSuggestions(
|
||||
String pattern, ActionCatalogueProvider catalogue) {
|
||||
List<Suggestion> suggestions = [];
|
||||
if (pattern.endsWith("{") == false) return suggestions;
|
||||
if (widget.candidate is Trigger) return suggestions;
|
||||
if (widget.triggerCandidate != null) {
|
||||
Trigger trigger = widget.triggerCandidate!;
|
||||
var triggerTemplate = catalogue.triggerTemplates[trigger.service]!.firstWhere(
|
||||
(element) => element.name == trigger.name
|
||||
);
|
||||
var triggerTemplate = catalogue.triggerTemplates[trigger.service]!
|
||||
.firstWhere((element) => element.name == trigger.name);
|
||||
for (var parameter in triggerTemplate.returnedValues) {
|
||||
suggestions.add(Suggestion(0, parameter, triggerTemplate));
|
||||
}
|
||||
}
|
||||
int index = 1;
|
||||
int indexOfCandidate = widget.reactionsCandidates.indexOf(widget.candidate as Reaction);
|
||||
int indexOfCandidate =
|
||||
widget.reactionsCandidates.indexOf(widget.candidate as Reaction);
|
||||
for (var reactionCandidate in widget.reactionsCandidates) {
|
||||
if (index == indexOfCandidate + 1) break;
|
||||
var reactionTemplate = catalogue.reactionTemplates[reactionCandidate.service]!.firstWhere(
|
||||
(element) => element.name == reactionCandidate.name
|
||||
);
|
||||
var reactionTemplate = catalogue
|
||||
.reactionTemplates[reactionCandidate.service]!
|
||||
.firstWhere((element) => element.name == reactionCandidate.name);
|
||||
for (var parameter in reactionTemplate.returnedValues) {
|
||||
suggestions.add(Suggestion(index, parameter, reactionTemplate));
|
||||
}
|
||||
@@ -94,63 +99,68 @@ class _ActionFormState extends State<ActionForm> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
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) {
|
||||
final textEditingController = TextEditingController(text: values[param.name] ?? param.value?.toString());
|
||||
return TypeAheadFormField<Suggestion>(
|
||||
key: Key(param.description),
|
||||
textFieldConfiguration: TextFieldConfiguration(
|
||||
autofocus: true,
|
||||
controller: textEditingController,
|
||||
enableSuggestions: widget.candidate is Reaction,
|
||||
decoration: InputDecoration(
|
||||
labelText: ReCase(param.name).titleCase,
|
||||
helperText: param.description
|
||||
),
|
||||
),
|
||||
onSaved: (value) {
|
||||
values[param.name] = value!;
|
||||
},
|
||||
suggestionsBoxDecoration: const SuggestionsBoxDecoration(
|
||||
elevation: 6,
|
||||
borderRadius: BorderRadius.all(Radius.circular(10))
|
||||
),
|
||||
validator: FormBuilderValidators.compose([
|
||||
FormBuilderValidators.required(context),
|
||||
]),
|
||||
hideOnEmpty: true,
|
||||
suggestionsCallback: (suggestion) => getSuggestions(suggestion, catalogue),
|
||||
onSuggestionSelected: (suggestion) {
|
||||
textEditingController.text += suggestion.toString();
|
||||
textEditingController.text += "}";
|
||||
values[param.name] = textEditingController.text;
|
||||
},
|
||||
itemBuilder: (context, suggestion) => ListTile(
|
||||
isThreeLine: true,
|
||||
dense: true,
|
||||
leading: suggestion.item3.service.getLogo(logoSize: 30),
|
||||
title: Text(suggestion.item2.name),
|
||||
subtitle: Text("${suggestion.item2.description}, from '${suggestion.item3.displayName()}'")
|
||||
));}),
|
||||
...[
|
||||
ElevatedButton(
|
||||
child: Text(AppLocalizations.of(context).save),
|
||||
onPressed: () {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
_formKey.currentState!.save();
|
||||
widget.onValidate(values);
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 10)
|
||||
]
|
||||
]
|
||||
)
|
||||
));
|
||||
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) {
|
||||
final textEditingController = TextEditingController(
|
||||
text: values[param.name] ?? param.value?.toString());
|
||||
return TypeAheadFormField<Suggestion>(
|
||||
key: Key(param.description),
|
||||
textFieldConfiguration: TextFieldConfiguration(
|
||||
autofocus: true,
|
||||
controller: textEditingController,
|
||||
enableSuggestions: widget.candidate is Reaction,
|
||||
decoration: InputDecoration(
|
||||
labelText: ReCase(param.name).titleCase,
|
||||
helperText: param.description),
|
||||
),
|
||||
onSaved: (value) {
|
||||
values[param.name] = value!;
|
||||
},
|
||||
suggestionsBoxDecoration:
|
||||
const SuggestionsBoxDecoration(
|
||||
elevation: 6,
|
||||
borderRadius:
|
||||
BorderRadius.all(Radius.circular(10))),
|
||||
validator: FormBuilderValidators.compose([
|
||||
FormBuilderValidators.required(context),
|
||||
]),
|
||||
hideOnEmpty: true,
|
||||
suggestionsCallback: (suggestion) =>
|
||||
getSuggestions(suggestion, catalogue),
|
||||
onSuggestionSelected: (suggestion) {
|
||||
textEditingController.text += suggestion.toString();
|
||||
textEditingController.text += "}";
|
||||
values[param.name] = textEditingController.text;
|
||||
},
|
||||
itemBuilder: (context, suggestion) => ListTile(
|
||||
isThreeLine: true,
|
||||
dense: true,
|
||||
leading:
|
||||
suggestion.item3.service.getLogo(logoSize: 30),
|
||||
title: Text(suggestion.item2.name),
|
||||
subtitle: Text(
|
||||
"${suggestion.item2.description}, from '${suggestion.item3.displayName}'")));
|
||||
}),
|
||||
...[
|
||||
ElevatedButton(
|
||||
child: Text(AppLocalizations.of(context).save),
|
||||
onPressed: () {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
_formKey.currentState!.save();
|
||||
widget.onValidate(values);
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 10)
|
||||
]
|
||||
])));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user