1
0
Fork 0

Finish to add configuration tutorial

This commit is contained in:
Florian RICHER (MrDev023) 2022-03-31 21:34:28 +02:00
parent a3b25c3ee1
commit 4e8fd12fdc
9 changed files with 236 additions and 39 deletions

View file

@ -1,19 +1,23 @@
import 'package:desktopapp/widgets/pages/home.dart';
import 'package:desktopapp/widgets/pages/tutorial.dart'; import 'package:desktopapp/widgets/pages/tutorial.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
enum Routes { streamlabsTutorial } enum Routes { streamlabsTutorial, home }
extension on Routes { extension RoutesExtension on Routes {
String get path { String get path {
switch (this) { switch (this) {
case Routes.streamlabsTutorial: case Routes.streamlabsTutorial:
return '/streamlabs/tutorial'; return '/streamlabs/tutorial';
case Routes.home:
return '/home';
} }
} }
} }
var routes = <String, WidgetBuilder>{ var routes = <String, WidgetBuilder>{
Routes.streamlabsTutorial.path: (context) => const TutorialPage(), Routes.streamlabsTutorial.path: (context) => TutorialPage(),
Routes.home.path: (context) => const HomePage(),
}; };
var initialRoute = Routes.streamlabsTutorial.path; var initialRoute = Routes.streamlabsTutorial.path;

30
lib/utils/logger.dart Normal file
View file

@ -0,0 +1,30 @@
import 'package:flutter/foundation.dart';
enum LoggerType { info, warning, error }
class Logger {
static void log(LoggerType logType, Object? instance, String message) {
if (kDebugMode) {
String className;
if (instance is Type) {
className = instance.toString();
} else {
className = instance?.runtimeType.toString() ?? 'GLOBAL';
}
String cat;
switch (logType) {
case LoggerType.info:
cat = 'INFO';
break;
case LoggerType.warning:
cat = 'WARNING';
break;
case LoggerType.error:
cat = 'ERROR';
break;
}
// ignore: avoid_print
print('[$cat][$className] $message');
}
}
}

View file

@ -2,7 +2,9 @@ import 'package:desktopapp/widgets/components/link.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class Step1 extends StatelessWidget { class Step1 extends StatelessWidget {
const Step1({Key? key}) : super(key: key); const Step1({Key? key, required this.onNext}) : super(key: key);
final VoidCallback onNext;
Widget buildFieldDescription(String field, String description) { Widget buildFieldDescription(String field, String description) {
return Row( return Row(
@ -60,7 +62,7 @@ class Step1 extends StatelessWidget {
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
TextButton(onPressed: () {}, child: const Text('Étape suivante')) TextButton(onPressed: onNext, child: const Text('Étape suivante'))
], ],
), ),
) )

View file

@ -4,7 +4,11 @@ import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
class Step2 extends StatelessWidget { class Step2 extends StatelessWidget {
Step2({Key? key}) : super(key: key); Step2({Key? key, required this.onPrevious, required this.onNext})
: super(key: key);
final VoidCallback onPrevious;
final VoidCallback onNext;
final TextEditingController _clientIdController = TextEditingController(); final TextEditingController _clientIdController = TextEditingController();
final TextEditingController _clientSecretController = TextEditingController(); final TextEditingController _clientSecretController = TextEditingController();
@ -86,8 +90,8 @@ class Step2 extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
TextButton( TextButton(
onPressed: () {}, child: const Text('Étape précédente')), onPressed: onPrevious, child: const Text('Étape précédente')),
TextButton(onPressed: () {}, child: const Text('Étape suivante')) TextButton(onPressed: onNext, child: const Text('Étape suivante'))
], ],
), ),
) )

View file

