diff --git a/mobile/lib/l10n/app_en.arb b/mobile/lib/l10n/app_en.arb index 382a4b8..4c16f2e 100644 --- a/mobile/lib/l10n/app_en.arb +++ b/mobile/lib/l10n/app_en.arb @@ -43,5 +43,8 @@ "tryToConnect": "Try to connect", "routeToApi": "Route to API", "setupAPIRoute": "Setup API Route", - "paramInheritTip": "To inherit parameters from previous actions, type '{' in the text field and tap on the choosen parameter" + "paramInheritTip": "To inherit parameters from previous actions, type '{' in the text field and tap on the choosen parameter", + "authenticatedToNoService": "You are not authenticated to any services.", + "connectService": "Connect service", + "cantEnablePipeline": "You can't enable this pipeline because it depends on a service that is not connected" } \ No newline at end of file diff --git a/mobile/lib/l10n/app_fr.arb b/mobile/lib/l10n/app_fr.arb index 776233e..7feb1a7 100644 --- a/mobile/lib/l10n/app_fr.arb +++ b/mobile/lib/l10n/app_fr.arb @@ -43,5 +43,8 @@ "tryToConnect": "Tester la connection", "routeToApi": "Route de l'API", "setupAPIRoute": "Choisir la route de l'API", - "paramInheritTip": "Afin d'hériter de variables venant d'actions précedentes, entrez '{' dans un champ et choisissez la valeur" + "paramInheritTip": "Afin d'hériter de variables venant d'actions précedentes, entrez '{' dans un champ et choisissez la valeur", + "authenticatedToNoService": "Aucun service n'est connecté", + "connectService": "Connexion aux services", + "cantEnablePipeline": "Impossible d'activer la pipeline car elle dépend d'un service qui n'est pas connecté" } \ No newline at end of file diff --git a/mobile/lib/src/aeris_api.dart b/mobile/lib/src/aeris_api.dart index e89f18e..bcd73d0 100644 --- a/mobile/lib/src/aeris_api.dart +++ b/mobile/lib/src/aeris_api.dart @@ -50,9 +50,6 @@ class AerisAPI { deepLinkRoute = "$scheme://arthichaud.me"; } - /// Name of the file that contains the JWT used for Aeris' API requestd - static const String jwtFile = 'aeris_jwt.txt'; - ///ROUTES /// Registers new user in the database and connects it. Returns false if register failed Future signUpUser(String username, String password) async { diff --git a/mobile/lib/src/models/pipeline.dart b/mobile/lib/src/models/pipeline.dart index 8783726..afac62c 100644 --- a/mobile/lib/src/models/pipeline.dart +++ b/mobile/lib/src/models/pipeline.dart @@ -1,3 +1,4 @@ +import 'package:aeris/src/models/service.dart'; import 'package:flutter/material.dart'; import 'package:aeris/src/models/reaction.dart'; import 'package:aeris/src/models/trigger.dart'; @@ -58,4 +59,8 @@ class Pipeline { }, 'reactions': reactions.map((e) => e.toJSON()).toList() }; + + bool dependsOn(Service service) { + return service == trigger.service || reactions.any((reaction) => reaction.service == service); + } } diff --git a/mobile/lib/src/providers/services_provider.dart b/mobile/lib/src/providers/services_provider.dart index 36b8372..281091f 100644 --- a/mobile/lib/src/providers/services_provider.dart +++ b/mobile/lib/src/providers/services_provider.dart @@ -10,7 +10,7 @@ class ServiceProvider extends ChangeNotifier { List get connectedServices => _connectedServices; /// Get the services the user is not connected to - List get availableServices => Service.all() + List get disconnectedServices => Service.all() .where((element) => !_connectedServices.contains(element)) .toList(); diff --git a/mobile/lib/src/views/pipeline_detail_page.dart b/mobile/lib/src/views/pipeline_detail_page.dart index b98f833..b08c08e 100644 --- a/mobile/lib/src/views/pipeline_detail_page.dart +++ b/mobile/lib/src/views/pipeline_detail_page.dart @@ -1,5 +1,7 @@ import 'package:aeris/src/aeris_api.dart'; import 'package:aeris/src/providers/pipelines_provider.dart'; +import 'package:aeris/src/providers/services_provider.dart'; +import 'package:aeris/src/views/service_page.dart'; import 'package:aeris/src/views/setup_action_page.dart'; import 'package:aeris/src/widgets/action_card_popup_menu.dart'; import 'package:aeris/src/widgets/aeris_card_page.dart'; @@ -28,7 +30,8 @@ class PipelineDetailPage extends StatefulWidget { class _PipelineDetailPageState extends State { @override Widget build(BuildContext context) => - Consumer(builder: (context, provider, _) { + Consumer(builder: (context, services, _) { + return Consumer(builder: (context, provider, _) { Pipeline pipeline = widget.pipeline; final cardHeader = Row( @@ -67,11 +70,24 @@ class _PipelineDetailPageState extends State { width: 60, value: pipeline.enabled, onToggle: (value) { - setState(() { - pipeline.enabled = !pipeline.enabled; - GetIt.I().editPipeline(pipeline); - provider.sortPipelines(); - }); + if (!pipeline.enabled && services.disconnectedServices.any( + (service) => pipeline.dependsOn(service)) + ) { + showDialog( + context: context, + builder: (BuildContext context) => WarningDialog( + message: AppLocalizations.of(context).cantEnablePipeline, + onAccept: () => showAerisCardPage(context, (_) => const ServicePage()), + actionButtonColor: Theme.of(context).colorScheme.secondaryContainer, + warnedAction: AppLocalizations.of(context).connectService) + ); + } else { + setState(() { + pipeline.enabled = !pipeline.enabled; + GetIt.I().editPipeline(pipeline); + provider.sortPipelines(); + }); + } }, ), ), @@ -184,4 +200,5 @@ class _PipelineDetailPageState extends State { ]), )); }); + }); } diff --git a/mobile/lib/src/views/service_page.dart b/mobile/lib/src/views/service_page.dart index 0489442..f1f6557 100644 --- a/mobile/lib/src/views/service_page.dart +++ b/mobile/lib/src/views/service_page.dart @@ -67,7 +67,7 @@ class ServicePage extends StatelessWidget { warnedAction: AppLocalizations.of(context).disconnect)), context), ...getServiceGroup( - serviceProvider.availableServices, + serviceProvider.disconnectedServices, AppLocalizations.of(context).available, const Icon(Icons.connect_without_contact, color: Colors.green), (Service service) { diff --git a/mobile/lib/src/views/setup_action_page.dart b/mobile/lib/src/views/setup_action_page.dart index c76b5a4..919a039 100644 --- a/mobile/lib/src/views/setup_action_page.dart +++ b/mobile/lib/src/views/setup_action_page.dart @@ -3,6 +3,9 @@ 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:aeris/src/providers/services_provider.dart'; +import 'package:aeris/src/views/service_page.dart'; +import 'package:aeris/src/widgets/colored_clickable_card.dart'; import 'package:flutter/material.dart'; import 'package:aeris/src/models/action.dart' as aeris; import 'package:aeris/src/models/service.dart'; @@ -11,6 +14,7 @@ import 'package:aeris/src/widgets/aeris_card_page.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:get_it/get_it.dart'; +import 'package:provider/provider.dart'; import 'package:skeleton_loader/skeleton_loader.dart'; ///Page to setup an action @@ -43,13 +47,11 @@ class _SetupActionPageState extends State { void initState() { super.initState(); serviceState = widget.action.service; - availableActions = - GetIt.I().getActionsFor(serviceState!, widget.action); + availableActions = GetIt.I().getActionsFor(serviceState!, widget.action); } - @override - Widget build(BuildContext context) { - final Widget serviceDropdown = DropdownButton( + Widget serviceDropdown(List services) { + return DropdownButton( value: serviceState, elevation: 8, underline: Container(), @@ -60,7 +62,7 @@ class _SetupActionPageState extends State { GetIt.I().getActionsFor(service!, widget.action); }); }, - items: Service.all().map>((Service service) { + items: services.map>((Service service) { return DropdownMenuItem( value: service, child: Row(children: [ @@ -74,20 +76,42 @@ class _SetupActionPageState extends State { ); }).toList(), ); + } + + @override + Widget build(BuildContext context) { var cardShape = const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(10))); return AerisCardPage( - body: Padding( + body: Consumer(builder: (context, services, _) => Padding( padding: const EdgeInsets.only(bottom: 20, left: 10, right: 10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - widget.action is Trigger - ? AppLocalizations.of(context).setupTrigger - : AppLocalizations.of(context).setupReaction, + children: services.connectedServices.isEmpty ? + ([ + Text(AppLocalizations.of(context).authenticatedToNoService, + style: const TextStyle( + fontSize: 25, + ) + ), + Padding(padding: const EdgeInsets.all(30), + child: ColoredClickableCard( + color: Theme.of(context) + .colorScheme + .secondaryContainer, + text:AppLocalizations.of(context).connectService, + onTap: () => showAerisCardPage( + context, (_) => const ServicePage()).then((value) => setState((){}) //TODO check, might be useless + ) + ) + ) + ]) + : [ + Text(widget.action is Trigger + ? AppLocalizations.of(context).setupTrigger + : AppLocalizations.of(context).setupReaction, style: const TextStyle( fontSize: 25, )), @@ -106,7 +130,7 @@ class _SetupActionPageState extends State { child: Padding( padding: const EdgeInsets.all(8.0), child: Align( - alignment: Alignment.centerRight, child: serviceDropdown), + alignment: Alignment.centerRight, child: serviceDropdown(services.connectedServices)), ), ), ], @@ -175,6 +199,6 @@ class _SetupActionPageState extends State { ] ], ), - )); + ))); } } diff --git a/mobile/lib/src/widgets/background/animated_background.dart b/mobile/lib/src/widgets/background/animated_background.dart index 652da0c..f0694a8 100644 --- a/mobile/lib/src/widgets/background/animated_background.dart +++ b/mobile/lib/src/widgets/background/animated_background.dart @@ -87,7 +87,9 @@ class _AnimatedBackgroundState extends State with TickerProv }); Timer(const Duration(milliseconds: 2500), () { - topAnimController.forward(); + try { + topAnimController.forward(); + } catch (e) {} }); bottomAnimController.forward(); diff --git a/mobile/lib/src/widgets/warning_dialog.dart b/mobile/lib/src/widgets/warning_dialog.dart index 4ca34ec..3289a3c 100644 --- a/mobile/lib/src/widgets/warning_dialog.dart +++ b/mobile/lib/src/widgets/warning_dialog.dart @@ -1,3 +1,4 @@ +import 'package:aeris/main.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -11,13 +12,18 @@ class WarningDialog extends StatelessWidget { ///The action to execute once the warning was accepted final void Function() onAccept; + ///Color of the button that trigger the action + Color? actionButtonColor; - const WarningDialog( + WarningDialog( {Key? key, required this.message, + this.actionButtonColor, required this.onAccept, required this.warnedAction}) - : super(key: key); + : super(key: key) { + actionButtonColor ??= Theme.of(Aeris.materialKey.currentContext!).colorScheme.error; + } @override Widget build(BuildContext context) { @@ -28,27 +34,24 @@ class WarningDialog extends StatelessWidget { ) ), content: Text(message), + actionsAlignment: MainAxisAlignment.spaceEvenly, + actionsOverflowDirection: VerticalDirection.up, actions: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - ElevatedButton( + ElevatedButton( style: ElevatedButton.styleFrom( primary: Theme.of(context).colorScheme.primaryContainer), onPressed: () => Navigator.pop(context), child: Text(AppLocalizations.of(context).cancel), ), - ElevatedButton( + ElevatedButton( style: ElevatedButton.styleFrom( - primary: Theme.of(context).colorScheme.error), + primary: actionButtonColor), onPressed: () => { Navigator.pop(context), onAccept(), }, child: Text(warnedAction) ) - ], - ), - ]); + ]); } }