Mobile Client: OAUth Links for services

This commit is contained in:
Arthi-chaud
2022-02-26 10:15:12 +01:00
parent 53b92b16a8
commit ec725ff037
7 changed files with 176 additions and 104 deletions
+15
View File
@@ -47,5 +47,20 @@
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>FlutterDeepLinkingEnabled</key>
<true/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>localhost:3000</string>
<key>CFBundleURLSchemes</key>
<array>
<string>http</string>
</array>
</dict>
</array>
</dict>
</plist>
+15
View File
@@ -45,5 +45,20 @@
<false/>
<key>UIStatusBarHidden</key>
<false/>
<key>FlutterDeepLinkingEnabled</key>
<true/>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>localhost:3000</string>
<key>CFBundleURLSchemes</key>
<array>
<string>http</string>
</array>
</dict>
</array>
</dict>
</plist>
+7 -7
View File
@@ -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<Pipeline> 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",
+44 -13
View File
@@ -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(),
+1 -1
View File
@@ -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
+59 -51
View File
@@ -30,61 +30,69 @@ class _HomePageState extends State<HomePage> {
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<String>(
context: context,
builder: (BuildContext context) => WarningDialog(
message: AppLocalizations.of(context).logoutWarningMessage,
onAccept: () {
GetIt.I<AerisAPI>().stopConnection();
Navigator.of(context).popAndPushNamed('/');
},
warnedAction: AppLocalizations.of(context).logout
)
),
context: context,
builder: (BuildContext context) => WarningDialog(
message: AppLocalizations.of(context).logoutWarningMessage,
onAccept: () {
GetIt.I<AerisAPI>().stopConnection();
Navigator.of(context).popAndPushNamed('/');
},
warnedAction: AppLocalizations.of(context).logout)),
);
return Consumer<PipelineProvider>(
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),
),
)),
));
}
}
+35 -32
View File
@@ -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<Widget> 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<Service> services = const [
List<Service> services = [
Service.discord(),
Service.gmail(),
Service.github(),
@@ -59,39 +61,40 @@ class ServicePage extends StatelessWidget {
return Consumer<PipelineProvider>(
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<AerisAPI>()
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<AerisAPI>()
.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),
],
),
);
),
);
}
}