diff --git a/mobile/ios/Runner/Info-Debug.plist b/mobile/ios/Runner/Info-Debug.plist
index 6cdf697..1f4b792 100644
--- a/mobile/ios/Runner/Info-Debug.plist
+++ b/mobile/ios/Runner/Info-Debug.plist
@@ -47,5 +47,20 @@
UIViewControllerBasedStatusBarAppearance
+ FlutterDeepLinkingEnabled
+
+ CFBundleURLTypes
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ localhost:3000
+ CFBundleURLSchemes
+
+ http
+
+
+
diff --git a/mobile/ios/Runner/Info-Release.plist b/mobile/ios/Runner/Info-Release.plist
index f759f12..911a728 100644
--- a/mobile/ios/Runner/Info-Release.plist
+++ b/mobile/ios/Runner/Info-Release.plist
@@ -45,5 +45,20 @@
UIStatusBarHidden
+ FlutterDeepLinkingEnabled
+
+ CFBundleURLTypes
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ localhost:3000
+ CFBundleURLSchemes
+
+ http
+
+
+
\ No newline at end of file
diff --git a/mobile/lib/src/aeris_api.dart b/mobile/lib/src/aeris_api.dart
index 0717f1d..c8c4ad7 100644
--- a/mobile/lib/src/aeris_api.dart
+++ b/mobile/lib/src/aeris_api.dart
@@ -22,7 +22,7 @@ enum AerisAPIRequestType { get, post, put, delete }
/// Call to interact with Aeris' Back end
class AerisAPI {
/// Get Connection state
- bool _connected = false;
+ bool _connected = true;
bool get isConnected => _connected;
late List fakeAPI;
@@ -35,23 +35,23 @@ class AerisAPI {
AerisAPI() {
baseRoute = dotenv.env['HOSTNAME']!;
var trigger1 = Trigger(
- service: const Service.spotify(),
+ service: Service.spotify(),
name: "Play song",
last: DateTime.now());
var trigger3 = Trigger(
- service: const Service.discord(),
+ service: Service.discord(),
name: "Send a message",
last: DateTime.now());
var trigger2 = Trigger(
- service: const Service.spotify(),
+ service: Service.spotify(),
name: "Play song",
last: DateTime.parse("2022-01-01"));
var reaction = Reaction(
- service: const Service.twitter(), parameters: [], name: "Post a tweet");
+ service: Service.twitter(), parameters: [], name: "Post a tweet");
var reaction2 = Reaction(
- service: const Service.gmail(), parameters: [], name: "Do smth");
+ service: Service.gmail(), parameters: [], name: "Do smth");
var reaction1 = Reaction(
- service: const Service.youtube(), parameters: [], name: "Do smth youtube");
+ service: Service.youtube(), parameters: [], name: "Do smth youtube");
var pipeline1 = Pipeline(
id: 10,
name: "My Action",
diff --git a/mobile/lib/src/models/service.dart b/mobile/lib/src/models/service.dart
index 4564a99..60c45d8 100644
--- a/mobile/lib/src/models/service.dart
+++ b/mobile/lib/src/models/service.dart
@@ -1,6 +1,10 @@
// Class for a service (Youtube, Gmail, ...)
+import 'dart:math';
+
import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_dotenv/flutter_dotenv.dart';
+import 'dart:core';
/// Data class used to store data about a service (logo, url, name)
class Service {
@@ -13,6 +17,9 @@ class Service {
///URL To a service's logo
final String logoUrl;
+ ///return the url to authenticate to service via OAuth2
+ final String authUrl;
+
Widget getLogo({double logoSize = 40}) => ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: CachedNetworkImage(
@@ -22,38 +29,62 @@ class Service {
height: logoSize,
));
- const Service.spotify()
+ static String _generateRandomString() {
+ var randomString = "";
+ var seed = Random();
+ var randomNumber = seed.nextInt(10) * 10;
+
+ for (var i = 0; i < 20 + randomNumber; i++) {
+ randomString += String.fromCharCode(33 + (seed.nextInt(10) * 94));
+ }
+ return randomString;
+ }
+
+ Service.spotify()
: name = "Spotify",
url = "https://www.spotify.com",
logoUrl =
- "https://www.presse-citron.net/app/uploads/2020/06/spotify-une-.jpg";
- const Service.gmail()
+ "https://www.presse-citron.net/app/uploads/2020/06/spotify-une-.jpg",
+ authUrl =
+ "https://accounts.spotify.com/authorize?client_id=${dotenv.env['SPOTIFY_CLIENT_ID']}&response_type=code&redirect_uri=https://localhost:3000/authorization/spotify";
+ Service.gmail()
: name = "Gmail",
url = "https://mail.google.com/",
logoUrl =
- "https://play-lh.googleusercontent.com/KSuaRLiI_FlDP8cM4MzJ23ml3og5Hxb9AapaGTMZ2GgR103mvJ3AAnoOFz1yheeQBBI";
- const Service.discord()
+ "https://play-lh.googleusercontent.com/KSuaRLiI_FlDP8cM4MzJ23ml3og5Hxb9AapaGTMZ2GgR103mvJ3AAnoOFz1yheeQBBI",
+ authUrl = "";
+
+ ///TODO find
+ Service.discord()
: name = "Discord",
url = "https://discord.com/app",
logoUrl =
- "https://play-lh.googleusercontent.com/fbrWR4LbtB_1Ulgz3_rw8bY3tx_zPU7A9ZOB5WYG_QmqOUUjA6JEzE_20GA4YBDWMx4";
- const Service.twitter()
+ "https://play-lh.googleusercontent.com/fbrWR4LbtB_1Ulgz3_rw8bY3tx_zPU7A9ZOB5WYG_QmqOUUjA6JEzE_20GA4YBDWMx4",
+ authUrl =
+ "https://discord.com/api/oauth2/authorize?response_type=code&client_id=${dotenv.env['DISCORD_CLIENT_ID']}&scope=applications.commands%20applications.entitlements%20applications.store.update%20bot%20guilds%20guilds.join%20guilds.members.read%20identify%20messages.read%20webhook.incoming&state=${_generateRandomString()}";
+ Service.twitter()
: name = "Twitter",
url = "https://twitter.com",
logoUrl =
- "https://f.hellowork.com/blogdumoderateur/2019/11/twitter-logo-1200x1200.jpg";
- const Service.github()
+ "https://f.hellowork.com/blogdumoderateur/2019/11/twitter-logo-1200x1200.jpg",
+ authUrl =
+ "https://twitter.com/i/oauth2/authorize?response_type=code&client_id=${dotenv.env['TWITTER_CLIENT_ID']}&redirect_uri=https://localhost:3000/authorization/twitter&scope=tweet.read%20users.read%20offline.access&state=${_generateRandomString()}&code_challenge=challenge&code_challenge_method=plain";
+ Service.github()
: name = "GitHub",
url = "https://github.com/",
- logoUrl = "https://avatars.githubusercontent.com/u/9919?s=280&v=4";
- const Service.youtube()
+ logoUrl = "https://avatars.githubusercontent.com/u/9919?s=280&v=4",
+ authUrl =
+ "https://github.com/login/oauth/authorize?client_id=${dotenv.env['GITHUB_CLIENT_ID']}&response_type=code&redirect_uri=http://localhost:3000/authorization/github";
+ Service.youtube()
: name = "Youtube",
url = "https://youtube.com",
logoUrl =
- "https://play-lh.googleusercontent.com/lMoItBgdPPVDJsNOVtP26EKHePkwBg-PkuY9NOrc-fumRtTFP4XhpUNk_22syN4Datc";
+ "https://play-lh.googleusercontent.com/lMoItBgdPPVDJsNOVtP26EKHePkwBg-PkuY9NOrc-fumRtTFP4XhpUNk_22syN4Datc",
+ authUrl =
+ "https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=${dotenv.env['GOOGLE_CLIENT_ID']}&scope=openid%20email&redirect_uri=http://localhost:3000/authorization/google&state=${_generateRandomString()}";
/// Returns a list of all the available services
- static all() => const [
+ static all() => [
Service.discord(),
Service.github(),
Service.gmail(),
diff --git a/mobile/lib/src/models/trigger.dart b/mobile/lib/src/models/trigger.dart
index 3fbd619..6b37b5f 100644
--- a/mobile/lib/src/models/trigger.dart
+++ b/mobile/lib/src/models/trigger.dart
@@ -47,7 +47,7 @@ class Trigger extends aeris_action.Action {
/// Template trigger, used as an 'empty' trigger
Trigger.template({Key? key, this.last})
- : super(service: const Service.twitter(), name: '', parameters: []);
+ : super(service: Service.twitter(), name: '', parameters: []);
@override
// ignore: avoid_renaming_method_parameters
diff --git a/mobile/lib/src/views/home_page.dart b/mobile/lib/src/views/home_page.dart
index 5aaef98..c964d11 100644
--- a/mobile/lib/src/views/home_page.dart
+++ b/mobile/lib/src/views/home_page.dart
@@ -30,61 +30,69 @@ class _HomePageState extends State {
Widget serviceActionButtons = IconButton(
icon: const Icon(Icons.electrical_services),
- onPressed: () => showAerisCardPage(context, (context) => const ServicePage())
- );
+ onPressed: () =>
+ showAerisCardPage(context, (context) => ServicePage()));
Widget logoutActionButton = IconButton(
icon: const Icon(Icons.logout),
onPressed: () => showDialog(
- context: context,
- builder: (BuildContext context) => WarningDialog(
- message: AppLocalizations.of(context).logoutWarningMessage,
- onAccept: () {
- GetIt.I().stopConnection();
- Navigator.of(context).popAndPushNamed('/');
- },
- warnedAction: AppLocalizations.of(context).logout
- )
- ),
+ context: context,
+ builder: (BuildContext context) => WarningDialog(
+ message: AppLocalizations.of(context).logoutWarningMessage,
+ onAccept: () {
+ GetIt.I().stopConnection();
+ Navigator.of(context).popAndPushNamed('/');
+ },
+ warnedAction: AppLocalizations.of(context).logout)),
);
return Consumer(
- builder: (context, provider, _) => AerisPage(
- floatingActionButton: FloatingActionButton(
- onPressed: () => showAerisCardPage(context, (_) => const CreatePipelinePage()),
- backgroundColor: Theme.of(context).colorScheme.secondary,
- elevation: 10,
- child: const Icon(Icons.add),
- ),
- actions: [
- HomePageSortMenu(
- collectionProvider: provider,
- ),
- serviceActionButtons,
- logoutActionButton
- ],
- body: provider.initialized == false
- ? ListView(physics: const BouncingScrollPhysics(),
- padding: const EdgeInsets.only(bottom: 20, top: 20, left: 10, right: 10),
- children: [SkeletonLoader(
- builder: ClickableCard(onTap:(){}, body: const SizedBox(height: 80)),
- items: 10,
- highlightColor: Theme.of(context).colorScheme.secondary
- )])
- : LiquidPullToRefresh(
- borderWidth: 2,
- animSpeedFactor: 3,
- color: Colors.transparent,
- showChildOpacityTransition: false,
- onRefresh: () => provider.fetchPipelines()
- .then((_) => setState(() {})), // refresh callback
- child: ListView.builder(
- physics: const BouncingScrollPhysics(),
- padding: const EdgeInsets.only(bottom: 20, top: 20, left: 10, right: 10),
- controller: listController,
- itemCount: provider.pipelineCount,
- itemBuilder: (BuildContext context, int index) =>
- PipelineCard(pipeline: provider.getPipelineAt(index),
- ),
- )),
- ));
+ builder: (context, provider, _) => AerisPage(
+ floatingActionButton: FloatingActionButton(
+ onPressed: () => showAerisCardPage(
+ context, (_) => const CreatePipelinePage()),
+ backgroundColor: Theme.of(context).colorScheme.secondary,
+ elevation: 10,
+ child: const Icon(Icons.add),
+ ),
+ actions: [
+ HomePageSortMenu(
+ collectionProvider: provider,
+ ),
+ serviceActionButtons,
+ logoutActionButton
+ ],
+ body: provider.initialized == false
+ ? ListView(
+ physics: const BouncingScrollPhysics(),
+ padding: const EdgeInsets.only(
+ bottom: 20, top: 20, left: 10, right: 10),
+ children: [
+ SkeletonLoader(
+ builder: ClickableCard(
+ onTap: () {},
+ body: const SizedBox(height: 80)),
+ items: 10,
+ highlightColor:
+ Theme.of(context).colorScheme.secondary)
+ ])
+ : LiquidPullToRefresh(
+ borderWidth: 2,
+ animSpeedFactor: 3,
+ color: Colors.transparent,
+ showChildOpacityTransition: false,
+ onRefresh: () => provider
+ .fetchPipelines()
+ .then((_) => setState(() {})), // refresh callback
+ child: ListView.builder(
+ physics: const BouncingScrollPhysics(),
+ padding: const EdgeInsets.only(
+ bottom: 20, top: 20, left: 10, right: 10),
+ controller: listController,
+ itemCount: provider.pipelineCount,
+ itemBuilder: (BuildContext context, int index) =>
+ PipelineCard(
+ pipeline: provider.getPipelineAt(index),
+ ),
+ )),
+ ));
}
}
diff --git a/mobile/lib/src/views/service_page.dart b/mobile/lib/src/views/service_page.dart
index 4cee766..a781498 100644
--- a/mobile/lib/src/views/service_page.dart
+++ b/mobile/lib/src/views/service_page.dart
@@ -9,11 +9,13 @@ import 'package:aeris/src/widgets/warning_dialog.dart';
import 'package:get_it/get_it.dart';
import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
+import 'package:url_launcher/url_launcher.dart';
///Page listing connected & available services
class ServicePage extends StatelessWidget {
- const ServicePage({Key? key}) : super(key: key);
+ ServicePage({Key? key}) : super(key: key);
+ ///TODO from an api call, determine what services are plugged
List getServiceGroup(String groupName, Icon trailingIcon,
void Function(Service) onTap, BuildContext context) {
UserServiceProvider uServicesProvider =
@@ -41,7 +43,7 @@ class ServicePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
- List services = const [
+ List services = [
Service.discord(),
Service.gmail(),
Service.github(),
@@ -59,39 +61,40 @@ class ServicePage extends StatelessWidget {
return Consumer(
builder: (context, provider, _) => AerisCardPage(
body: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- ...[
- Align(
- alignment: Alignment.center,
- child: Text(AppLocalizations.of(context).services, style: const TextStyle(fontSize: 25)),
- ),
- const SizedBox(height: 60)
- ],
- ...getServiceGroup(
- AppLocalizations.of(context).connected,
- const Icon(Icons.delete, color: Colors.red),
- (Service service) => showDialog(
- context: context,
- builder: (BuildContext context) => WarningDialog(
- message: AppLocalizations.of(context)
- .disconnectServiceWarningMessage,
- onAccept: () => GetIt.I()
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ ...[
+ Align(
+ alignment: Alignment.center,
+ child: Text(AppLocalizations.of(context).services,
+ style: const TextStyle(fontSize: 25)),
+ ),
+ const SizedBox(height: 60)
+ ],
+ ...getServiceGroup(
+ AppLocalizations.of(context).connected,
+ const Icon(Icons.delete, color: Colors.red),
+ (Service service) => showDialog(
+ context: context,
+ builder: (BuildContext context) => WarningDialog(
+ message: AppLocalizations.of(context)
+ .disconnectServiceWarningMessage,
+ onAccept: () => GetIt.I()
.disconnectService(service)
.then((_) => provider.fetchPipelines()),
- warnedAction:
- AppLocalizations.of(context).disconnect)),
- context),
- ...getServiceGroup(
- AppLocalizations.of(context).available,
- const Icon(Icons.connect_without_contact,
- color: Colors.green),
- (Service service) =>
+ warnedAction: AppLocalizations.of(context).disconnect)),
+ context),
+ ...getServiceGroup(
+ AppLocalizations.of(context).available,
+ const Icon(Icons.connect_without_contact, color: Colors.green),
+ (Service service) => {
print("Connected") /* TODO open page to connect service*/,
- context),
- ],
- ),
+ launch(Uri.parse(service.authUrl).toString())
+ },
+ context),
+ ],
),
- );
+ ),
+ );
}
}