mirror of
https://github.com/zoriya/Aeris.git
synced 2026-06-07 04:16:42 +00:00
Mobile Client: OAUth Links for services
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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",
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
),
|
||||
)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
],
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user