diff --git a/lib/components/Home/Home.dart b/lib/components/Home/Home.dart index 26f628dc..63f849b3 100644 --- a/lib/components/Home/Home.dart +++ b/lib/components/Home/Home.dart @@ -19,12 +19,14 @@ import 'package:spotube/components/Library/UserLibrary.dart'; import 'package:spotube/hooks/useBreakpointValue.dart'; import 'package:spotube/hooks/useHotKeys.dart'; import 'package:spotube/hooks/usePaginatedFutureProvider.dart'; +import 'package:spotube/hooks/useUpdateChecker.dart'; import 'package:spotube/models/Logger.dart'; import 'package:spotube/provider/SpotifyRequests.dart'; List spotifyScopes = [ "playlist-modify-public", "playlist-modify-private", + "playlist-read-private", "user-library-read", "user-library-modify", "user-read-private", @@ -52,6 +54,8 @@ class Home extends HookConsumerWidget { // initializing global hot keys useHotKeys(ref); + // checks for latest version of the application + useUpdateChecker(ref); final titleBarContents = Container( color: Theme.of(context).scaffoldBackgroundColor, diff --git a/lib/components/Playlist/PlaylistCreateDialog.dart b/lib/components/Playlist/PlaylistCreateDialog.dart index 7fef9060..ff3f4ea8 100644 --- a/lib/components/Playlist/PlaylistCreateDialog.dart +++ b/lib/components/Playlist/PlaylistCreateDialog.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:spotube/provider/SpotifyDI.dart'; +import 'package:spotube/provider/SpotifyRequests.dart'; class PlaylistCreateDialog extends HookConsumerWidget { const PlaylistCreateDialog({Key? key}) : super(key: key); @@ -43,13 +44,16 @@ class PlaylistCreateDialog extends HookConsumerWidget { final me = await spotify.me.get(); await spotify.playlists .createPlaylist( - me.id!, - playlistName.text, - collaborative: collaborative.value, - public: public.value, - description: description.text, - ) - .then((_) => Navigator.pop(context)); + me.id!, + playlistName.text, + collaborative: collaborative.value, + public: public.value, + description: description.text, + ) + .then((_) { + ref.refresh(currentUserPlaylistsQuery); + Navigator.pop(context); + }); }, ) ], diff --git a/lib/components/Settings/Settings.dart b/lib/components/Settings/Settings.dart index 9747eb92..72f86dac 100644 --- a/lib/components/Settings/Settings.dart +++ b/lib/components/Settings/Settings.dart @@ -217,6 +217,7 @@ class Settings extends HookConsumerWidget { children: [ const Text("Download lyrics along with the Track"), Switch.adaptive( + activeColor: Theme.of(context).primaryColor, value: preferences.saveTrackLyrics, onChanged: (state) { preferences.setSaveTrackLyrics(state); @@ -265,6 +266,22 @@ class Settings extends HookConsumerWidget { ) ], ), + const SizedBox(height: 10), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Expanded( + flex: 2, + child: Text("Check for Update)"), + ), + Switch.adaptive( + activeColor: Theme.of(context).primaryColor, + value: preferences.checkUpdate, + onChanged: (checked) => + preferences.setCheckUpdate(checked), + ) + ], + ), if (auth.isLoggedIn) Builder(builder: (context) { Auth auth = ref.watch(authProvider); diff --git a/lib/hooks/useUpdateChecker.dart b/lib/hooks/useUpdateChecker.dart new file mode 100644 index 00000000..ca5b7e69 --- /dev/null +++ b/lib/hooks/useUpdateChecker.dart @@ -0,0 +1,90 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:http/http.dart' as http; +import 'package:spotube/components/Shared/AnchorButton.dart'; +import 'package:spotube/hooks/usePackageInfo.dart'; +import 'package:spotube/provider/UserPreferences.dart'; +import 'package:url_launcher/url_launcher_string.dart'; +import 'package:version/version.dart'; + +void useUpdateChecker(WidgetRef ref) { + final isCheckUpdateEnabled = + ref.watch(userPreferencesProvider.select((s) => s.checkUpdate)); + final packageInfo = usePackageInfo( + appName: 'Spotube', + packageName: 'spotube', + ); + final Future> Function() checkUpdate = useCallback( + () async { + final value = await http.get( + Uri.parse( + "https://api.github.com/repos/KRTirtho/spotube/releases/latest"), + ); + final tagName = + (jsonDecode(value.body)["tag_name"] as String).replaceAll("v", ""); + final currentVersion = packageInfo.version == "Unknown" + ? null + : Version.parse( + packageInfo.version, + ); + final latestVersion = Version.parse(tagName); + return [currentVersion, latestVersion]; + }, + [packageInfo.version], + ); + + final context = useContext(); + + download(String url) => launchUrlString( + url, + mode: LaunchMode.externalApplication, + ); + + useEffect(() { + if (!isCheckUpdateEnabled) return null; + checkUpdate().then((value) { + if (value.first == null) return; + if (value.first! <= value.last) return; + showDialog( + context: context, + builder: (context) { + final url = + "https://github.com/KRTirtho/spotube/releases/tag/v${value.last}"; + return AlertDialog( + title: const Text("Spotube has an update"), + actions: [ + ElevatedButton( + child: const Text("Download Now"), + onPressed: () => download(url), + ), + ], + content: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text("Spotube v${value.last} has been released"), + Row( + children: [ + const Text("Read the latest "), + AnchorButton( + "release notes", + style: const TextStyle(color: Colors.blue), + onTap: () => launchUrlString( + url, + mode: LaunchMode.externalApplication, + ), + ), + ], + ), + ], + ), + ); + }); + }); + return null; + }, [packageInfo, isCheckUpdateEnabled]); +} diff --git a/lib/provider/UserPreferences.dart b/lib/provider/UserPreferences.dart index 6ffd7c83..cb0bde6b 100644 --- a/lib/provider/UserPreferences.dart +++ b/lib/provider/UserPreferences.dart @@ -20,6 +20,8 @@ class UserPreferences extends PersistedChangeNotifier { HotKey? prevTrackHotKey; HotKey? playPauseHotKey; + bool checkUpdate; + MaterialColor accentColorScheme; MaterialColor backgroundColorScheme; UserPreferences({ @@ -33,6 +35,7 @@ class UserPreferences extends PersistedChangeNotifier { this.nextTrackHotKey, this.prevTrackHotKey, this.playPauseHotKey, + this.checkUpdate = true, }) : super(); void setThemeMode(ThemeMode mode) { @@ -95,10 +98,17 @@ class UserPreferences extends PersistedChangeNotifier { updatePersistence(); } + void setCheckUpdate(bool check) { + checkUpdate = check; + notifyListeners(); + updatePersistence(); + } + @override FutureOr loadFromLocal(Map map) { saveTrackLyrics = map["saveTrackLyrics"] ?? false; recommendationMarket = map["recommendationMarket"] ?? recommendationMarket; + checkUpdate = map["checkUpdate"] ?? checkUpdate; geniusAccessToken = map["geniusAccessToken"] ?? getRandomElement(lyricsSecrets); nextTrackHotKey = map["nextTrackHotKey"] != null @@ -133,6 +143,7 @@ class UserPreferences extends PersistedChangeNotifier { "themeMode": themeMode.index, "backgroundColorScheme": backgroundColorScheme.value, "accentColorScheme": accentColorScheme.value, + "checkUpdate": checkUpdate, }; } } diff --git a/pubspec.lock b/pubspec.lock index b6f97602..98aa82f4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -870,6 +870,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.2" + version: + dependency: "direct main" + description: + name: version + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0" win32: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index c7e48629..a2abee56 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -61,6 +61,7 @@ dependencies: marquee: ^2.2.1 scroll_to_index: ^2.1.1 package_info_plus: ^1.4.2 + version: ^2.0.0 dev_dependencies: flutter_test: