diff --git a/lib/components/desktop_login/login_form.dart b/lib/components/desktop_login/login_form.dart index 42b8fb56d..b9783f876 100644 --- a/lib/components/desktop_login/login_form.dart +++ b/lib/components/desktop_login/login_form.dart @@ -20,6 +20,8 @@ class TokenLoginForm extends HookConsumerWidget { final keyCodeController = useTextEditingController(); final mounted = useIsMounted(); + final isLoading = useState(false); + return ConstrainedBox( constraints: const BoxConstraints( maxWidth: 400, @@ -45,27 +47,35 @@ class TokenLoginForm extends HookConsumerWidget { ), const SizedBox(height: 20), FilledButton( - onPressed: () async { - if (keyCodeController.text.isEmpty || - directCodeController.text.isEmpty) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(context.l10n.fill_in_all_fields), - behavior: SnackBarBehavior.floating, - ), - ); - return; - } - final cookieHeader = - "sp_dc=${directCodeController.text}; sp_key=${keyCodeController.text}"; + onPressed: isLoading.value + ? null + : () async { + try { + isLoading.value = true; + if (keyCodeController.text.isEmpty || + directCodeController.text.isEmpty) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(context.l10n.fill_in_all_fields), + behavior: SnackBarBehavior.floating, + ), + ); + return; + } + final cookieHeader = + "sp_dc=${directCodeController.text}; sp_key=${keyCodeController.text}"; - authenticationNotifier.setCredentials( - await AuthenticationCredentials.fromCookie(cookieHeader), - ); - if (mounted()) { - onDone?.call(); - } - }, + authenticationNotifier.setCredentials( + await AuthenticationCredentials.fromCookie( + cookieHeader), + ); + if (mounted()) { + onDone?.call(); + } + } finally { + isLoading.value = false; + } + }, child: Text(context.l10n.submit), ) ], diff --git a/lib/provider/authentication_provider.dart b/lib/provider/authentication_provider.dart index c09cb1999..f1cf58ec9 100644 --- a/lib/provider/authentication_provider.dart +++ b/lib/provider/authentication_provider.dart @@ -4,6 +4,9 @@ import 'dart:convert'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:http/http.dart'; +import 'package:spotube/collections/routes.dart'; +import 'package:spotube/components/shared/dialogs/prompt_dialog.dart'; +import 'package:spotube/extensions/context.dart'; import 'package:spotube/utils/persisted_state_notifier.dart'; import 'package:spotube/utils/platform.dart'; @@ -21,24 +24,44 @@ class AuthenticationCredentials { }); static Future fromCookie(String cookie) async { - final Map body = await get( - Uri.parse( - "https://open.spotify.com/get_access_token?reason=transport&productType=web_player", - ), - headers: { - "Cookie": cookie, - "User-Agent": - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" - }, - ).then((res) => jsonDecode(res.body)); + try { + final res = await get( + Uri.parse( + "https://open.spotify.com/get_access_token?reason=transport&productType=web_player", + ), + headers: { + "Cookie": cookie, + "User-Agent": + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" + }, + ); + final body = jsonDecode(res.body); + + if (res.statusCode >= 400) { + throw Exception( + "Failed to get access token: ${body['error'] ?? res.reasonPhrase}", + ); + } - return AuthenticationCredentials( - cookie: cookie, - accessToken: body['accessToken'], - expiration: DateTime.fromMillisecondsSinceEpoch( - body['accessTokenExpirationTimestampMs'], - ), - ); + return AuthenticationCredentials( + cookie: cookie, + accessToken: body['accessToken'], + expiration: DateTime.fromMillisecondsSinceEpoch( + body['accessTokenExpirationTimestampMs'], + ), + ); + } catch (e) { + if (rootNavigatorKey?.currentContext != null) { + showPromptDialog( + context: rootNavigatorKey!.currentContext!, + title: rootNavigatorKey!.currentContext!.l10n + .error("Authentication Failure"), + message: e.toString(), + cancelText: null, + ); + } + rethrow; + } } factory AuthenticationCredentials.fromJson(Map json) {