Compare commits

...

5 Commits

Author SHA1 Message Date
austinried
3fcb938f2b playlists screen 2025-12-06 16:38:38 +09:00
austinried
97ea3c3230 devtools 2025-12-06 15:45:03 +09:00
austinried
71132a1f0e fix extra rebuild due to hierarchy change 2025-12-06 15:44:51 +09:00
austinried
f3969dc6af playlists list 2025-12-06 15:12:53 +09:00
austinried
a4e4c6fa57 songs list tab 2025-12-06 09:42:53 +09:00
20 changed files with 1261 additions and 99 deletions

26
.vscode/launch.json vendored
View File

@ -1,26 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "debug",
"request": "launch",
"type": "dart",
"flutterMode": "debug"
},
{
"name": "profile mode",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
},
{
"name": "release mode",
"request": "launch",
"type": "dart",
"flutterMode": "release"
}
]
}

3
devtools_options.yaml Normal file
View File

@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:

View File

@ -4,6 +4,7 @@ import 'screens/album_screen.dart';
import 'screens/artist_screen.dart';
import 'screens/library_screen.dart';
import 'screens/now_playing_screen.dart';
import 'screens/playlist_screen.dart';
import 'screens/preload_screen.dart';
import 'screens/root_shell_screen.dart';
import 'screens/settings_screen.dart';
@ -23,14 +24,19 @@ final router = GoRouter(
builder: (context, state) => LibraryScreen(),
routes: [
GoRoute(
path: 'album/:id',
path: 'albums/:id',
builder: (context, state) =>
AlbumScreen(id: state.pathParameters['id']!),
),
GoRoute(
path: 'artist',
path: 'artists',
builder: (context, state) => ArtistScreen(),
),
GoRoute(
path: 'playlists/:id',
builder: (context, state) =>
PlaylistScreen(id: state.pathParameters['id']!),
),
],
),
],

View File

@ -10,6 +10,7 @@ import '../state/source.dart';
import '../ui/cover_art_theme.dart';
import '../ui/gradient.dart';
import '../ui/lists/header.dart';
import '../ui/lists/items.dart';
import '../ui/lists/songs_list.dart';
class AlbumScreen extends HookConsumerWidget {
@ -40,9 +41,9 @@ class AlbumScreen extends HookConsumerWidget {
sourceId: sourceId,
filter: IList([SongsFilter.albumId(album.id)]),
sort: IList([
SongsSortingTerm(dir: SortDirection.asc, by: SongsColumn.disc),
SongsSortingTerm(dir: SortDirection.asc, by: SongsColumn.track),
SongsSortingTerm(dir: SortDirection.asc, by: SongsColumn.title),
SortingTerm.songsAsc(SongsColumn.disc),
SortingTerm.songsAsc(SongsColumn.track),
SortingTerm.songsAsc(SongsColumn.title),
]),
);
@ -61,7 +62,13 @@ class AlbumScreen extends HookConsumerWidget {
onMore: () {},
),
),
SongsList(query: query),
SongsList(
query: query,
itemBuilder: (context, item, index) => SongListTile(
song: item.song,
onTap: () {},
),
),
],
),
),

View File

@ -1,13 +1,19 @@
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:material_symbols_icons/symbols.dart';
import '../../database/query.dart';
import '../../l10n/generated/app_localizations.dart';
import '../state/services.dart';
import '../state/source.dart';
import '../ui/lists/albums_grid.dart';
import '../ui/lists/artists_list.dart';
import '../ui/lists/items.dart';
import '../ui/lists/playlists_list.dart';
import '../ui/lists/songs_list.dart';
import '../util/custom_scroll_fix.dart';
const kIconSize = 26.0;
@ -51,7 +57,38 @@ class LibraryScreen extends HookConsumerWidget {
builder: (context) => CustomScrollProvider(
tabController: tabController,
parent: PrimaryScrollController.of(context),
child: TabBarView(
child: LibraryTabBarView(tabController: tabController),
),
),
),
);
}
}
class LibraryTabBarView extends HookConsumerWidget {
const LibraryTabBarView({
super.key,
required this.tabController,
});
final TabController tabController;
@override
Widget build(BuildContext context, WidgetRef ref) {
final sourceId = ref.watch(sourceIdProvider);
final songsQuery = SongsQuery(
sourceId: sourceId,
sort: IList([
SortingTerm.songsAsc(SongsColumn.albumArtist),
SortingTerm.songsAsc(SongsColumn.album),
SortingTerm.songsAsc(SongsColumn.disc),
SortingTerm.songsAsc(SongsColumn.track),
SortingTerm.songsAsc(SongsColumn.title),
]),
);
return TabBarView(
controller: tabController,
children: LibraryTab.values
.map(
@ -59,15 +96,22 @@ class LibraryScreen extends HookConsumerWidget {
index: LibraryTab.values.indexOf(tab),
sliver: switch (tab) {
LibraryTab.albums => AlbumsGrid(),
LibraryTab.artists => ArtistsList(),
LibraryTab.playlists => PlaylistsList(),
LibraryTab.songs => SongsList(
query: songsQuery,
itemBuilder: (context, item, index) => SongListTile(
song: item.song,
coverArt: item.albumCoverArt,
showLeading: true,
onTap: () {},
),
),
_ => ArtistsList(),
},
),
)
.toList(),
),
),
),
),
);
}
}

View File

@ -0,0 +1,77 @@
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import '../../database/query.dart';
import '../../l10n/generated/app_localizations.dart';
import '../state/database.dart';
import '../state/source.dart';
import '../ui/cover_art_theme.dart';
import '../ui/gradient.dart';
import '../ui/lists/header.dart';
import '../ui/lists/items.dart';
import '../ui/lists/songs_list.dart';
class PlaylistScreen extends HookConsumerWidget {
const PlaylistScreen({
super.key,
required this.id,
});
final String id;
@override
Widget build(BuildContext context, WidgetRef ref) {
final l = AppLocalizations.of(context);
final db = ref.watch(databaseProvider);
final sourceId = ref.watch(sourceIdProvider);
final getPlaylist = useMemoized(
() => db.libraryDao.getPlaylist(sourceId, id).getSingle(),
);
final playlist = useFuture(getPlaylist).data;
if (playlist == null) {
return Container();
}
final query = SongsQuery(
sourceId: sourceId,
filter: IList([SongsFilter.playlistId(playlist.id)]),
sort: IList([
SortingTerm.songsAsc(SongsColumn.playlistPosition),
]),
);
return CoverArtTheme(
coverArt: playlist.coverArt,
child: Scaffold(
body: GradientScrollView(
slivers: [
SliverToBoxAdapter(
child: SongsListHeader(
title: playlist.name,
// subtitle: playlist.albumArtist,
coverArt: playlist.coverArt,
playText: l.resourcesPlaylistActionsPlay,
onPlay: () {},
onMore: () {},
),
),
SongsList(
query: query,
itemBuilder: (context, item, index) => SongListTile(
song: item.song,
coverArt: item.albumCoverArt,
showLeading: true,
onTap: () {},
),
),
],
),
),
);
}
}

