Fixed bug => save history error.

This commit is contained in:
stonegate 2020-08-16 16:05:04 +08:00
parent fda2c2266c
commit 017e15b129
4 changed files with 236 additions and 201 deletions

View File

@ -521,7 +521,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
), ),
], ],
), ),
child: PlaylistButton()), child: _PlaylistButton()),
], ],
), ),
Container(height: 2, color: context.primaryColor), Container(height: 2, color: context.primaryColor),
@ -536,19 +536,15 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
} }
} }
class PlaylistButton extends StatefulWidget { class _PlaylistButton extends StatefulWidget {
PlaylistButton({Key key}) : super(key: key); _PlaylistButton({Key key}) : super(key: key);
@override @override
PlaylistButtonState createState() => PlaylistButtonState(); __PlaylistButtonState createState() => __PlaylistButtonState();
} }
class PlaylistButtonState extends State<PlaylistButton> { class __PlaylistButtonState extends State<_PlaylistButton> {
bool _loadPlay; bool _loadPlay;
static String _stringForSeconds(int seconds) {
if (seconds == null) return null;
return '${(seconds ~/ 60)}:${(seconds.truncate() % 60).toString().padLeft(2, '0')}';
}
_getPlaylist() async { _getPlaylist() async {
await Provider.of<AudioPlayerNotifier>(context, listen: false) await Provider.of<AudioPlayerNotifier>(context, listen: false)
@ -567,7 +563,7 @@ class PlaylistButtonState extends State<PlaylistButton> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var audio = Provider.of<AudioPlayerNotifier>(context, listen: false); var audio = context.watch<AudioPlayerNotifier>();
final s = context.s; final s = context.s;
return MyPopupMenuButton<int>( return MyPopupMenuButton<int>(
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@ -639,7 +635,7 @@ class PlaylistButtonState extends State<PlaylistButton> {
child: Column( child: Column(
children: <Widget>[ children: <Widget>[
Text( Text(
_stringForSeconds(data.item3 ~/ 1000), (data.item3 ~/ 1000).toTime,
// style: // style:
// TextStyle(color: Colors.white) // TextStyle(color: Colors.white)
), ),
@ -654,7 +650,7 @@ class PlaylistButtonState extends State<PlaylistButton> {
), ),
), ),
Divider( Divider(
height: 2, height: 1,
), ),
], ],
), ),
@ -677,6 +673,9 @@ class PlaylistButtonState extends State<PlaylistButton> {
), ),
), ),
), ),
PopupMenuDivider(
height: 1,
),
PopupMenuItem( PopupMenuItem(
value: 2, value: 2,
child: Container( child: Container(
@ -692,6 +691,9 @@ class PlaylistButtonState extends State<PlaylistButton> {
), ),
), ),
), ),
PopupMenuDivider(
height: 1,
),
], ],
onSelected: (value) { onSelected: (value) {
if (value == 0) { if (value == 0) {
@ -757,14 +759,14 @@ class _RecentUpdateState extends State<_RecentUpdate>
await Future.delayed(Duration(seconds: 3)); await Future.delayed(Duration(seconds: 3));
if (mounted) { if (mounted) {
setState(() { setState(() {
_top = _top + 33; _top = _top + 30;
_loadMore = false; _loadMore = false;
}); });
} }
} }
/// Episodes loaded first time. /// Episodes loaded first time.
int _top = 99; int _top = 90;
/// Load more episodes when scroll to bottom. /// Load more episodes when scroll to bottom.
bool _loadMore; bool _loadMore;
@ -822,7 +824,9 @@ class _RecentUpdateState extends State<_RecentUpdate>
if (scrollInfo.metrics.pixels == if (scrollInfo.metrics.pixels ==
scrollInfo.metrics.maxScrollExtent && scrollInfo.metrics.maxScrollExtent &&
snapshot.data.length == _top) { snapshot.data.length == _top) {
_loadMoreEpisode(); if (!_loadMore) {
_loadMoreEpisode();
}
} }
return true; return true;
}, },
@ -1060,13 +1064,13 @@ class _MyFavoriteState extends State<_MyFavorite>
await Future.delayed(Duration(seconds: 3)); await Future.delayed(Duration(seconds: 3));
if (mounted) { if (mounted) {
setState(() { setState(() {
_top = _top + 33; _top = _top + 30;
_loadMore = false; _loadMore = false;
}); });
} }
} }
int _top = 99; int _top = 90;
bool _loadMore; bool _loadMore;
Layout _layout; Layout _layout;
int _sortBy; int _sortBy;
@ -1110,7 +1114,9 @@ class _MyFavoriteState extends State<_MyFavorite>
if (scrollInfo.metrics.pixels == if (scrollInfo.metrics.pixels ==
scrollInfo.metrics.maxScrollExtent && scrollInfo.metrics.maxScrollExtent &&
snapshot.data.length == _top) { snapshot.data.length == _top) {
_loadMoreEpisode(); if (!_loadMore) {
_loadMoreEpisode();
}
} }
return true; return true;
}, },

View File

@ -1,5 +1,3 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
@ -515,6 +513,9 @@ class _HistoryList extends StatefulWidget {
class __HistoryListState extends State<_HistoryList> { class __HistoryListState extends State<_HistoryList> {
var dbHelper = DBHelper(); var dbHelper = DBHelper();
bool _loadMore = false;
Future _getData;
Future<List<PlayHistory>> getPlayRecords(int top) async { Future<List<PlayHistory>> getPlayRecords(int top) async {
List<PlayHistory> playHistory; List<PlayHistory> playHistory;
playHistory = await dbHelper.getPlayRecords(top); playHistory = await dbHelper.getPlayRecords(top);
@ -527,208 +528,237 @@ class __HistoryListState extends State<_HistoryList> {
_loadMoreData() async { _loadMoreData() async {
if (mounted) { if (mounted) {
setState(() { setState(() {
_top = _top + 20; _loadMore = true;
});
}
await Future.delayed(Duration(milliseconds: 500));
_top = _top + 20;
if (mounted) {
setState(() {
_getData = getPlayRecords(_top);
_loadMore = false;
}); });
} }
} }
int _top = 20; int _top;
@override
void initState() {
super.initState();
_top = 20;
_getData = getPlayRecords(_top);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final s = context.s; final s = context.s;
final audio = context.watch<AudioPlayerNotifier>(); final audio = context.watch<AudioPlayerNotifier>();
return FutureBuilder<List<PlayHistory>>( return FutureBuilder<List<PlayHistory>>(
future: getPlayRecords(_top), future: _getData,
builder: (context, snapshot) { builder: (context, snapshot) {
return snapshot.hasData return snapshot.hasData
? NotificationListener<ScrollNotification>( ? NotificationListener<ScrollNotification>(
onNotification: (scrollInfo) { onNotification: (scrollInfo) {
if (scrollInfo.metrics.pixels == if (scrollInfo.metrics.pixels ==
scrollInfo.metrics.maxScrollExtent && scrollInfo.metrics.maxScrollExtent &&
snapshot.data.length == _top) _loadMoreData(); snapshot.data.length == _top) {
if (!_loadMore) {
_loadMoreData();
}
}
return true; return true;
}, },
child: ListView.builder( child: ListView.builder(
scrollDirection: Axis.vertical, scrollDirection: Axis.vertical,
itemCount: snapshot.data.length, itemCount: snapshot.data.length + 1,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final seekValue = snapshot.data[index].seekValue; if (index == snapshot.data.length) {
final seconds = snapshot.data[index].seconds; return SizedBox(
final date = snapshot height: 2,
.data[index].playdate.millisecondsSinceEpoch; child: _loadMore
final episode = snapshot.data[index].episode; ? LinearProgressIndicator()
var c = : Center());
(Theme.of(context).brightness == Brightness.light) } else {
? episode.primaryColor.colorizedark() final seekValue = snapshot.data[index].seekValue;
: episode.primaryColor.colorizeLight(); final seconds = snapshot.data[index].seconds;
return SizedBox( final date = snapshot
height: 90.0, .data[index].playdate.millisecondsSinceEpoch;
child: Column( final episode = snapshot.data[index].episode;
mainAxisAlignment: MainAxisAlignment.spaceAround, final c = episode.backgroudColor(context);
children: [ return SizedBox(
Expanded( height: 90.0,
child: Center( child: Column(
child: ListTile( mainAxisAlignment: MainAxisAlignment.spaceAround,
contentPadding: children: [
EdgeInsets.fromLTRB(24, 8, 20, 8), Expanded(
onTap: () => audio.episodeLoad(episode), child: Center(
leading: CircleAvatar( child: ListTile(
backgroundColor: c.withOpacity(0.5), contentPadding:
backgroundImage: episode.avatarImage), EdgeInsets.fromLTRB(24, 8, 20, 8),
title: Padding( onTap: () => audio.episodeLoad(episode),
padding: leading: CircleAvatar(
EdgeInsets.symmetric(vertical: 5.0), backgroundColor: c.withOpacity(0.5),
child: Text( backgroundImage: episode.avatarImage),
snapshot.data[index].title, title: Padding(
maxLines: 1, padding:
overflow: TextOverflow.ellipsis, EdgeInsets.symmetric(vertical: 5.0),
child: Text(
snapshot.data[index].title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
), ),
), subtitle: Container(
subtitle: Container( height: 35,
height: 35, child: Row(
child: Row( mainAxisAlignment:
mainAxisAlignment: MainAxisAlignment.start,
MainAxisAlignment.start, crossAxisAlignment:
crossAxisAlignment: CrossAxisAlignment.center,
CrossAxisAlignment.center, children: <Widget>[
children: <Widget>[ if (seekValue < 0.9)
if (seekValue < 0.9) Padding(
Padding( padding:
padding: const EdgeInsets.symmetric(
const EdgeInsets.symmetric( vertical: 5.0),
vertical: 5.0), child: Material(
child: Material( color: Colors.transparent,
color: Colors.transparent, child: InkWell(
child: InkWell( onTap: () async {
onTap: () async { audio.episodeLoad(episode,
audio.episodeLoad(episode, startPosition:
startPosition: (seconds * 1000)
(seconds * 1000) .toInt());
.toInt()); },
}, child: Stack(children: [
child: Stack(children: [ ShaderMask(
ShaderMask( shaderCallback:
shaderCallback: (bounds) { (bounds) {
return LinearGradient( return LinearGradient(
begin: Alignment begin: Alignment
.centerLeft, .centerLeft,
colors: <Color>[ colors: <Color>[
Colors.cyan[600] Colors.cyan[600]
.withOpacity( .withOpacity(
0.8), 0.8),
Colors.white70 Colors.white70
], ],
stops: [ stops: [
seekValue, seekValue,
seekValue seekValue
], ],
tileMode: tileMode:
TileMode.mirror, TileMode.mirror,
).createShader(bounds); ).createShader(
}, bounds);
child: Container( },
height: 25, child: Container(
alignment: height: 25,
Alignment.center, alignment:
padding: EdgeInsets Alignment.center,
.symmetric( padding: EdgeInsets
horizontal: 20), .symmetric(
decoration: horizontal:
BoxDecoration( 20),
borderRadius: decoration:
BorderRadius.all( BoxDecoration(
Radius borderRadius:
.circular( BorderRadius.all(
20.0)), Radius.circular(
color: context 20.0)),
.accentColor, color: context
), .accentColor,
child: Text( ),
seconds.toTime, child: Text(
style: TextStyle( seconds.toTime,
color: style: TextStyle(
Colors.white), color: Colors
.white),
),
), ),
), ),
), ]),
]), ),
), ),
), ),
SizedBox(
child: Selector<
AudioPlayerNotifier,
Tuple2<List<EpisodeBrief>,
bool>>(
selector: (_, audio) => Tuple2(
audio.queue.playlist,
audio.queueUpdate),
builder: (_, data, __) {
return data.item1
.contains(episode)
? IconButton(
icon: Icon(
Icons
.playlist_add_check,
color: context
.accentColor),
onPressed: () async {
audio
.delFromPlaylist(
episode);
Fluttertoast
.showToast(
msg: s
.toastRemovePlaylist,
gravity:
ToastGravity
.BOTTOM,
);
})
: IconButton(
icon: Icon(
Icons
.playlist_add,
color: Colors
.grey[700]),
onPressed: () async {
audio.addToPlaylist(
episode);
Fluttertoast
.showToast(
msg: s
.toastAddPlaylist,
gravity:
ToastGravity
.BOTTOM,
);
});
},
),
), ),
SizedBox( Spacer(),
child: Selector< Text(
AudioPlayerNotifier, date.toDate(context),
Tuple2<List<EpisodeBrief>, style: TextStyle(
bool>>( fontSize: 15,
selector: (_, audio) => Tuple2( ),
audio.queue.playlist,
audio.queueUpdate),
builder: (_, data, __) {
return data.item1
.contains(episode)
? IconButton(
icon: Icon(
Icons
.playlist_add_check,
color: context
.accentColor),
onPressed: () async {
audio.delFromPlaylist(
episode);
Fluttertoast
.showToast(
msg: s
.toastRemovePlaylist,
gravity:
ToastGravity
.BOTTOM,
);
})
: IconButton(
icon: Icon(
Icons.playlist_add,
color: Colors
.grey[700]),
onPressed: () async {
audio.addToPlaylist(
episode);
Fluttertoast
.showToast(
msg: s
.toastAddPlaylist,
gravity:
ToastGravity
.BOTTOM,
);
});
},
), ),
), ],
Spacer(), ),
Text(
date.toDate(context),
style: TextStyle(
fontSize: 15,
),
),
],
), ),
), ),
), ),
), ),
), Divider(height: 1)
Divider(height: 1) ],
], ),
), );
); }
}), }),
) )
: Center( : Center(
child: SizedBox( child: SizedBox(
height: 25, height: 25,
width: 25, width: 25,
child: CircularProgressIndicator()), child: CircularProgressIndicator(
strokeWidth: 2,
)),
); );
}); });
} }

View File

@ -235,30 +235,27 @@ class DBHelper {
[_milliseconds, 1, id]); [_milliseconds, 1, id]);
} }
Future<int> saveHistory(PlayHistory history) async { Future<void> saveHistory(PlayHistory history) async {
var dbClient = await database; var dbClient = await database;
var _milliseconds = DateTime.now().millisecondsSinceEpoch; final milliseconds = DateTime.now().millisecondsSinceEpoch;
var recent = await getPlayHistory(1); var recent = await getPlayHistory(1);
if (recent.length == 1) { if (recent.isNotEmpty && recent.first.url == history.url) {
if (recent.first.url == history.url) { await dbClient.rawDelete("DELETE FROM PlayHistory WHERE add_date = ?",
await dbClient.rawDelete("DELETE FROM PlayHistory WHERE add_date = ?", [recent.first.playdate.millisecondsSinceEpoch]);
[recent.first.playdate.millisecondsSinceEpoch]);
}
} }
var result = await dbClient.transaction((txn) async { await dbClient.transaction((txn) async {
return await txn.rawInsert( return await txn.rawInsert(
"""REPLACE INTO PlayHistory (title, enclosure_url, seconds, seek_value, add_date, listen_time) """INSERT INTO PlayHistory (title, enclosure_url, seconds, seek_value, add_date, listen_time)
VALUES (?, ?, ?, ?, ?, ?) """, VALUES (?, ?, ?, ?, ?, ?) """,
[ [
history.title, history.title,
history.url, history.url,
history.seconds, history.seconds,
history.seekValue, history.seekValue,
_milliseconds, milliseconds,
history.seekValue > 0.95 ? 1 : 0 history.seekValue > 0.95 ? 1 : 0
]); ]);
}); });
return result;
} }
Future<List<PlayHistory>> getPlayHistory(int top) async { Future<List<PlayHistory>> getPlayHistory(int top) async {

View File

@ -267,7 +267,7 @@ class AudioPlayerNotifier extends ChangeNotifier {
await lastWorkStorage.saveInt(0); await lastWorkStorage.saveInt(0);
} }
playlistLoad() async { Future<void> playlistLoad() async {
await _queue.getPlaylist(); await _queue.getPlaylist();
_backgroundAudioDuration = 0; _backgroundAudioDuration = 0;
_backgroundAudioPosition = 0; _backgroundAudioPosition = 0;
@ -442,11 +442,13 @@ class AudioPlayerNotifier extends ChangeNotifier {
await dbHelper.saveHistory(history); await dbHelper.saveHistory(history);
} }
if (event is Map && event['playerRunning'] == false) { if (event is Map && event['playerRunning'] == false) {
_playerRunning = false; if (_playerRunning) {
notifyListeners(); _playerRunning = false;
final history = PlayHistory(_episode.title, _episode.enclosureUrl, notifyListeners();
_lastPostion ~/ 1000, _seekSliderValue); final history = PlayHistory(_episode.title, _episode.enclosureUrl,
await dbHelper.saveHistory(history); _lastPostion ~/ 1000, _seekSliderValue);
await dbHelper.saveHistory(history);
}
} }
}); });