diff --git a/assets/images/cluster-photo-00.jpg b/assets/images/cluster-photo-00.jpg new file mode 100644 index 0000000..f0923bc Binary files /dev/null and b/assets/images/cluster-photo-00.jpg differ diff --git a/lib/main.dart b/lib/main.dart index e98d001..f372473 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -17,11 +17,30 @@ class MyApp extends StatelessWidget { return MaterialApp( title: '42 API Client', initialRoute: '/', - routes: { - '/': (context) => const InitScreen(), - '/home': (context) => HomeScreen(), - '/login': (context) => const LoginScreen(), - '/profile': (context) => const ProfileScreen(), + onGenerateRoute: (settings) { + final uri = Uri.parse(settings.name ?? '/'); + + if (uri.path == '/') { + return MaterialPageRoute(builder: (_) => const InitScreen()); + } + if (uri.path == '/home') { + return MaterialPageRoute(builder: (_) => const HomeScreen()); + } + if (uri.path == '/login') { + return MaterialPageRoute(builder: (_) => const LoginScreen()); + } + + if (uri.pathSegments.length == 2 && uri.pathSegments[0] == 'profile') { + final login = uri.pathSegments[1]; + return MaterialPageRoute( + builder: (_) => ProfileScreen(login: login), + ); + } + return MaterialPageRoute( + builder: (_) => const Scaffold( + body: Center(child: Text('404 - Page not found')), + ), + ); }, ); } diff --git a/lib/methods/api.dart b/lib/methods/api.dart index 9450487..be27714 100644 --- a/lib/methods/api.dart +++ b/lib/methods/api.dart @@ -1,15 +1,24 @@ import 'package:shared_preferences/shared_preferences.dart'; +import 'package:http/http.dart'; Future checkToken() async { final prefs = await SharedPreferences.getInstance(); - return prefs.containsKey('user_token'); + if (prefs.containsKey('user_token') == false) { return false; } + final date = DateTime.fromMillisecondsSinceEpoch(prefs.getInt('expiration') ?? 0); + return date.isAfter(DateTime.now()); } -Future saveToken(String token) async +// Future> fetchProfileData(String login) async +// { +// +// } +Future saveToken(String token, String refresh, DateTime expiration) async { final prefs = await SharedPreferences.getInstance(); await prefs.setString('user_token', token); + await prefs.setString('refresh_token', refresh); + await prefs.setInt('expiration', expiration.millisecondsSinceEpoch); } Future getToken() async diff --git a/lib/views/home_screen.dart b/lib/views/home_screen.dart index a6fcb81..3435c66 100644 --- a/lib/views/home_screen.dart +++ b/lib/views/home_screen.dart @@ -1,20 +1,69 @@ // home_screen.dart import 'package:flutter/material.dart'; -class HomeScreen extends StatelessWidget { +class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); + @override + State createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State { + final TextEditingController _controller = TextEditingController(); + + void _handleInput(BuildContext context, String input) { + // Replace with your desired logic + print("Input received: $input"); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text('You entered: $input')), + ); + Navigator.pushReplacementNamed(context, '/profile/${input}'); + } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("Home")), - body: Center( - child: ElevatedButton( - onPressed: () { - Navigator.pushNamed(context, '/profile'); - }, - child: const Text('Go to Profile'), - ), + body: Stack( + fit: StackFit.expand, + children: [ + Image.asset( + 'assets/images/cluster-photo-00.jpg', + fit: BoxFit.cover, + ), + Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + TextField( + controller: _controller, + decoration: const InputDecoration( + hintText: 'Enter something...', + border: OutlineInputBorder(), + filled: true, + fillColor: Colors.white70, + ), + ), + const SizedBox(height: 10), + ElevatedButton( + onPressed: () { + _handleInput(context, _controller.text); + }, + child: const Text('Submit Input'), + ), + const SizedBox(height: 20), + ElevatedButton( + onPressed: () { + Navigator.pushNamed(context, '/profile'); + }, + child: const Text('Go to Profile'), + ), + ], + ), + ), + ), + ], ), ); } diff --git a/lib/views/login_screen.dart b/lib/views/login_screen.dart index 2d91a63..e122dde 100644 --- a/lib/views/login_screen.dart +++ b/lib/views/login_screen.dart @@ -1,13 +1,15 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_appauth/flutter_appauth.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:swifty/methods/api.dart'; const FlutterAppAuth _appAuth = FlutterAppAuth(); Future redirect_to_oauth(BuildContext context) async { - final String _clientId = dotenv.env['CLIENT-ID'] ?? ''; - final String _clientSecret = dotenv.env['CLIENT-SECRET'] ?? ''; + final String _clientId = "CLIENT TODO"; + final String _clientSecret = "SECRET TODO"; final String _redirectUrl = 'swifty-companion://oauth2/callback'; final String _authorizationEndpoint = 'https://api.intra.42.fr/oauth/authorize'; final String _tokenEndpoint = 'https://api.intra.42.fr/oauth/token'; @@ -18,36 +20,30 @@ Future redirect_to_oauth(BuildContext context) async { authorizationEndpoint: _authorizationEndpoint, tokenEndpoint: _tokenEndpoint, )); - print("swap"); try { - print("trying"); final AuthorizationResponse? result = await _appAuth.authorize(request); - print("tried"); - if (result != null) { - print("nonull result"); - Navigator.pushReplacementNamed(context, "/home"); - print('Authorization Code: ${result.authorizationCode}'); - final token_request = TokenRequest( - _clientId, - _redirectUrl, - clientSecret: _clientSecret, - authorizationCode: result.authorizationCode, - grantType: 'authorization_code', - serviceConfiguration: AuthorizationServiceConfiguration( - authorizationEndpoint: "https://api.intra.42.fr/oauth/authorize", - tokenEndpoint: _tokenEndpoint, - ), - ); - print("pre token"); - final TokenResponse? tokenResponse = await _appAuth.token(token_request); - print("token"); - print(tokenResponse?.accessToken); - print(tokenResponse?.refreshToken); - // You can now use this code to exchange for an access token (via a separate API call to the token endpoint) - } + if (result == null) { throw PlatformException(code: "fatal_error"); } + final token_request = TokenRequest( + _clientId, + _redirectUrl, + clientSecret: _clientSecret, + authorizationCode: result.authorizationCode, + grantType: 'authorization_code', + serviceConfiguration: AuthorizationServiceConfiguration( + authorizationEndpoint: "https://api.intra.42.fr/oauth/authorize", + tokenEndpoint: _tokenEndpoint, + ), + ); + final TokenResponse? tokenResponse = await _appAuth.token(token_request); + saveToken(tokenResponse?.accessToken ?? '', tokenResponse?.refreshToken ?? '', tokenResponse?.accessTokenExpirationDateTime ?? DateTime.now()); + Navigator.pushReplacementNamed(context, "/home"); + } + on PlatformException catch (e) + { + if (e.code == "authorize_failed" && e.message?.contains('User cancelled flow') == true) { return; } } catch (e) { - print("error $e"); + return; } } class LoginScreen extends StatelessWidget { diff --git a/lib/views/profile_screen.dart b/lib/views/profile_screen.dart index 79de781..e6336f9 100644 --- a/lib/views/profile_screen.dart +++ b/lib/views/profile_screen.dart @@ -2,7 +2,8 @@ import 'package:flutter/material.dart'; class ProfileScreen extends StatelessWidget { - const ProfileScreen({super.key}); + final String login; + const ProfileScreen({super.key, required this.login}); @override Widget build(BuildContext context) { @@ -11,9 +12,9 @@ class ProfileScreen extends StatelessWidget { body: Center( child: ElevatedButton( onPressed: () { - Navigator.pushNamed(context, '/'); + Navigator.pushNamed(context, '/login'); }, - child: const Text('Go to Home'), + child: Text('Go to ${login}'), ), ), ); diff --git a/pubspec.yaml b/pubspec.yaml index 6fc924f..0a9b3c5 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -56,6 +56,7 @@ dev_dependencies: flutter: assets: - assets/images/42_logo.png + - assets/images/cluster-photo-00.jpg # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class.