mirror of
https://github.com/austinried/subtracks.git
synced 2025-12-27 00:59:28 +01:00
artist list with images
This commit is contained in:
parent
ee2a276f2f
commit
d18ca13f48
@ -10,7 +10,7 @@ import '../state/database.dart';
|
||||
import '../state/settings.dart';
|
||||
import 'list_items.dart';
|
||||
|
||||
const kPageSize = 30;
|
||||
const kPageSize = 60;
|
||||
|
||||
class AlbumsGrid extends HookConsumerWidget {
|
||||
const AlbumsGrid({super.key});
|
||||
|
||||
81
lib/app/lists/artists_list.dart
Normal file
81
lib/app/lists/artists_list.dart
Normal file
@ -0,0 +1,81 @@
|
||||
import 'package:drift/drift.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 '../../sources/models.dart';
|
||||
import '../hooks/use_paging_controller.dart';
|
||||
import '../state/database.dart';
|
||||
import '../state/settings.dart';
|
||||
import 'list_items.dart';
|
||||
|
||||
const kPageSize = 30;
|
||||
|
||||
typedef _ArtistItem = ({Artist artist, int? albumCount});
|
||||
|
||||
class ArtistsList extends HookConsumerWidget {
|
||||
const ArtistsList({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final db = ref.watch(databaseProvider);
|
||||
final sourceId = ref.watch(sourceIdProvider);
|
||||
|
||||
final controller = usePagingController<int, _ArtistItem>(
|
||||
getNextPageKey: (state) =>
|
||||
state.lastPageIsEmpty ? null : state.nextIntPageKey,
|
||||
fetchPage: (pageKey) async {
|
||||
final albumCount = db.albums.id.count();
|
||||
|
||||
final query =
|
||||
db.artists.select().join([
|
||||
leftOuterJoin(
|
||||
db.albums,
|
||||
db.albums.artistId.equalsExp(db.artists.id),
|
||||
),
|
||||
])
|
||||
..addColumns([albumCount])
|
||||
..where(
|
||||
db.artists.sourceId.equals(sourceId) &
|
||||
db.albums.sourceId.equals(sourceId),
|
||||
)
|
||||
..groupBy([db.artists.sourceId, db.artists.id])
|
||||
..orderBy([OrderingTerm.asc(db.artists.name)])
|
||||
..limit(kPageSize, offset: (pageKey - 1) * kPageSize);
|
||||
|
||||
return (await query.get())
|
||||
.map(
|
||||
(row) => (
|
||||
artist: row.readTable(db.artists),
|
||||
albumCount: row.read(albumCount),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
},
|
||||
);
|
||||
|
||||
return PagingListener(
|
||||
controller: controller,
|
||||
builder: (context, state, fetchNextPage) {
|
||||
return PagedSliverList(
|
||||
state: state,
|
||||
fetchNextPage: fetchNextPage,
|
||||
builderDelegate: PagedChildBuilderDelegate<_ArtistItem>(
|
||||
itemBuilder: (context, item, index) {
|
||||
final (:artist, :albumCount) = item;
|
||||
|
||||
return ArtistListTile(
|
||||
artist: artist,
|
||||
albumCount: albumCount,
|
||||
onTap: () async {
|
||||
context.push('/artist/${artist.id}');
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,3 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
|
||||
@ -33,19 +32,28 @@ class AlbumGridTile extends HookConsumerWidget {
|
||||
}
|
||||
|
||||
class ArtistListTile extends StatelessWidget {
|
||||
const ArtistListTile({super.key});
|
||||
const ArtistListTile({
|
||||
super.key,
|
||||
required this.artist,
|
||||
this.albumCount,
|
||||
this.onTap,
|
||||
});
|
||||
|
||||
final Artist artist;
|
||||
final int? albumCount;
|
||||
final void Function()? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
leading: CircleClip(
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: 'https://placehold.net/400x400.png',
|
||||
placeholder: (context, url) => CircularProgressIndicator(),
|
||||
errorWidget: (context, url, error) => Icon(Icons.error),
|
||||
child: artist.coverArt != null
|
||||
? CoverArtImage(coverArt: artist.coverArt)
|
||||
: CachedImage(artist.smallImage),
|
||||
),
|
||||
),
|
||||
title: Text('Some Artist'),
|
||||
title: Text(artist.name),
|
||||
subtitle: albumCount != null ? Text('$albumCount albums') : null,
|
||||
onTap: onTap,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
|
||||
import '../lists/albums_grid.dart';
|
||||
import '../lists/artists_list.dart';
|
||||
import '../state/services.dart';
|
||||
import '../util/custom_scroll_fix.dart';
|
||||
|
||||
@ -210,7 +210,7 @@ class _NewWidgetState extends State<NewWidget>
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
sliver: AlbumsGrid(),
|
||||
sliver: ArtistsList(),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
@ -437,6 +437,9 @@ extension ArtistToDb on models.Artist {
|
||||
id: id,
|
||||
name: name,
|
||||
starred: Value(starred),
|
||||
coverArt: Value(coverArt),
|
||||
smallImage: Value(smallImage),
|
||||
largeImage: Value(largeImage),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -708,8 +708,45 @@ class Artists extends Table with TableInfo<Artists, models.Artist> {
|
||||
requiredDuringInsert: false,
|
||||
$customConstraints: '',
|
||||
);
|
||||
static const VerificationMeta _coverArtMeta = const VerificationMeta(
|
||||
'coverArt',
|
||||
);
|
||||
late final GeneratedColumn<String> coverArt = GeneratedColumn<String>(
|
||||
'cover_art',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: false,
|
||||
$customConstraints: '',
|
||||
);
|
||||
late final GeneratedColumnWithTypeConverter<Uri?, String> smallImage =
|
||||
GeneratedColumn<String>(
|
||||
'small_image',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: false,
|
||||
$customConstraints: '',
|
||||
).withConverter<Uri?>(Artists.$convertersmallImagen);
|
||||
late final GeneratedColumnWithTypeConverter<Uri?, String> largeImage =
|
||||
GeneratedColumn<String>(
|
||||
'large_image',
|
||||
aliasedName,
|
||||
true,
|
||||
type: DriftSqlType.string,
|
||||
requiredDuringInsert: false,
|
||||
$customConstraints: '',
|
||||
).withConverter<Uri?>(Artists.$converterlargeImagen);
|
||||
@override
|
||||
List<GeneratedColumn> get $columns => [sourceId, id, name, starred];
|
||||
List<GeneratedColumn> get $columns => [
|
||||
sourceId,
|
||||
id,
|
||||
name,
|
||||
starred,
|
||||
coverArt,
|
||||
smallImage,
|
||||
largeImage,
|
||||
];
|
||||
@override
|
||||
String get aliasedName => _alias ?? actualTableName;
|
||||
@override
|
||||
@ -749,6 +786,12 @@ class Artists extends Table with TableInfo<Artists, models.Artist> {
|
||||
starred.isAcceptableOrUnknown(data['starred']!, _starredMeta),
|
||||
);
|
||||
}
|
||||
if (data.containsKey('cover_art')) {
|
||||
context.handle(
|
||||
_coverArtMeta,
|
||||
coverArt.isAcceptableOrUnknown(data['cover_art']!, _coverArtMeta),
|
||||
);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
@ -770,6 +813,22 @@ class Artists extends Table with TableInfo<Artists, models.Artist> {
|
||||
DriftSqlType.dateTime,
|
||||
data['${effectivePrefix}starred'],
|
||||
),
|
||||
coverArt: attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}cover_art'],
|
||||
),
|
||||
smallImage: Artists.$convertersmallImagen.fromSql(
|
||||
attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}small_image'],
|
||||
),
|
||||
),
|
||||
largeImage: Artists.$converterlargeImagen.fromSql(
|
||||
attachedDatabase.typeMapping.read(
|
||||
DriftSqlType.string,
|
||||
data['${effectivePrefix}large_image'],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -778,6 +837,12 @@ class Artists extends Table with TableInfo<Artists, models.Artist> {
|
||||
return Artists(attachedDatabase, alias);
|
||||
}
|
||||
|
||||
static TypeConverter<Uri, String> $convertersmallImage = const UriConverter();
|
||||
static TypeConverter<Uri?, String?> $convertersmallImagen =
|
||||
NullAwareTypeConverter.wrap($convertersmallImage);
|
||||
static TypeConverter<Uri, String> $converterlargeImage = const UriConverter();
|
||||
static TypeConverter<Uri?, String?> $converterlargeImagen =
|
||||
NullAwareTypeConverter.wrap($converterlargeImage);
|
||||
@override
|
||||
List<String> get customConstraints => const [
|
||||
'PRIMARY KEY(source_id, id)',
|
||||
@ -792,12 +857,18 @@ class ArtistsCompanion extends UpdateCompanion<models.Artist> {
|
||||
final Value<String> id;
|
||||
final Value<String> name;
|
||||
final Value<DateTime?> starred;
|
||||
final Value<String?> coverArt;
|
||||
final Value<Uri?> smallImage;
|
||||
final Value<Uri?> largeImage;
|
||||
final Value<int> rowid;
|
||||
const ArtistsCompanion({
|
||||
this.sourceId = const Value.absent(),
|
||||
this.id = const Value.absent(),
|
||||
this.name = const Value.absent(),
|
||||
this.starred = const Value.absent(),
|
||||
this.coverArt = const Value.absent(),
|
||||
this.smallImage = const Value.absent(),
|
||||
this.largeImage = const Value.absent(),
|
||||
this.rowid = const Value.absent(),
|
||||
});
|
||||
ArtistsCompanion.insert({
|
||||
@ -805,6 +876,9 @@ class ArtistsCompanion extends UpdateCompanion<models.Artist> {
|
||||
required String id,
|
||||
required String name,
|
||||
this.starred = const Value.absent(),
|
||||
this.coverArt = const Value.absent(),
|
||||
this.smallImage = const Value.absent(),
|
||||
this.largeImage = const Value.absent(),
|
||||
this.rowid = const Value.absent(),
|
||||
}) : sourceId = Value(sourceId),
|
||||
id = Value(id),
|
||||
@ -814,6 +888,9 @@ class ArtistsCompanion extends UpdateCompanion<models.Artist> {
|
||||
Expression<String>? id,
|
||||
Expression<String>? name,
|
||||
Expression<DateTime>? starred,
|
||||
Expression<String>? coverArt,
|
||||
Expression<String>? smallImage,
|
||||
Expression<String>? largeImage,
|
||||
Expression<int>? rowid,
|
||||
}) {
|
||||
return RawValuesInsertable({
|
||||
@ -821,6 +898,9 @@ class ArtistsCompanion extends UpdateCompanion<models.Artist> {
|
||||
if (id != null) 'id': id,
|
||||
if (name != null) 'name': name,
|
||||
if (starred != null) 'starred': starred,
|
||||
if (coverArt != null) 'cover_art': coverArt,
|
||||
if (smallImage != null) 'small_image': smallImage,
|
||||
if (largeImage != null) 'large_image': largeImage,
|
||||
if (rowid != null) 'rowid': rowid,
|
||||
});
|
||||
}
|
||||
@ -830,6 +910,9 @@ class ArtistsCompanion extends UpdateCompanion<models.Artist> {
|
||||
Value<String>? id,
|
||||
Value<String>? name,
|
||||
Value<DateTime?>? starred,
|
||||
Value<String?>? coverArt,
|
||||
Value<Uri?>? smallImage,
|
||||
Value<Uri?>? largeImage,
|
||||
Value<int>? rowid,
|
||||
}) {
|
||||
return ArtistsCompanion(
|
||||
@ -837,6 +920,9 @@ class ArtistsCompanion extends UpdateCompanion<models.Artist> {
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
starred: starred ?? this.starred,
|
||||
coverArt: coverArt ?? this.coverArt,
|
||||
smallImage: smallImage ?? this.smallImage,
|
||||
largeImage: largeImage ?? this.largeImage,
|
||||
rowid: rowid ?? this.rowid,
|
||||
);
|
||||
}
|
||||
@ -856,6 +942,19 @@ class ArtistsCompanion extends UpdateCompanion<models.Artist> {
|
||||
if (starred.present) {
|
||||
map['starred'] = Variable<DateTime>(starred.value);
|
||||
}
|
||||
if (coverArt.present) {
|
||||
map['cover_art'] = Variable<String>(coverArt.value);
|
||||
}
|
||||
if (smallImage.present) {
|
||||
map['small_image'] = Variable<String>(
|
||||
Artists.$convertersmallImagen.toSql(smallImage.value),
|
||||
);
|
||||
}
|
||||
if (largeImage.present) {
|
||||
map['large_image'] = Variable<String>(
|
||||
Artists.$converterlargeImagen.toSql(largeImage.value),
|
||||
);
|
||||
}
|
||||
if (rowid.present) {
|
||||
map['rowid'] = Variable<int>(rowid.value);
|
||||
}
|
||||
@ -869,6 +968,9 @@ class ArtistsCompanion extends UpdateCompanion<models.Artist> {
|
||||
..write('id: $id, ')
|
||||
..write('name: $name, ')
|
||||
..write('starred: $starred, ')
|
||||
..write('coverArt: $coverArt, ')
|
||||
..write('smallImage: $smallImage, ')
|
||||
..write('largeImage: $largeImage, ')
|
||||
..write('rowid: $rowid')
|
||||
..write(')'))
|
||||
.toString();
|
||||
@ -2864,6 +2966,9 @@ typedef $ArtistsCreateCompanionBuilder =
|
||||
required String id,
|
||||
required String name,
|
||||
Value<DateTime?> starred,
|
||||
Value<String?> coverArt,
|
||||
Value<Uri?> smallImage,
|
||||
Value<Uri?> largeImage,
|
||||
Value<int> rowid,
|
||||
});
|
||||
typedef $ArtistsUpdateCompanionBuilder =
|
||||
@ -2872,6 +2977,9 @@ typedef $ArtistsUpdateCompanionBuilder =
|
||||
Value<String> id,
|
||||
Value<String> name,
|
||||
Value<DateTime?> starred,
|
||||
Value<String?> coverArt,
|
||||
Value<Uri?> smallImage,
|
||||
Value<Uri?> largeImage,
|
||||
Value<int> rowid,
|
||||
});
|
||||
|
||||
@ -2902,6 +3010,23 @@ class $ArtistsFilterComposer extends Composer<_$SubtracksDatabase, Artists> {
|
||||
column: $table.starred,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnFilters<String> get coverArt => $composableBuilder(
|
||||
column: $table.coverArt,
|
||||
builder: (column) => ColumnFilters(column),
|
||||
);
|
||||
|
||||
ColumnWithTypeConverterFilters<Uri?, Uri, String> get smallImage =>
|
||||
$composableBuilder(
|
||||
column: $table.smallImage,
|
||||
builder: (column) => ColumnWithTypeConverterFilters(column),
|
||||
);
|
||||
|
||||
ColumnWithTypeConverterFilters<Uri?, Uri, String> get largeImage =>
|
||||
$composableBuilder(
|
||||
column: $table.largeImage,
|
||||
builder: (column) => ColumnWithTypeConverterFilters(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $ArtistsOrderingComposer extends Composer<_$SubtracksDatabase, Artists> {
|
||||
@ -2931,6 +3056,21 @@ class $ArtistsOrderingComposer extends Composer<_$SubtracksDatabase, Artists> {
|
||||
column: $table.starred,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<String> get coverArt => $composableBuilder(
|
||||
column: $table.coverArt,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<String> get smallImage => $composableBuilder(
|
||||
column: $table.smallImage,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
|
||||
ColumnOrderings<String> get largeImage => $composableBuilder(
|
||||
column: $table.largeImage,
|
||||
builder: (column) => ColumnOrderings(column),
|
||||
);
|
||||
}
|
||||
|
||||
class $ArtistsAnnotationComposer
|
||||
@ -2953,6 +3093,21 @@ class $ArtistsAnnotationComposer
|
||||
|
||||
GeneratedColumn<DateTime> get starred =>
|
||||
$composableBuilder(column: $table.starred, builder: (column) => column);
|
||||
|
||||
GeneratedColumn<String> get coverArt =>
|
||||
$composableBuilder(column: $table.coverArt, builder: (column) => column);
|
||||
|
||||
GeneratedColumnWithTypeConverter<Uri?, String> get smallImage =>
|
||||
$composableBuilder(
|
||||
column: $table.smallImage,
|
||||
builder: (column) => column,
|
||||
);
|
||||
|
||||
GeneratedColumnWithTypeConverter<Uri?, String> get largeImage =>
|
||||
$composableBuilder(
|
||||
column: $table.largeImage,
|
||||
builder: (column) => column,
|
||||
);
|
||||
}
|
||||
|
||||
class $ArtistsTableManager
|
||||
@ -2990,12 +3145,18 @@ class $ArtistsTableManager
|
||||
Value<String> id = const Value.absent(),
|
||||
Value<String> name = const Value.absent(),
|
||||
Value<DateTime?> starred = const Value.absent(),
|
||||
Value<String?> coverArt = const Value.absent(),
|
||||
Value<Uri?> smallImage = const Value.absent(),
|
||||
Value<Uri?> largeImage = const Value.absent(),
|
||||
Value<int> rowid = const Value.absent(),
|
||||
}) => ArtistsCompanion(
|
||||
sourceId: sourceId,
|
||||
id: id,
|
||||
name: name,
|
||||
starred: starred,
|
||||
coverArt: coverArt,
|
||||
smallImage: smallImage,
|
||||
largeImage: largeImage,
|
||||
rowid: rowid,
|
||||
),
|
||||
createCompanionCallback:
|
||||
@ -3004,12 +3165,18 @@ class $ArtistsTableManager
|
||||
required String id,
|
||||
required String name,
|
||||
Value<DateTime?> starred = const Value.absent(),
|
||||
Value<String?> coverArt = const Value.absent(),
|
||||
Value<Uri?> smallImage = const Value.absent(),
|
||||
Value<Uri?> largeImage = const Value.absent(),
|
||||
Value<int> rowid = const Value.absent(),
|
||||
}) => ArtistsCompanion.insert(
|
||||
sourceId: sourceId,
|
||||
id: id,
|
||||
name: name,
|
||||
starred: starred,
|
||||
coverArt: coverArt,
|
||||
smallImage: smallImage,
|
||||
largeImage: largeImage,
|
||||
rowid: rowid,
|
||||
),
|
||||
withReferenceMapper: (p0) => p0
|
||||
|
||||
@ -65,6 +65,9 @@ CREATE TABLE artists(
|
||||
id TEXT NOT NULL,
|
||||
name TEXT NOT NULL COLLATE NOCASE,
|
||||
starred DATETIME,
|
||||
cover_art TEXT,
|
||||
small_image TEXT MAPPED BY `const UriConverter()`,
|
||||
large_image TEXT MAPPED BY `const UriConverter()`,
|
||||
PRIMARY KEY (source_id, id),
|
||||
FOREIGN KEY (source_id) REFERENCES sources (id) ON DELETE CASCADE
|
||||
) WITH Artist;
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:octo_image/octo_image.dart';
|
||||
|
||||
import '../app/state/settings.dart';
|
||||
import '../app/state/source.dart';
|
||||
|
||||
class CoverArtImage extends HookConsumerWidget {
|
||||
@ -20,13 +21,15 @@ class CoverArtImage extends HookConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final source = ref.watch(sourceProvider);
|
||||
final sourceId = ref.watch(sourceIdProvider);
|
||||
|
||||
final imageProviderKeys = [source, coverArt, thumbnail];
|
||||
final imageProviderKeys = [source, sourceId, coverArt, thumbnail];
|
||||
final buildImageProvider = useCallback(
|
||||
() => CachedNetworkImageProvider(
|
||||
coverArt != null
|
||||
? source.coverArtUri(coverArt!, thumbnail: thumbnail).toString()
|
||||
: 'https://placehold.net/400x400.png',
|
||||
cacheKey: '$sourceId$coverArt$thumbnail',
|
||||
),
|
||||
imageProviderKeys,
|
||||
);
|
||||
@ -40,11 +43,63 @@ class CoverArtImage extends HookConsumerWidget {
|
||||
imageProviderKeys,
|
||||
);
|
||||
|
||||
return OctoImage(
|
||||
return BaseImage(
|
||||
image: imageProvider.value,
|
||||
placeholderBuilder: (context) => Icon(Symbols.album_rounded),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CachedImage extends HookConsumerWidget {
|
||||
const CachedImage(
|
||||
this.uri, {
|
||||
super.key,
|
||||
});
|
||||
|
||||
final Uri? uri;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final imageProviderKeys = [uri];
|
||||
final buildImageProvider = useCallback(
|
||||
() {
|
||||
final imageUrl = uri?.toString() ?? 'https://placehold.net/400x400.png';
|
||||
return CachedNetworkImageProvider(imageUrl, cacheKey: imageUrl);
|
||||
},
|
||||
imageProviderKeys,
|
||||
);
|
||||
|
||||
final imageProvider = useState(buildImageProvider());
|
||||
useEffect(
|
||||
() {
|
||||
imageProvider.value = buildImageProvider();
|
||||
return;
|
||||
},
|
||||
imageProviderKeys,
|
||||
);
|
||||
|
||||
return BaseImage(
|
||||
image: imageProvider.value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BaseImage extends HookConsumerWidget {
|
||||
const BaseImage({
|
||||
super.key,
|
||||
required this.image,
|
||||
this.fit = BoxFit.cover,
|
||||
});
|
||||
|
||||
final ImageProvider image;
|
||||
final BoxFit fit;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return OctoImage(
|
||||
image: image,
|
||||
placeholderBuilder: (context) => Icon(Symbols.cached_rounded),
|
||||
errorBuilder: (context, error, trace) => Icon(Icons.error),
|
||||
fit: BoxFit.cover,
|
||||
fit: fit,
|
||||
fadeOutDuration: Duration(milliseconds: 100),
|
||||
fadeInDuration: Duration(milliseconds: 200),
|
||||
);
|
||||
|
||||
@ -22,9 +22,12 @@ void main() async {
|
||||
.insertOnConflictUpdate(
|
||||
SubsonicSettingsCompanion.insert(
|
||||
sourceId: Value(1),
|
||||
address: Uri.parse('http://10.0.2.2:4533'),
|
||||
username: 'admin',
|
||||
password: 'password',
|
||||
address: Uri.parse('http://demo.subsonic.org'),
|
||||
username: 'guest1',
|
||||
password: 'guest',
|
||||
// address: Uri.parse('http://10.0.2.2:4533'),
|
||||
// username: 'admin',
|
||||
// password: 'password',
|
||||
useTokenAuth: Value(true),
|
||||
),
|
||||
);
|
||||
|
||||
@ -8,6 +8,7 @@ abstract class Artist with _$Artist {
|
||||
required String id,
|
||||
required String name,
|
||||
DateTime? starred,
|
||||
String? coverArt,
|
||||
Uri? smallImage,
|
||||
Uri? largeImage,
|
||||
}) = _Artist;
|
||||
|
||||
@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$Artist {
|
||||
|
||||
String get id; String get name; DateTime? get starred; Uri? get smallImage; Uri? get largeImage;
|
||||
String get id; String get name; DateTime? get starred; String? get coverArt; Uri? get smallImage; Uri? get largeImage;
|
||||
/// Create a copy of Artist
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@ -25,16 +25,16 @@ $ArtistCopyWith<Artist> get copyWith => _$ArtistCopyWithImpl<Artist>(this as Art
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is Artist&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.starred, starred) || other.starred == starred)&&(identical(other.smallImage, smallImage) || other.smallImage == smallImage)&&(identical(other.largeImage, largeImage) || other.largeImage == largeImage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is Artist&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.starred, starred) || other.starred == starred)&&(identical(other.coverArt, coverArt) || other.coverArt == coverArt)&&(identical(other.smallImage, smallImage) || other.smallImage == smallImage)&&(identical(other.largeImage, largeImage) || other.largeImage == largeImage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,name,starred,smallImage,largeImage);
|
||||
int get hashCode => Object.hash(runtimeType,id,name,starred,coverArt,smallImage,largeImage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Artist(id: $id, name: $name, starred: $starred, smallImage: $smallImage, largeImage: $largeImage)';
|
||||
return 'Artist(id: $id, name: $name, starred: $starred, coverArt: $coverArt, smallImage: $smallImage, largeImage: $largeImage)';
|
||||
}
|
||||
|
||||
|
||||
@ -45,7 +45,7 @@ abstract mixin class $ArtistCopyWith<$Res> {
|
||||
factory $ArtistCopyWith(Artist value, $Res Function(Artist) _then) = _$ArtistCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String id, String name, DateTime? starred, Uri? smallImage, Uri? largeImage
|
||||
String id, String name, DateTime? starred, String? coverArt, Uri? smallImage, Uri? largeImage
|
||||
});
|
||||
|
||||
|
||||
@ -62,12 +62,13 @@ class _$ArtistCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of Artist
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? starred = freezed,Object? smallImage = freezed,Object? largeImage = freezed,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? name = null,Object? starred = freezed,Object? coverArt = freezed,Object? smallImage = freezed,Object? largeImage = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||
as String,starred: freezed == starred ? _self.starred : starred // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,smallImage: freezed == smallImage ? _self.smallImage : smallImage // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,coverArt: freezed == coverArt ? _self.coverArt : coverArt // ignore: cast_nullable_to_non_nullable
|
||||
as String?,smallImage: freezed == smallImage ? _self.smallImage : smallImage // ignore: cast_nullable_to_non_nullable
|
||||
as Uri?,largeImage: freezed == largeImage ? _self.largeImage : largeImage // ignore: cast_nullable_to_non_nullable
|
||||
as Uri?,
|
||||
));
|
||||
@ -154,10 +155,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String name, DateTime? starred, Uri? smallImage, Uri? largeImage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id, String name, DateTime? starred, String? coverArt, Uri? smallImage, Uri? largeImage)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Artist() when $default != null:
|
||||
return $default(_that.id,_that.name,_that.starred,_that.smallImage,_that.largeImage);case _:
|
||||
return $default(_that.id,_that.name,_that.starred,_that.coverArt,_that.smallImage,_that.largeImage);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@ -175,10 +176,10 @@ return $default(_that.id,_that.name,_that.starred,_that.smallImage,_that.largeIm
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String name, DateTime? starred, Uri? smallImage, Uri? largeImage) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id, String name, DateTime? starred, String? coverArt, Uri? smallImage, Uri? largeImage) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Artist():
|
||||
return $default(_that.id,_that.name,_that.starred,_that.smallImage,_that.largeImage);case _:
|
||||
return $default(_that.id,_that.name,_that.starred,_that.coverArt,_that.smallImage,_that.largeImage);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@ -195,10 +196,10 @@ return $default(_that.id,_that.name,_that.starred,_that.smallImage,_that.largeIm
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String name, DateTime? starred, Uri? smallImage, Uri? largeImage)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id, String name, DateTime? starred, String? coverArt, Uri? smallImage, Uri? largeImage)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _Artist() when $default != null:
|
||||
return $default(_that.id,_that.name,_that.starred,_that.smallImage,_that.largeImage);case _:
|
||||
return $default(_that.id,_that.name,_that.starred,_that.coverArt,_that.smallImage,_that.largeImage);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@ -210,12 +211,13 @@ return $default(_that.id,_that.name,_that.starred,_that.smallImage,_that.largeIm
|
||||
|
||||
|
||||
class _Artist implements Artist {
|
||||
const _Artist({required this.id, required this.name, this.starred, this.smallImage, this.largeImage});
|
||||
const _Artist({required this.id, required this.name, this.starred, this.coverArt, this.smallImage, this.largeImage});
|
||||
|
||||
|
||||
@override final String id;
|
||||
@override final String name;
|
||||
@override final DateTime? starred;
|
||||
@override final String? coverArt;
|
||||
@override final Uri? smallImage;
|
||||
@override final Uri? largeImage;
|
||||
|
||||
@ -229,16 +231,16 @@ _$ArtistCopyWith<_Artist> get copyWith => __$ArtistCopyWithImpl<_Artist>(this, _
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Artist&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.starred, starred) || other.starred == starred)&&(identical(other.smallImage, smallImage) || other.smallImage == smallImage)&&(identical(other.largeImage, largeImage) || other.largeImage == largeImage));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Artist&&(identical(other.id, id) || other.id == id)&&(identical(other.name, name) || other.name == name)&&(identical(other.starred, starred) || other.starred == starred)&&(identical(other.coverArt, coverArt) || other.coverArt == coverArt)&&(identical(other.smallImage, smallImage) || other.smallImage == smallImage)&&(identical(other.largeImage, largeImage) || other.largeImage == largeImage));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,id,name,starred,smallImage,largeImage);
|
||||
int get hashCode => Object.hash(runtimeType,id,name,starred,coverArt,smallImage,largeImage);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'Artist(id: $id, name: $name, starred: $starred, smallImage: $smallImage, largeImage: $largeImage)';
|
||||
return 'Artist(id: $id, name: $name, starred: $starred, coverArt: $coverArt, smallImage: $smallImage, largeImage: $largeImage)';
|
||||
}
|
||||
|
||||
|
||||
@ -249,7 +251,7 @@ abstract mixin class _$ArtistCopyWith<$Res> implements $ArtistCopyWith<$Res> {
|
||||
factory _$ArtistCopyWith(_Artist value, $Res Function(_Artist) _then) = __$ArtistCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String id, String name, DateTime? starred, Uri? smallImage, Uri? largeImage
|
||||
String id, String name, DateTime? starred, String? coverArt, Uri? smallImage, Uri? largeImage
|
||||
});
|
||||
|
||||
|
||||
@ -266,12 +268,13 @@ class __$ArtistCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of Artist
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? starred = freezed,Object? smallImage = freezed,Object? largeImage = freezed,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? name = null,Object? starred = freezed,Object? coverArt = freezed,Object? smallImage = freezed,Object? largeImage = freezed,}) {
|
||||
return _then(_Artist(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||
as String,starred: freezed == starred ? _self.starred : starred // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,smallImage: freezed == smallImage ? _self.smallImage : smallImage // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime?,coverArt: freezed == coverArt ? _self.coverArt : coverArt // ignore: cast_nullable_to_non_nullable
|
||||
as String?,smallImage: freezed == smallImage ? _self.smallImage : smallImage // ignore: cast_nullable_to_non_nullable
|
||||
as Uri?,largeImage: freezed == largeImage ? _self.largeImage : largeImage // ignore: cast_nullable_to_non_nullable
|
||||
as Uri?,
|
||||
));
|
||||
|
||||
@ -2,12 +2,21 @@ import 'package:xml/xml.dart';
|
||||
|
||||
import '../models.dart';
|
||||
|
||||
Uri? uriOrNullParse(String? value) {
|
||||
if (value == null || value.trim().isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Uri.tryParse(value);
|
||||
}
|
||||
|
||||
Artist mapArtist(XmlElement e, XmlElement? info) => Artist(
|
||||
id: e.getAttribute('id')!,
|
||||
name: e.getAttribute('name')!,
|
||||
starred: DateTime.tryParse(e.getAttribute('starred').toString()),
|
||||
smallImage: Uri.tryParse(info?.getElement('smallImageUrl')?.innerText ?? ''),
|
||||
largeImage: Uri.tryParse(info?.getElement('largeImageUrl')?.innerText ?? ''),
|
||||
coverArt: e.getAttribute('coverArt'),
|
||||
smallImage: uriOrNullParse(info?.getElement('smallImageUrl')?.innerText),
|
||||
largeImage: uriOrNullParse(info?.getElement('largeImageUrl')?.innerText),
|
||||
);
|
||||
|
||||
Album mapAlbum(
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user