mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00
Re initiating ipc login is now no longer required for https://github.com/rinukkusu/spotify-dart/pull/118
This commit is contained in:
parent
31551ac1dc
commit
5accf5c56c
@ -6,7 +6,6 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
|
||||
import 'package:oauth2/oauth2.dart' show AuthorizationException;
|
||||
import 'package:spotify/spotify.dart' hide Image, Player, Search;
|
||||
|
||||
import 'package:spotube/components/Category/CategoryCard.dart';
|
||||
@ -17,16 +16,11 @@ import 'package:spotube/components/Search/Search.dart';
|
||||
import 'package:spotube/components/Shared/PageWindowTitleBar.dart';
|
||||
import 'package:spotube/components/Player/Player.dart';
|
||||
import 'package:spotube/components/Library/UserLibrary.dart';
|
||||
import 'package:spotube/helpers/get-random-element.dart';
|
||||
import 'package:spotube/helpers/oauth-login.dart';
|
||||
import 'package:spotube/hooks/useBreakpointValue.dart';
|
||||
import 'package:spotube/hooks/useHotKeys.dart';
|
||||
import 'package:spotube/hooks/usePagingController.dart';
|
||||
import 'package:spotube/hooks/useSharedPreferences.dart';
|
||||
import 'package:spotube/models/LocalStorageKeys.dart';
|
||||
import 'package:spotube/models/Logger.dart';
|
||||
import 'package:spotube/models/generated_secrets.dart';
|
||||
import 'package:spotube/provider/Auth.dart';
|
||||
import 'package:spotube/provider/SpotifyDI.dart';
|
||||
import 'package:spotube/provider/UserPreferences.dart';
|
||||
|
||||
@ -48,7 +42,6 @@ class Home extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, ref) {
|
||||
Auth auth = ref.watch(authProvider);
|
||||
String recommendationMarket = ref.watch(userPreferencesProvider.select(
|
||||
(value) => (value.recommendationMarket),
|
||||
));
|
||||
@ -98,70 +91,12 @@ class Home extends HookConsumerWidget {
|
||||
}, [recommendationMarket]);
|
||||
|
||||
useEffect(() {
|
||||
if (localStorage == null) return null;
|
||||
final String? clientId =
|
||||
localStorage.getString(LocalStorageKeys.clientId);
|
||||
final String? clientSecret =
|
||||
localStorage.getString(LocalStorageKeys.clientSecret);
|
||||
final String? accessToken =
|
||||
localStorage.getString(LocalStorageKeys.accessToken);
|
||||
final String? refreshToken =
|
||||
localStorage.getString(LocalStorageKeys.refreshToken);
|
||||
final String? expirationStr =
|
||||
localStorage.getString(LocalStorageKeys.expiration);
|
||||
|
||||
try {
|
||||
final DateTime? expiration =
|
||||
expirationStr != null ? DateTime.parse(expirationStr) : null;
|
||||
final anonCred = getRandomElement(spotifySecrets);
|
||||
SpotifyApiCredentials apiCredentials =
|
||||
clientId != null && clientSecret != null
|
||||
? SpotifyApiCredentials(
|
||||
clientId,
|
||||
clientSecret,
|
||||
accessToken: accessToken,
|
||||
refreshToken: refreshToken,
|
||||
expiration: expiration,
|
||||
scopes: spotifyScopes,
|
||||
)
|
||||
: SpotifyApiCredentials(
|
||||
anonCred["clientId"],
|
||||
anonCred["clientSecret"],
|
||||
);
|
||||
|
||||
SpotifyApi spotify = SpotifyApi(apiCredentials);
|
||||
if (clientId != null && clientSecret != null) {
|
||||
spotify.getCredentials().then((credentials) {
|
||||
if (credentials.accessToken?.isNotEmpty == true) {
|
||||
auth.setAuthState(
|
||||
clientId: clientId,
|
||||
clientSecret: clientSecret,
|
||||
accessToken:
|
||||
credentials.accessToken, // accessToken can be new/refreshed
|
||||
refreshToken: refreshToken,
|
||||
expiration: credentials.expiration,
|
||||
isLoggedIn: true,
|
||||
);
|
||||
}
|
||||
pagingController.addPageRequestListener(listener);
|
||||
// the world is full of surprises and the previously working
|
||||
// fine pageRequestListener now doesn't notify the listeners
|
||||
// automatically after assigning a listener. So doing it manually
|
||||
pagingController.notifyPageRequestListeners(0);
|
||||
}).catchError((e, stack) {
|
||||
if (e is AuthorizationException) {
|
||||
oauthLogin(
|
||||
auth,
|
||||
clientId: clientId,
|
||||
clientSecret: clientSecret,
|
||||
);
|
||||
}
|
||||
logger.e("useEffect.spotify.getCredentials", e, stack);
|
||||
});
|
||||
} else {
|
||||
pagingController.addPageRequestListener(listener);
|
||||
pagingController.notifyPageRequestListeners(0);
|
||||
}
|
||||
} catch (e, stack) {
|
||||
logger.e("initState", e, stack);
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ Future<void> oauthLogin(Auth auth,
|
||||
if (responseUri != null) {
|
||||
final SpotifyApi spotify =
|
||||
SpotifyApi.fromAuthCodeGrant(grant, responseUri);
|
||||
var credentials = await spotify.getCredentials();
|
||||
final credentials = await spotify.getCredentials();
|
||||
if (credentials.accessToken != null) {
|
||||
accessToken = credentials.accessToken;
|
||||
await localStorage.setString(
|
||||
@ -56,7 +56,6 @@ Future<void> oauthLogin(Auth auth,
|
||||
accessToken: accessToken,
|
||||
refreshToken: refreshToken,
|
||||
expiration: expiration,
|
||||
isLoggedIn: true,
|
||||
);
|
||||
} catch (e, stack) {
|
||||
logger.e("oauthLogin", e, stack);
|
||||
|
@ -8,7 +8,7 @@ final logger = getLogger("ServerIPC");
|
||||
Future<String?> connectIpc(String authUri, String redirectUri) async {
|
||||
try {
|
||||
logger.i("[Launching]: $authUri");
|
||||
await launch(authUri);
|
||||
await launchUrl(Uri.parse(authUri));
|
||||
|
||||
HttpServer server =
|
||||
await HttpServer.bind(InternetAddress.loopbackIPv4, 4304);
|
||||
|
@ -3,10 +3,10 @@ abstract class LocalStorageKeys {
|
||||
static String recommendationMarket = 'recommendation_market';
|
||||
static String ytSearchFormate = 'youtube_search_format';
|
||||
|
||||
static String clientId = 'client_id';
|
||||
static String clientSecret = 'client_secret';
|
||||
static String accessToken = 'access_token';
|
||||
static String refreshToken = 'refresh_token';
|
||||
static String clientId = 'clientId';
|
||||
static String clientSecret = 'clientSecret';
|
||||
static String accessToken = 'accessToken';
|
||||
static String refreshToken = 'refreshToken';
|
||||
static String expiration = "expiration";
|
||||
static String geniusAccessToken = "genius_access_token";
|
||||
|
||||
|
@ -1,26 +1,32 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'dart:async';
|
||||
|
||||
class Auth with ChangeNotifier {
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:spotube/utils/PersistedChangeNotifier.dart';
|
||||
|
||||
class Auth extends PersistedChangeNotifier {
|
||||
String? _clientId;
|
||||
String? _clientSecret;
|
||||
String? _accessToken;
|
||||
String? _refreshToken;
|
||||
DateTime? _expiration;
|
||||
|
||||
bool _isLoggedIn = false;
|
||||
Auth() : super();
|
||||
|
||||
String? get clientId => _clientId;
|
||||
String? get clientSecret => _clientSecret;
|
||||
String? get accessToken => _accessToken;
|
||||
String? get refreshToken => _refreshToken;
|
||||
DateTime? get expiration => _expiration;
|
||||
bool get isLoggedIn => _isLoggedIn;
|
||||
|
||||
bool get isAnonymous =>
|
||||
!_isLoggedIn && _clientId == null && _clientSecret == null;
|
||||
_clientId == null &&
|
||||
_clientSecret == null &&
|
||||
accessToken == null &&
|
||||
refreshToken == null;
|
||||
|
||||
bool get isLoggedIn => !isAnonymous && _expiration != null;
|
||||
|
||||
void setAuthState({
|
||||
bool? isLoggedIn,
|
||||
bool safe = true,
|
||||
String? clientId,
|
||||
String? clientSecret,
|
||||
@ -31,7 +37,6 @@ class Auth with ChangeNotifier {
|
||||
if (safe) {
|
||||
if (clientId != null) _clientId = clientId;
|
||||
if (clientSecret != null) _clientSecret = clientSecret;
|
||||
if (isLoggedIn != null) _isLoggedIn = isLoggedIn;
|
||||
if (refreshToken != null) _refreshToken = refreshToken;
|
||||
if (accessToken != null) _accessToken = accessToken;
|
||||
if (expiration != null) _expiration = expiration;
|
||||
@ -43,6 +48,7 @@ class Auth with ChangeNotifier {
|
||||
_expiration = expiration;
|
||||
}
|
||||
notifyListeners();
|
||||
updatePersistence();
|
||||
}
|
||||
|
||||
logout() {
|
||||
@ -51,14 +57,34 @@ class Auth with ChangeNotifier {
|
||||
_accessToken = null;
|
||||
_refreshToken = null;
|
||||
_expiration = null;
|
||||
_isLoggedIn = false;
|
||||
notifyListeners();
|
||||
updatePersistence();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "Auth(clientId: $clientId, clientSecret: $clientSecret, accessToken: $accessToken, refreshToken: $refreshToken, expiration: $expiration, isLoggedIn: $isLoggedIn)";
|
||||
}
|
||||
|
||||
@override
|
||||
FutureOr<void> loadFromLocal(Map<String, dynamic> map) {
|
||||
_clientId = map["clientId"].isNotEmpty ? map["clientId"] : null;
|
||||
_clientSecret = map["clientSecret"].isNotEmpty ? map["clientSecret"] : null;
|
||||
_accessToken = map["accessToken"];
|
||||
_refreshToken = map["refreshToken"];
|
||||
_expiration = DateTime.tryParse(map["expiration"]);
|
||||
}
|
||||
|
||||
var authProvider = ChangeNotifierProvider<Auth>((ref) => Auth());
|
||||
@override
|
||||
FutureOr<Map<String, dynamic>> toMap() {
|
||||
return {
|
||||
"clientId": _clientId,
|
||||
"clientSecret": _clientSecret,
|
||||
"accessToken": _accessToken,
|
||||
"refreshToken": _refreshToken,
|
||||
"expiration": _expiration.toString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
final authProvider = ChangeNotifierProvider<Auth>((ref) => Auth());
|
||||
|
@ -1,13 +1,11 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:spotify/spotify.dart';
|
||||
import 'package:spotube/components/Home/Home.dart';
|
||||
import 'package:spotube/helpers/get-random-element.dart';
|
||||
import 'package:spotube/models/LocalStorageKeys.dart';
|
||||
import 'package:spotube/models/generated_secrets.dart';
|
||||
import 'package:spotube/provider/Auth.dart';
|
||||
|
||||
var spotifyProvider = Provider<SpotifyApi>((ref) {
|
||||
final spotifyProvider = Provider<SpotifyApi>((ref) {
|
||||
Auth authState = ref.watch(authProvider);
|
||||
final anonCred = getRandomElement(spotifySecrets);
|
||||
SpotifyApiCredentials apiCredentials = authState.isAnonymous
|
||||
@ -26,20 +24,13 @@ var spotifyProvider = Provider<SpotifyApi>((ref) {
|
||||
|
||||
return SpotifyApi(
|
||||
apiCredentials,
|
||||
onCredentialsRefreshed: (credentials) async {
|
||||
SharedPreferences localStorage = await SharedPreferences.getInstance();
|
||||
localStorage.setString(
|
||||
LocalStorageKeys.refreshToken,
|
||||
credentials.refreshToken!,
|
||||
);
|
||||
localStorage.setString(
|
||||
LocalStorageKeys.accessToken,
|
||||
credentials.accessToken!,
|
||||
);
|
||||
localStorage.setString(LocalStorageKeys.clientId, credentials.clientId!);
|
||||
localStorage.setString(
|
||||
LocalStorageKeys.clientSecret,
|
||||
credentials.clientSecret!,
|
||||
onCredentialsRefreshed: (credentials) {
|
||||
authState.setAuthState(
|
||||
clientId: credentials.clientId,
|
||||
clientSecret: credentials.clientSecret,
|
||||
accessToken: credentials.accessToken,
|
||||
refreshToken: credentials.refreshToken,
|
||||
expiration: credentials.expiration,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
53
lib/utils/PersistedChangeNotifier.dart
Normal file
53
lib/utils/PersistedChangeNotifier.dart
Normal file
@ -0,0 +1,53 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
abstract class PersistedChangeNotifier extends ChangeNotifier {
|
||||
late SharedPreferences _localStorage;
|
||||
PersistedChangeNotifier() {
|
||||
SharedPreferences.getInstance().then((value) => _localStorage = value).then(
|
||||
(_) async {
|
||||
final persistedMap = (await toMap())
|
||||
.entries
|
||||
.toList()
|
||||
.fold<Map<String, dynamic>>({}, (acc, entry) {
|
||||
if (entry.value != null) {
|
||||
if (entry.value is bool) {
|
||||
acc[entry.key] = _localStorage.getBool(entry.key) ?? false;
|
||||
} else if (entry.value is int) {
|
||||
acc[entry.key] = _localStorage.getInt(entry.key) ?? -1;
|
||||
} else if (entry.value is double) {
|
||||
acc[entry.key] = _localStorage.getDouble(entry.key) ?? -1.0;
|
||||
} else if (entry.value is String) {
|
||||
acc[entry.key] = _localStorage.getString(entry.key) ?? "";
|
||||
}
|
||||
} else {
|
||||
acc[entry.key] = _localStorage.get(entry.key);
|
||||
}
|
||||
return acc;
|
||||
});
|
||||
await loadFromLocal(persistedMap);
|
||||
notifyListeners();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
FutureOr<void> loadFromLocal(Map<String, dynamic> map);
|
||||
|
||||
FutureOr<Map<String, dynamic>> toMap();
|
||||
|
||||
Future<void> updatePersistence() async {
|
||||
for (final entry in (await toMap()).entries) {
|
||||
if (entry.value is bool) {
|
||||
await _localStorage.setBool(entry.key, entry.value);
|
||||
} else if (entry.value is int) {
|
||||
await _localStorage.setInt(entry.key, entry.value);
|
||||
} else if (entry.value is double) {
|
||||
await _localStorage.setDouble(entry.key, entry.value);
|
||||
} else if (entry.value is String) {
|
||||
await _localStorage.setString(entry.key, entry.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -724,11 +724,9 @@ packages:
|
||||
spotify:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: HEAD
|
||||
resolved-ref: e36eb5884de44d39b310d0878779a697048061bd
|
||||
url: "https://github.com/KRTirtho/spotify-dart.git"
|
||||
source: git
|
||||
path: "../spotify-dart"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.7.0"
|
||||
sqflite:
|
||||
dependency: transitive
|
||||
|
@ -38,7 +38,8 @@ dependencies:
|
||||
http: ^0.13.4
|
||||
shared_preferences: ^2.0.11
|
||||
spotify:
|
||||
git: https://github.com/KRTirtho/spotify-dart.git
|
||||
# git: https://github.com/KRTirtho/spotify-dart.git
|
||||
path: ../spotify-dart
|
||||
url_launcher: ^6.0.17
|
||||
youtube_explode_dart: ^1.10.8
|
||||
infinite_scroll_pagination: ^3.1.0
|
||||
|
Loading…
Reference in New Issue
Block a user