View File

@ -46,11 +46,11 @@ class CoverArtTheme extends HookConsumerWidget {
final colorScheme = useFuture(getColorScheme).data;
return colorScheme != null
? Theme(
data: subtracksTheme(colorScheme),
return Theme(
data: colorScheme == null
? Theme.of(context)
: subtracksTheme(colorScheme),
child: child,
)
: child;
);
}
}

View File

@ -27,10 +27,7 @@ class AlbumsGrid extends HookConsumerWidget {
AlbumsQuery(
sourceId: ref.read(sourceIdProvider),
sort: IList([
AlbumsSortingTerm(
dir: SortDirection.desc,
by: AlbumsColumn.created,
),
SortingTerm.albumsDesc(AlbumsColumn.created),
]),
limit: kPageSize,
offset: (pageKey - 1) * kPageSize,
@ -56,7 +53,7 @@ class AlbumsGrid extends HookConsumerWidget {
itemBuilder: (context, item, index) => AlbumGridTile(
album: item,
onTap: () async {
context.push('/album/${item.id}');
context.push('/albums/${item.id}');
},
),
),

View File

@ -27,10 +27,7 @@ class ArtistsList extends HookConsumerWidget {
ArtistsQuery(
sourceId: ref.read(sourceIdProvider),
sort: IList([
ArtistsSortingTerm(
dir: SortDirection.asc,
by: ArtistsColumn.name,
),
SortingTerm.artistsAsc(ArtistsColumn.name),
]),
limit: kPageSize,
offset: (pageKey - 1) * kPageSize,
@ -55,7 +52,7 @@ class ArtistsList extends HookConsumerWidget {
artist: artist,
albumCount: albumCount,
onTap: () async {
context.push('/artist/${artist.id}');
context.push('/artists/${artist.id}');
},
);
},

View File

@ -62,8 +62,9 @@ class SongsListHeader extends HookConsumerWidget {
style: theme.textTheme.headlineMedium,
textAlign: TextAlign.center,
),
if (subtitle != null)
Text(
subtitle ?? '',
subtitle!,
style: theme.textTheme.headlineSmall,
textAlign: TextAlign.center,
),

View File

@ -38,12 +38,12 @@ class ArtistListTile extends StatelessWidget {
const ArtistListTile({
super.key,
required this.artist,
this.albumCount,
required this.albumCount,
this.onTap,
});
final Artist artist;
final int? albumCount;
final int albumCount;
final void Function()? onTap;
@override
@ -56,7 +56,33 @@ class ArtistListTile extends StatelessWidget {
),
),
title: Text(artist.name),
subtitle: albumCount != null ? Text('$albumCount albums') : null,
subtitle: Text('$albumCount albums'),
onTap: onTap,
);
}
}
class PlaylistListTile extends StatelessWidget {
const PlaylistListTile({
super.key,
required this.playlist,
this.albumCount,
this.onTap,
});
final Playlist playlist;
final int? albumCount;
final void Function()? onTap;
@override
Widget build(BuildContext context) {
return ListTile(
leading: CoverArtImage(
coverArt: playlist.coverArt,
thumbnail: true,
),
title: Text(playlist.name),
subtitle: Text(playlist.comment ?? ''),
onTap: onTap,
);
}
@ -66,19 +92,25 @@ class SongListTile extends StatelessWidget {
const SongListTile({
super.key,
required this.song,
this.coverArt,
this.showLeading = false,
this.onTap,
});
final Song song;
final String? coverArt;
final bool showLeading;
final void Function()? onTap;
@override
Widget build(BuildContext context) {
return ListTile(
// leading: CoverArtImage(
// coverArt: song.coverArt,
// thumbnail: true,
// ),
leading: showLeading
? CoverArtImage(
coverArt: coverArt,
thumbnail: true,
)
: null,
title: Text(song.title),
subtitle: Text(song.artist ?? ''),
onTap: onTap,

View File

@ -0,0 +1,61 @@
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import '../../../database/query.dart';
import '../../../sources/models.dart';
import '../../hooks/use_on_source.dart';
import '../../hooks/use_paging_controller.dart';
import '../../state/database.dart';
import '../../state/source.dart';
import 'items.dart';
const kPageSize = 30;
class PlaylistsList extends HookConsumerWidget {
const PlaylistsList({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final db = ref.watch(databaseProvider);
final controller = usePagingController<int, Playlist>(
getNextPageKey: (state) =>
state.lastPageIsEmpty ? null : state.nextIntPageKey,
fetchPage: (pageKey) => db.libraryDao.listPlaylists(
PlaylistsQuery(
sourceId: ref.read(sourceIdProvider),
sort: IList([
SortingTerm.playlistsDesc(PlaylistsColumn.created),
]),
limit: kPageSize,
offset: (pageKey - 1) * kPageSize,
),
),
);
useOnSourceChange(ref, (_) => controller.refresh());
useOnSourceSync(ref, controller.refresh);
return PagingListener(
controller: controller,
builder: (context, state, fetchNextPage) {
return PagedSliverList(
state: state,
fetchNextPage: fetchNextPage,
builderDelegate: PagedChildBuilderDelegate<Playlist>(
itemBuilder: (context, item, index) {
return PlaylistListTile(
playlist: item,
onTap: () {
context.push('/playlists/${item.id}');
},
);
},
),
);
},
);
}
}

View File

@ -2,12 +2,11 @@ import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:infinite_scroll_pagination/infinite_scroll_pagination.dart';
import '../../../database/dao/library_dao.dart';
import '../../../database/query.dart';
import '../../../sources/models.dart';
import '../../hooks/use_on_source.dart';
import '../../hooks/use_paging_controller.dart';
import '../../state/database.dart';
import 'items.dart';
const kPageSize = 30;
@ -15,14 +14,17 @@ class SongsList extends HookConsumerWidget {
const SongsList({
super.key,
required this.query,
required this.itemBuilder,
});
final SongsQuery query;
final Widget Function(BuildContext context, SongListItem item, int index)
itemBuilder;
@override
Widget build(BuildContext context, WidgetRef ref) {
final db = ref.watch(databaseProvider);
final controller = usePagingController<int, Song>(
final controller = usePagingController<int, SongListItem>(
getNextPageKey: (state) =>
state.lastPageIsEmpty ? null : state.nextIntPageKey,
fetchPage: (pageKey) => db.libraryDao.listSongs(
@ -42,13 +44,8 @@ class SongsList extends HookConsumerWidget {
return PagedSliverList(
state: state,
fetchNextPage: fetchNextPage,
builderDelegate: PagedChildBuilderDelegate<Song>(
itemBuilder: (context, item, index) {
return SongListTile(
song: item,
onTap: () async {},
);
},
builderDelegate: PagedChildBuilderDelegate<SongListItem>(
itemBuilder: itemBuilder,
),
);
},

View File

@ -6,7 +6,15 @@ import '../query.dart';
part 'library_dao.g.dart';
typedef AristListItem = ({models.Artist artist, int? albumCount});
typedef AristListItem = ({
models.Artist artist,
int albumCount,
});
typedef SongListItem = ({
models.Song song,
String? albumCoverArt,
});
extension on SortDirection {
OrderingMode toMode() => switch (this) {
@ -111,7 +119,7 @@ class LibraryDao extends DatabaseAccessor<SubtracksDatabase>
.get();
}
Future<List<models.Song>> listSongs(SongsQuery q) {
Future<List<SongListItem>> listSongs(SongsQuery q) {
var joinPlaylistSongs = false;
var filter = songs.sourceId.equals(q.sourceId);
@ -127,6 +135,11 @@ class LibraryDao extends DatabaseAccessor<SubtracksDatabase>
final query =
songs.select().join([
leftOuterJoin(
albums,
albums.id.equalsExp(songs.albumId) &
albums.sourceId.equals(q.sourceId),
),
if (joinPlaylistSongs)
leftOuterJoin(
playlistSongs,
@ -134,6 +147,9 @@ class LibraryDao extends DatabaseAccessor<SubtracksDatabase>
playlistSongs.songId.equalsExp(songs.id),
),
])
..addColumns([
albums.coverArt,
])
..where(filter)
..orderBy(
q.sort
@ -144,6 +160,10 @@ class LibraryDao extends DatabaseAccessor<SubtracksDatabase>
SongsColumn.starred => songs.starred,
SongsColumn.disc => songs.disc,
SongsColumn.track => songs.track,
SongsColumn.album => songs.album,
SongsColumn.artist => songs.artist,
SongsColumn.albumArtist => albums.albumArtist,
SongsColumn.playlistPosition => playlistSongs.position,
},
mode: sort.dir.toMode(),
),
@ -153,7 +173,50 @@ class LibraryDao extends DatabaseAccessor<SubtracksDatabase>
_limitQuery(query: query, limit: q.limit, offset: q.offset);
return query.map((row) => (row.readTable(songs))).get();
return query
.map(
(row) => (
song: row.readTable(songs),
albumCoverArt: row.read(albums.coverArt),
),
)
.get();
}
Future<List<models.Playlist>> listPlaylists(PlaylistsQuery q) {
final query = playlists.select()
..where((playlists) {
var filter = playlists.sourceId.equals(q.sourceId);
for (final queryFilter in q.filter) {
filter &= switch (queryFilter) {
PlaylistsFilterPublic(:final public) => playlists.public.equals(
public,
),
PlaylistsFilterNameSearch() => CustomExpression(''),
_ => CustomExpression(''),
};
}
return filter;
})
..orderBy(
q.sort
.map(
(sort) =>
(albums) => OrderingTerm(
expression: switch (sort.by) {
PlaylistsColumn.name => playlists.name,
PlaylistsColumn.created => playlists.created,
},
mode: sort.dir.toMode(),
),
)
.toList(),
);
_limitQuery(query: query, limit: q.limit, offset: q.offset);
return query.get();
}
Selectable<models.Album> getAlbum(int sourceId, String id) {
@ -162,6 +225,12 @@ class LibraryDao extends DatabaseAccessor<SubtracksDatabase>
);
}
Selectable<models.Playlist> getPlaylist(int sourceId, String id) {
return db.managers.playlists.filter(
(f) => f.sourceId.equals(sourceId) & f.id.equals(id),
);
}
void _limitQuery({
required LimitContainerMixin query,
required int? limit,

View File

@ -1471,6 +1471,15 @@ class Playlists extends Table with TableInfo<Playlists, models.Playlist> {
requiredDuringInsert: true,
$customConstraints: 'NOT NULL',
);
static const VerificationMeta _publicMeta = const VerificationMeta('public');
late final GeneratedColumn<bool> public = GeneratedColumn<bool>(
'public',
aliasedName,
true,
type: DriftSqlType.bool,
requiredDuringInsert: false,
$customConstraints: '',
);
@override
List<GeneratedColumn> get $columns => [
sourceId,
@ -1480,6 +1489,7 @@ class Playlists extends Table with TableInfo<Playlists, models.Playlist> {
coverArt,
created,
changed,
public,
];
@override
String get aliasedName => _alias ?? actualTableName;
@ -1542,6 +1552,12 @@ class Playlists extends Table with TableInfo<Playlists, models.Playlist> {
} else if (isInserting) {
context.missing(_changedMeta);
}
if (data.containsKey('public')) {
context.handle(
_publicMeta,
public.isAcceptableOrUnknown(data['public']!, _publicMeta),
);
}
return context;
}
@ -1575,6 +1591,10 @@ class Playlists extends Table with TableInfo<Playlists, models.Playlist> {
DriftSqlType.string,
data['${effectivePrefix}cover_art'],
),
public: attachedDatabase.typeMapping.read(
DriftSqlType.bool,
data['${effectivePrefix}public'],
),
);
}
@ -1600,6 +1620,7 @@ class PlaylistsCompanion extends UpdateCompanion<models.Playlist> {
final Value<String?> coverArt;
final Value<DateTime> created;
final Value<DateTime> changed;
final Value<bool?> public;
final Value<int> rowid;
const PlaylistsCompanion({
this.sourceId = const Value.absent(),
@ -1609,6 +1630,7 @@ class PlaylistsCompanion extends UpdateCompanion<models.Playlist> {
this.coverArt = const Value.absent(),
this.created = const Value.absent(),
this.changed = const Value.absent(),
this.public = const Value.absent(),
this.rowid = const Value.absent(),
});
PlaylistsCompanion.insert({
@ -1619,6 +1641,7 @@ class PlaylistsCompanion extends UpdateCompanion<models.Playlist> {
this.coverArt = const Value.absent(),
required DateTime created,
required DateTime changed,
this.public = const Value.absent(),
this.rowid = const Value.absent(),
}) : sourceId = Value(sourceId),
id = Value(id),
@ -1633,6 +1656,7 @@ class PlaylistsCompanion extends UpdateCompanion<models.Playlist> {
Expression<String>? coverArt,
Expression<DateTime>? created,
Expression<DateTime>? changed,
Expression<bool>? public,
Expression<int>? rowid,
}) {
return RawValuesInsertable({
@ -1643,6 +1667,7 @@ class PlaylistsCompanion extends UpdateCompanion<models.Playlist> {
if (coverArt != null) 'cover_art': coverArt,
if (created != null) 'created': created,
if (changed != null) 'changed': changed,
if (public != null) 'public': public,
if (rowid != null) 'rowid': rowid,
});
}
@ -1655,6 +1680,7 @@ class PlaylistsCompanion extends UpdateCompanion<models.Playlist> {
Value<String?>? coverArt,
Value<DateTime>? created,
Value<DateTime>? changed,
Value<bool?>? public,
Value<int>? rowid,
}) {
return PlaylistsCompanion(
@ -1665,6 +1691,7 @@ class PlaylistsCompanion extends UpdateCompanion<models.Playlist> {
coverArt: coverArt ?? this.coverArt,
created: created ?? this.created,
changed: changed ?? this.changed,
public: public ?? this.public,
rowid: rowid ?? this.rowid,
);
}
@ -1693,6 +1720,9 @@ class PlaylistsCompanion extends UpdateCompanion<models.Playlist> {
if (changed.present) {
map['changed'] = Variable<DateTime>(changed.value);
}
if (public.present) {
map['public'] = Variable<bool>(public.value);
}
if (rowid.present) {
map['rowid'] = Variable<int>(rowid.value);
}
@ -1709,6 +1739,7 @@ class PlaylistsCompanion extends UpdateCompanion<models.Playlist> {
..write('coverArt: $coverArt, ')
..write('created: $created, ')
..write('changed: $changed, ')
..write('public: $public, ')
..write('rowid: $rowid')
..write(')'))
.toString();
@ -3432,6 +3463,7 @@ typedef $PlaylistsCreateCompanionBuilder =
Value<String?> coverArt,
required DateTime created,
required DateTime changed,
Value<bool?> public,
Value<int> rowid,
});
typedef $PlaylistsUpdateCompanionBuilder =
@ -3443,6 +3475,7 @@ typedef $PlaylistsUpdateCompanionBuilder =
Value<String?> coverArt,
Value<DateTime> created,
Value<DateTime> changed,
Value<bool?> public,
Value<int> rowid,
});
@ -3489,6 +3522,11 @@ class $PlaylistsFilterComposer
column: $table.changed,
builder: (column) => ColumnFilters(column),
);
ColumnFilters<bool> get public => $composableBuilder(
column: $table.public,
builder: (column) => ColumnFilters(column),
);
}
class $PlaylistsOrderingComposer
@ -3534,6 +3572,11 @@ class $PlaylistsOrderingComposer
column: $table.changed,
builder: (column) => ColumnOrderings(column),
);
ColumnOrderings<bool> get public => $composableBuilder(
column: $table.public,
builder: (column) => ColumnOrderings(column),
);
}
class $PlaylistsAnnotationComposer
@ -3565,6 +3608,9 @@ class $PlaylistsAnnotationComposer
GeneratedColumn<DateTime> get changed =>
$composableBuilder(column: $table.changed, builder: (column) => column);
GeneratedColumn<bool> get public =>
$composableBuilder(column: $table.public, builder: (column) => column);
}
class $PlaylistsTableManager
@ -3605,6 +3651,7 @@ class $PlaylistsTableManager
Value<String?> coverArt = const Value.absent(),
Value<DateTime> created = const Value.absent(),
Value<DateTime> changed = const Value.absent(),
Value<bool?> public = const Value.absent(),
Value<int> rowid = const Value.absent(),
}) => PlaylistsCompanion(
sourceId: sourceId,
@ -3614,6 +3661,7 @@ class $PlaylistsTableManager
coverArt: coverArt,
created: created,
changed: changed,
public: public,
rowid: rowid,
),
createCompanionCallback:
@ -3625,6 +3673,7 @@ class $PlaylistsTableManager
Value<String?> coverArt = const Value.absent(),
required DateTime created,
required DateTime changed,
Value<bool?> public = const Value.absent(),
Value<int> rowid = const Value.absent(),
}) => PlaylistsCompanion.insert(
sourceId: sourceId,
@ -3634,6 +3683,7 @@ class $PlaylistsTableManager
coverArt: coverArt,
created: created,
changed: changed,
public: public,
rowid: rowid,
),
withReferenceMapper: (p0) => p0

View File

@ -27,6 +27,15 @@ enum SongsColumn {
starred,
disc,
track,
album,
artist,
albumArtist,
playlistPosition,
}
enum PlaylistsColumn {
name,
created,
}
@freezed
@ -36,16 +45,53 @@ abstract class SortingTerm with _$SortingTerm {
required AlbumsColumn by,
}) = AlbumsSortingTerm;
static AlbumsSortingTerm albumsAsc(AlbumsColumn by) {
return AlbumsSortingTerm(dir: SortDirection.asc, by: by);
}
static AlbumsSortingTerm albumsDesc(AlbumsColumn by) {
return AlbumsSortingTerm(dir: SortDirection.desc, by: by);
}
const factory SortingTerm.artists({
required SortDirection dir,
required ArtistsColumn by,
}) = ArtistsSortingTerm;
static ArtistsSortingTerm artistsAsc(ArtistsColumn by) {
return ArtistsSortingTerm(dir: SortDirection.asc, by: by);
}
static ArtistsSortingTerm artistsDesc(ArtistsColumn by) {
return ArtistsSortingTerm(dir: SortDirection.desc, by: by);
}
const factory SortingTerm.songs({
required SortDirection dir,
required SongsColumn by,
}) = SongsSortingTerm;
static SongsSortingTerm songsAsc(SongsColumn by) {
return SongsSortingTerm(dir: SortDirection.asc, by: by);
}
static SongsSortingTerm songsDesc(SongsColumn by) {
return SongsSortingTerm(dir: SortDirection.desc, by: by);
}
const factory SortingTerm.playlists({
required SortDirection dir,
required PlaylistsColumn by,
}) = PlaylistsSortingTerm;
static PlaylistsSortingTerm playlistsAsc(PlaylistsColumn by) {
return PlaylistsSortingTerm(dir: SortDirection.asc, by: by);
}
static PlaylistsSortingTerm playlistsDesc(PlaylistsColumn by) {
return PlaylistsSortingTerm(dir: SortDirection.desc, by: by);
}
factory SortingTerm.fromJson(Map<String, Object?> json) =>
_$SortingTermFromJson(json);
}
@ -119,3 +165,27 @@ abstract class SongsFilter with _$SongsFilter {
factory SongsFilter.fromJson(Map<String, Object?> json) =>
_$SongsFilterFromJson(json);
}
@freezed
abstract class PlaylistsQuery with _$PlaylistsQuery {
const factory PlaylistsQuery({
required int sourceId,
@Default(IListConst([])) IList<PlaylistsFilter> filter,
required IList<PlaylistsSortingTerm> sort,
int? limit,
int? offset,
}) = _PlaylistsQuery;
factory PlaylistsQuery.fromJson(Map<String, Object?> json) =>
_$PlaylistsQueryFromJson(json);
}
@freezed
abstract class PlaylistsFilter with _$PlaylistsFilter {
const factory PlaylistsFilter.nameSearch(String name) =
PlaylistsFilterNameSearch;
const factory PlaylistsFilter.public(bool public) = PlaylistsFilterPublic;
factory PlaylistsFilter.fromJson(Map<String, Object?> json) =>
_$PlaylistsFilterFromJson(json);
}

View File

@ -27,6 +27,10 @@ SortingTerm _$SortingTermFromJson(
return SongsSortingTerm.fromJson(
json
);
case 'playlists':
return PlaylistsSortingTerm.fromJson(
json
);
default:
throw CheckedFromJsonException(
@ -116,13 +120,14 @@ extension SortingTermPatterns on SortingTerm {
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( AlbumsSortingTerm value)? albums,TResult Function( ArtistsSortingTerm value)? artists,TResult Function( SongsSortingTerm value)? songs,required TResult orElse(),}){
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( AlbumsSortingTerm value)? albums,TResult Function( ArtistsSortingTerm value)? artists,TResult Function( SongsSortingTerm value)? songs,TResult Function( PlaylistsSortingTerm value)? playlists,required TResult orElse(),}){
final _that = this;
switch (_that) {
case AlbumsSortingTerm() when albums != null:
return albums(_that);case ArtistsSortingTerm() when artists != null:
return artists(_that);case SongsSortingTerm() when songs != null:
return songs(_that);case _:
return songs(_that);case PlaylistsSortingTerm() when playlists != null:
return playlists(_that);case _:
return orElse();
}
@ -140,13 +145,14 @@ return songs(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( AlbumsSortingTerm value) albums,required TResult Function( ArtistsSortingTerm value) artists,required TResult Function( SongsSortingTerm value) songs,}){
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( AlbumsSortingTerm value) albums,required TResult Function( ArtistsSortingTerm value) artists,required TResult Function( SongsSortingTerm value) songs,required TResult Function( PlaylistsSortingTerm value) playlists,}){
final _that = this;
switch (_that) {
case AlbumsSortingTerm():
return albums(_that);case ArtistsSortingTerm():
return artists(_that);case SongsSortingTerm():
return songs(_that);case _:
return songs(_that);case PlaylistsSortingTerm():
return playlists(_that);case _:
throw StateError('Unexpected subclass');
}
@ -163,13 +169,14 @@ return songs(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( AlbumsSortingTerm value)? albums,TResult? Function( ArtistsSortingTerm value)? artists,TResult? Function( SongsSortingTerm value)? songs,}){
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( AlbumsSortingTerm value)? albums,TResult? Function( ArtistsSortingTerm value)? artists,TResult? Function( SongsSortingTerm value)? songs,TResult? Function( PlaylistsSortingTerm value)? playlists,}){
final _that = this;
switch (_that) {
case AlbumsSortingTerm() when albums != null:
return albums(_that);case ArtistsSortingTerm() when artists != null:
return artists(_that);case SongsSortingTerm() when songs != null:
return songs(_that);case _:
return songs(_that);case PlaylistsSortingTerm() when playlists != null:
return playlists(_that);case _:
return null;
}
@ -186,12 +193,13 @@ return songs(_that);case _:
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function( SortDirection dir, AlbumsColumn by)? albums,TResult Function( SortDirection dir, ArtistsColumn by)? artists,TResult Function( SortDirection dir, SongsColumn by)? songs,required TResult orElse(),}) {final _that = this;
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function( SortDirection dir, AlbumsColumn by)? albums,TResult Function( SortDirection dir, ArtistsColumn by)? artists,TResult Function( SortDirection dir, SongsColumn by)? songs,TResult Function( SortDirection dir, PlaylistsColumn by)? playlists,required TResult orElse(),}) {final _that = this;
switch (_that) {
case AlbumsSortingTerm() when albums != null:
return albums(_that.dir,_that.by);case ArtistsSortingTerm() when artists != null:
return artists(_that.dir,_that.by);case SongsSortingTerm() when songs != null:
return songs(_that.dir,_that.by);case _:
return songs(_that.dir,_that.by);case PlaylistsSortingTerm() when playlists != null:
return playlists(_that.dir,_that.by);case _:
return orElse();
}
@ -209,12 +217,13 @@ return songs(_that.dir,_that.by);case _:
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function( SortDirection dir, AlbumsColumn by) albums,required TResult Function( SortDirection dir, ArtistsColumn by) artists,required TResult Function( SortDirection dir, SongsColumn by) songs,}) {final _that = this;
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function( SortDirection dir, AlbumsColumn by) albums,required TResult Function( SortDirection dir, ArtistsColumn by) artists,required TResult Function( SortDirection dir, SongsColumn by) songs,required TResult Function( SortDirection dir, PlaylistsColumn by) playlists,}) {final _that = this;
switch (_that) {
case AlbumsSortingTerm():
return albums(_that.dir,_that.by);case ArtistsSortingTerm():
return artists(_that.dir,_that.by);case SongsSortingTerm():
return songs(_that.dir,_that.by);case _:
return songs(_that.dir,_that.by);case PlaylistsSortingTerm():
return playlists(_that.dir,_that.by);case _:
throw StateError('Unexpected subclass');
}
@ -231,12 +240,13 @@ return songs(_that.dir,_that.by);case _:
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function( SortDirection dir, AlbumsColumn by)? albums,TResult? Function( SortDirection dir, ArtistsColumn by)? artists,TResult? Function( SortDirection dir, SongsColumn by)? songs,}) {final _that = this;
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function( SortDirection dir, AlbumsColumn by)? albums,TResult? Function( SortDirection dir, ArtistsColumn by)? artists,TResult? Function( SortDirection dir, SongsColumn by)? songs,TResult? Function( SortDirection dir, PlaylistsColumn by)? playlists,}) {final _that = this;
switch (_that) {
case AlbumsSortingTerm() when albums != null:
return albums(_that.dir,_that.by);case ArtistsSortingTerm() when artists != null:
return artists(_that.dir,_that.by);case SongsSortingTerm() when songs != null:
return songs(_that.dir,_that.by);case _:
return songs(_that.dir,_that.by);case PlaylistsSortingTerm() when playlists != null:
return playlists(_that.dir,_that.by);case _:
return null;
}
@ -467,6 +477,81 @@ as SongsColumn,
}
}
/// @nodoc
@JsonSerializable()
class PlaylistsSortingTerm implements SortingTerm {
const PlaylistsSortingTerm({required this.dir, required this.by, final String? $type}): $type = $type ?? 'playlists';
factory PlaylistsSortingTerm.fromJson(Map<String, dynamic> json) => _$PlaylistsSortingTermFromJson(json);
@override final SortDirection dir;
@override final PlaylistsColumn by;
@JsonKey(name: 'runtimeType')
final String $type;
/// Create a copy of SortingTerm
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$PlaylistsSortingTermCopyWith<PlaylistsSortingTerm> get copyWith => _$PlaylistsSortingTermCopyWithImpl<PlaylistsSortingTerm>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$PlaylistsSortingTermToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PlaylistsSortingTerm&&(identical(other.dir, dir) || other.dir == dir)&&(identical(other.by, by) || other.by == by));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,dir,by);
@override
String toString() {
return 'SortingTerm.playlists(dir: $dir, by: $by)';
}
}
/// @nodoc
abstract mixin class $PlaylistsSortingTermCopyWith<$Res> implements $SortingTermCopyWith<$Res> {
factory $PlaylistsSortingTermCopyWith(PlaylistsSortingTerm value, $Res Function(PlaylistsSortingTerm) _then) = _$PlaylistsSortingTermCopyWithImpl;
@override @useResult
$Res call({
SortDirection dir, PlaylistsColumn by
});
}
/// @nodoc
class _$PlaylistsSortingTermCopyWithImpl<$Res>
implements $PlaylistsSortingTermCopyWith<$Res> {
_$PlaylistsSortingTermCopyWithImpl(this._self, this._then);
final PlaylistsSortingTerm _self;
final $Res Function(PlaylistsSortingTerm) _then;
/// Create a copy of SortingTerm
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? dir = null,Object? by = null,}) {
return _then(PlaylistsSortingTerm(
dir: null == dir ? _self.dir : dir // ignore: cast_nullable_to_non_nullable
as SortDirection,by: null == by ? _self.by : by // ignore: cast_nullable_to_non_nullable
as PlaylistsColumn,
));
}
}
@ -2306,6 +2391,619 @@ as String,
}
}
/// @nodoc
mixin _$PlaylistsQuery {
int get sourceId; IList<PlaylistsFilter> get filter; IList<PlaylistsSortingTerm> get sort; int? get limit; int? get offset;
/// Create a copy of PlaylistsQuery
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$PlaylistsQueryCopyWith<PlaylistsQuery> get copyWith => _$PlaylistsQueryCopyWithImpl<PlaylistsQuery>(this as PlaylistsQuery, _$identity);
/// Serializes this PlaylistsQuery to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PlaylistsQuery&&(identical(other.sourceId, sourceId) || other.sourceId == sourceId)&&const DeepCollectionEquality().equals(other.filter, filter)&&const DeepCollectionEquality().equals(other.sort, sort)&&(identical(other.limit, limit) || other.limit == limit)&&(identical(other.offset, offset) || other.offset == offset));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,sourceId,const DeepCollectionEquality().hash(filter),const DeepCollectionEquality().hash(sort),limit,offset);
@override
String toString() {
return 'PlaylistsQuery(sourceId: $sourceId, filter: $filter, sort: $sort, limit: $limit, offset: $offset)';
}
}
/// @nodoc
abstract mixin class $PlaylistsQueryCopyWith<$Res> {
factory $PlaylistsQueryCopyWith(PlaylistsQuery value, $Res Function(PlaylistsQuery) _then) = _$PlaylistsQueryCopyWithImpl;
@useResult
$Res call({
int sourceId, IList<PlaylistsFilter> filter, IList<PlaylistsSortingTerm> sort, int? limit, int? offset
});
}
/// @nodoc
class _$PlaylistsQueryCopyWithImpl<$Res>
implements $PlaylistsQueryCopyWith<$Res> {
_$PlaylistsQueryCopyWithImpl(this._self, this._then);
final PlaylistsQuery _self;
final $Res Function(PlaylistsQuery) _then;
/// Create a copy of PlaylistsQuery
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? sourceId = null,Object? filter = null,Object? sort = null,Object? limit = freezed,Object? offset = freezed,}) {
return _then(_self.copyWith(
sourceId: null == sourceId ? _self.sourceId : sourceId // ignore: cast_nullable_to_non_nullable
as int,filter: null == filter ? _self.filter : filter // ignore: cast_nullable_to_non_nullable
as IList<PlaylistsFilter>,sort: null == sort ? _self.sort : sort // ignore: cast_nullable_to_non_nullable
as IList<PlaylistsSortingTerm>,limit: freezed == limit ? _self.limit : limit // ignore: cast_nullable_to_non_nullable
as int?,offset: freezed == offset ? _self.offset : offset // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
/// Adds pattern-matching-related methods to [PlaylistsQuery].
extension PlaylistsQueryPatterns on PlaylistsQuery {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _PlaylistsQuery value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _PlaylistsQuery() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _PlaylistsQuery value) $default,){
final _that = this;
switch (_that) {
case _PlaylistsQuery():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _PlaylistsQuery value)? $default,){
final _that = this;
switch (_that) {
case _PlaylistsQuery() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int sourceId, IList<PlaylistsFilter> filter, IList<PlaylistsSortingTerm> sort, int? limit, int? offset)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _PlaylistsQuery() when $default != null:
return $default(_that.sourceId,_that.filter,_that.sort,_that.limit,_that.offset);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int sourceId, IList<PlaylistsFilter> filter, IList<PlaylistsSortingTerm> sort, int? limit, int? offset) $default,) {final _that = this;
switch (_that) {
case _PlaylistsQuery():
return $default(_that.sourceId,_that.filter,_that.sort,_that.limit,_that.offset);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int sourceId, IList<PlaylistsFilter> filter, IList<PlaylistsSortingTerm> sort, int? limit, int? offset)? $default,) {final _that = this;
switch (_that) {
case _PlaylistsQuery() when $default != null:
return $default(_that.sourceId,_that.filter,_that.sort,_that.limit,_that.offset);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class _PlaylistsQuery implements PlaylistsQuery {
const _PlaylistsQuery({required this.sourceId, this.filter = const IListConst([]), required this.sort, this.limit, this.offset});
factory _PlaylistsQuery.fromJson(Map<String, dynamic> json) => _$PlaylistsQueryFromJson(json);
@override final int sourceId;
@override@JsonKey() final IList<PlaylistsFilter> filter;
@override final IList<PlaylistsSortingTerm> sort;
@override final int? limit;
@override final int? offset;
/// Create a copy of PlaylistsQuery
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$PlaylistsQueryCopyWith<_PlaylistsQuery> get copyWith => __$PlaylistsQueryCopyWithImpl<_PlaylistsQuery>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$PlaylistsQueryToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PlaylistsQuery&&(identical(other.sourceId, sourceId) || other.sourceId == sourceId)&&const DeepCollectionEquality().equals(other.filter, filter)&&const DeepCollectionEquality().equals(other.sort, sort)&&(identical(other.limit, limit) || other.limit == limit)&&(identical(other.offset, offset) || other.offset == offset));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,sourceId,const DeepCollectionEquality().hash(filter),const DeepCollectionEquality().hash(sort),limit,offset);
@override
String toString() {
return 'PlaylistsQuery(sourceId: $sourceId, filter: $filter, sort: $sort, limit: $limit, offset: $offset)';
}
}
/// @nodoc
abstract mixin class _$PlaylistsQueryCopyWith<$Res> implements $PlaylistsQueryCopyWith<$Res> {
factory _$PlaylistsQueryCopyWith(_PlaylistsQuery value, $Res Function(_PlaylistsQuery) _then) = __$PlaylistsQueryCopyWithImpl;
@override @useResult
$Res call({
int sourceId, IList<PlaylistsFilter> filter, IList<PlaylistsSortingTerm> sort, int? limit, int? offset
});
}
/// @nodoc
class __$PlaylistsQueryCopyWithImpl<$Res>
implements _$PlaylistsQueryCopyWith<$Res> {
__$PlaylistsQueryCopyWithImpl(this._self, this._then);
final _PlaylistsQuery _self;
final $Res Function(_PlaylistsQuery) _then;
/// Create a copy of PlaylistsQuery
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? sourceId = null,Object? filter = null,Object? sort = null,Object? limit = freezed,Object? offset = freezed,}) {
return _then(_PlaylistsQuery(
sourceId: null == sourceId ? _self.sourceId : sourceId // ignore: cast_nullable_to_non_nullable
as int,filter: null == filter ? _self.filter : filter // ignore: cast_nullable_to_non_nullable
as IList<PlaylistsFilter>,sort: null == sort ? _self.sort : sort // ignore: cast_nullable_to_non_nullable
as IList<PlaylistsSortingTerm>,limit: freezed == limit ? _self.limit : limit // ignore: cast_nullable_to_non_nullable
as int?,offset: freezed == offset ? _self.offset : offset // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
PlaylistsFilter _$PlaylistsFilterFromJson(
Map<String, dynamic> json
) {
switch (json['runtimeType']) {
case 'nameSearch':
return PlaylistsFilterNameSearch.fromJson(
json
);
case 'public':
return PlaylistsFilterPublic.fromJson(
json
);
default:
throw CheckedFromJsonException(
json,
'runtimeType',
'PlaylistsFilter',
'Invalid union type "${json['runtimeType']}"!'
);
}
}
/// @nodoc
mixin _$PlaylistsFilter {
/// Serializes this PlaylistsFilter to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PlaylistsFilter);
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'PlaylistsFilter()';
}
}
/// @nodoc
class $PlaylistsFilterCopyWith<$Res> {
$PlaylistsFilterCopyWith(PlaylistsFilter _, $Res Function(PlaylistsFilter) __);
}
/// Adds pattern-matching-related methods to [PlaylistsFilter].
extension PlaylistsFilterPatterns on PlaylistsFilter {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( PlaylistsFilterNameSearch value)? nameSearch,TResult Function( PlaylistsFilterPublic value)? public,required TResult orElse(),}){
final _that = this;
switch (_that) {
case PlaylistsFilterNameSearch() when nameSearch != null:
return nameSearch(_that);case PlaylistsFilterPublic() when public != null:
return public(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( PlaylistsFilterNameSearch value) nameSearch,required TResult Function( PlaylistsFilterPublic value) public,}){
final _that = this;
switch (_that) {
case PlaylistsFilterNameSearch():
return nameSearch(_that);case PlaylistsFilterPublic():
return public(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( PlaylistsFilterNameSearch value)? nameSearch,TResult? Function( PlaylistsFilterPublic value)? public,}){
final _that = this;
switch (_that) {
case PlaylistsFilterNameSearch() when nameSearch != null:
return nameSearch(_that);case PlaylistsFilterPublic() when public != null:
return public(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function( String name)? nameSearch,TResult Function( bool public)? public,required TResult orElse(),}) {final _that = this;
switch (_that) {
case PlaylistsFilterNameSearch() when nameSearch != null:
return nameSearch(_that.name);case PlaylistsFilterPublic() when public != null:
return public(_that.public);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function( String name) nameSearch,required TResult Function( bool public) public,}) {final _that = this;
switch (_that) {
case PlaylistsFilterNameSearch():
return nameSearch(_that.name);case PlaylistsFilterPublic():
return public(_that.public);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function( String name)? nameSearch,TResult? Function( bool public)? public,}) {final _that = this;
switch (_that) {
case PlaylistsFilterNameSearch() when nameSearch != null:
return nameSearch(_that.name);case PlaylistsFilterPublic() when public != null:
return public(_that.public);case _:
return null;
}
}
}
/// @nodoc
@JsonSerializable()
class PlaylistsFilterNameSearch implements PlaylistsFilter {
const PlaylistsFilterNameSearch(this.name, {final String? $type}): $type = $type ?? 'nameSearch';
factory PlaylistsFilterNameSearch.fromJson(Map<String, dynamic> json) => _$PlaylistsFilterNameSearchFromJson(json);
final String name;
@JsonKey(name: 'runtimeType')
final String $type;
/// Create a copy of PlaylistsFilter
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$PlaylistsFilterNameSearchCopyWith<PlaylistsFilterNameSearch> get copyWith => _$PlaylistsFilterNameSearchCopyWithImpl<PlaylistsFilterNameSearch>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$PlaylistsFilterNameSearchToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PlaylistsFilterNameSearch&&(identical(other.name, name) || other.name == name));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,name);
@override
String toString() {
return 'PlaylistsFilter.nameSearch(name: $name)';
}
}
/// @nodoc
abstract mixin class $PlaylistsFilterNameSearchCopyWith<$Res> implements $PlaylistsFilterCopyWith<$Res> {
factory $PlaylistsFilterNameSearchCopyWith(PlaylistsFilterNameSearch value, $Res Function(PlaylistsFilterNameSearch) _then) = _$PlaylistsFilterNameSearchCopyWithImpl;
@useResult
$Res call({
String name
});
}
/// @nodoc
class _$PlaylistsFilterNameSearchCopyWithImpl<$Res>
implements $PlaylistsFilterNameSearchCopyWith<$Res> {
_$PlaylistsFilterNameSearchCopyWithImpl(this._self, this._then);
final PlaylistsFilterNameSearch _self;
final $Res Function(PlaylistsFilterNameSearch) _then;
/// Create a copy of PlaylistsFilter
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? name = null,}) {
return _then(PlaylistsFilterNameSearch(
null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class PlaylistsFilterPublic implements PlaylistsFilter {
const PlaylistsFilterPublic(this.public, {final String? $type}): $type = $type ?? 'public';
factory PlaylistsFilterPublic.fromJson(Map<String, dynamic> json) => _$PlaylistsFilterPublicFromJson(json);
final bool public;
@JsonKey(name: 'runtimeType')
final String $type;
/// Create a copy of PlaylistsFilter
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$PlaylistsFilterPublicCopyWith<PlaylistsFilterPublic> get copyWith => _$PlaylistsFilterPublicCopyWithImpl<PlaylistsFilterPublic>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$PlaylistsFilterPublicToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PlaylistsFilterPublic&&(identical(other.public, public) || other.public == public));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,public);
@override
String toString() {
return 'PlaylistsFilter.public(public: $public)';
}
}
/// @nodoc
abstract mixin class $PlaylistsFilterPublicCopyWith<$Res> implements $PlaylistsFilterCopyWith<$Res> {
factory $PlaylistsFilterPublicCopyWith(PlaylistsFilterPublic value, $Res Function(PlaylistsFilterPublic) _then) = _$PlaylistsFilterPublicCopyWithImpl;
@useResult
$Res call({
bool public
});
}
/// @nodoc
class _$PlaylistsFilterPublicCopyWithImpl<$Res>
implements $PlaylistsFilterPublicCopyWith<$Res> {
_$PlaylistsFilterPublicCopyWithImpl(this._self, this._then);
final PlaylistsFilterPublic _self;
final $Res Function(PlaylistsFilterPublic) _then;
/// Create a copy of PlaylistsFilter
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? public = null,}) {
return _then(PlaylistsFilterPublic(
null == public ? _self.public : public // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
// dart format on

View File

@ -71,6 +71,31 @@ const _$SongsColumnEnumMap = {
SongsColumn.starred: 'starred',
SongsColumn.disc: 'disc',
SongsColumn.track: 'track',
SongsColumn.album: 'album',
SongsColumn.artist: 'artist',
SongsColumn.albumArtist: 'albumArtist',
SongsColumn.playlistPosition: 'playlistPosition',
};
PlaylistsSortingTerm _$PlaylistsSortingTermFromJson(
Map<String, dynamic> json,
) => PlaylistsSortingTerm(
dir: $enumDecode(_$SortDirectionEnumMap, json['dir']),
by: $enumDecode(_$PlaylistsColumnEnumMap, json['by']),
$type: json['runtimeType'] as String?,
);
Map<String, dynamic> _$PlaylistsSortingTermToJson(
PlaylistsSortingTerm instance,
) => <String, dynamic>{
'dir': _$SortDirectionEnumMap[instance.dir]!,
'by': _$PlaylistsColumnEnumMap[instance.by]!,
'runtimeType': instance.$type,
};
const _$PlaylistsColumnEnumMap = {
PlaylistsColumn.name: 'name',
PlaylistsColumn.created: 'created',
};
_AlbumsQuery _$AlbumsQueryFromJson(Map<String, dynamic> json) => _AlbumsQuery(
@ -224,3 +249,55 @@ Map<String, dynamic> _$SongsFilterPlaylistIdToJson(
'playlistId': instance.playlistId,
'runtimeType': instance.$type,
};
_PlaylistsQuery _$PlaylistsQueryFromJson(Map<String, dynamic> json) =>
_PlaylistsQuery(
sourceId: (json['sourceId'] as num).toInt(),
filter: json['filter'] == null
? const IListConst([])
: IList<PlaylistsFilter>.fromJson(
json['filter'],
(value) =>
PlaylistsFilter.fromJson(value as Map<String, dynamic>),
),
sort: IList<PlaylistsSortingTerm>.fromJson(
json['sort'],
(value) => PlaylistsSortingTerm.fromJson(value as Map<String, dynamic>),
),
limit: (json['limit'] as num?)?.toInt(),
offset: (json['offset'] as num?)?.toInt(),
);
Map<String, dynamic> _$PlaylistsQueryToJson(_PlaylistsQuery instance) =>
<String, dynamic>{
'sourceId': instance.sourceId,
'filter': instance.filter.toJson((value) => value),
'sort': instance.sort.toJson((value) => value),
'limit': instance.limit,
'offset': instance.offset,
};
PlaylistsFilterNameSearch _$PlaylistsFilterNameSearchFromJson(
Map<String, dynamic> json,
) => PlaylistsFilterNameSearch(
json['name'] as String,
$type: json['runtimeType'] as String?,
);
Map<String, dynamic> _$PlaylistsFilterNameSearchToJson(
PlaylistsFilterNameSearch instance,
) => <String, dynamic>{'name': instance.name, 'runtimeType': instance.$type};
PlaylistsFilterPublic _$PlaylistsFilterPublicFromJson(
Map<String, dynamic> json,
) => PlaylistsFilterPublic(
json['public'] as bool,
$type: json['runtimeType'] as String?,
);
Map<String, dynamic> _$PlaylistsFilterPublicToJson(
PlaylistsFilterPublic instance,
) => <String, dynamic>{
'public': instance.public,
'runtimeType': instance.$type,
};

View File

@ -136,6 +136,7 @@ CREATE TABLE playlists(
cover_art TEXT,
created DATETIME NOT NULL,
changed DATETIME NOT NULL,
public BOOLEAN,
PRIMARY KEY (source_id, id),
FOREIGN KEY (source_id) REFERENCES sources (id) ON DELETE CASCADE
) WITH Playlist;

View File

@ -34,6 +34,7 @@ Playlist mapPlaylist(XmlElement e) => Playlist(
coverArt: e.getAttribute('coverArt'),
created: DateTime.parse(e.getAttribute('created')!),
changed: DateTime.parse(e.getAttribute('changed')!),
public: bool.tryParse(e.getAttribute('public') ?? ''),
owner: e.getAttribute('owner'),
);