Skip to content

Commit

Permalink
Improved queue history loading, added loading indication.
Browse files Browse the repository at this point in the history
  • Loading branch information
Komodo5197 committed Dec 5, 2023
1 parent bd88212 commit c2bfcd7
Show file tree
Hide file tree
Showing 6 changed files with 396 additions and 91 deletions.
79 changes: 74 additions & 5 deletions lib/components/now_playing_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,73 @@ class NowPlayingBar extends ConsumerWidget {
Key? key,
}) : super(key: key);

Widget loadingQueueBar(BuildContext context, WidgetRef ref) {
const elevation = 16.0;
const horizontalPadding = 8.0;
const albumImageSize = 70.0;

// TODO get these colors working well
// maybe move streambuilder so we aren't in theme context?
// TODO disable ability to click now playing by moving out of gesture widget

return Padding(
padding: const EdgeInsets.only(left: 12.0, bottom: 12.0, right: 12.0),
child: Material(
shadowColor: Theme.of(context).colorScheme.primary.withOpacity(0.75),
borderRadius: BorderRadius.circular(12.0),
clipBehavior: Clip.antiAlias,
color: Theme.of(context).brightness == Brightness.dark
? IconTheme.of(context).color!.withOpacity(0.1)
: Theme.of(context).cardColor,
elevation: elevation,
child: SafeArea(
child: Container(
width: MediaQuery.of(context).size.width,
height: albumImageSize,
padding: EdgeInsets.zero,
child: Container(
clipBehavior: Clip.antiAlias,
decoration: ShapeDecoration(
color: Color.alphaBlend(
Theme.of(context).brightness == Brightness.dark
? IconTheme.of(context).color!.withOpacity(0.35)
: IconTheme.of(context).color!.withOpacity(0.5),
Theme.of(context).brightness == Brightness.dark
? Colors.black
: Colors.white),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
width: albumImageSize,
height: albumImageSize,
decoration: const ShapeDecoration(
shape: Border(),
color: Color.fromRGBO(0, 0, 0, 0.3),
),
// TODO add some sort of loading spinner here
child: Icon(TablerIcons.player_pause)),
Expanded(
child: Container(
height: albumImageSize,
padding: const EdgeInsets.only(left: 12, right: 4),
alignment: Alignment.centerLeft,
child: Text(AppLocalizations.of(context)!.queueLoadingMessage)),
),
],
),
),
)),
),
);
}

@override
Widget build(BuildContext context, WidgetRef ref) {
// BottomNavBar's default elevation is 8 (https://api.flutter.dev/flutter/material/BottomNavigationBar/elevation.html)
Expand Down Expand Up @@ -69,7 +136,11 @@ class NowPlayingBar extends ConsumerWidget {
child: StreamBuilder<FinampQueueInfo?>(
stream: queueService.getQueueStream(),
builder: (context, snapshot) {
if (snapshot.hasData && snapshot.data!.currentTrack != null) {
if (snapshot.hasData &&
snapshot.data!.saveState == SavedQueueState.loading) {
return loadingQueueBar(context, ref);
} else if (snapshot.hasData &&
snapshot.data!.currentTrack != null) {
final currentTrack = snapshot.data!.currentTrack!;
final currentTrackBaseItem =
currentTrack.item.extras?["itemJson"] != null
Expand All @@ -80,10 +151,8 @@ class NowPlayingBar extends ConsumerWidget {
padding: const EdgeInsets.only(
left: 12.0, bottom: 12.0, right: 12.0),
child: Material(
shadowColor: Theme.of(context)
.colorScheme
.primary
.withOpacity(0.75),
shadowColor:
Theme.of(context).colorScheme.primary.withOpacity(0.75),
borderRadius: BorderRadius.circular(12.0),
clipBehavior: Clip.antiAlias,
color: Theme.of(context).brightness == Brightness.dark
Expand Down
49 changes: 37 additions & 12 deletions lib/components/queue_restore_tile.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import 'package:finamp/models/jellyfin_models.dart';
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:hive/hive.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:intl/intl.dart';

import '../models/finamp_models.dart';
import '../services/queue_service.dart';
import 'album_image.dart';
import 'error_snackbar.dart';

class QueueRestoreTile extends StatelessWidget {
Expand All @@ -13,24 +17,45 @@ class QueueRestoreTile extends StatelessWidget {

@override
Widget build(BuildContext context) {
final _queuesBox = Hive.box<FinampStorableQueueInfo>("Queues");
final _queueService = GetIt.instance<QueueService>();
final queuesBox = Hive.box<FinampStorableQueueInfo>("Queues");
final queueService = GetIt.instance<QueueService>();
int itemCount = info.queue.length + info.nextUp.length + ((info.currentTrack == null)?0:1);
Future<BaseItemDto?> track = (info.currentTrack == null)?Future.value(null):queueService.getTrackFromId(info.currentTrack!);

return ListTile(
// TODO attempt to load current track album cover here
title: Text('Queue contaning $itemCount songs.'),
subtitle: Text('Created '+DateTime.fromMillisecondsSinceEpoch(info.creation).toString()),// TODO format date better?
title: Text(AppLocalizations.of(context)!.queueRestoreTitle(
DateTime.fromMillisecondsSinceEpoch(info.creation)
)),
leading: FutureBuilder<BaseItemDto?>(
future: track,
builder: (context, snapshot) => AlbumImage(item: snapshot.data)
),
isThreeLine: true,
subtitle: FutureBuilder<BaseItemDto?>(
future: track,
initialData: null,
builder: (context, snapshot) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children:((snapshot.data?.name == null)?<Text>[]:[// exclude subtitle line 1 if song name is null
Text(
AppLocalizations.of(context)!.queueRestoreSubtitle1(snapshot.data!.name!),
overflow: TextOverflow.ellipsis
)])+[
Text(AppLocalizations.of(context)!.queueRestoreSubtitle2(itemCount))
]
)
),
trailing: IconButton(
icon: const Icon(Icons.delete),//TODO change to button with word restore or better icon
onPressed: () async {
var latest = _queuesBox.get("latest");
icon: const Icon(Icons.arrow_circle_right_outlined),
onPressed: () {
var latest = queuesBox.get("latest");
if ( latest != null ){
await _queuesBox.put(latest.creation.toString(), latest);
queuesBox.put(latest.creation.toString(), latest);
}
await _queueService.loadSavedQueue(info).catchError((x) => errorSnackbar(x,context));
// TODO add some sort of loading spinner on click
Navigator.of(context).pop();
BuildContext parentcontext = Navigator.of(context).context;
queueService.loadSavedQueue(info).catchError((x) => errorSnackbar(x,parentcontext));
Navigator.of(context).popUntil((route) => route.isFirst && ! route.willHandlePopInternally);
}),
);
}
Expand Down
37 changes: 37 additions & 0 deletions lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -605,5 +605,42 @@
"queuesScreen": "Restore Now Playing",
"@queuesScreen": {
"description": "Title for the screen where older now playing queues can be restored"
},
"queueRestoreButtonLabel": "Restore",
"@queueRestoreButtonLabel": {
"description": "Button to restore archived now playing queue, overwriting current queue"
},
"queueRestoreTitle": "Saved {date}",
"@queueRestoreTitle": {
"description": "Description of when a saved queue was saved",
"placeholders": {
"date" : {
"type": "DateTime",
"format": "yyy-MM-dd hh:mm:ss",
"isCustomDateFormat": "true"
}
}
},
"queueRestoreSubtitle1": "{song, select, finamp_null{Could not load track.} other{Playing: {song}}}",
"@queueRestoreSubtitle1": {
"description": "Description of playing song in a saved queue",
"placeholders": {
"song": {
"type": "String"
}
}
},
"queueRestoreSubtitle2": "{count,plural,=1{1 Song} other{{count} Songs}}",
"@queueRestoreSubtitle2": {
"description": "Description of length of a saved queue",
"placeholders": {
"count": {
"type": "int"
}
}
},
"queueLoadingMessage": "Loading saved Queue...",
"@queueLoadingMessage": {
"description": "Message displayed on now-playing bar when a saved queue is loading."
}
}
16 changes: 16 additions & 0 deletions lib/models/finamp_models.dart
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,7 @@ class FinampQueueInfo {
required this.nextUp,
required this.queue,
required this.source,
required this.saveState,
});

@HiveField(0)
Expand All @@ -781,6 +782,9 @@ class FinampQueueInfo {

@HiveField(4)
QueueItemSource source;

@HiveField(5)
SavedQueueState saveState;
}

@HiveType(typeId: 60)
Expand Down Expand Up @@ -844,3 +848,15 @@ class FinampStorableQueueInfo {
return "previous:$previousTracks current:$currentTrack seek:$currentTrackSeek next:$nextUp queue:$queue";
}
}

@HiveType(typeId: 62)
enum SavedQueueState {
@HiveField(0)
preInit,
@HiveField(1)
init,
@HiveField(2)
loading,
@HiveField(3)
saving
}
Loading

0 comments on commit c2bfcd7

Please sign in to comment.