diff --git a/lib/presentation/pages/home_page.dart b/lib/presentation/pages/home_page.dart index 918c280..8e31514 100644 --- a/lib/presentation/pages/home_page.dart +++ b/lib/presentation/pages/home_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../theming.dart'; import '../widgets/highlight.dart'; +import '../widgets/highlight_artist.dart'; import '../widgets/shuffle_all_button.dart'; import '../widgets/smart_lists.dart'; @@ -35,6 +36,11 @@ class _HomePageState extends State { padding: EdgeInsets.symmetric(horizontal: HORIZONTAL_PADDING), child: Highlight(), ), + const SizedBox(height: 12.0), + const Padding( + padding: EdgeInsets.symmetric(horizontal: HORIZONTAL_PADDING), + child: HighlightArtist(), + ), const Padding( padding: EdgeInsets.only( left: HORIZONTAL_PADDING, diff --git a/lib/presentation/widgets/highlight_artist.dart b/lib/presentation/widgets/highlight_artist.dart index c295a3a..4ed5661 100644 --- a/lib/presentation/widgets/highlight_artist.dart +++ b/lib/presentation/widgets/highlight_artist.dart @@ -3,11 +3,15 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:get_it/get_it.dart'; import '../../domain/entities/artist.dart'; +import '../../domain/entities/shuffle_mode.dart'; +import '../gradients.dart'; import '../pages/artist_details_page.dart'; import '../state/audio_store.dart'; import '../state/music_data_store.dart'; import '../state/navigation_store.dart'; import '../theming.dart'; +import 'play_shuffle_button.dart'; +import 'playlist_cover.dart'; class HighlightArtist extends StatelessWidget { const HighlightArtist({Key? key}) : super(key: key); @@ -50,24 +54,11 @@ class HighlightArtist extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.all(8.0), - child: SizedBox( - width: 100.0, - child: AspectRatio( - aspectRatio: 1, - child: Container( - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(2.0), - boxShadow: const [ - BoxShadow(color: Colors.black54, blurRadius: 4, offset: Offset(0, 0), spreadRadius: -1), - ], - ), - // child: Image( - // image: getAlbumImage(artist.albumArtPath), - // fit: BoxFit.cover, - // ), - ), - ), + child: PlaylistCover( + gradient: CUSTOM_GRADIENTS['kashmir']!, + icon: Icons.person_rounded, + size: 100.0, + circle: true, ), ), Expanded( @@ -93,15 +84,12 @@ class HighlightArtist extends StatelessWidget { ), ), ), - IconButton( - icon: const Icon( - Icons.play_circle_fill_rounded, - size: 48.0, - ), - iconSize: 48.0, - onPressed: () => {}, //audioStore.playArtist(artist), - splashRadius: 28.0, + PlayShuffleButton( + onPressed: () => audioStore.shuffleArtist(artist), + size: 56.0, + shuffleMode: ShuffleMode.plus, ), + const SizedBox(width: 4.0), ], ), ), diff --git a/lib/presentation/widgets/play_shuffle_button.dart b/lib/presentation/widgets/play_shuffle_button.dart index 0abe452..0300694 100644 --- a/lib/presentation/widgets/play_shuffle_button.dart +++ b/lib/presentation/widgets/play_shuffle_button.dart @@ -12,6 +12,7 @@ class PlayShuffleButton extends StatelessWidget { this.shuffleMode, }) : super(key: key); + /// Main icon size will be reduced by 8. final double size; final ShuffleMode? shuffleMode; final Function onPressed; diff --git a/lib/presentation/widgets/playlist_cover.dart b/lib/presentation/widgets/playlist_cover.dart index 884c546..b8df4ee 100644 --- a/lib/presentation/widgets/playlist_cover.dart +++ b/lib/presentation/widgets/playlist_cover.dart @@ -7,15 +7,33 @@ class PlaylistCover extends StatelessWidget { required this.gradient, required this.icon, this.shadows = const [], + this.circle = false, }) : super(key: key); final double size; final Gradient gradient; final IconData icon; final List shadows; + final bool circle; @override Widget build(BuildContext context) { + BoxDecoration deco; + + if (circle) { + deco = BoxDecoration( + gradient: gradient, + shape: BoxShape.circle, + boxShadow: shadows, + ); + } else { + deco = BoxDecoration( + gradient: gradient, + borderRadius: const BorderRadius.all(Radius.circular(8.0)), + boxShadow: shadows, + ); + } + return SizedBox( width: size, height: size, @@ -27,11 +45,7 @@ class PlaylistCover extends StatelessWidget { size: size / 2.0, ), ), - decoration: BoxDecoration( - gradient: gradient, - borderRadius: const BorderRadius.all(Radius.circular(8.0)), - boxShadow: shadows, - ), + decoration: deco, ), ); } diff --git a/lib/system/datasources/moor/music_data_dao.dart b/lib/system/datasources/moor/music_data_dao.dart index f5955d9..885aa33 100644 --- a/lib/system/datasources/moor/music_data_dao.dart +++ b/lib/system/datasources/moor/music_data_dao.dart @@ -13,7 +13,7 @@ import '../music_data_source_contract.dart'; part 'music_data_dao.g.dart'; @DriftAccessor( - tables: [Albums, Artists, Songs, MoorAlbumOfDay, Playlists, PlaylistEntries, KeyValueEntries]) + tables: [Albums, Artists, Songs, Playlists, PlaylistEntries, KeyValueEntries]) class MusicDataDao extends DatabaseAccessor with _$MusicDataDaoMixin implements MusicDataSource { @@ -189,33 +189,36 @@ class MusicDataDao extends DatabaseAccessor @override Future getAlbumOfDay() async { - final query = select(moorAlbumOfDay) - .join([innerJoin(albums, albums.id.equalsExp(moorAlbumOfDay.albumId))]); + final value = await (select(keyValueEntries)..where((tbl) => tbl.key.equals(ALBUM_OF_DAY))) + .getSingleOrNull() + .then((entry) => entry?.value); - return (query..limit(1)).getSingleOrNull().then( - (result) { - if (result == null) return null; - return AlbumOfDay( - AlbumModel.fromMoor(result.readTable(albums)), - DateTime.fromMillisecondsSinceEpoch( - result.readTable(moorAlbumOfDay).milliSecSinceEpoch, - ), - ); - }, - ); + if (value == null) { + return null; + } + + final dict = jsonDecode(value) as Map; + + final int id = dict['id'] as int; + final int millisecondsSinceEpoch = dict['date'] as int; + + final AlbumModel? album = await (select(albums)..where((tbl) => tbl.id.equals(id))) + .getSingleOrNull() + .then((value) => value == null ? null : AlbumModel.fromMoor(value)); + + if (album == null) return null; + + return AlbumOfDay(album, DateTime.fromMillisecondsSinceEpoch(millisecondsSinceEpoch)); } @override Future setAlbumOfDay(AlbumOfDay albumOfDay) async { - transaction(() async { - await delete(moorAlbumOfDay).go(); - await into(moorAlbumOfDay).insert( - MoorAlbumOfDayCompanion( - albumId: Value(albumOfDay.albumModel.id), - milliSecSinceEpoch: Value(albumOfDay.date.millisecondsSinceEpoch), - ), - ); - }); + await into(keyValueEntries).insertOnConflictUpdate( + KeyValueEntriesCompanion( + key: const Value(ALBUM_OF_DAY), + value: Value(albumOfDay.toJSON()), + ), + ); } @override @@ -228,11 +231,12 @@ class MusicDataDao extends DatabaseAccessor return null; } - final dict = jsonDecode(value); - final String name = dict['name'] as String; + final dict = jsonDecode(value) as Map; + + final int id = dict['id'] as int; final int millisecondsSinceEpoch = dict['date'] as int; - final ArtistModel? artist = await (select(artists)..where((tbl) => tbl.name.equals(name))) + final ArtistModel? artist = await (select(artists)..where((tbl) => tbl.id.equals(id))) .getSingleOrNull() .then((value) => value == null ? null : ArtistModel.fromMoor(value)); @@ -246,7 +250,7 @@ class MusicDataDao extends DatabaseAccessor await into(keyValueEntries).insertOnConflictUpdate( KeyValueEntriesCompanion( key: const Value(ARTIST_OF_DAY), - value: Value(artistOfDay.toString()), + value: Value(artistOfDay.toJSON()), ), ); } diff --git a/lib/system/datasources/moor/music_data_dao.g.dart b/lib/system/datasources/moor/music_data_dao.g.dart index f055976..f709055 100644 --- a/lib/system/datasources/moor/music_data_dao.g.dart +++ b/lib/system/datasources/moor/music_data_dao.g.dart @@ -10,7 +10,6 @@ mixin _$MusicDataDaoMixin on DatabaseAccessor { $AlbumsTable get albums => attachedDatabase.albums; $ArtistsTable get artists => attachedDatabase.artists; $SongsTable get songs => attachedDatabase.songs; - $MoorAlbumOfDayTable get moorAlbumOfDay => attachedDatabase.moorAlbumOfDay; $PlaylistsTable get playlists => attachedDatabase.playlists; $PlaylistEntriesTable get playlistEntries => attachedDatabase.playlistEntries; $KeyValueEntriesTable get keyValueEntries => attachedDatabase.keyValueEntries; diff --git a/lib/system/datasources/moor_database.dart b/lib/system/datasources/moor_database.dart index 60aca0a..c1ad0c4 100644 --- a/lib/system/datasources/moor_database.dart +++ b/lib/system/datasources/moor_database.dart @@ -104,14 +104,6 @@ class LibraryFolders extends Table { TextColumn get path => text()(); } -class MoorAlbumOfDay extends Table { - IntColumn get albumId => integer()(); - IntColumn get milliSecSinceEpoch => integer()(); - - @override - Set get primaryKey => {albumId}; -} - @DataClassName('MoorSmartList') class SmartLists extends Table { IntColumn get id => integer().autoIncrement()(); @@ -168,7 +160,6 @@ class PlaylistEntries extends Table { QueueEntries, AvailableSongEntries, Songs, - MoorAlbumOfDay, SmartLists, SmartListArtists, Playlists, @@ -254,7 +245,6 @@ class MoorDatabase extends _$MoorDatabase { await m.alterTable(TableMigration(playlists)); } if (from < 7) { - await m.addColumn(artists, artists.id); await m.alterTable( TableMigration(artists, columnTransformer: { artists.id: artists.rowId, diff --git a/lib/system/datasources/moor_database.g.dart b/lib/system/datasources/moor_database.g.dart index 8bf301e..1e26646 100644 --- a/lib/system/datasources/moor_database.g.dart +++ b/lib/system/datasources/moor_database.g.dart @@ -2018,186 +2018,6 @@ class $SongsTable extends Songs with TableInfo<$SongsTable, MoorSong> { } } -class MoorAlbumOfDayData extends DataClass - implements Insertable { - final int albumId; - final int milliSecSinceEpoch; - MoorAlbumOfDayData({required this.albumId, required this.milliSecSinceEpoch}); - factory MoorAlbumOfDayData.fromData(Map data, - {String? prefix}) { - final effectivePrefix = prefix ?? ''; - return MoorAlbumOfDayData( - albumId: const IntType() - .mapFromDatabaseResponse(data['${effectivePrefix}album_id'])!, - milliSecSinceEpoch: const IntType().mapFromDatabaseResponse( - data['${effectivePrefix}milli_sec_since_epoch'])!, - ); - } - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - map['album_id'] = Variable(albumId); - map['milli_sec_since_epoch'] = Variable(milliSecSinceEpoch); - return map; - } - - MoorAlbumOfDayCompanion toCompanion(bool nullToAbsent) { - return MoorAlbumOfDayCompanion( - albumId: Value(albumId), - milliSecSinceEpoch: Value(milliSecSinceEpoch), - ); - } - - factory MoorAlbumOfDayData.fromJson(Map json, - {ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return MoorAlbumOfDayData( - albumId: serializer.fromJson(json['albumId']), - milliSecSinceEpoch: serializer.fromJson(json['milliSecSinceEpoch']), - ); - } - @override - Map toJson({ValueSerializer? serializer}) { - serializer ??= driftRuntimeOptions.defaultSerializer; - return { - 'albumId': serializer.toJson(albumId), - 'milliSecSinceEpoch': serializer.toJson(milliSecSinceEpoch), - }; - } - - MoorAlbumOfDayData copyWith({int? albumId, int? milliSecSinceEpoch}) => - MoorAlbumOfDayData( - albumId: albumId ?? this.albumId, - milliSecSinceEpoch: milliSecSinceEpoch ?? this.milliSecSinceEpoch, - ); - @override - String toString() { - return (StringBuffer('MoorAlbumOfDayData(') - ..write('albumId: $albumId, ') - ..write('milliSecSinceEpoch: $milliSecSinceEpoch') - ..write(')')) - .toString(); - } - - @override - int get hashCode => Object.hash(albumId, milliSecSinceEpoch); - @override - bool operator ==(Object other) => - identical(this, other) || - (other is MoorAlbumOfDayData && - other.albumId == this.albumId && - other.milliSecSinceEpoch == this.milliSecSinceEpoch); -} - -class MoorAlbumOfDayCompanion extends UpdateCompanion { - final Value albumId; - final Value milliSecSinceEpoch; - const MoorAlbumOfDayCompanion({ - this.albumId = const Value.absent(), - this.milliSecSinceEpoch = const Value.absent(), - }); - MoorAlbumOfDayCompanion.insert({ - this.albumId = const Value.absent(), - required int milliSecSinceEpoch, - }) : milliSecSinceEpoch = Value(milliSecSinceEpoch); - static Insertable custom({ - Expression? albumId, - Expression? milliSecSinceEpoch, - }) { - return RawValuesInsertable({ - if (albumId != null) 'album_id': albumId, - if (milliSecSinceEpoch != null) - 'milli_sec_since_epoch': milliSecSinceEpoch, - }); - } - - MoorAlbumOfDayCompanion copyWith( - {Value? albumId, Value? milliSecSinceEpoch}) { - return MoorAlbumOfDayCompanion( - albumId: albumId ?? this.albumId, - milliSecSinceEpoch: milliSecSinceEpoch ?? this.milliSecSinceEpoch, - ); - } - - @override - Map toColumns(bool nullToAbsent) { - final map = {}; - if (albumId.present) { - map['album_id'] = Variable(albumId.value); - } - if (milliSecSinceEpoch.present) { - map['milli_sec_since_epoch'] = Variable(milliSecSinceEpoch.value); - } - return map; - } - - @override - String toString() { - return (StringBuffer('MoorAlbumOfDayCompanion(') - ..write('albumId: $albumId, ') - ..write('milliSecSinceEpoch: $milliSecSinceEpoch') - ..write(')')) - .toString(); - } -} - -class $MoorAlbumOfDayTable extends MoorAlbumOfDay - with TableInfo<$MoorAlbumOfDayTable, MoorAlbumOfDayData> { - @override - final GeneratedDatabase attachedDatabase; - final String? _alias; - $MoorAlbumOfDayTable(this.attachedDatabase, [this._alias]); - final VerificationMeta _albumIdMeta = const VerificationMeta('albumId'); - @override - late final GeneratedColumn albumId = GeneratedColumn( - 'album_id', aliasedName, false, - type: const IntType(), requiredDuringInsert: false); - final VerificationMeta _milliSecSinceEpochMeta = - const VerificationMeta('milliSecSinceEpoch'); - @override - late final GeneratedColumn milliSecSinceEpoch = GeneratedColumn( - 'milli_sec_since_epoch', aliasedName, false, - type: const IntType(), requiredDuringInsert: true); - @override - List get $columns => [albumId, milliSecSinceEpoch]; - @override - String get aliasedName => _alias ?? 'moor_album_of_day'; - @override - String get actualTableName => 'moor_album_of_day'; - @override - VerificationContext validateIntegrity(Insertable instance, - {bool isInserting = false}) { - final context = VerificationContext(); - final data = instance.toColumns(true); - if (data.containsKey('album_id')) { - context.handle(_albumIdMeta, - albumId.isAcceptableOrUnknown(data['album_id']!, _albumIdMeta)); - } - if (data.containsKey('milli_sec_since_epoch')) { - context.handle( - _milliSecSinceEpochMeta, - milliSecSinceEpoch.isAcceptableOrUnknown( - data['milli_sec_since_epoch']!, _milliSecSinceEpochMeta)); - } else if (isInserting) { - context.missing(_milliSecSinceEpochMeta); - } - return context; - } - - @override - Set get $primaryKey => {albumId}; - @override - MoorAlbumOfDayData map(Map data, {String? tablePrefix}) { - return MoorAlbumOfDayData.fromData(data, - prefix: tablePrefix != null ? '$tablePrefix.' : null); - } - - @override - $MoorAlbumOfDayTable createAlias(String alias) { - return $MoorAlbumOfDayTable(attachedDatabase, alias); - } -} - class MoorSmartList extends DataClass implements Insertable { final int id; final String name; @@ -3884,7 +3704,6 @@ abstract class _$MoorDatabase extends GeneratedDatabase { late final $AvailableSongEntriesTable availableSongEntries = $AvailableSongEntriesTable(this); late final $SongsTable songs = $SongsTable(this); - late final $MoorAlbumOfDayTable moorAlbumOfDay = $MoorAlbumOfDayTable(this); late final $SmartListsTable smartLists = $SmartListsTable(this); late final $SmartListArtistsTable smartListArtists = $SmartListArtistsTable(this); @@ -3908,7 +3727,6 @@ abstract class _$MoorDatabase extends GeneratedDatabase { queueEntries, availableSongEntries, songs, - moorAlbumOfDay, smartLists, smartListArtists, playlists, diff --git a/lib/system/models/album_model.dart b/lib/system/models/album_model.dart index df3bf6d..73f446d 100644 --- a/lib/system/models/album_model.dart +++ b/lib/system/models/album_model.dart @@ -79,4 +79,8 @@ class AlbumOfDay { final AlbumModel albumModel; final DateTime date; + + String toJSON() { + return '{"id": ${albumModel.id}, "date": ${date.millisecondsSinceEpoch}}'; + } } diff --git a/lib/system/models/artist_model.dart b/lib/system/models/artist_model.dart index 932bb79..64ee680 100644 --- a/lib/system/models/artist_model.dart +++ b/lib/system/models/artist_model.dart @@ -34,8 +34,7 @@ class ArtistOfDay { final ArtistModel artistModel; final DateTime date; - @override - String toString() { - return '{"name": "${artistModel.name}", "date": ${date.millisecondsSinceEpoch}}'; + String toJSON() { + return '{"id": ${artistModel.id}, "date": ${date.millisecondsSinceEpoch}}'; } } \ No newline at end of file