@ -1,30 +1,45 @@
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:desktopapp/classes/routes.dart';
import 'package:desktopapp/utils/logger.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:desktopapp/widgets/components/link.dart' as link; import 'package:url_launcher/url_launcher.dart';
class Step3 extends StatelessWidget { class Step3 extends StatelessWidget {
const Step3({Key? key}) : super(key: key); const Step3({Key? key, required this.onPrevious}) : super(key: key);
final VoidCallback onPrevious;
Widget buildContent(BuildContext context, SharedPreferences prefs) { Widget buildContent(BuildContext context, SharedPreferences prefs) {
var clientId = prefs.getString('client_id');
return Column(children: [ return Column(children: [
const Padding( const Padding(
padding: EdgeInsets.symmetric(vertical: 20), padding: EdgeInsets.symmetric(vertical: 20),
child: Text('Étape 3:Authorisation de l\'application', child: Text('Étape 3: Autorisation de l\'application',
style: TextStyle(fontSize: 20)), style: TextStyle(fontSize: 20)),
), ),
link.Link( const Text(
uri: 'Pour terminer la configuration, il reste plus qu\'à se connecter à Streamlabs pour autoriser l\'application à créer des alertes.'),
'https://streamlabs.com/api/v1.0/authorize?redirect_uri=http://localhost:1234/&client_id=$clientId&response_type=code&scope=alerts.create', const SizedBox(height: 10),
label: 'Cliquez ici pour vous connecter à Streamlabs', TextButton(
) onPressed: () async {
waitAuthorization(context, prefs);
var clientId = prefs.getString('client_id');
await Future.delayed(const Duration(seconds: 1));
launch(
'https://streamlabs.com/api/v1.0/authorize?redirect_uri=http://localhost:1234/&client_id=$clientId&response_type=code&scope=alerts.create');
},
child: const Text('Cliquez ici pour vous connecter à Streamlabs')),
]); ]);
} }
Future waitAuthorizationCallback() async { Future<String?> waitAuthorizationCallback(SharedPreferences prefs) async {
var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 1214); var server = await HttpServer.bind(InternetAddress.loopbackIPv4, 1234);
Logger.log(LoggerType.info, this,
'Server started at ${server.address.address}:${server.port}');
var request = await server.first; var request = await server.first;
var uri = Uri.dataFromString(request.requestedUri.toString()); var uri = Uri.dataFromString(request.requestedUri.toString());
@ -32,30 +47,116 @@ class Step3 extends StatelessWidget {
var code = query['code']; var code = query['code'];
if (code != null) { if (code != null) {
var prefs = await SharedPreferences.getInstance();
prefs.setString('code', code); prefs.setString('code', code);
request.response
..headers.contentType = ContentType.text
..write('You can close tab now')
..close();
} else {
request.response
..headers.contentType = ContentType.text
..write('Internal error')
..close();
} }
request.response return code;
..headers.contentType = ContentType.text }
..write('You can close tab now')
..close();
// SEND TOKEN ASK TO API Future<void> waitAuthorization(
// POST https://streamlabs.com/api/v1.0/token BuildContext context, SharedPreferences prefs) async {
// { var code = await waitAuthorizationCallback(prefs);
// "grant_type": "authorization_code",
// "code": "CODE", if (code != null) {
// "redirect_uri": "http://localhost:1234/", if (prefs.containsKey('credentials')) {
// "client_id": "CLIENT_ID", showDialog(
// "client_secret": "CLIENT_SECRET" context: context,
// } builder: (_) => AlertDialog(
// SAVE RESPONSE TOKEN AS CREDENTIALS IN SHARED PREFERENCES title: const Text('Info'),
content: const Text(
'Connexion réussi. Appuyez sur OK pour acceder à l\'application.'),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
Navigator.of(context)
.pushReplacementNamed(Routes.home.path);
},
child: const Text('OK'))
],
));
} else {
Dio dio = Dio(BaseOptions(
contentType: Headers.jsonContentType,
responseType: ResponseType.json,
validateStatus: (_) => true));
var data = jsonEncode({
'grant_type': 'authorization_code',
'client_id': prefs.getString('client_id'),
'client_secret': prefs.getString('client_secret'),
'redirect_uri': 'http://localhost:1234/',
'code': code,
});
var response =
await dio.post('https://streamlabs.com/api/v1.0/token', data: data);
if (response.statusCode == HttpStatus.ok) {
prefs.setString('credentials', response.data.toString());
Logger.log(LoggerType.info, this,
'Authorization success : ${response.data}');
showDialog(
context: context,
builder: (_) => AlertDialog(
title: const Text('Info'),
content: const Text(
'Connexion réussi. Appuyez sur OK pour acceder à l\'application.'),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
Navigator.of(context)
.pushReplacementNamed(Routes.home.path);
},
child: const Text('OK'))
],
));
} else {
Logger.log(LoggerType.error, this,
'Authorization error : ${response.data} with $data and ${response.requestOptions.headers}');
showDialog(
context: context,
builder: (_) => AlertDialog(
title: const Text('Erreur'),
content: const Text('Impossible de se connecter.'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('OK'))
],
));
}
}
} else {
Logger.log(LoggerType.error, this,
'Authorization error : impossible to get code');
showDialog(
context: context,
builder: (_) => AlertDialog(
title: const Text('Erreur'),
content: const Text('Impossible de se connecter.'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('OK'))
],
));
}
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
waitAuthorizationCallback();
return Column( return Column(
children: [ children: [
Expanded( Expanded(
@ -76,8 +177,7 @@ class Step3 extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
TextButton( TextButton(
onPressed: () {}, child: const Text('Étape précédente')), onPressed: onPrevious, child: const Text('Étape précédente'))
TextButton(onPressed: () {}, child: const Text('Étape suivante'))
], ],
), ),
) )

View file

@ -0,0 +1,10 @@
import 'package:flutter/material.dart';
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Scaffold(body: Text('Test'));
}
}

View file

@ -4,10 +4,42 @@ import 'package:desktopapp/widgets/components/tutorials/step3.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class TutorialPage extends StatelessWidget { class TutorialPage extends StatelessWidget {
const TutorialPage({Key? key}) : super(key: key); TutorialPage({Key? key}) : super(key: key);
final PageController videoPageController = PageController(
initialPage: 0,
viewportFraction: 1,
);
void onPrevious() {
videoPageController.previousPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeIn,
);
}
void onNext() {
videoPageController.nextPage(
duration: const Duration(milliseconds: 300),
curve: Curves.easeIn,
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold(body: Step3()); return Scaffold(
body: CustomScrollView(
controller: videoPageController,
scrollDirection: Axis.horizontal,
physics: const PageScrollPhysics(),
slivers: [
SliverFillViewport(
delegate: SliverChildListDelegate([
Step1(onNext: onNext),
Step2(onPrevious: onPrevious, onNext: onNext),
Step3(onPrevious: onPrevious),
])),
],
));
} }
} }

View file

@ -57,6 +57,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.4" version: "1.0.4"
dio:
dependency: "direct main"
description:
name: dio
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.6"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -114,6 +121,13 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.dartlang.org"
source: hosted
version: "4.0.0"
js: js:
dependency: transitive dependency: transitive
description: description:

View file

@ -38,6 +38,7 @@ dependencies:
url_launcher: ^6.0.20 url_launcher: ^6.0.20
shared_preferences: ^2.0.13 shared_preferences: ^2.0.13
flash: ^2.0.3+2 flash: ^2.0.3+2
dio: ^4.0.6
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: