refactor: AppDatabase from provider to singleton

This commit is contained in:
Kingkor Roy Tirtho 2025-04-20 12:41:02 +06:00
parent dcf0c62e5d
commit 937906139b
37 changed files with 191 additions and 177 deletions

View File

@ -8,7 +8,6 @@ import 'package:flutter/services.dart';
import 'package:flutter_discord_rpc/flutter_discord_rpc.dart'; import 'package:flutter_discord_rpc/flutter_discord_rpc.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:get_it/get_it.dart';
import 'package:home_widget/home_widget.dart'; import 'package:home_widget/home_widget.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -31,7 +30,6 @@ import 'package:spotube/hooks/configurators/use_has_touch.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart'; import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart';
import 'package:spotube/provider/audio_player/audio_player_streams.dart'; import 'package:spotube/provider/audio_player/audio_player_streams.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/glance/glance.dart'; import 'package:spotube/provider/glance/glance.dart';
import 'package:spotube/provider/server/bonsoir.dart'; import 'package:spotube/provider/server/bonsoir.dart';
import 'package:spotube/provider/server/server.dart'; import 'package:spotube/provider/server/server.dart';
@ -73,7 +71,9 @@ Future<void> main(List<String> rawArgs) async {
() => KVStoreService.init(), () => KVStoreService.init(),
dependsOn: [SharedPreferences], dependsOn: [SharedPreferences],
); );
getIt.registerLazySingleton<AppDatabase>(() => AppDatabase());
getIt.registerSingleton(SpotubeAudioPlayer()); getIt.registerSingleton(SpotubeAudioPlayer());
getIt.registerSingleton<WindowManager>(windowManager);
await registerWindowsScheme("spotify"); await registerWindowsScheme("spotify");
@ -112,8 +112,6 @@ Future<void> main(List<String> rawArgs) async {
await EncryptedKvStoreService.initialize(); await EncryptedKvStoreService.initialize();
final database = AppDatabase();
if (kIsDesktop) { if (kIsDesktop) {
await localNotifier.setup(appName: "Spotube"); await localNotifier.setup(appName: "Spotube");
await WindowManagerTools.initialize(); await WindowManagerTools.initialize();
@ -124,14 +122,11 @@ Future<void> main(List<String> rawArgs) async {
} }
runApp( runApp(
ProviderScope( const ProviderScope(
overrides: [ observers: [
databaseProvider.overrideWith((ref) => database),
],
observers: const [
AppLoggerProviderObserver(), AppLoggerProviderObserver(),
], ],
child: const Spotube(), child: Spotube(),
), ),
); );
}); });

View File

