Skip to content

Commit

Permalink
Merge pull request #80 from riverscuomo/74-switch-to-heroku-postgres-…
Browse files Browse the repository at this point in the history
…database-1

now auth back to client
  • Loading branch information
riverscuomo authored Sep 5, 2024
2 parents 76168eb + 54b5a2b commit 2f2dce6
Show file tree
Hide file tree
Showing 15 changed files with 179 additions and 313 deletions.
8 changes: 0 additions & 8 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Flutter Windows",
"request": "launch",
"type": "dart",
"program": "lib/main.dart",
"args": [],
"cwd": "${workspaceFolder}",
},
{
"name": "Flutter Web",
"request": "launch",
Expand Down
47 changes: 8 additions & 39 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:spotkin_flutter/app_core.dart';

import 'helpers/load_config.dart';
import 'spotify_theme_data.dart';

Expand All @@ -27,25 +26,9 @@ void main() async {
runApp(
MultiProvider(
providers: [
Provider<SpotifyService>(
create: (context) => SpotifyService(
clientId: config['SPOTIFY_CLIENT_ID']!,
clientSecret: config['SPOTIFY_CLIENT_SECRET']!,
redirectUri: config['SPOTIFY_REDIRECT_URI']!,
scope: config['SPOTIFY_SCOPE']!,
),
),
Provider<BackendService>(
create: (context) => BackendService(
backendUrl: config['BACKEND_URL']!,
spotifyService: context.read<SpotifyService>(),
),
),
Provider<JobProvider>(
create: (context) => JobProvider(
context.read<BackendService>(),
),
),
ChangeNotifierProvider(
create: (_) =>
JobProvider()), // Ensure this is ChangeNotifierProvider
],
child: MyApp(config),
),
Expand All @@ -68,32 +51,18 @@ class MyApp extends StatelessWidget {
color: Colors.black,
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 800),
constraints: const BoxConstraints(maxWidth: 1000),
child: child!,
),
),
);
},
// Initial route should just navigate to the auth screen without URL parsing
initialRoute: '/',
onGenerateRoute: (settings) {
final uri = Uri.parse(settings.name ?? '/');
if (uri.path == '/') {
if (uri.queryParameters.containsKey('access_token') ||
uri.queryParameters.containsKey('error')) {
// This is a callback from Spotify
return MaterialPageRoute(
builder: (context) => AuthScreen(config: config),
settings: settings,
);
} else {
// Normal root route
return MaterialPageRoute(
builder: (context) => AuthScreen(config: config),
);
}
}
// Handle other routes here if needed
return null;
return MaterialPageRoute(
builder: (context) => AuthScreen(config: config),
);
},
);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/providers/job_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import 'package:spotkin_flutter/app_core.dart';
class JobProvider extends ChangeNotifier {
List<Job> _jobs = [];
bool _isLoading = false;
final BackendService _backendService;
final _backendService = getIt<BackendService>();

List<Job> get jobs => _jobs;
bool get isLoading => _isLoading;

JobProvider(this._backendService) {
JobProvider() {
loadJobs();
}

Expand Down
114 changes: 41 additions & 73 deletions lib/screens/auth_screen.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:flutter_web_auth_2/flutter_web_auth_2.dart';
import 'package:spotkin_flutter/app_core.dart';

class AuthScreen extends StatefulWidget {
Expand All @@ -13,87 +12,64 @@ class AuthScreen extends StatefulWidget {
}

class _AuthScreenState extends State<AuthScreen> {
late final SpotifyService spotifyService;
bool _isLoading = true;
int _authAttempts = 0;
static const int MAX_AUTH_ATTEMPTS = 3;
bool _isLoading = false;

@override
void initState() {
super.initState();
spotifyService = SpotifyService(
clientId: widget.config['SPOTIFY_CLIENT_ID']!,
clientSecret: widget.config['SPOTIFY_CLIENT_SECRET']!,
redirectUri: widget.config['SPOTIFY_REDIRECT_URI']!,
scope: widget.config['SPOTIFY_SCOPE']!,
);

_checkExistingAuth();
}

@override
void didChangeDependencies() {
super.didChangeDependencies();
_handleIncomingLink();
// Automatically initiate login flow on screen load
_initiateSpotifyLogin();
}

Future<void> _checkExistingAuth() async {
Future<void> _initiateSpotifyLogin() async {
setState(() => _isLoading = true);
try {
print('AUTHSCREEN: Checking existing authentication...');
if (await spotifyService.checkAuthentication()) {
print('AUTHSCREEN: Existing authentication is valid');
_navigateToHomeScreen();
return;
}
} catch (e) {
print('AUTHSCREEN: Error checking existing auth: $e');
}
setState(() => _isLoading = false);
}

void _handleIncomingLink() {
final uri = Uri.parse(ModalRoute.of(context)!.settings.name ?? '');
if (uri.queryParameters.containsKey('access_token')) {
final accessToken = uri.queryParameters['access_token']!;
_handleAccessToken(accessToken);
} else if (uri.queryParameters.containsKey('error')) {
_showErrorSnackBar(
'Authentication failed: ${uri.queryParameters['error']}');
}
}
// 1. Build the Spotify OAuth URL
final authUrl = Uri.https('accounts.spotify.com', '/authorize', {
'client_id': widget.config['SPOTIFY_CLIENT_ID'],
'response_type': 'token', // Use 'code' if using authorization code flow
'redirect_uri':
'http://localhost:8888/auth.html', // Your registered redirect URI
'scope': widget.config['SPOTIFY_SCOPE'],
});

Future<void> _handleAccessToken(String accessToken) async {
try {
await spotifyService.setAccessToken(accessToken);
_navigateToHomeScreen();
// 2. Start the OAuth process using flutter_web_auth_2
final result = await FlutterWebAuth2.authenticate(
url: authUrl.toString(),
callbackUrlScheme: 'http', // The scheme used in your redirect URI
);

// 3. Extract the access token from the result
final fragment = Uri.parse(result).fragment;
final accessToken = Uri.splitQueryString(fragment)['access_token'];

if (accessToken != null) {
print('Access token: $accessToken');
_handleAccessToken(accessToken); // Handle the access token
} else {
print('Failed to extract access token');
}
} catch (e) {
print('Error handling access token: $e');
_showErrorSnackBar('Failed to set access token');
print('Error during Spotify login: $e');
_showErrorSnackBar('Failed to login with Spotify');
} finally {
setState(() => _isLoading = false);
}
}

Future<void> _navigateToHomeScreen() async {
Future<void> _handleAccessToken(String accessToken) async {
// Handle storing the access token and navigate to the home screen
Navigator.of(context).pushReplacement(
MaterialPageRoute(
builder: (context) => HomeScreen(
config: widget.config,
backendUrl: widget.config['BACKEND_URL']!,
),
builder: (context) => HomeScreen(config: widget.config),
),
);
}

void _initiateSpotifyLogin() {
final backendUrl = widget.config['BACKEND_URL']!;
final loginUrl = '$backendUrl/spotify-login';
Utils.myLaunch(loginUrl);
}

void _showErrorSnackBar(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(content: Text(message)));
}

@override
Expand All @@ -102,17 +78,9 @@ class _AuthScreenState extends State<AuthScreen> {
body: Center(
child: _isLoading
? CircularProgressIndicator()
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _initiateSpotifyLogin,
child: Text('Login with Spotify'),
),
if (_authAttempts > 0)
Text(
'Authentication attempts: $_authAttempts/${MAX_AUTH_ATTEMPTS}'),
],
: ElevatedButton(
onPressed: _initiateSpotifyLogin,
child: const Text('Login with Spotify'),
),
),
);
Expand Down
51 changes: 24 additions & 27 deletions lib/screens/home_screen.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:provider/provider.dart';
import 'package:spotify/spotify.dart';
import 'package:spotkin_flutter/app_core.dart';
Expand All @@ -10,20 +11,17 @@ const maxJobs = 2;
class HomeScreen extends StatefulWidget {
final Map<String, dynamic> config;

final String backendUrl;

const HomeScreen({
Key? key,
required this.config,
required this.backendUrl,
}) : super(key: key);

@override
_HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
late BackendService _backendService;
final BackendService _backendService = getIt<BackendService>();
final SpotifyService spotifyService = getIt<SpotifyService>();
late TabController _tabController;

Expand All @@ -36,13 +34,15 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
@override
void initState() {
super.initState();
_verifyToken();
_backendService = BackendService(
spotifyService: spotifyService,
backendUrl: widget.backendUrl,
);
_verifyToken(); // You can keep the token verification in initState
}

_initTabController();
@override
void didChangeDependencies() {
super.didChangeDependencies();
SchedulerBinding.instance.addPostFrameCallback((_) {
_initTabController();
});
}

@override
Expand All @@ -63,6 +63,10 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {

void _initTabController() {
final jobProvider = Provider.of<JobProvider>(context, listen: false);
if (jobProvider == null) {
print("JobProvider is null");
return;
}
final jobs = jobProvider.jobs;
_showAddJobButton = jobs.isNotEmpty &&
!jobs.any((job) => job.isNull) &&
Expand Down Expand Up @@ -305,16 +309,23 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
Widget build(BuildContext context) {
return Consumer<JobProvider>(
builder: (context, jobProvider, child) {
if (jobProvider.isLoading) {
return const Center(
child: CircularProgressIndicator(),
);
}

final jobs = jobProvider.jobs;
final jobsIterable =
jobs.isNotEmpty ? jobs.asMap().entries : [MapEntry(0, Job.empty())];

// Ensure tab controller is up to date
_updateTabController();

return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
title: const Text('Spotkin'),
titleTextStyle: Theme.of(context).textTheme.titleLarge,
automaticallyImplyLeading: false,
actions: const [
InfoButton(),
Expand All @@ -329,25 +340,14 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
SliverAppBar(
pinned: true,
floating: true,
expandedHeight: 0,
bottom: TabBar(
isScrollable: true,
tabAlignment: TabAlignment.start,
labelStyle: Theme.of(context).textTheme.labelMedium,
controller: _tabController,
onTap: (index) {
if (index == jobsIterable.length && _showAddJobButton) {
_tabController.animateTo(jobsIterable.length);
_addNewJob(Job.empty());
}
},
tabs: [
...jobsIterable.map((entry) {
return Tab(
child: Text(
entry.value.targetPlaylist.name ?? 'New job',
style: Theme.of(context).textTheme.labelMedium,
),
entry.value.targetPlaylist.name ?? 'New job'),
);
}),
if (_showAddJobButton) const Tab(icon: Icon(Icons.add))
Expand All @@ -366,8 +366,6 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
child: Column(
children: [
TargetPlaylistWidget(
// targetPlaylist: job.targetPlaylist,
// job: job,
index: jobEntry.key,
isProcessing: isProcessing,
processJob: _processJob,
Expand All @@ -393,14 +391,13 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
const SizedBox(height: 15),
ElevatedButton(
onPressed: () {
_tabController?.animateTo(jobsIterable.length);
_addNewJob(Job.empty());
},
child: const Text('Add new job'),
),
],
),
)
),
],
),
),
Expand Down
Loading

0 comments on commit 2f2dce6

Please sign in to comment.