- Flutter app (Android/iOS): home, explore, albums, search, player with equalizer, mini player, sign up/sign in with email codes, profile, encrypted offline downloads, About page - server/: one-command deploy of self-hosted Supabase behind HestiaCP/nginx (or Caddy), migrations, backups, email templates - supabase/migrations: schema, row-level security, profiles - tools/: song scanner and importer Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
49 lines
1.6 KiB
Dart
49 lines
1.6 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
import '../audio/player_providers.dart';
|
|
|
|
/// Overridden in main() once SharedPreferences has loaded.
|
|
final sharedPrefsProvider = Provider<SharedPreferences>(
|
|
(ref) => throw UnimplementedError('sharedPrefsProvider must be overridden in main()'),
|
|
);
|
|
|
|
/// Liked song ids, stored on the device. Synced to Supabase once sign-in is wired up.
|
|
class LikedSongs extends Notifier<Set<String>> {
|
|
static const _key = 'liked_track_ids';
|
|
|
|
@override
|
|
Set<String> build() => {...?ref.watch(sharedPrefsProvider).getStringList(_key)};
|
|
|
|
bool isLiked(String id) => state.contains(id);
|
|
|
|
void toggle(String id) {
|
|
state = state.contains(id) ? ({...state}..remove(id)) : {...state, id};
|
|
ref.read(sharedPrefsProvider).setStringList(_key, state.toList());
|
|
}
|
|
}
|
|
|
|
final likedSongsProvider = NotifierProvider<LikedSongs, Set<String>>(LikedSongs.new);
|
|
|
|
/// Most recent first, capped at 30.
|
|
class RecentlyPlayed extends Notifier<List<String>> {
|
|
static const _key = 'recent_track_ids';
|
|
static const _max = 30;
|
|
|
|
@override
|
|
List<String> build() {
|
|
ref.listen(mediaItemProvider, (_, next) {
|
|
final id = next.value?.id;
|
|
if (id != null && (state.isEmpty || state.first != id)) _push(id);
|
|
});
|
|
return ref.watch(sharedPrefsProvider).getStringList(_key) ?? const [];
|
|
}
|
|
|
|
void _push(String id) {
|
|
state = [id, ...state.where((e) => e != id)].take(_max).toList();
|
|
ref.read(sharedPrefsProvider).setStringList(_key, state);
|
|
}
|
|
}
|
|
|
|
final recentlyPlayedProvider = NotifierProvider<RecentlyPlayed, List<String>>(RecentlyPlayed.new);
|