@ -4,13 +4,13 @@ import 'package:drift/drift.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:media_kit/media_kit.dart' hide Track; import 'package:media_kit/media_kit.dart' hide Track;
import 'package:spotify/spotify.dart' hide Playlist; import 'package:spotify/spotify.dart' hide Playlist;
import 'package:spotube/collections/vars.dart';
import 'package:spotube/extensions/list.dart'; import 'package:spotube/extensions/list.dart';
import 'package:spotube/extensions/track.dart'; import 'package:spotube/extensions/track.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/models/local_track.dart'; import 'package:spotube/models/local_track.dart';
import 'package:spotube/provider/audio_player/state.dart'; import 'package:spotube/provider/audio_player/state.dart';
import 'package:spotube/provider/blacklist_provider.dart'; import 'package:spotube/provider/blacklist_provider.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/discord_provider.dart'; import 'package:spotube/provider/discord_provider.dart';
import 'package:spotube/provider/server/sourced_track.dart'; import 'package:spotube/provider/server/sourced_track.dart';
import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/services/audio_player/audio_player.dart';
@ -20,7 +20,7 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
BlackListNotifier get _blacklist => ref.read(blacklistProvider.notifier); BlackListNotifier get _blacklist => ref.read(blacklistProvider.notifier);
Future<void> _syncSavedState() async { Future<void> _syncSavedState() async {
final database = ref.read(databaseProvider); final database = getIt.get<AppDatabase>();
var playerState = var playerState =
await database.select(database.audioPlayerStateTable).getSingleOrNull(); await database.select(database.audioPlayerStateTable).getSingleOrNull();
@ -102,7 +102,7 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
Future<void> _updatePlayerState( Future<void> _updatePlayerState(
AudioPlayerStateTableCompanion companion, AudioPlayerStateTableCompanion companion,
) async { ) async {
final database = ref.read(databaseProvider); final database = getIt.get<AppDatabase>();
await (database.update(database.audioPlayerStateTable) await (database.update(database.audioPlayerStateTable)
..where((tb) => tb.id.equals(0))) ..where((tb) => tb.id.equals(0)))
@ -112,7 +112,7 @@ class AudioPlayerNotifier extends Notifier<AudioPlayerState> {
Future<void> _updatePlaylist( Future<void> _updatePlaylist(
Playlist playlist, Playlist playlist,
) async { ) async {
final database = ref.read(databaseProvider); final database = getIt.get<AppDatabase>();
await database.batch((batch) { await database.batch((batch) {
batch.update( batch.update(

View File

@ -11,10 +11,10 @@ import 'package:flutter_inappwebview/flutter_inappwebview.dart'
hide X509Certificate; hide X509Certificate;
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/routes.dart'; import 'package:spotube/collections/routes.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/components/dialogs/prompt_dialog.dart'; import 'package:spotube/components/dialogs/prompt_dialog.dart';
import 'package:spotube/extensions/context.dart'; import 'package:spotube/extensions/context.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/logger/logger.dart';
import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/platform.dart';
import 'package:otp_util/otp_util.dart'; import 'package:otp_util/otp_util.dart';
@ -51,10 +51,10 @@ class AuthenticationNotifier extends AsyncNotifier<AuthenticationTableData?> {
return dio; return dio;
}(); }();
AppDatabase get database => getIt.get<AppDatabase>();
@override @override
build() async { build() async {
final database = ref.watch(databaseProvider);
final data = await (database.select(database.authenticationTable) final data = await (database.select(database.authenticationTable)
..where((s) => s.id.equals(0))) ..where((s) => s.id.equals(0)))
.getSingleOrNull(); .getSingleOrNull();
@ -92,7 +92,6 @@ class AuthenticationNotifier extends AsyncNotifier<AuthenticationTableData?> {
} }
Future<void> refreshCredentials() async { Future<void> refreshCredentials() async {
final database = ref.read(databaseProvider);
final refreshedCredentials = final refreshedCredentials =
await credentialsFromCookie(state.asData!.value!.cookie.value); await credentialsFromCookie(state.asData!.value!.cookie.value);
@ -102,7 +101,6 @@ class AuthenticationNotifier extends AsyncNotifier<AuthenticationTableData?> {
} }
Future<void> login(String cookie) async { Future<void> login(String cookie) async {
final database = ref.read(databaseProvider);
final refreshedCredentials = await credentialsFromCookie(cookie); final refreshedCredentials = await credentialsFromCookie(cookie);
await database await database
@ -289,7 +287,6 @@ class AuthenticationNotifier extends AsyncNotifier<AuthenticationTableData?> {
Future<void> logout() async { Future<void> logout() async {
state = const AsyncData(null); state = const AsyncData(null);
final database = ref.read(databaseProvider);
await (database.delete(database.authenticationTable) await (database.delete(database.authenticationTable)
..where((s) => s.id.equals(0))) ..where((s) => s.id.equals(0)))
.go(); .go();

View File

@ -1,17 +1,17 @@
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/current_playlist.dart'; import 'package:spotube/models/current_playlist.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
class BlackListNotifier extends AsyncNotifier<List<BlacklistTableData>> { class BlackListNotifier extends AsyncNotifier<List<BlacklistTableData>> {
AppDatabase get db => getIt.get<AppDatabase>();
@override @override
build() async { build() async {
final database = ref.watch(databaseProvider); final subscription = db
.select(db.blacklistTable)
final subscription = database
.select(database.blacklistTable)
.watch() .watch()
.listen((event) => state = AsyncData(event)); .listen((event) => state = AsyncData(event));
@ -19,17 +19,15 @@ class BlackListNotifier extends AsyncNotifier<List<BlacklistTableData>> {
subscription.cancel(); subscription.cancel();
}); });
return await database.select(database.blacklistTable).get(); return await db.select(db.blacklistTable).get();
} }
AppDatabase get _database => ref.read(databaseProvider);
Future<void> add(BlacklistTableCompanion element) async { Future<void> add(BlacklistTableCompanion element) async {
_database.into(_database.blacklistTable).insert(element); db.into(db.blacklistTable).insert(element);
} }
Future<void> remove(String elementId) async { Future<void> remove(String elementId) async {
await (_database.delete(_database.blacklistTable) await (db.delete(db.blacklistTable)
..where((tbl) => tbl.elementId.equals(elementId))) ..where((tbl) => tbl.elementId.equals(elementId)))
.go(); .go();
} }

View File

@ -1,4 +0,0 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/models/database/database.dart';
final databaseProvider = Provider((ref) => AppDatabase());

View File

@ -1,11 +1,11 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
class PlaybackHistoryActions { class PlaybackHistoryActions {
final Ref ref; final Ref ref;
AppDatabase get _db => ref.read(databaseProvider); AppDatabase get _db => getIt.get<AppDatabase>();
PlaybackHistoryActions(this.ref); PlaybackHistoryActions(this.ref);

View File

@ -2,13 +2,13 @@ import 'dart:convert';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
class RecentlyPlayedItemNotifier extends AsyncNotifier<List<HistoryTableData>> { class RecentlyPlayedItemNotifier extends AsyncNotifier<List<HistoryTableData>> {
@override @override
build() async { build() async {
final database = ref.watch(databaseProvider); final database = getIt.get<AppDatabase>();
final query = database.customSelect( final query = database.customSelect(
""" """

View File

@ -4,8 +4,8 @@ import 'dart:convert';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:drift/extensions/json1.dart'; import 'package:drift/extensions/json1.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
class PlaybackHistorySummary { class PlaybackHistorySummary {
final Duration duration; final Duration duration;
@ -47,7 +47,7 @@ class PlaybackHistorySummaryNotifier
extends AsyncNotifier<PlaybackHistorySummary> { extends AsyncNotifier<PlaybackHistorySummary> {
@override @override
build() async { build() async {
final database = ref.watch(databaseProvider); final database = getIt.get<AppDatabase>();
final uniqItemIdCountingCol = final uniqItemIdCountingCol =
database.historyTable.itemId.count(distinct: true); database.historyTable.itemId.count(distinct: true);

View File

@ -4,7 +4,8 @@ import 'package:collection/collection.dart';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/provider/database/database.dart'; import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/history/top.dart'; import 'package:spotube/provider/history/top.dart';
import 'package:spotube/provider/spotify/spotify.dart'; import 'package:spotube/provider/spotify/spotify.dart';
@ -39,7 +40,7 @@ class HistoryTopAlbumsNotifier extends FamilyPaginatedAsyncNotifier<
HistoryTopAlbumsNotifier() : super(); HistoryTopAlbumsNotifier() : super();
Selectable<AlbumSimple> createAlbumsQuery({int? limit, int? offset}) { Selectable<AlbumSimple> createAlbumsQuery({int? limit, int? offset}) {
final database = ref.read(databaseProvider); final database = getIt.get<AppDatabase>();
final duration = switch (arg) { final duration = switch (arg) {
HistoryDuration.allTime => '0', HistoryDuration.allTime => '0',

View File

@ -2,8 +2,8 @@ import 'package:collection/collection.dart';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/history/top.dart'; import 'package:spotube/provider/history/top.dart';
import 'package:spotube/provider/spotify/spotify.dart'; import 'package:spotube/provider/spotify/spotify.dart';
@ -39,7 +39,7 @@ class HistoryTopPlaylistsNotifier extends FamilyPaginatedAsyncNotifier<
SimpleSelectStatement<$HistoryTableTable, HistoryTableData> SimpleSelectStatement<$HistoryTableTable, HistoryTableData>
createPlaylistsQuery() { createPlaylistsQuery() {
final database = ref.read(databaseProvider); final database = getIt.get<AppDatabase>();
return database.select(database.historyTable) return database.select(database.historyTable)
..where( ..where(

View File

@ -2,8 +2,8 @@ import 'package:collection/collection.dart';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/history/top.dart'; import 'package:spotube/provider/history/top.dart';
import 'package:spotube/provider/spotify/spotify.dart'; import 'package:spotube/provider/spotify/spotify.dart';
@ -56,7 +56,7 @@ class HistoryTopTracksNotifier extends FamilyPaginatedAsyncNotifier<
SimpleSelectStatement<$HistoryTableTable, HistoryTableData> SimpleSelectStatement<$HistoryTableTable, HistoryTableData>
createTracksQuery() { createTracksQuery() {
final database = ref.read(databaseProvider); final database = getIt.get<AppDatabase>();
return database.select(database.historyTable) return database.select(database.historyTable)
..where( ..where(

View File

@ -5,24 +5,24 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:scrobblenaut/scrobblenaut.dart'; import 'package:scrobblenaut/scrobblenaut.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/env.dart'; import 'package:spotube/collections/env.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/extensions/artist_simple.dart'; import 'package:spotube/extensions/artist_simple.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/logger/logger.dart';
class ScrobblerNotifier extends AsyncNotifier<Scrobblenaut?> { class ScrobblerNotifier extends AsyncNotifier<Scrobblenaut?> {
final StreamController<Track> _scrobbleController = final StreamController<Track> _scrobbleController =
StreamController<Track>.broadcast(); StreamController<Track>.broadcast();
AppDatabase get db => getIt.get<AppDatabase>();
@override @override
build() async { build() async {
final database = ref.watch(databaseProvider); final loginInfo = await (db.select(db.scrobblerTable)
final loginInfo = await (database.select(database.scrobblerTable)
..where((t) => t.id.equals(0))) ..where((t) => t.id.equals(0)))
.getSingleOrNull(); .getSingleOrNull();
final subscription = final subscription =
database.select(database.scrobblerTable).watch().listen((event) async { db.select(db.scrobblerTable).watch().listen((event) async {
try { try {
if (event.isNotEmpty) { if (event.isNotEmpty) {
state = await AsyncValue.guard( state = await AsyncValue.guard(
@ -83,8 +83,6 @@ class ScrobblerNotifier extends AsyncNotifier<Scrobblenaut?> {
String username, String username,
String password, String password,
) async { ) async {
final database = ref.read(databaseProvider);
final lastFm = await LastFM.authenticate( final lastFm = await LastFM.authenticate(
apiKey: Env.lastFmApiKey, apiKey: Env.lastFmApiKey,
apiSecret: Env.lastFmApiSecret, apiSecret: Env.lastFmApiSecret,
@ -94,7 +92,7 @@ class ScrobblerNotifier extends AsyncNotifier<Scrobblenaut?> {
if (!lastFm.isAuth) throw Exception("Invalid credentials"); if (!lastFm.isAuth) throw Exception("Invalid credentials");
await database.into(database.scrobblerTable).insert( await db.into(db.scrobblerTable).insert(
ScrobblerTableCompanion.insert( ScrobblerTableCompanion.insert(
id: const Value(0), id: const Value(0),
username: username, username: username,
@ -105,8 +103,8 @@ class ScrobblerNotifier extends AsyncNotifier<Scrobblenaut?> {
Future<void> logout() async { Future<void> logout() async {
state = const AsyncValue.data(null); state = const AsyncValue.data(null);
final database = ref.read(databaseProvider);
await database.delete(database.scrobblerTable).go(); await db.delete(db.scrobblerTable).go();
} }
void scrobble(Track track) { void scrobble(Track track) {

View File

@ -1,5 +1,5 @@
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/logger/logger.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
@ -17,7 +17,7 @@ class SourcedSegments {
Future<List<SkipSegmentTableData>> getAndCacheSkipSegments( Future<List<SkipSegmentTableData>> getAndCacheSkipSegments(
String id, Ref ref) async { String id, Ref ref) async {
final database = ref.read(databaseProvider); final database = getIt.get<AppDatabase>();
try { try {
final cached = await (database.select(database.skipSegmentTable) final cached = await (database.select(database.skipSegmentTable)
..where((s) => s.trackId.equals(id))) ..where((s) => s.trackId.equals(id)))

View File

@ -123,7 +123,7 @@ class SyncedLyricsNotifier extends FamilyAsyncNotifier<SubtitleSimple, Track?> {
@override @override
FutureOr<SubtitleSimple> build(track) async { FutureOr<SubtitleSimple> build(track) async {
try { try {
final database = ref.watch(databaseProvider); final database = getIt.get<AppDatabase>();
final spotify = ref.watch(spotifyProvider); final spotify = ref.watch(spotifyProvider);
final auth = await ref.watch(authenticationProvider.future); final auth = await ref.watch(authenticationProvider.future);

View File

@ -6,9 +6,9 @@ import 'dart:math';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:spotube/collections/assets.gen.dart'; import 'package:spotube/collections/assets.gen.dart';
import 'package:spotube/collections/env.dart'; import 'package:spotube/collections/env.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/authentication/authentication.dart'; import 'package:spotube/provider/authentication/authentication.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/logger/logger.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:dio/dio.dart'; import 'package:dio/dio.dart';

View File

@ -1,9 +1,10 @@
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/provider/tray_manager/tray_menu.dart'; import 'package:spotube/provider/tray_manager/tray_menu.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/provider/window_manager/window_manager.dart';
import 'package:spotube/utils/platform.dart'; import 'package:spotube/utils/platform.dart';
import 'package:tray_manager/tray_manager.dart'; import 'package:tray_manager/tray_manager.dart';
import 'package:window_manager/window_manager.dart';
class SystemTrayManager with TrayListener { class SystemTrayManager with TrayListener {
final Ref ref; final Ref ref;
@ -40,7 +41,7 @@ class SystemTrayManager with TrayListener {
@override @override
onTrayIconMouseDown() { onTrayIconMouseDown() {
if (kIsWindows) { if (kIsWindows) {
ref.read(windowManagerProvider).show(); getIt.get<WindowManager>().show();
} else { } else {
trayManager.popUpContextMenu(); trayManager.popUpContextMenu();
} }
@ -49,7 +50,7 @@ class SystemTrayManager with TrayListener {
@override @override
onTrayIconRightMouseDown() { onTrayIconRightMouseDown() {
if (!kIsWindows) { if (!kIsWindows) {
ref.read(windowManagerProvider).show(); getIt.get<WindowManager>().show();
} else { } else {
trayManager.popUpContextMenu(); trayManager.popUpContextMenu();
} }

View File

@ -1,11 +1,12 @@
import 'dart:io'; import 'dart:io';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/provider/audio_player/audio_player.dart'; import 'package:spotube/provider/audio_player/audio_player.dart';
import 'package:spotube/provider/window_manager/window_manager.dart';
import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:media_kit/media_kit.dart' hide Track; import 'package:media_kit/media_kit.dart' hide Track;
import 'package:tray_manager/tray_manager.dart'; import 'package:tray_manager/tray_manager.dart';
import 'package:window_manager/window_manager.dart';
final audioPlayerLoopMode = StreamProvider<PlaylistMode>((ref) { final audioPlayerLoopMode = StreamProvider<PlaylistMode>((ref) {
return audioPlayer.loopModeStream; return audioPlayer.loopModeStream;
@ -19,7 +20,7 @@ final audioPlayerPlaying = StreamProvider<bool>((ref) {
}); });
final trayMenuProvider = Provider((ref) { final trayMenuProvider = Provider((ref) {
final windowManager = ref.watch(windowManagerProvider); final windowManager = getIt.get<WindowManager>();
final playlistNotifier = ref.watch(audioPlayerProvider.notifier); final playlistNotifier = ref.watch(audioPlayerProvider.notifier);
final isPlaybackPlaying = final isPlaybackPlaying =

View File

@ -5,11 +5,10 @@ import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart' as paths; import 'package:path_provider/path_provider.dart' as paths;
import 'package:shadcn_flutter/shadcn_flutter.dart' hide join; import 'package:shadcn_flutter/shadcn_flutter.dart' hide join;
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart'; import 'package:spotube/modules/settings/color_scheme_picker_dialog.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/user_preferences/default_download_dir_provider.dart'; import 'package:spotube/provider/user_preferences/default_download_dir_provider.dart';
import 'package:spotube/provider/window_manager/window_manager.dart';
import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/logger/logger.dart';
import 'package:spotube/services/sourced_track/enums.dart'; import 'package:spotube/services/sourced_track/enums.dart';
@ -20,10 +19,10 @@ import 'package:open_file/open_file.dart';
typedef UserPreferences = PreferencesTableData; typedef UserPreferences = PreferencesTableData;
class UserPreferencesNotifier extends Notifier<PreferencesTableData> { class UserPreferencesNotifier extends Notifier<PreferencesTableData> {
AppDatabase get db => getIt.get<AppDatabase>();
@override @override
build() { build() {
final db = ref.watch(databaseProvider);
(db.select(db.preferencesTable)..where((tbl) => tbl.id.equals(0))) (db.select(db.preferencesTable)..where((tbl) => tbl.id.equals(0)))
.getSingleOrNull() .getSingleOrNull()
.then((result) async { .then((result) async {
@ -50,7 +49,7 @@ class UserPreferencesNotifier extends Notifier<PreferencesTableData> {
state = event; state = event;
if (kIsDesktop) { if (kIsDesktop) {
await ref.read(windowManagerProvider).setTitleBarStyle( await getIt.get<WindowManager>().setTitleBarStyle(
state.systemTitleBar state.systemTitleBar
? TitleBarStyle.normal ? TitleBarStyle.normal
: TitleBarStyle.hidden, : TitleBarStyle.hidden,
@ -71,29 +70,25 @@ class UserPreferencesNotifier extends Notifier<PreferencesTableData> {
return PreferencesTable.defaults(); return PreferencesTable.defaults();
} }
Future<String> _getDefaultDownloadDirectory() async { // Future<String> _getDefaultDownloadDirectory() async {
if (kIsAndroid) return "/storage/emulated/0/Download/Spotube"; // if (kIsAndroid) return "/storage/emulated/0/Download/Spotube";
if (kIsMacOS) { // if (kIsMacOS) {
return join((await paths.getLibraryDirectory()).path, "Caches"); // return join((await paths.getLibraryDirectory()).path, "Caches");
} // }
return paths.getDownloadsDirectory().then((dir) { // return paths.getDownloadsDirectory().then((dir) {
return join(dir!.path, "Spotube"); // return join(dir!.path, "Spotube");
}); // });
} // }
Future<void> setData(PreferencesTableCompanion data) async { Future<void> setData(PreferencesTableCompanion data) async {
final db = ref.read(databaseProvider);
final query = db.update(db.preferencesTable)..where((t) => t.id.equals(0)); final query = db.update(db.preferencesTable)..where((t) => t.id.equals(0));
await query.write(data); await query.write(data);
} }
Future<void> reset() async { Future<void> reset() async {
final db = ref.read(databaseProvider);
final query = db.update(db.preferencesTable); final query = db.update(db.preferencesTable);
await query.replace(PreferencesTableCompanion.insert(id: const Value(0))); await query.replace(PreferencesTableCompanion.insert(id: const Value(0)));

View File

@ -1,4 +0,0 @@
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:window_manager/window_manager.dart';
final windowManagerProvider = Provider((ref) => windowManager);

View File

@ -2,8 +2,8 @@ import 'package:collection/collection.dart';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/services/sourced_track/enums.dart'; import 'package:spotube/services/sourced_track/enums.dart';
import 'package:spotube/services/sourced_track/exceptions.dart'; import 'package:spotube/services/sourced_track/exceptions.dart';
@ -66,7 +66,8 @@ class InvidiousSourcedTrack extends SourcedTrack {
); );
} }
final database = ref.read(databaseProvider); final database = getIt.get<AppDatabase>();
final cachedSource = await (database.select(database.sourceMatchTable) final cachedSource = await (database.select(database.sourceMatchTable)
..where((s) => s.trackId.equals(track.id!)) ..where((s) => s.trackId.equals(track.id!))
..limit(1) ..limit(1)
@ -258,7 +259,7 @@ class InvidiousSourcedTrack extends SourcedTrack {
final manifest = final manifest =
await pipedClient.videos.get(newSourceInfo.id, local: true); await pipedClient.videos.get(newSourceInfo.id, local: true);
final database = ref.read(databaseProvider); final database = getIt.get<AppDatabase>();
await database.into(database.sourceMatchTable).insert( await database.into(database.sourceMatchTable).insert(
SourceMatchTableCompanion.insert( SourceMatchTableCompanion.insert(
trackId: id!, trackId: id!,

View File

@ -2,8 +2,8 @@ import 'package:collection/collection.dart';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/sourced_track/enums.dart'; import 'package:spotube/services/sourced_track/enums.dart';
import 'package:spotube/services/sourced_track/exceptions.dart'; import 'package:spotube/services/sourced_track/exceptions.dart';
import 'package:spotube/services/sourced_track/models/source_info.dart'; import 'package:spotube/services/sourced_track/models/source_info.dart';
@ -41,7 +41,7 @@ class JioSaavnSourcedTrack extends SourcedTrack {
required Ref ref, required Ref ref,
bool weakMatch = false, bool weakMatch = false,
}) async { }) async {
final database = ref.read(databaseProvider); final database = getIt.get<AppDatabase>();
final cachedSource = await (database.select(database.sourceMatchTable) final cachedSource = await (database.select(database.sourceMatchTable)
..where((s) => s.trackId.equals(track.id!)) ..where((s) => s.trackId.equals(track.id!))
..limit(1) ..limit(1)
@ -214,7 +214,7 @@ class JioSaavnSourcedTrack extends SourcedTrack {
final (:info, :source) = toSiblingType(item); final (:info, :source) = toSiblingType(item);
final database = ref.read(databaseProvider); final database = getIt.get<AppDatabase>();
await database.into(database.sourceMatchTable).insert( await database.into(database.sourceMatchTable).insert(
SourceMatchTableCompanion.insert( SourceMatchTableCompanion.insert(
trackId: id!, trackId: id!,

View File

@ -3,8 +3,8 @@ import 'package:drift/drift.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:piped_client/piped_client.dart'; import 'package:piped_client/piped_client.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/services/sourced_track/enums.dart'; import 'package:spotube/services/sourced_track/enums.dart';
@ -63,7 +63,7 @@ class PipedSourcedTrack extends SourcedTrack {
); );
} }
final database = ref.read(databaseProvider); final database = getIt.get<AppDatabase>();
final cachedSource = await (database.select(database.sourceMatchTable) final cachedSource = await (database.select(database.sourceMatchTable)
..where((s) => s.trackId.equals(track.id!)) ..where((s) => s.trackId.equals(track.id!))
..limit(1) ..limit(1)
@ -287,7 +287,7 @@ class PipedSourcedTrack extends SourcedTrack {
final manifest = await pipedClient.streams(newSourceInfo.id); final manifest = await pipedClient.streams(newSourceInfo.id);
final database = ref.read(databaseProvider); final database = getIt.get<AppDatabase>();
await database.into(database.sourceMatchTable).insert( await database.into(database.sourceMatchTable).insert(
SourceMatchTableCompanion.insert( SourceMatchTableCompanion.insert(
trackId: id!, trackId: id!,

View File

@ -2,8 +2,8 @@ import 'package:collection/collection.dart';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/youtube_engine/youtube_engine.dart'; import 'package:spotube/provider/youtube_engine/youtube_engine.dart';
import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/logger/logger.dart';
import 'package:spotube/services/song_link/song_link.dart'; import 'package:spotube/services/song_link/song_link.dart';
@ -66,7 +66,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
return sourcedTrack; return sourcedTrack;
} }
final database = ref.read(databaseProvider); final database = getIt.get<AppDatabase>();
final cachedSource = await (database.select(database.sourceMatchTable) final cachedSource = await (database.select(database.sourceMatchTable)
..where((s) => s.trackId.equals(track.id!)) ..where((s) => s.trackId.equals(track.id!))
..limit(1) ..limit(1)
@ -261,8 +261,8 @@ class YoutubeSourcedTrack extends SourcedTrack {
.where((item) => item.isNotEmpty); .where((item) => item.isNotEmpty);
// Single word and duration match with 3 second tolerance // Single word and duration match with 3 second tolerance
if (ytWords.any((word) => spWords.contains(word)) && if (ytWords.any((word) => spWords.contains(word)) &&
(videoInfo.duration - track.duration!) (videoInfo.duration - track.duration!).abs().inMilliseconds <=
.abs().inMilliseconds <= 3000) { 3000) {
return videoInfo; return videoInfo;
} }
return null; return null;
@ -356,7 +356,7 @@ class YoutubeSourcedTrack extends SourcedTrack {
.read(youtubeEngineProvider) .read(youtubeEngineProvider)
.getStreamManifest(newSourceInfo.id); .getStreamManifest(newSourceInfo.id);
final database = ref.read(databaseProvider); final database = getIt.get<AppDatabase>();
await database.into(database.sourceMatchTable).insert( await database.into(database.sourceMatchTable).insert(
SourceMatchTableCompanion.insert( SourceMatchTableCompanion.insert(

View File

@ -7,11 +7,12 @@ import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:html/dom.dart' hide Text; import 'package:html/dom.dart' hide Text;
import 'package:shadcn_flutter/shadcn_flutter.dart' hide Element; import 'package:shadcn_flutter/shadcn_flutter.dart' hide Element;
import 'package:spotify/spotify.dart'; import 'package:spotify/spotify.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/pages/library/user_local_tracks/user_local_tracks.dart'; import 'package:spotube/pages/library/user_local_tracks/user_local_tracks.dart';
import 'package:spotube/modules/root/update_dialog.dart'; import 'package:spotube/modules/root/update_dialog.dart';
import 'package:spotube/models/lyrics.dart'; import 'package:spotube/models/lyrics.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/services/dio/dio.dart'; import 'package:spotube/services/dio/dio.dart';
import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/logger/logger.dart';
import 'package:spotube/services/sourced_track/sourced_track.dart'; import 'package:spotube/services/sourced_track/sourced_track.dart';
@ -333,7 +334,7 @@ abstract class ServiceUtils {
WidgetRef ref, WidgetRef ref,
) async { ) async {
if (!Env.enableUpdateChecker) return; if (!Env.enableUpdateChecker) return;
final database = ref.read(databaseProvider); final database = getIt.get<AppDatabase>();
final checkUpdate = await (database.selectOnly(database.preferencesTable) final checkUpdate = await (database.selectOnly(database.preferencesTable)
..addColumns([database.preferencesTable.checkUpdate]) ..addColumns([database.preferencesTable.checkUpdate])
..where(database.preferencesTable.id.equals(0))) ..where(database.preferencesTable.id.equals(0)))

View File

@ -2,6 +2,10 @@
// GENERATED CODE, DO NOT EDIT BY HAND. // GENERATED CODE, DO NOT EDIT BY HAND.
// ignore_for_file: type=lint // ignore_for_file: type=lint
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:flutter/material.dart' show ThemeMode;
import 'package:spotify/spotify.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/services/sourced_track/enums.dart';
class AuthenticationTable extends Table class AuthenticationTable extends Table
with TableInfo<AuthenticationTable, AuthenticationTableData> { with TableInfo<AuthenticationTable, AuthenticationTableData> {

View File

@ -2,6 +2,10 @@
// GENERATED CODE, DO NOT EDIT BY HAND. // GENERATED CODE, DO NOT EDIT BY HAND.
// ignore_for_file: type=lint // ignore_for_file: type=lint
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:flutter/material.dart' show ThemeMode;
import 'package:spotify/spotify.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/services/sourced_track/enums.dart';
class AuthenticationTable extends Table class AuthenticationTable extends Table
with TableInfo<AuthenticationTable, AuthenticationTableData> { with TableInfo<AuthenticationTable, AuthenticationTableData> {

View File

@ -2,6 +2,10 @@
// GENERATED CODE, DO NOT EDIT BY HAND. // GENERATED CODE, DO NOT EDIT BY HAND.
// ignore_for_file: type=lint // ignore_for_file: type=lint
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:flutter/material.dart' show ThemeMode;
import 'package:spotify/spotify.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/services/sourced_track/enums.dart';
class AuthenticationTable extends Table class AuthenticationTable extends Table
with TableInfo<AuthenticationTable, AuthenticationTableData> { with TableInfo<AuthenticationTable, AuthenticationTableData> {

View File

@ -2,6 +2,10 @@
// GENERATED CODE, DO NOT EDIT BY HAND. // GENERATED CODE, DO NOT EDIT BY HAND.
// ignore_for_file: type=lint // ignore_for_file: type=lint
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:flutter/material.dart' show ThemeMode;
import 'package:spotify/spotify.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/services/sourced_track/enums.dart';
class AuthenticationTable extends Table class AuthenticationTable extends Table
with TableInfo<AuthenticationTable, AuthenticationTableData> { with TableInfo<AuthenticationTable, AuthenticationTableData> {

View File

@ -2,6 +2,10 @@
// GENERATED CODE, DO NOT EDIT BY HAND. // GENERATED CODE, DO NOT EDIT BY HAND.
// ignore_for_file: type=lint // ignore_for_file: type=lint
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:flutter/material.dart' show ThemeMode;
import 'package:spotify/spotify.dart';
import 'package:spotube/models/database/database.dart';
import 'package:spotube/services/sourced_track/enums.dart';
class AuthenticationTable extends Table class AuthenticationTable extends Table
with TableInfo<AuthenticationTable, AuthenticationTableData> { with TableInfo<AuthenticationTable, AuthenticationTableData> {

View File

@ -20,13 +20,13 @@ void main() {
// These simple tests verify all possible schema updates with a simple (no // These simple tests verify all possible schema updates with a simple (no
// data) migration. This is a quick way to ensure that written database // data) migration. This is a quick way to ensure that written database
// migrations properly alter the schema. // migrations properly alter the schema.
final versions = GeneratedHelper.versions; const versions = GeneratedHelper.versions;
for (final (i, fromVersion) in versions.indexed) { for (final (i, fromVersion) in versions.indexed) {
group('from $fromVersion', () { group('from $fromVersion', () {
for (final toVersion in versions.skip(i + 1)) { for (final toVersion in versions.skip(i + 1)) {
test('to $toVersion', () async { test('to $toVersion', () async {
final schema = await verifier.schemaAt(fromVersion); final schema = await verifier.schemaAt(fromVersion);
final db = Database(schema.newConnection()); final db = AppDatabase(schema.newConnection());
await verifier.migrateAndValidate(db, toVersion); await verifier.migrateAndValidate(db, toVersion);
await db.close(); await db.close();
}); });

View File

@ -1,5 +0,0 @@
import 'package:mocktail/mocktail.dart';
import 'package:spotube/provider/audio_player/audio_player_streams.dart';
class MockAudioPlayerStreamListeners extends Mock
implements AudioPlayerStreamListeners {}

View File

@ -0,0 +1,6 @@
part of 'mocks.dart';
class MockAudioPlayerStreamListeners extends Mock
implements AudioPlayerStreamListeners {}
class MockSpotubeAudioPlayer extends Mock implements SpotubeAudioPlayer {}

View File

@ -0,0 +1,3 @@
part of 'mocks.dart';
class MockKVStoreService extends Mock implements KVStoreService {}

View File

@ -0,0 +1,12 @@
library mocks;
import 'package:mocktail/mocktail.dart';
import 'package:window_manager/window_manager.dart';
import 'package:spotube/services/kv_store/kv_store.dart';
import 'package:spotube/provider/audio_player/audio_player_streams.dart';
import 'package:spotube/services/audio_player/audio_player.dart';
part 'audio_player_mock.dart';
part 'kv_store_mock.dart';
part 'window_manager_mock.dart';

View File

@ -1,4 +1,3 @@
import 'package:mocktail/mocktail.dart'; part of 'mocks.dart';
import 'package:window_manager/window_manager.dart';
class MockWindowManager extends Mock implements WindowManager {} class MockWindowManager extends Mock implements WindowManager {}

View File

@ -4,36 +4,54 @@ import 'package:drift/native.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import 'package:spotube/collections/vars.dart';
import 'package:spotube/models/database/database.dart'; import 'package:spotube/models/database/database.dart';
import 'package:spotube/provider/audio_player/audio_player_streams.dart'; import 'package:spotube/provider/audio_player/audio_player_streams.dart';
import 'package:spotube/provider/database/database.dart';
import 'package:spotube/provider/user_preferences/default_download_dir_provider.dart'; import 'package:spotube/provider/user_preferences/default_download_dir_provider.dart';
import 'package:spotube/provider/user_preferences/user_preferences_provider.dart'; import 'package:spotube/provider/user_preferences/user_preferences_provider.dart';
import 'package:spotube/provider/window_manager/window_manager.dart'; import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/kv_store/kv_store.dart';
import 'package:spotube/services/logger/logger.dart'; import 'package:spotube/services/logger/logger.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
import '../create_container.dart'; import '../create_container.dart';
import '../mocks/audio_player_listeners_mock.dart'; import '../mocks/mocks.dart';
import '../mocks/window_manager_mock.dart';
List<Override> _createDefaultOverrides() => [ void main() {
databaseProvider.overrideWith( group('UserPreferences', () {
(ref) { TestWidgetsFlutterBinding.ensureInitialized();
late ProviderContainer container;
late MockWindowManager mockWindowManager;
setUpAll(() {
registerFallbackValue(TitleBarStyle.normal);
AppLogger.initialize(false);
});
setUp(() {
mockWindowManager = MockWindowManager();
getIt.registerSingleton<SpotubeAudioPlayer>(MockSpotubeAudioPlayer());
getIt.registerSingleton<KVStoreService>(MockKVStoreService());
getIt.registerSingleton<WindowManager>(mockWindowManager);
getIt.registerLazySingleton(
() {
final database = AppDatabase(NativeDatabase.memory()); final database = AppDatabase(NativeDatabase.memory());
ref.onDispose(database.close); addTearDown(() {
database.close();
});
return database; return database;
}, },
), );
container = createContainer(
overrides: [
audioPlayerStreamListenersProvider.overrideWith( audioPlayerStreamListenersProvider.overrideWith(
(ref) { (ref) {
final streamListeners = MockAudioPlayerStreamListeners(); final streamListeners = MockAudioPlayerStreamListeners();
when(() => streamListeners.updatePalette()).thenReturn(
Future.value(),
);
return streamListeners; return streamListeners;
}, },
), ),
@ -41,51 +59,37 @@ List<Override> _createDefaultOverrides() => [
(ref) { (ref) {
return Future.value("/storage/emulated/0/Download/Spotube"); return Future.value("/storage/emulated/0/Download/Spotube");
}, },
) ),
]; ],
);
void main() { when(() => mockWindowManager.setTitleBarStyle(any()))
group('UserPreferences', () { .thenAnswer((_) async {});
setUpAll(() { });
registerFallbackValue(TitleBarStyle.normal);
AppLogger.initialize(false); tearDown(() {
getIt.reset();
}); });
test('Initial value should be equal the default values', () { test('Initial value should be equal the default values', () {
final ref = createContainer(overrides: _createDefaultOverrides()); final preferences = container.read(userPreferencesProvider);
final preferences = ref.read(userPreferencesProvider);
final defaultPreferences = PreferencesTable.defaults(); final defaultPreferences = PreferencesTable.defaults();
expect(preferences, defaultPreferences); expect(preferences, defaultPreferences);
}); });
test('[setSystemTitleBar] should update UI titlebar', () async { test('setSystemTitleBar should update UI titlebar', () async {
TestWidgetsFlutterBinding.ensureInitialized(); when(() => audioPlayer.setAudioNormalization(any()))
.thenAnswer((_) async {});
final ref = createContainer(overrides: [ final db = getIt.get<AppDatabase>();
..._createDefaultOverrides(), final preferences = container.read(userPreferencesProvider);
windowManagerProvider.overrideWith(
(ref) {
final mockWindowManager = MockWindowManager();
when(() => mockWindowManager.setTitleBarStyle(any()))
.thenAnswer((_) => Future.value());
return mockWindowManager;
},
)
]);
final db = ref.read(databaseProvider);
final preferences = ref.read(userPreferencesProvider);
await Future.delayed(const Duration(milliseconds: 300)); await Future.delayed(const Duration(milliseconds: 300));
final preferencesNotifier = ref.read(userPreferencesProvider.notifier); final preferencesNotifier =
container.read(userPreferencesProvider.notifier);
expect(preferences.systemTitleBar, false); expect(preferences.systemTitleBar, false);
await preferencesNotifier.setSystemTitleBar(true);
final completer = Completer<bool>(); final completer = Completer<bool>();
final subscription = (db.select(db.preferencesTable) final subscription = (db.select(db.preferencesTable)
..where((tbl) => tbl.id.equals(0))) ..where((tbl) => tbl.id.equals(0)))
@ -98,14 +102,11 @@ void main() {
subscription.cancel(); subscription.cancel();
}); });
final systemTitleBar = await completer.future; preferencesNotifier.setSystemTitleBar(true);
expect(systemTitleBar, true); await expectLater(completer.future, completion(equals(true)));
verify( verify(() => mockWindowManager.setTitleBarStyle(TitleBarStyle.hidden))
() => ref .called(1);
.read(windowManagerProvider)
.setTitleBarStyle(TitleBarStyle.normal),
).called(1);
}); });
}); });
} }

View File

@ -6,9 +6,7 @@ import 'package:spotube/provider/volume_provider.dart';
import 'package:spotube/services/audio_player/audio_player.dart'; import 'package:spotube/services/audio_player/audio_player.dart';
import 'package:spotube/services/kv_store/kv_store.dart'; import 'package:spotube/services/kv_store/kv_store.dart';
class MockSpotubeAudioPlayer extends Mock implements SpotubeAudioPlayer {} import 'mocks/mocks.dart';
class MockKVStoreService extends Mock implements KVStoreService {}
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();